Jetty9 WebSocketとChromeのバグ

サンプル を参考に実装してみたけど、いくつかメッセージを送受信するとエラーがずらっと。

Oct 22 23:58:29 [qtp1685059636-58] WARN org.eclipse.jetty.websocket.core.extensions.compress.DeflateCompressionMethod$InflaterProcess:
...
org.eclipse.jetty.websocket.core.api.BadPayloadException: java.util.zip.DataFormatException: invalid distance too far back
Caused by: java.util.zip.DataFormatException: invalid distance too far back

接続してたクライアントは Chrome 安定版(M22)だったんだけど、これに関連する Issue が、最近更新されていて、内容が Java の deflate 実装では BFINAL 1 の方で圧縮データを作成してるけど、Chrome の実装では BFINAL 0 にしか対応していなかったというもの(WebSocket の draft では BFINAL 0, 1 の両方をサポートしてる)。M24 に Fix コードが取り込まれるようだけど、そもそも deflate-frame extensionpermessage-compress extension に移行されるはずなので、WebkitDeflateFrameExtension をオフにしても問題ないと判断して、このあたり を参考に拡張を登録解除して対処した。

import org.eclipse.jetty.server._
import org.eclipse.jetty.websocket.core.api._
import org.eclipse.jetty.websocket.server._
import org.slf4j._

class MyWebSocketHandler extends WebSocketListener {

  val logger = LoggerFactory.getLogger(classOf[MyWebSocketHandler])

  def onWebSocketBinary(payload: Array[Byte], offset: Int, len: Int) {
    logger.info("WebSocket.onBinaryMessage: {}", len)
  }

  def onWebSocketClose(statusCode: Int, reason: String) {
    logger.info("onWebSocketClose: {}: {}", statusCode, reason)
  }

  def onWebSocketConnect(connection: WebSocketConnection) {
    logger.info("WebSocket.onOpen")
  }

  def onWebSocketException(error: WebSocketException) {
    error.printStackTrace
  }

  def onWebSocketText(message: String) {
    logger.info("WebSocket.onTextMessage: {}", message)
  }
}

object WebSocketServer {

  def main(args: Array[String]) {

    val server = new Server
    val connector = new ServerConnector(server)

    connector.setPort(9090)
    server.addConnector(connector)

    val handler = new WebSocketHandler() {

      override def configurePolicy(policy: WebSocketPolicy) {
        policy.setBufferSize(1024)
        policy.setIdleTimeout(30000)
        policy.setMaxTextMessageSize(1024)
        policy.setMaxBinaryMessageSize(1024)
      }

      override def configure(factory: WebSocketServerFactory) {
        factory.getExtensionRegistry.unregister("x-webkit-deflate-frame")

        factory.setCreator(new WebSocketCreator() {
          def createWebSocket(req: UpgradeRequest, resp: UpgradeResponse): Object = {
            new MyWebSocketHandler
          }
        })
      }
    }

    server.setHandler(handler)
    server.start
    server.join
  }
}

WebSocket Extension がまともに実装されてる WebSocket ライブラリはじめて見た。
permessage-compress もバッチリ実装されてる。
 
それはそうと、この拡張問題は安定版の Chrome に混入しちゃってる問題なので、今後 WebSocket の拡張に対応したサーバー(恐らく普通に対応することになるはず)を書くときは、古いバージョンの Chrome で接続されることを考慮した場合、x-webkit-deflate-frame のサポートに関して少し注意しないといけない。