web-socket-jsのFlash接続について

web-socket-jsとは

WebSocketに対応していないブラウザに対してFlashを利用して擬似的に
WebSocket接続を行うもの。
socket.ioなどのライブラリはWebSocketに対応していないとFlashやLong Cometに置き換えるがweb-socket-jsは一貫してWebSocketを貫き通す。

ソースコードGithubにあがっているのでとても使いやすい。
対応ブラウザもGithub上のREADMEに書いてある。

Flash接続について

web-socket-jsを利用する場合十分に考慮しないといけないのがFlashのSocket接続だと思う。
ソケットポリシーファイルのやり取りが必要になりFlashの仕様として、以下の手順を踏むことになっている。(リンク先引用)

  1. まず843番ポート(ソケットマスターポリシーファイル)にアクセス。繋がらない or 3秒以内に返事がない場合は、次へ。
  2. Security.loadPolicyFile("xmlsocket://...");で指定されたポートがあれば、そこにアクセス。繋がらない or しばらく(20秒ぐらい?)返事がない場合は、次へ。URLがxmlsocket://...の場合のみ有効なので注意。Security.loadPolicyFile("http://...");で指定されたファイルはSocket、XMLSocketによる通信では無視される。
  3. Socketの接続先と同じポートにアクセス。適切なソケットポリシーファイルが受信できなければ、SecurityError
843ポートに接続する

一番最初にここを見に行くのでここでソケットマスターポリシーファイルを返せるなら一番これがいい。でも、大体社内では閉じられていたりするので厳しかったりもする。
ちなみに閉じた状態で試してみると3回SYN飛ばして諦めてた。

Security.loadPolicyFileで接続する

ここは設定次第で接続しにいくみたいだけどよくわからなかった。
web-socket-jsではWebSocket.loadFlashPolicyFileを事前に設定すると呼ばれないようだ。
(loadFlashPolicyFileの設定先はFlashが読みにいくそれとは違うみたい)

Socketの接続先と同じポートにアクセス

最後にここにたどり着く。
web-socket-jsの作者が開発されているwebsocket-rubyでは以下のように対応されている。以下引用。

    def create_web_socket(socket)
      ch = socket.getc()
      if ch == ?<
        # This is Flash socket policy file request, not an actual Web Socket connection.
        send_flash_socket_policy_file(socket)
        return nil
      else
        socket.ungetc(ch) if ch
        return WebSocket.new(socket, :server => self)
      end
    end

    # Handles Flash socket policy file request sent when web-socket-js is used:
    # http://github.com/gimite/web-socket-js/tree/master
    def send_flash_socket_policy_file(socket)
      socket.puts('<?xml version="1.0"?>')
      socket.puts('<!DOCTYPE cross-domain-policy SYSTEM ' +
        '"http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">')
      socket.puts('<cross-domain-policy>')
      for domain in @accepted_domains
        next if domain == "file://"
        socket.puts("<allow-access-from domain=\"#{domain}\" to-ports=\"#{@port}\"/>")
      end
      socket.puts('</cross-domain-policy>')
      socket.close()
    end

Socketから取得したデータに対して"<"で始まっていればポリシーマスターファイルを返している。
実際Flashからリクエストが来た時の中身はこんな感じ。

<policy-file-request/>\x00

URLのパスとか全く考慮されてないので柔軟には扱えないかなと思う。

まとめ

長くなってしまったけどweb-socket-js使うときはこの流れを考慮したほうがいいと思う。
em-websocketなどはsamplesに843で返す簡易サーバがあるし、デフォルトでも対応してる。他のライブラリでもどの手順でポリシーファイルを意識するかは大事だと思う。
ちなみにChromeはポリシーファイル返してすぐWebSocket接続リクエストが来たけどFirefoxだとタイムラグが1.5秒くらいあいてた。