LoginSignup
11
11

More than 5 years have passed since last update.

Crystal + HTTP Server

Posted at

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#listenHTTP::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 でなんとかするのやめて素直にパーサ書きましょうよ、絶対間違ってますって。

まとめ

まだ早い

11
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
11