title: Safari 5 / iOS, WebSocket handshake only works sometimes
body: <p>I have made a class in java that allows me to wrap an existing socket with the WebSocket protocols. I have everything working for the <a href="https://www.rfc-editor.org/rfc/rfc6455" rel="nofollow noreferrer">RFC6445</a> protocol working and everything works in chrome and FF. However Safari and iOS is using the <a href="https://datatracker.ietf.org/doc/html/draft-hixie-thewebsocketprotocol-76" rel="nofollow noreferrer">hixie76</a> / <a href="https://datatracker.ietf.org/doc/html/draft-ietf-hybi-thewebsocketprotocol-00#page-7" rel="nofollow noreferrer">HyBi00</a> protocol (according to Wikipedia).</p>
  <p>I have everything working and Safari and iOS correctly handshake and start sending/receiving messages... well, at least most of the time.</p>
  <p>About 20-30% of the time, the handshake fails and Safari closes the connection. (Java reads a -1 byte upon trying to read first frame). Safari does not report any errors in the console, but just calls the onclose event handler.</p>
  <p>Why would the handshakes only work part of the time?</p>
  <p>Here is my handshake code:</p>
  <p>Note: No exceptions are thrown and the &quot;Handshake Complete&quot; is written to the console. But then upon trying to read the first frame the connection is closed. (Java returns -1 on inst.read())</p>
  <pre><code>// Headers are read in a previous method which wraps the socket using RFC6445
  // protocol. If it detects 2 keys it will call this and pass in the headers.
  public static MessagingWebSocket wrapOldProtocol(HashMap&lt;String, String&gt; headers, PushbackInputStream pin, Socket sock) throws IOException, NoSuchAlgorithmException {
      // SPEC
      // https://datatracker.ietf.org/doc/html/draft-hixie-thewebsocketprotocol-76#page-32
      
      // Read the &quot;key3&quot; value. This is 8 random bytes after the headers.
      byte[] key3 = new byte[8];
      for ( int i=0; i&lt;key3.length; i++ ) {
          key3[i] = (byte)pin.read();
      }
      
      // Grab the two keys we need to use for the handshake
      String key1 = headers.get(&quot;Sec-WebSocket-Key1&quot;);
      String key2 = headers.get(&quot;Sec-WebSocket-Key2&quot;);
      
      // Count the spaces in both keys
      // Abort the connection is either key has 0 spaces
      int spaces1 = StringUtils.countMatches(key1, &quot; &quot;);
      int spaces2 = StringUtils.countMatches(key2, &quot; &quot;);
      if ( spaces1 == 0 || spaces2 == 0 ) {
          throw new IOException(&quot;Bad Handshake Request, Possible Cross-protocol attack&quot;);
      }
      
      // Strip all non-digit characters from each key
      // Use the remaining value as a base-10 integer.
      // Abort if either number is not a multiple of it's #spaces counterpart
      // Need to use long because the values are unsigned
      long num1 = Long.parseLong( key1.replaceAll(&quot;\\D&quot;, &quot;&quot;) );
      long num2 = Long.parseLong( key2.replaceAll(&quot;\\D&quot;, &quot;&quot;) );
      if ( !(num1 % spaces1 == 0) || !(num2 % spaces2 == 0) ) {
          throw new IOException(&quot;Bad Handshake Request. Possible non-conforming client&quot;);
      }
  
      // Part1/2 is key num divided by the # of spaces
      int part1 = (int)(num1 / spaces1);
      int part2 = (int)(num2 / spaces2);
      
      // Now calculate the challenge response
      // MD5( num1 + num2 + key3 )  ... concat, not add
      MessageDigest md = MessageDigest.getInstance(&quot;MD5&quot;);
      md.update(ByteBuffer.allocate(4).putInt(part1));
      md.update(ByteBuffer.allocate(4).putInt(part2));
      md.update(key3);
      byte[] response = md.digest();
      
      // Now build the server handshake response
      // Ignore Sec-WebSocket-Protocol (we don't use this)
      String origin = headers.get(&quot;Origin&quot;);
      String location = &quot;ws://&quot; + headers.get(&quot;Host&quot;) + &quot;/&quot;;
      
      StringBuilder sb = new StringBuilder();
      sb.append(&quot;HTTP/1.1 101 WebSocket Protocol Handshake&quot;).append(&quot;\r\n&quot;);
      sb.append(&quot;Upgrade: websocket&quot;).append(&quot;\r\n&quot;);
      sb.append(&quot;Connection: Upgrade&quot;).append(&quot;\r\n&quot;);
      sb.append(&quot;Sec-WebSocket-Origin: &quot;).append(origin).append(&quot;\r\n&quot;);
      sb.append(&quot;Sec-WebSocket-Location: &quot;).append(location).append(&quot;\r\n&quot;);
      sb.append(&quot;\r\n&quot;);
      
      // Anything left in the buffer?
      if ( pin.available() &gt; 0 ) {
          throw new IOException(&quot;Unexpected bytes after handshake!&quot;);
      }
      
      // Send the handshake &amp; challenge response
      OutputStream out = sock.getOutputStream();
      out.write(sb.toString().getBytes());
      out.write(response);
      out.flush();
      
      System.out.println(&quot;[MessagingWebSocket] Handshake Complete.&quot;);
      
      // Return the wrapper socket class.
      MessagingWebSocket ws = new MessagingWebSocket(sock);
      ws.oldProtocol = true;
      return ws;
  }
  </code></pre>
  <p>Thanks!</p>
  <p>Note: I am not looking for third-party alternatives for WebSockets such at jWebSocket, Jetty and Socket.IO. I already know about many of these.</p>