0

I run a chat server that uses Atmosphere as the framework for websocket support. Atmosphere uses Jersey because I use the atmosphere-jersey maven dependency.

The chat server runs on Tomcat 7 and we are upgrading to Tomcat 8.

One of our clients uses Tyrus as their Java websocket API.

When our chat server is deployed on Tomcat 7 (with Java 6,7, or 8), there are no issues.

With our chat server on Tomcat 8 (Java 8), our testing client that uses Atmosphere works fine but the client that uses Tyrus does not. The error that we see is:

2017-01-17 02:36:51,114 ERROR () [http-nio-8443-exec-11] ReflectorServletProcessor (?) - onRequest()
java.lang.IllegalArgumentException: Schema specific part is opaque
        at com.sun.jersey.api.uri.UriBuilderImpl.checkSsp(UriBuilderImpl.java:529)
        at com.sun.jersey.api.uri.UriBuilderImpl.replacePath(UriBuilderImpl.java:256)
        at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:703)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
        at org.atmosphere.util.AtmosphereFilterChain.doFilter(AtmosphereFilterChain.java:135)
        at org.atmosphere.util.AtmosphereFilterChain.invokeFilterChain(AtmosphereFilterChain.java:96)
        at org.atmosphere.handler.ReflectorServletProcessor$FilterChainServletWrapper.service(ReflectorServletProcessor.java:317)
        at org.atmosphere.handler.ReflectorServletProcessor.onRequest(ReflectorServletProcessor.java:160)
        at org.atmosphere.cpr.AsynchronousProcessor.action(AsynchronousProcessor.java:199)
        at org.atmosphere.cpr.AsynchronousProcessor.suspended(AsynchronousProcessor.java:107)
        at org.atmosphere.container.Servlet30CometSupport.service(Servlet30CometSupport.java:66)
        at org.atmosphere.cpr.AtmosphereFramework.doCometSupport(AtmosphereFramework.java:2078)
        at org.atmosphere.websocket.DefaultWebSocketProcessor.dispatch(DefaultWebSocketProcessor.java:571)
        at org.atmosphere.websocket.DefaultWebSocketProcessor$3.run(DefaultWebSocketProcessor.java:333)
        at org.atmosphere.util.VoidExecutorService.execute(VoidExecutorService.java:101)
        at org.atmosphere.websocket.DefaultWebSocketProcessor.dispatch(DefaultWebSocketProcessor.java:328)
        at org.atmosphere.websocket.DefaultWebSocketProcessor.invokeWebSocketProtocol(DefaultWebSocketProcessor.java:525)
        at org.atmosphere.websocket.DefaultWebSocketProcessor.invokeWebSocketProtocol(DefaultWebSocketProcessor.java:428)
        at org.atmosphere.container.JSR356Endpoint$1.onMessage(JSR356Endpoint.java:213)
        at org.atmosphere.container.JSR356Endpoint$1.onMessage(JSR356Endpoint.java:210)
        at org.apache.tomcat.websocket.WsFrameBase.sendMessageText(WsFrameBase.java:393)
        at org.apache.tomcat.websocket.WsFrameBase.processDataText(WsFrameBase.java:494)
        at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:289)
        at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:130)
        at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:60)
        at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler$WsReadListener.onDataAvailable(WsHttpUpgradeHandler.java:183)
        at org.apache.coyote.http11.upgrade.AbstractServletInputStream.onDataAvailable(AbstractServletInputStream.java:198)
        at org.apache.coyote.http11.upgrade.AbstractProcessor.upgradeDispatch(AbstractProcessor.java:96)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:669)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1456)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:745)

This occurs because with Tomcat 7, the request URL from the websocket request is seen as similar to "http:// localhost:8080/chat/" (The request is made to ws://localhost:8080/chat/ but it's changed to http:// somewhere). But with Tomcat 8, the request URL from the websocket request is similar to "/chat/" and the schema is not there and Jersey throws an error.

To combat this, Atmosphere looks for the header "origin" which has the schema when an Atmosphere client makes a request. But when a Tyrus client makes a request, that "origin" header does not include the http schema resulting in the error shown above.

I am wondering if anyone has any ideas for a simple solution to this or have come across a similar issue. I've done a lot of searching for a simple solution but have not found one yet. Without one, we are looking at having to stop using Atmosphere and use something else which is a lot of code refactoring.

These are some links to ticket/bugs around this issue that would seem to have addressed this yet I have the correct versions and still have issues.

https://bz.apache.org/bugzilla/show_bug.cgi?id=56573 https://github.com/Atmosphere/atmosphere/issues/1839

Atmosphere version: 2.4.8

Jersey version (via atmosphere): 1.19

Tyrus version: 1.13

Zack
  • 13
  • 5

1 Answers1

0

The workaround for this was to alter the "origin" header through a Filter and add the http:// schema if it did not exist. This article helped with figuring it out: Modify request parameter with servlet filter

import java.io.IOException;
import java.util.Collections;
import java.util.Enumeration;
import javax.servlet.*;

public class FixSchemaFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void destroy() {}

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        if (request instanceof HttpServletRequest) {
            chain.doFilter(new FixSchemaHttpServletRequest((HttpServletRequest) request), response);
        } else {
            chain.doFilter(request, response);
        }
    }

    public class FixSchemaHttpServletRequest extends HttpServletRequestWrapper {

        public FixSchemaHttpServletRequest(HttpServletRequest request) {
            super(request);
        }

        @Override
        public String getHeader(String header) {
            String value = super.getHeader(header);
            if (header.equalsIgnoreCase("origin") && !value.startsWith("http://")) {
                return "http://" + value;
            }
            return value;
        }

        @Override
        public Enumeration getHeaders(String header) {
            Enumeration enumeration = super.getHeaders(header);
            if (header.equalsIgnoreCase("origin") && super.getHeader(header) != null && !super.getHeader(header).startsWith("http://")) {
                return Collections.enumeration(Collections.singleton(getHeader(header)));
            }
            return enumeration;
        }
    }

}

Then add the filter to the web.xml

<filter>
        <filter-name>FixSchemaFilter</filter-name>
        <filter-class>com.example.FixSchemaFilter</filter-class>
</filter>
<filter-mapping>
        <filter-name>FixSchemaFilter</filter-name>
        <url-pattern>/*</url-pattern>
</filter-mapping>
Community
  • 1
  • 1
Zack
  • 13
  • 5