Safari and WebSockets Issues - Atmosphere/atmosphere GitHub Wiki

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.

First, let's take a look at Atmospehre's supported WebSockets Server

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

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. Server Sides Events, long-polling, http streaming, JSONP or polling can then be used in that scenario to reconnect. With Atmosphere JQuery PlugIn, the reconnect is done transparently, e.g the Safari 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);
    }

All you need to define, in web.xml, the version you don't want to support

    <description>Atmosphere Chat</description>
    <display-name>Atmosphere Chat</display-name>
    <servlet>
        <description>AtmosphereServlet</description>
        <servlet-name>AtmosphereServlet</servlet-name>
        <servlet-class>org.atmosphere.cpr.AtmosphereServlet</servlet-class>
        <init-param>
          <param-name>org.atmosphere.websocket.bannedVersion</param-name>
          <param-value>-1</param-value>
        </init-param>
        <!-- Use it with mvn jetty:run -->
        <load-on-startup>0</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>
    <servlet-mapping>
        <servlet-name>AtmosphereServlet</servlet-name>
	<url-pattern>/*</url-pattern>
    </servlet-mapping>
    <filter>
        <filter-name>WebSocket</filter-name>
        <filter-class>org.atmosphere.websocket.WebSocketHandshakeFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>WebSocket</filter-name>
        <servlet-name>AtmosphereServlet</servlet-name>
    </filter-mapping>
⚠️ **GitHub.com Fallback** ⚠️