Archive

Archive for May 16, 2012

Safari’s WebSocket implementation and Java: Problematic!

The current Safari version (~5.1.5 … on OS X and iOS) implements an old version of the WebSockets specifications. This old version can  cause major issues with Java WebServer  in production. Here is some recommendations to workaround Safari. Important note: my observation are based on large deployments using the Atmosphere Framework.

First, let’s take a look at Java WebServers supporting WebSockets

WebServers Version Specification Safari Stability
Tomcat 7.0.27 and up hybi-13 and up NOT SUPPORTED
Jetty 7.0 to 7.4.5 Up to hybi-12 UNSTABLE: Server suffer High CPU when Safari’s WebSocket connection get closed.
Jetty 7.5.x to 7.6.2 Up to hybi-12 UNSTABLE: Server suffer High CPU when Safari’s WebSocket connection get closed.
Jetty 7.5.x to 7.6.2 Up to hybi-13 UNSTABLE: Server suffer High CPU when Safari’s WebSocket connection get closed.
Jetty 8.x to 8.1.2 Up to hybi-13 UNSTABLE: Server suffer High CPU when Safari’s WebSocket connection get closed.
Jetty 7.6.3 All hybi version STABLE
Jetty 8.1.3 All hybi version STABLE
GlassFish 3.1.1 All hybi version UNSTABLE: Suffer many API bugs
GlassFish 3.1.2 All hybi version STABLE
NettoSphere (based on Netty Framework) 1.x All hybi version STABLE

My recommendation is if you need to put a WebSocket application in production, use Jetty 7.6.3 or 8.1.3. GlassFish is also a good server but much more heavyweight to use if you are planning to write pure WebSocket applications. NettoSphere is fairly new and until Atmosphere 1.0.0 is released, I’m not yet recommending it (yet!). Note that the Netty Framework’s WebSocket implementation can be considered a STABLE as well, but to run Atmosphere on top of it you need NettoSphere.

Now if you can’t any of the stable WebServer, you can still use WebSockets. All you need to do is to write a Servlet’s Filter that will detect the WebSocket version and force Safari to downgrade to another “transport” or communication channel. Server Sides Events, long-polling, http streaming, polling or JSONP can then be used to reconnect. You just need to implement the reconnect inside your websocket#onClose function. With Atmosphere JQuery PlugIn, the reconnect is done transparently, e./g no special code needed. The Atmosphere Filter looks like:

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        String draft = filterConfig
            .getInitParameter(ApplicationConfig.WEB_SOCKET_BANNED_VERSION);
        if (draft != null) {
            bannedVersion = draft.split(",");
            logger.debug("Blocked WebSocket Draft version {}", draft);
        }
    }

    @Override
    public void doFilter(ServletRequest request, 
                         ServletResponse response, 
                         FilterChain chain) 
        throws IOException, ServletException {

        HttpServletRequest r = HttpServletRequest.class.cast(request);
        if (Utils.webSocketEnabled(r)) {
            int draft =r.getIntHeader("Sec-WebSocket-Version");
            if (draft < 0) {
                draft = r.getIntHeader("Sec-WebSocket-Draft");
            }

            if (bannedVersion != null) {
                for (String s : bannedVersion) {
                    if (Integer.parseInt(s) == draft) {
                       HttpServletResponse.class.cast(response) 
                           .sendError(501, "Websocket protocol not supported"); 
                       return;
                    }
                }
            }
        }
        chain.doFilter(request, response);
    }

So if you aren’t using the Atmosphere Framework, make sure you have some sort of Filter than will block Safari from creating problem.

If you are planning to use WebSocket and Java, I strongly recommend you look at Atmosphere instead of using private native API and get stuck on a server forever. For more information, ping me on Twitter!

Advertisements