Crystal で HTTP Server を作ってみるテスト。
HTTP::Handler#call
実にシンプルな解答からしておくと、こうなる。
require "http/server"
class MyHandler < HTTP::Handler
def call(request : HTTP::Request) : HTTP::Response
HTTP::Response.ok("text/plain", "ok")
end
end
server = HTTP::Server.new(3000, MyHandler.new)
server.listen
(Qiita が Crystal のシンタックスハイライトに対応してないから rb 指定したけど綺麗に色付けされるしすげえな)
一個一個みていこう。
HTTP::Handler
の継承
実はコレ、なくても構わない。もっと簡単に書ける方法として、以下のようなコードが出てくる。
require "http/server"
server = HTTP::Server.new(3000) do |request|
HTTP::Response.ok("text/plain", "ok")
end
server.listen
で、コレは内部的になにをやってるかを追っかけて行くと、この辺のコードが出てくる。
HTTP::Sever
の @handler
に格納されるのは HTTP::Handler
じゃなくちゃいけなさそうなんだけど、 @handler
の要件を満たすのは #call(HTTP::Request) : HTTP::Response
を満たす振る舞いを持つ『なんらか』で良い。んで、このブロックは Proc
オブジェクトになるわけだけど、 Proc#call
を持つので、それが成立する。実際、 HTTP::Handler
は abstract class だけど #call
は実装してなくて、持ってるのは #call_next
という middleware stack を実現する用の処理だったり。
使っている HTTP::Response.ok
は 200 レスポンスを返すためのヘルパみたいなメソッド。もうちょい難しくいろいろ書けるのだが、今回は一旦割愛。
引数の型指定
def call(request : HTTP::Request) : HTTP::Response
のようにして、型を指定しているものの、活用はされていない。実際消しても動く。その辺は賢くみてくれるらしい。すごいぞ Crystal。
ただし、これだと nil
などは返せないので nilable なメソッドを作りたければ HTTP::Response?
と書く必要がある。
Middleware stack
今の状態だと実際にアクセスが来てるのかわからないので、アクセスログを出すようにする。標準でついてくるちっさな Logging Handler があるので、利用してみよう。
require "http/server"
class MyHandler < HTTP::Handler
def call(request : HTTP::Request) : HTTP::Response?
HTTP::Response.ok("text/plain", "ok")
end
end
- server = HTTP::Server.new(3000, MyHandler.new)
+ server = HTTP::Server.new(3000, [HTTP::LogHandler.new, MyHandler.new])
server.listen
こう書く。Server は第二引数に Array(HTTP::Handler)
を受け付けているので、こういう書き方ができる。実際には、受け取った HTTP::Handler の配列から、一つの HTTP::Handler
にして返しているのだが、それを行っている HTTP::Server.build_middleware
の行儀が悪いと思うのは、引数に対する破壊的操作を嫌う性分のせいだろうか?あんまりいい実装じゃない感じがするので、できればどうにかなってほしい。
HTTP::Server#listen
と HTTP::Server#listen_fork
Mac OS X だと現状 #listen_fork
は動かない。
[warn] kevent: Bad file descriptor
[PCL] Stale coroutine called: curr=0x10621b000 exitto=0x7fca1b800000 caller=0x7fca1b800000
みたいなログがぶわっと出るので、多分 fork 周りでなんらかのエラーがあるのだが、深追いする気も起きないので一旦放置で。
WAF とか
特にこれといって探してないけど、まだ Crystal の WAF はなさそうなので、今から参入して作れば有名 WAF 製作者になるチャンスだと思う。
というか、 Crystal の依存管理は go よろしく github から直で持ってくる感じなのが『いいけど悪い』という感覚。 rubygems のイケてるところは、探しやすさだって含まれてると思う。 GitHub が悪いわけじゃないけど、 GitHub の中からよさそうなライブラリを探すのは至難の業なので、 v1.0.0 までにはなんらかのホスティングが始まってるといいなーと思って期待してる……んですけどどうなることやら。Manas という会社が支援してるらしいし金はあるだろうからシュッとやってほしい。
HTML 周りとか
まだ見てない。 HTTP::Builder
から jBuilder とかの臭いを感じる。その DSL でなんとかするのやめて素直にパーサ書きましょうよ、絶対間違ってますって。
まとめ
まだ早い