Socket.onmessage

Do WebSockets dream of Network Revolution?

Socket.onmessage header image 1

Shibuya.XSSに参加してきました

4/5, 2012 · 雑記

表題の通り、Shibuya.XSS テクニカルトーク#1に参加させていただきました。定員30名に対して、希望者が241名。実に8倍の難関イベントに参加できた、と言うからには、ログを残しておかないと。
ただ、内容自体は技術的、その他諸事情によりオフレコなものが多かった為、ログは個人的な感想だけになります。ごめんなさい。

資料等は発表者の方々が可能な範囲で公開していただけると思うので、それを楽しみに待ちましょう。

  • opening
  • 株式会社mixi コラボルーム
    とっても広ーい。机、電源、Wi-Fi、自販機等完備。こういうOpenな場所があるというのは素晴らしいですね。
    そして、机の上には食事とお酒が!本当に、本当にありがとうございました。美味しかったです。
    徳丸さんの乾杯で勉強会スタート。

  • DOM Based XSSの傾向と対策 by malaさん
  • 問題の原因、影響、システム側対処方法、ユーザの自衛方法、攻撃者側の視点等、内容盛りだくさんでした。
    @sugyanさんが呟かれてた、これこれが印象に残ってます。

  • x-autocompletetypeの実験 by はまちちゃん(さん)
  • mixiでは個人的にWi-Fi切ってた(あのメンバーの中でWindowsマシンのWi-FiをONにする勇気が私にはなかった)ので、今アクセスしてみたのですが、ウイルスバスターさんが入っているPCでは、
    アクセスしようとしているURLには潜在的なセキュリティリスクが存在します。
    とでて、ちゃんとブロックされました、とw
    実験サイト:http://hamachiya.com/junk/x-autocompletetype.php

  • サニタイズいうぞキャンペーン by 竹迫さん
  • 「jQueryは甘え」すごい勢いでretweetされてましたねw
    Closure Templateのauto escapeがcontext依存のescapeをしてくれるのは凄いな、と素直に感心しました。

  • オフレコ by 春山さん
  • ダークサイド
    koikeさんのこのつぶやきは実際シリアスな問題

  • JSON Array hijacking with MBCS by 長谷川さん
  • JSON(に限らず)、返答を返すときは、Content-Typeとcharsetはちゃんとつけましょーね。

  • mixi Scrap Challenge開催してみた by 坂本さん
  • mixiのクローンサイトを構築、脆弱性を仕込んで攻撃してもらう学生向けセキュリティイベント。
    学生なら参加したかったな…トオイメ

  • AjaxアプリケーションのXSS対策入門 by 徳丸さん
  • 入門よりはちょっと上だったと思う。。
    X-Content-Type-Options: nosniff
    大切。

  • The Sexy Assassinで紹介されてるCSS HTML Attribute Readerがどこまで危険か検証してみた by 吾郷さん
  • 資料が既に公開されてます。
    CSS baseのexploitは全く知見が無いので、一度ちゃんと調べてみたいな、と思た。

  • 実際の体験談 by sendさん
  • 実際に起こったらどのくらい大変なのかがよーくわかった。一番良い方法は、運用に関わらないことだな、と再認識。(違

以上で予定の発表等は終わりましたが、ここから20分程度、裏LTが開催。…これ、参加できなかった人は泣いていいと思いますよ。

と、言うところで、感想終わり。
最後になりましたが、楽しい勉強会を開催してくださった発表者の方々、場所と食事まで提供してくださったmixiの方々、本当にありがとうございました。

コメントは受け付けていません。Tags:

node.js & socket.ioのバージョン変更

3/19, 2012 · 雑記

ベンチマークで用いているnode.jsとsocket.ioをそれぞれ、v0.6.13、v0.9.2にあげてみました。いや、nvm便利ですね。
node.js: v0.6.13
socket.io: v0.9.2
一応安定板最新、のはず。開発速度早すぎなので、すぐに古くなりますけど。

Server側は、Ubuntuで構築してみてもいるのですが、kernel versionが違うと色々動作がかわりますね。UbuntuだとMessage個数を10,000にしようが落ちないので、node.jsのどこかにBUGが潜んでそうです・・・誰か追ってみると喜ばれるんじゃないかな。ぼくは・・・興味が微妙に無いのと時間もありません、ごめんなさい。

なお、MessagePack版もいくつかBUG Fixしてます。
Nagleアルゴリズムの件では、某Googlerにご迷惑をおかけしてしまいましたが。ごめんなさい。
おかげさまで、MessagePack版は完全に再起動必要なくなりました&速度的メモリー的な面でとってもいい感じになりました。後は使い勝手を整備しなきゃ。

ではでは。

コメントは受け付けていません。Tags: ··

node.jsのパフォーマンスを調べてみた

3/13, 2012 · 技術情報

こんばんは。

巷では「node.jsは軽量かつ高速だよー」と噂されてるにもかかわらず、色々探しても、どれほど軽量でどれほど高速なのかを検証しているところが日本語ではあまり見つかりません。

その為、今回は、WebSocketを用いたsocket.ioの速度/メモリ使用量調査をしてみました。また、msgpack-rpc改変版をWebSocketの上に実装したバージョンと比較もしてみました。

テストは以下ですが、次の注意点を踏まえてお試しください。

  1. デモは、どちらもさくらのVPS 512、CentOS 5.7, kernel 2.6.18-274.18.1.el5 x86_64上に構築してあります。
  2. node.jsのバージョンは、v0.6.12。socket.ioのバージョンは、0.9.1-1を利用していますので、最新版はより良いパフォーマンスが出るかもしれません。
  3. socket-io版は、デモ中、Num of Messages(echo backの個数)を2000以上にするとnode.jsが死ぬ場合があります(3000以上にすると確実に死にます)。これについては後述します。
  4. socket.io版が落ちた場合は、node.jsをrestartしてみてください。ただし、そのとき接続しているすべての人に影響が出る為、アクセス人数によっては動作がおかしくなるかもしれません。
  5. msgpack-rpc版は、WebSocketのRFC版およびBinary Transportを用いますので、ChromeもしくはFirefox Nightlyでしか動作しません。
  6. msgpack-rpc版は、Firefox Nightlyを用いた場合でも、利用者の端末がproxy配下だと動作しない場合があります。(これについては、この記事の2および3を参照)
  7. msgpack-rpc版およびsocket.io版ともに、複数人が同時にテストを開始すると、VPS側のulimit -nが1024に設定してある関係上、パフォーマンスが落ちたり、テスト結果がエラーになることがあります。
  8. msgpack-rpc版とsocket.io版を出来る限り同一条件にする為、一回のテストごとにWebSocketの接続と切断を行っています。
  9. このblogやテストページは、共通のlighttpdから配信しています。(node.jsはcross originで利用)その為、このblog等にアクセスが多数発生していると、msgpack-rpc版で利用しているlighttpdの使用メモリが増えたり、パフォーマンスが落ちる可能性があります。

テスト

テストの動作

  1. Num of Messagesに設定した数値は、WebSocket Connectionを張った状態でエコーサーバに送るメッセージの個数となります。
    例えば1000とした場合には、”0,”, “1,”, “2,” … “999,”という文字列1000個がエコーサーバに送られます。
  2. Num of Tryに設定した数値は、何回テストをするかとなります。
    例えば10とした場合には、上記1の動作を10回行います。
  3. STARTボタンを押すと、サーバとの間でWebSocket Connectionをopenし、上記1で設定した個数分、ブラウザからエコーサーバにメッセージを送出します
  4. エコーサーバからの返答がNum of Messagesに達した場合は、WebSocket Connectionをcloseします。
    また、メッセージを送り出してから返答のすべてを受け取るまでの時間を保存し、そのエコーバックが正しいかどうかを検証します。
  5. 上記2で設定した回数分、WebSocketのopen、メッセージの送受信、WebSocketのcloseを繰り返します。
  6. 最後に、メッセージを送り出してから返答のすべてを受け取るまでにかかった時間の平均、終わった段階でのtopコマンドの結果を表示します。(計測が終わってからのtopなので、テスト実行した本人のcpu使用率は0%です。)

パラメータの意味

  1. Num of Messagesの数値を大きくすると、メッセージの送受信がバーストした場合の速度が得られることとなります。
  2. Num of Tryの数値を大きくすると、基本的には、より正確な平均速度が得られることとなります。ただし、ネットワークの状態に左右されることになりますので、ネットワークのRTTが不安定な場合はよりばらつきが出ることとなります

結果

  • 速度に関して。
    node.js + socket.ioは、100メッセージ程度の送受信に於いては、とても高速です。私の環境では平均で60msほどでした。しかしながら、送受信するメッセージの個数が増えていくと、とても遅くなります。
    これは、node.jsがsingle threadで動作しているため、メッセージの送受信がバーストし、{read, write} handlerに時間がかかり始める為だと感じます。
    ただ、後述の問題がある為、私のサーバサイドの作り方に問題があるのかもしれません。もし、どなたかご存知であれば、ご指摘ください。
  • 使用メモリ量に関して。
    node.js + socket.ioは、送受信するメッセージ個数にもよりますが、実メモリ使用量で大体10MB〜100MBほどとなります。この数値は一般的なウェブサービスにとっては問題にならない値かな、というのが正直な感想です。
    しかし、WebSocket界隈では、様々なDeviceをブラウザにつなごう、という試みをされていらっしゃる方々も多く、そういった場合に利用される組み込み機器に導入して安心して利用できるメモリ使用量か、と言われると、私は少し不安になります。(古い考えかなぁ、とも思いますけどw)
    また、V8に起因するのでしょうが、利用可能なメモリがあればあるだけ確保しようと言う挙動、そしてそれを押さえる方法が無いことも問題になり得るかな、と感じます。(ただし、私が不勉強なだけで、このあたりはチューニング可能なのかもしれません。これもご指摘いただけるとうれしいです。)

まとめ

まず、一般的なウェブサービスで利用する際には、やはり素晴らしく強力なツールですね。速度、使用メモリ共に申し分無いと言えます。(マルチクライアント環境のテストはまたの機会に)
また、パフォーマンスとは違いますが、node.jsとsocket.ioを用いたアプリケーションの作りやすさは異常です。JavaScriptの知識は必要ですが、それ以外はほんと、簡単です。
(ただ、何か問題が起きたときの解析のしにくさもありますので、エンジニアとしては様々な知識を得ておく必要はあります。(DevSumiでも言及されてましたね)

素晴らしいプラットフォームだな、と改めて認識しました。

さて、一方、msgpack-rpc版ですが、試していただけましたでしょうか?

現在の実装では、アプリケーションの作り方が面倒であったり、マルチブラウザ対応(WebSocket RFCの上だけでしか動かないとか)といった面で、まだまだ使い物にはなりません。
ただ、速度面では10000メッセージであっても2秒程度、その際のメモリ使用量も5MB程度というパフォーマンスは少し見応えがあるんじゃないかな、と思っていますがいかがでしょうか?
(殆どが、msgpackとuupaaさんのコードが素晴らしい、という一言につきるんですけどもw)

今後も細々と開発を進めていきますので、ご興味がございましたらissueやら何やらとご連絡いただけるとうれしいです。
ではでは。

<追記>
速度やシリアライズ後のデータサイズが素晴らしいmessage pack{-rpc}ですが、少し難点を感じたことだけ、追記しておきますね。

まず、C++版で用いられているネットワーク周りのライブラリ、mpioですが、古橋さんのoriginal repositryでは、epoll利用時(以外でも起きるかもしれませんが調べてません)かつ、writevしたさいにEINTRやEAGAINを受けた際に正しく動作しません。これは、mpioでpull reqされているこのpatchを当ててあげてください。なお、楽をしようとして、pull reqを送られているaarond10さんのrepositryをそのまま用いようとすると、libaioを用いることが出来ないkernel versionを使っているとcompileできません。(えぇ、私の環境です。はっはっは)

次に、型無し言語でclientを作成し、型あり言語でserverを作成するというRPCを行う場合、ちょっと面倒ですね。msgpack-rpc server側で受け取った際の引き数の型がどうなってるのかを、しっかり考えておかなきゃならんのです。
例えば、C++版 serverで引き数をfloatで受けるコードを作成し、JavaScript版 clientで、以下のようなコードを書いたとしましょう。

for (var i = 1.0; i > -1.0; i -= 0.1) {
  var a = Number(i.toFixed(1));
  client.call_async("method", a);
}

これは、動作しません。なぜなら、JavaScriptでは、0.0や1.0は、0, 1に等しいので、packした場合、float型にならないからです。
この辺りは気にしてサーバ側を書けば何とかなりますが、ちょっと使い勝手が悪いところかな、と。

あと、msgpack-rpc版のechoserverが死ぬほどメモリを喰ってるように見える場合(topの結果、RESの値が大きい場合)もありますが、これは頻繁にwritev失敗すると起きます。ただ、これは速度とのトレードオフであろうと思われる実装なので、メモリリークしてる訳ではないです。
これも、組み込みの場合はちょっと改変が必要になりますね。
<追記終わり>

今回のテストでの注意点

  1. node.jsが死ぬ
    Num of Messagesを3000ほどにすると、node.jsが以下のメッセージを吐いて死にます。私のアプリケーションの作りが悪いのかもしれませんので、どなたかご存知でしたら教えてください。なお、サーバ側のコードは、これです。
    node/node_modules/socket.io/lib/manager.js:0
    (function (exports, require, module, __filename, __dirname) { /*!
    ^
    RangeError: Maximum call stack size exceeded
  2. WebSocketのstateがopenになってからsendしている
    今回のテストでは、
    client = io.connect(uri);
    client = new msgpack.rpc.client(uri);
    のどちらも、WebSocketのonopen event(socket-ioではconnected event)を待ってからメッセージをsendしています。
    一般的には、openされたかどうかを気にしないでコードを記述する場合が多いかな、とは思いますが、その場合、実はどちらの実装も一旦Arrayにメッセージをpushしておき、onopen eventを受けた際に、一気にメッセージを送り出す、という動きになります。
    メッセージの送受信だけにかかるパフォーマンスとして、より正しく計測したかった為、このような実装となっていますので、ご承知ください。

コメントは受け付けていません。Tags: ··

MessagePack RPCのnotifyをServerから送れるようにしてみたよー

2/21, 2012 · 技術情報

こんばんは。
先日の、「MessagePack RPC for JavaScript出来たよー」の記事、最後のあたりで言及していた件について。

MessagePack RPCの仕様に於いては、
[type, msgid, method, params]
などと言ったArrayにオブジェクトを纏め、送信し合う事によってRPCを実現しましょう、と言う取り決めがなされています。
また、

  1. type === 0は、Request
  2. type === 1は、Response
  3. type === 2は、Event

と決められています。

通常RPCと言うと、Request-Responseだけを想定している事が多く(Remote Procedure Callなので当たり前なんですが)、Eventと言う仕様はあまり見た事がありません。しかし、例えばServerで何かEventが発生した時に、ClientへNotificationを送付する、と言った利用方法は非常に有効な場面が存在します。

と言う事で、「やったー、Eventが使えるるー!」、そう考えてコードを書き始めて数時間。

class Server < SessionPool
send_notifyが無い・・・だ、と・・・。

class ClientSocket < TCPTransport::BasicSocket
def on_notify(method, param)
    raise Error.new("notify message on client session")
end

Clientがnotify受け取ったら、Error・・・だ、と・・・。

はい、ごめんなさい。私が悪かったです。やっぱりRPCですもんね。

と、泣いてばかりではつまんないので、とりあえずmsgpack-rpcをforkさせて頂き、サーバからnotifyが送れるようにしてみたので、この辺りにcommitしておきました。(ちょっとした事情でcppでの実装になったのはご愛嬌)
また、このサーバ実装(C++)と、Notifyが受け取れるClient実装(JavaScript)を用いたデモもこちらにおいてみました。

更に、backendに置いたMessagePack RPC Serverはonmessage.ws:9090に口を開けてますので、このあたりのサンプルコードで、
client = MessagePack::RPC::Client.new('onmessage.ws', 9090)
とか呼んでいただくと、ちゃんとnative TCP socketからもMessagePack RPC出来るかと思います。その際、method == “multicast”、param == ["String"]とすると、ちゃんとWebと繋がってる事も確認出来るかなと思います。(ブラウザは、Multicastと書いてある所にjoinしておいて下さいね)

さて、実装面ですが、本来、双方向でNotify等を送り合えるようにする為には、Session, SessionPoolあたりから上は再設計かな、と思いますし、再設計が必要ならI/O周りを今ならlibuvあたりにbindするかな、とか思います。今回は、こんな事がしたいんだー、と言うアピールの為のプロトタイプ、と言う事でご了承ください。

以上、私は双方向で利用したいな、と感じているのですが、他の皆様はどうなんでしょうか?利用されてる方にお聞きしてみたいですー。

ではでは。

コメントは受け付けていません。Tags:

MessagePack RPC for JavaScript出来たよー

2/9, 2012 · 技術情報

こんばんは。やっと色々実装できたので公開します。

msgpack.codec.js
msgpack.rpc.js

不完全ですが、MessagePack RPCのJavaScript binding出来ました。

ベースとして、@uupaaさんが作成されていらっしゃったmsgpack.jsを利用させていただきました。uupaaさんのコードはWeb上でよく見させていただいているのですが、本当に勉強になりますね。

さて、uupaaさんのコードですが、transportにhttpを用いる環境(ajax)では必要が無いため、MessagePackのstream deserializeには対応していません。
ただ、私が作成しているmod_websocketと連携させてMessagePackを扱おうとすると、どうしてもstream deserializeが必要になるため、その部分を追加させていただきました。diffはこのくらいです。
(ただ、現状unpackerのlayerでchunkをbufferingして回避しているだけです。効率を重視するには、本来decodeを進めつつ、オブジェクトを作成していく必要があります=>誰か作って)

またそれを用い、WebSocket(binary transport)の上にMessagePack RPCを実現するコードも作りました。(これまた現状は、sourceの読み込み順序などを考えていないのと、call_asyncのみしかmethodをimplしてありません)

とまぁ、色々問題は残ってはいますが、これを用いることによって、簡単かつ効率的にブラウザ – サーバ間のメッセージ交換を行うことが可能となっています。

では実際に利用してみましょう。

  1. lighttpdをmod_websocket付きでinstallしましょう => refer here or Quick Start
    なお、msgpackを用いる場合は、libicuいらないので、
    $ ./configure --with-websocket --without-libicu
    でmakeするのがよろしいかと思います。
  2. このlighttpd.conf
    1. server.port
    2. server.document-root
    3. server.errorlog

    の3つを各自に合った設定に書き換えてください

  3. 先ほど書き換えたconfiguration fileを利用してlighttpdを起動してください
    もし、80番ポート等特権ポートを利用する際には、sudoする必要があります。
    $ [sudo] lighttpd -f lighttpd.conf
  4. このindex.html.txtをindex.htmlに名前変更してdocument rootに配置しましょう
  5. このruby版MessagePack RPCサーバをserver.rbに名前変更して起動してください(MessagePack::RPCは利用可能にしておいてくださいね)
    $ ruby ./server.rb
  6. Chrome 16以降(WebSocket version: RFC, Binary Transport有効)で<http://ipaddr:port/index.html>にアクセスしてみてください

これだけです。

今回実装したコードの特長は、

  1. お好きな言語で実装したMessagePack RPCサーバとブラウザを繋ぎます
  2. サーバ側の実装を行う方はMessagePack Stream Serializerを用いてMessageをchunkしつつ送付することが可能となり、heapやstackを圧迫しないサーバ実装が可能です
  3. ブラウザとWebサーバ(lighttpd)間で実際に送りあうメッセージフレームは、
    +------------------------+-------------------+
    | WebSocket Frame Header | MessagePack Frame |
    +------------------------+-------------------+

    となっており、またmod_websocket側でWebSocket Frame Headerを削除、MessagePack Frameを転送しているだけですので、無駄なハンドシェイクやフレームヘッダ、メッセージフォーマットの変換などが存在しません

以上となります。

この実装を利用したデモはこちら。iframe中に示されたrubyのコードが実際に動作しているサーバ実装です。($ln -sしてあります)
そして、デモでは、サーバ側から1秒おきにイベントが送付されてきています。
サーバのコード中では、@svr.notify(...)の部分ですね。

しかし、これは、実際にgem等でinstall可能なMessagePack RPCのコードでは実現できません。この部分については、個人的に少し要望があり、MessagePackの開発者の方々にお話を伺いたいな、と思っていますので、また別記事としてpostさせていただきますね。

ではでは。

コメントは受け付けていません。Tags: ··

telnet over WebSocketを作ってみた&その意味

9/30, 2011 · 雑記

5月に参加させていただいたWebSocket勉強会からはや4ヶ月。時が過ぎるのは速いですね。さて、勉強会の場でデモさせていただいた時には、WebSocketはtext frameしか扱えなかったので、ws{s}トンネルの中をsmtp通してました。(デモは動作しませんが、資料はこちら)

そして数日前。

chromium trunkでWebSocketのbinary frame supportが入った、というpostを拝見したのち、chrome dev trunkでも正常に動いているように見えたので、jsdo.itにコードを作ってみました
(2011/9/30時点では、chrome betaでも動作するようです。dev trunkの日本語文字表示の不具合、早く治らないかなぁ…)

構成は以下の様になってます

今回もサーバサイドのアプリケーションは作成せず、既存のtelnetdを使ってるだけです。
ただ、インターネット上に23番ポートをopenにするとか怖すぎますので、以下の対応を行ってあります。

  • telnetdはlocal loopbackにのみbind
  • pamの設定をいじってdemo userだけlogin可能に
  • demo userのlogin時にchrootを行い、/home/demoを/に
  • pingなど繋がってるかどうかを確かめるための最低限のコマンドのみ設置

さて、このデモの意味ですが、最近流行りのnode.jsとはちょっと違ったサーバサイドの在り方について考えてみたいな、という目的をもっています。

最近、WebSocketと言えばやはりnode.js(and socket.io)と言うのが一般的かと思います。
ここで、何かWebSocketを利用したアプリケーションをつくろう、と思い立った際、私たちは何をしなくてはならないでしょうか?

  1. node.jsの枠組みで、サーバサイドJavaScriptを用いてサーバアプリケーションを作成
  2. JavaScriptを用いたクライアントアプリケーションを作成

この2つが必要となりますよね。図にすると、こんな感じ。

とても綺麗なモデルです。WebSocketを知らなくてもJavaScriptさえ知っていれば利用できるフレームワークですし、WebSocketがつかえないブラウザでもbi-directionalなメッセージ通信が行えるようにするsocket.ioの存在も素晴らしいです。(また、モデルとは別件ですが、npmの存在も素晴らしい)

でも、私にはサーバサイドも必ずJavaScriptで作成しなければいけない点が不満だったのと、今まで多く作ってきたTCPサーバをうまく活用したいな、という思いがありました。

サーバサイドには既に多くのtcpサーバアプリケーションが存在します。例えば、今回用いたようなtelnetdや前回利用したsmtpdなど。
これらをそのままブラウザから使えたとしたら、とても楽になるんじゃないでしょうか?

ここで、既存のtcpサーバアプリケーションをブラウザ経由で使いたい!と思ったときに、node.jsを利用すると、

こんな感じになるでしょうか。すると、私たちが作らなくてはいけないもの、というのはピンク色で囲った部分となり、以下のようになります。

これはこれでとっても素晴らしいのですが、私はもう少しシンプルにしたかった。こんな感じに。

これを実現しようとして作成しているのが、mod_websocket for lighttpdと、jsSocket.jsです。この2つを利用すると、モデルが以下のように変化します。

こうすることによって、実際に私たちが作らなくてはいけないコード、アプリケーションは以下の通りとなります。

これで、ブラウザサイドはJavaScript、サーバサイドは好きな言語と、綺麗に分割出来るようになりました。また、lighttpd – サーバアプリケーションの間はネットワークが介せますので、今まで利用してきたTCPサーバはそのままに、lighttpdを適当な場所に設置するだけでブラウザと通信することが可能になりました。

WebSocketが新しいバージョンとなりbinary frameが扱えるようになったことで、今までTCPで実現されていたすべてのプロトコルとお話しできるようになります。今まで悩まされていたNAT, Firewallなどを超えて、様々なサーバと繋いでみるのも楽しいかもしれません。

また逆に、WebSocketはこういった使い方ができてしまうため、多分、WAFを非常に慎重に開発/設定しないといけない時代が来るのかなぁ、とも思います。(私はwith lightyで作ってますが、同様のモデルをnode他どのようなWebSocketサーバでも構築可能です)
HTTP万歳!ポート番号なんていらんかったんやー!と喜んで利用していると、困ったことになるかもしれませんね(^^;

ではでは。

コメントは受け付けていません。Tags:

WebSocketのバージョンとproxyの現状

7/29, 2011 · 技術情報

WebSocketとproxy周りの話について簡単に。
ちょっと追いきれない所もあるので、もし知っている方がいらっしゃいましたら、コメント等で教えていただけると幸いです。

以下、hixie-76とかietf-00とか言うのは、WebSocketのバージョンを表します。(なお、正式な名称ではありません。hybiのバージョンに基づいて、便宜的に私がそう呼んでいるだけです)

  1. hixie-76(≒ietf-00)のみ、繋がらない場合がある
  2. handshakeに於いて、content-length無しでbodyに8byte文字列が付加される。
    (また、bodyの8byteは別packetで送付される。下図)

    browser — (handshakeのheader部分) –> server
    browser — (handshakeのbody部分) –> server
    browser <-- (handshake response) --- server

    proxyによっては、上図の2packet目を通さないものが存在し、bodyの8byteを削除してしまうので繋がらない。

  3. ietf-00, ietf-10など全てのバージョンで繋がらない場合がある
  4. proxyがHTTP headerを書き換える、もしくは知らないHeader bodyを削除する為、繋がらない。

    具体的には、proxyがConnection headerの中を書き換える場合がある。
    例えば、FirefoxのWebSocket handshakeでは、

    Connection: keep-alive, Upgrade

    と言うHTTP headerが付加されているが、proxyを通した後のheaderが、

    Connection: keep-alive

    となる場合がある。勿論、これはWebSocket handshakeとして不正なので、WebSocket serverとは接続不可能。

  5. proxy存在時のブラウザの動作相違により特定のブラウザが繋がらない場合がある
    • Firefox
    • proxyがある場合でも、CONNECTしないで、対象Web server/WebSocket serverにGET発行する。
      CONNECTしないでGETが発行された場合、proxyはHTTP headerの中身を走査する(はず)
      その為、上記2番目の現象がほぼ”確実に”発生する。

    • Webkit系
    • proxyが存在した場合は、proxyにCONNECTしてから、対象Web server/WebSocket serverにGET発行
      本来であれば、CONNECT後proxyがpacketを走査することは無いはずであるが、まれに上記2番目の現象が発生する。(原因不明)

  6. softbankの3G網限定で繋がらない。但し、hixie-76(≒ietf-00)の場合のみ
  7. 80番ポートに限ってのみ、上記1の問題が”確実に”発生する。(mobile Safariで利用するWebSocketがietf-10にupdateされたら大丈夫になるはず)
    つまり、WebSocket serverを80番で待ち受けさせた上で、

    var ws = new WebSocket(‘ws://foo.com/bar’);

    というコードを書いた時に、接続できない。
    原因は不明ですが個人的な予測としては、softbankのnetwork内に於いて80番ポートを監視するtransparent proxyが存在、そのproxyが悪さしているのかな、と。

  8. proxy存在下でiPhoneからWebSocketを利用しようとするとmobile Safariがcrashする
  9. 以下のようなネットワークに居る場合で、iPhoneからのWebSocketを利用しようとすると、mobile Safariがcrashします。以前、PC版のSafariでも似たようなことが起こっていた(bug報告済)ので、BUGかな、と。(どこにBUG報告していいのか分からず放置中…)

    iPhone(Wi-Fi) – proxy – GW – internet – WebSocket server

なお、現時点(2011/07/28)のブラウザがどのWebSocketバージョンをサポートしているか、と言うのは以下。(全てのブラウザをチェックしているわけではないので、詳細なバージョンとかは各位で調べてくだちぃ)

  • IE9
  • 下周りにSilverLightを用いる形で、WebSocketが利用可能(なはず。動作させたことは無いので間違ってたらごめんなさい)
    サポートするWebSoketのバージョンはhybi-00(多分)

  • Firefox
  • stableでは、about:configでWebSocketを利用可能に変更する必要がある
    サポートするWebSocketのバージョンはstableではhybi-00、trunk(Nightly)ではhybi-10

  • Safari
  • サポートするWebSocketのバージョンはhybi-00

  • mobile Safari(iOS4.x)
  • サポートするWebSocketのバージョンはhybi-00

  • Chrome
  • サポートするWebSocketのバージョンはstable, beta channelではhybi-00、dev channelではhybi-10

  • mobile Chrome(android)
  • WebSocketは利用不能

  • Opera
  • trunkでしか利用できない、またabout:configでWebSocketを利用可能に変更する必要がある
    サポートするWebSocketのバージョンはtrunk(11.50)がhybi-00

なお、hybi-10ではbinary frameも仕様に含まれていますが、私が確認したところでは、binary frameが扱えるブラウザはまだ存在しないようです。

それでは、また。

コメントは受け付けていません。Tags:

MessagePackとWebSocketの連携

7/14, 2011 · 技術情報

こんばんは、ProxyとFirewallがある場合のWebSocket動作をまとめようと思ってはいるのですが、少し先延ばし…すません。

今回は、MessagePackとWebSocketの連携を試してみました。
デモはこちら
注意:iPhoneをご利用の方で、3G回線経由、およびProxyが存在するWi-Fi環境ではデモを実行できません。特にProxyが存在するWi-Fi環境からアクセスすると、mobile Safariがクラッシュするはずです。(ProxyとFirewallがある場合のWebSocket動作でまた記述します。Proxyの無い、ご自宅のWi-Fi環境経由などでしたらデモを実行できるはずです)

デモはありきたりなチャットアプリケーションですが、伝送されるデータは、@uupaaさん作のmsgpack.jsを利用させていただいて、MessagePack形式(但し、現在のところWebSocketでbinaryが扱えないので正直MessagePack形式と呼べるものではありません)にpack, unpackされています。

現在hybi-10がLastCallな為、近いうちにbinaryも扱えるようになると思いますので、その時にまた更新しようかな、と。

デモからリンクを貼っているこの資料を見ていただくと、どんな感じでメッセージが流れているのか、そして、この先、どんな実装をしようとしているのかがより分かっていただけると思います。

また、デモで利用しているコード自体は、こちらから取得可能(但し、index.htmlおよび、msgpack.codec.jsはありません)となっていますので、ご興味がありましたら各自お試しください。

なおデモでは、WebSocketおよびmsgpack.codec.jsの処理をWorkerに落とし込んでいませんが、

http://onmessage.ws/demos/msgpack_chat/index.html中

p = new Plug('ws://onmessage.ws/msgpack_chat', 'plug.msgpack.js', true);

と改変していただくと、WebSocket、msgpack.{pack, unpack}をWorker側で処理できるようになっています。

なお、サーバ側は、ここにあるチャットサーバが動作していて、現在の所は単に受け取ったMessagePack形式っぽいデータを繋がっているユーザ全員に再配布しているだけです。

しかし近い将来、バイナリデータをWebSocketで送受信できるようになると、サーバ側をMessagePack::RPC::Serverなどで作ることが可能になるはずだと考えていまして、今回はそれが可能かどうかの検証サンプル、と言うことになります。

何かご質問、ご指摘等ございましたら、お気軽に@nori0428にMention飛ばしていただけると嬉しいでございます。

ではでは。

コメントは受け付けていません。Tags: ·

proxyよ、お前は何をしてるんだ。と言うお話し

6/20, 2011 · 技術情報

mod_websocket for lightyhybi-09(≒hybi-08)実装してみました。

まだ、ping送れなかったり(pongは送れます)、BUGやらmagic numやらイマイチな実装が多いですが、おいおい直していきますのでご容赦を。

以下、動作確認してて困った(てる)お話。

  1. WebSocketを使おうとしても、proxy配下で使えないときがある
  2. proxy配下でWebSocket使うときは、proxyに対してConnectしてからWebSocketを張ったほうがいいよ、と仕様5.1. Client Requirementsで決められています。(SHOULDなのですが、かなりの確率で問題が起きるのでMUSTにした方が良い気がします…)

    さて、以下のログを見てください。
    なお、使用ブラウザは、Chromium 13.0.782.10 (Developer Build 87958) Ubuntu 10.04です。
    まずは、ブラウザを動作させている側で取得したpacket dumpの結果

    1. BROWSER >>> PROXY
    2. CONNECT onmessage.ws:80 HTTP/1.1
      Host: onmessage.ws
      Proxy-Connection: keep-alive
      
    3. PROXY >>> BROWSER
    4. HTTP/1.1 200 Connection established
      
    5. BROWSER >>> WEB SERVER
    6. GET /chat HTTP/1.1
      Upgrade: WebSocket
      Connection: Upgrade
      Host: onmessage.ws
      Origin: http://onmessage.ws
      Sec-WebSocket-Key1: 2 N7  x7  #  5  E 4=9  5X8`4
      Sec-WebSocket-Key2: %A  1E1 370[  z1 5}1 @8  Z0
      
      .C.%.5..
      

    うん、ちゃんと送れてるみたいですね。次に、ウェブサーバ側で取得したpacket dumpの結果。

    1. PROXY >>> WEB SERVER
    2. GET /chat HTTP/1.1
      Host: onmessage.ws
      Origin: http://onmessage.ws
      Sec-WebSocket-Key1: 2 N7  x7  #  5  E 4=9  5X8`4
      Sec-WebSocket-Key2: %A  1E1 370[  z1 5}1 @8  Z0
      Cache-Control: max-stale=0
      Connection: close
      X-BlueCoat-Via: 32B0045A9A1F999C
      

    HTTP/1.1でConnectしてるのにPacketの中身書き換えるようなproxyは死ねばいいのに。
    しかし、この現象、毎回起こるわけではなく、時々こうなる、と言う状況でして、proxyが一体どう処理しているのか、正直良く分かりません。

  3. FireFox 7 + proxy環境でWebSocketがつかえない
  4. FireFox 7では、proxyがあったとしてもConnectしないようです。
    その為、先の問題1と同様に、ヘッダがproxyによって書き換えられてしまう場合があり、WebSocketが繋がらない、と言う状況が発生します。
    注:FireFox7でのWebSocketは、hybi-08(07?)ベースなので、Sec-WebSocket-Key3に相当するbodyは存在しません。
    繋がらない場合は大抵、Connectionヘッダが、Upgrade→closeに書き換えられています。

    proxyが存在する場合には、出きればConnectして欲しいなぁ、と思う所でございます。

  5. iPhoneの3G回線経由でWebSocketが使えない
  6. どなたか、現状存在するWebSocket Server(nodeでもなんでもOK)を80番ポートで立ち上げて、iPhoneでアクセスしてみてください。
    ちゃんと繋がりますか?繋がったらごめんなさい、私の間違いです。コメントでもtwitterでも何でもいいので、mention頂けると嬉しいです。

    繋がらなかった場合の理由は以下です。

    iPhoneの3G回線経由でWebSocket(portnum == 80)使うと、hixie-76(hybi-00)で定義されている、Sec-WebSocket-Key3が無くなる

    これは、proxy配下にある時にWebSocketがつかえない、と言う問題と等しいです。

    まぁ、理由は分からなくもないんです。hixie-76に於けるWebSocket handshakeでは、Content-Lengthヘッダが無いのにもかかわらず、initial GET requestに8byteのbodyがくっ付いてきます。
    その為、多分、SoftBankの回線内でproxyとかWAFとか入ってるんだと思うんですけど、これがその8byteを削っちゃうんじゃないかな、と。

    一応、このサーバ(onmessage.ws)上でtcpdump使って80番ポートと8080番ポートでWebSocket Server立ち上げて確認してみたところ、80番ポートで立ち上げた時だけ8byteが届かないので、こう結論付けてますが、もし間違ってたら教えてください。

    追記
    iPhoneでアクセスできるサンプル
    iPhoneでアクセスできないサンプル
    なお、コード、サーバはポート番号が違う以外は全く同じです。hybi-00(hixie-76)対応のブラウザ(chromeとかsafariとか)でしたら、どちらも問題なくアクセスできるはずですので、それらブラウザとiPhoneで相互通信してみてくださいませ。

とりあえず、現状こんな所です。

しかし、non block read使いつつWebSocketのpacketをhandleするのは正直疲れます。(modだと、chunk処理し終わる度にlighty側に制御戻さなきゃならんし)
4.4にControl frames themselves MUST NOT be fragmentedとか記述があるけど、無理じゃないでしょうか?結局payload dataが来るまで1byteずつcheckしていくしかない、はず。いや、私の実力が伴ってないことは否めませんので、この理解もまた、間違ってたら教えていただきたいのですけど。

仕様がdraft段階、信頼できるサーバもクライアントもいない中で仕様を実装していくと何が正しいのかわからんくなるぜー、と言う嘆きでした。ではでは。

コメントは受け付けていません。Tags:

WebSocket and Proxy

5/31, 2011 · 技術情報

竹居さんの日記を読ませて頂きました。

勉強会中も、「WebSocketはproxy(http proxy)を越えられるのか?」と言う発言がちらほら見えましたが、私自身も「多分越えられる」とか「越えられないproxyもあるよね」とか、曖昧な回答しか出来てなくて気持ち悪かったので、真面目に調べてみました。
(なお、私自身はproxyが越えられなかったという経験がありません…)

  1. 前提
  2. ここで言うWebSocketのproxy越えとは、企業内LANなどの内部ネットワークからインターネットに接続する際に、ブラウザが直接インターネット上のサイトに対して接続するのでは無く、中継サーバ(poxy)を介した通信を行うことが出来るかどうかを指します。逆に言うと、負荷分散の為のlbやReverseProxyを介した接続は想定していません。

  3. The WebSocket protocol – draft-ietf-hybi-thewebsocketprotocol-07
  4. Opening Handshake
    5.1. Client Requirements
    3. _Proxy Usage_

    ここを見て頂くと分かりますが、proxyを経由する必要がある場合には、

    CONNECT example.com:80 HTTP/1.1
    Host: example.com

    などとしてproxyと接続するべき(SHOULD)と規定されています。
    そして、この動作はRFC 2616およびRFC 2817が元になっています。

  5. RFC 2616 – Hypertext Transfer Protocol — HTTP/1.1
  6. こちらで見ると分かりますが、RFC 2616は、RFC 2817で一部更新されています。

  7. RFC 2817 – Upgrading to TLS Within HTTP/1.1
  8. この文書は、既存のTCP接続を通してトランスポート層セキュリティ(TLS)を始めるためのHTTP/1.1におけるUpgradeメカニズムの使い方を説明するものです。 これによって、全てのhttp/https通信を同じポート番号(通常80)にて行うことができます。 更に、RFC 2616にて予約だけされていたCONNECTメソッドについて具体的な定義を与えます。 最後に、この文書は、公的なHTTPステータスコードのための、及び公的・私的なUpgrade製品トークンのためのIANAレジストリを設立します。

    参照: http://www.studyinghttp.net/rfc_ja/#RFC2817

    RFC 2817の5章、Upgrade across Proxiesと見比べてみるとWebSocketも同じ仕様であるのが分かるかと思います。日本語翻訳

以上、仕様上は問題なくproxyは越えられるはずです。少なくともTLSを透過出来ているproxyであれば。
またブラウザ側の動作も、私が確認した限りではChrome, Safari, Firefoxは仕様通りに動いているように見えます。(そういえばOpera見てないな・・・)

WebSocketが通らないproxy環境にいらっしゃる方でpacket dumpが公開出来る方がいらっしゃれば、ちょっとdumpを見せて頂きたいなぁ、と思う所でございます。無理だと思ってはいますが・・・(^^;

コメントは受け付けていません。Tags: