node.js 使ったことがなかったので読んでみる。今日はコアの部分。
下準備
githubからcloneしてきて、checkout 1.8.4した。masterじゃないので注意*1。
npm install を実行すると依存モジュールがnode_modules/ 以下に入る。そうするとmake test でテストを走らせられるようになる。expressoというのでテストをしてるっぽいが、OS X上だとテストが通らない。2.x 系のコードだとテストは通るっぽい*2のでまあ気にしない。
依存とかは package.json を見れば分かる。
patch.js
node.js の機能へのモンキーパッチが入っている。具体的にはプライベートフィールド_headerSentを露出させるアクセサ、Set-CookieとContent-Typeヘッダ周りの拡張、HTTPヘッダ吐き出し直前に新しいフックの追加など。
util.js
ユーティリティ。特筆すべき点はないと思う。
connect.js
エントリポイント。createServer関数を提供しているが、こいつは単にhttp.Serverやhttps.Serverのコンストラクタを呼んでるだけ。typeof で引数を判定して関数のオーバーロードを実現するのはJSの常套句。
また、middleware の一覧の作成もここで行っている。middleware/直下で拡張子が.jsのファイルを片っ端から記憶する。readdirSync でロードしてるのでブロックするが、起動時なので実害はないはず。ここでは各middleware へのアクセサを作るだけで、ロードは利用時まで遅延して行われる。
http.js
ミドルウェアの機能を実現している心臓部。node.jsのhttp.Serverを継承している。(ドキュメントにはない気がするけど)http.Serverのコンストラクタにはrequestイベントのリスナを渡すことになっており、handleメソッドをリスナとして登録している。
handleの前にまずuseメソッド。ミドルウェア(後述)の他に、http.ServerやServer(handlerメソッドを持つもの)のインスタンスを渡せるよう考慮されている。ミドルウェアは登録順にstackプロパティへ保管される。
で、本丸のhandleメソッド。こいつがリスナとなっており、リクエストが発生するごとに適切なミドルウェアを適用してレスポンスを返す責務を担っている。ただし、通常のリスナはreq, resの2引数だが、このメソッドはoutを含んだ3引数となる。これは、handleメソッド自体もミドルウェアとなっているため。
handleメソッド内の記述を読むと、middleware の仕様が読み取れる。具体的には、err, req, res, nextの4引数をとる関数か、req, res, nextの3(以下)引数をとる関数となる。前者はエラーハンドリング、後者は通常の処理を担当している*3。
middlewareのnextという引数がコールバックとなっていて、この引数を呼び出すと次のミドルウェアへ処理が移る。後続のmiddlewareへ処理を渡さない場合は、nextを呼ばずにres.end を呼んでレスポンスを完了するとよい。
nextはエラーの運搬も担当している。全てのミドルウェアを処理してもレスポンスが返せなかった場合は、next経由でエラーが報告されていれば500、そうでなければ404ステータスとしてレスポンスを返している。