Wai + WarpでWebサーバを作る
WarpってWebサーバらしいですよ。
いやまあ今回はそういう前置きはいいんですけど、
Yesodの人ことSnoymanさんが作っているWaiとWarpでWebサーバを書いてみようと思います。
まずは調査。
Waiとはなんぞや。
Network.Wai
Web Application Interfaceらしいです。単体だとナンノコッチャという感じですが、ドキュメントを見るとHTTPのRequestとResponseっぽい型の定義があって、それらを扱うApplicationという型があります。
定義:
type Application = Request -> ResourceT IO Response
RequestをもらってResponseを返す関数がApplicationです。まあそのまんまですね。
RequestはHTTPの定義通りなので省略、Responseはなんか若干めんどくさいけど後で少し。
それとは別にWarpというのがあります。
こいつは、いわゆるWebサーバの本体っていうか、HTTPのコネクションを管理したり、ソケットを管理したりするやつみたいです。
Network.Wai.Handler.Warp
run :: Port -> Application -> IO ()
こいつを使います。
PortはIntなのでいいんですが、このためにはApplicationを作らなければいけません。
ということで、簡単に作りました。
import Network.Wai import Network.Wai.Handler.Warp import Network.HTTP.Types import Blaze.ByteString.Builder.Char.Utf8 server :: Application server _ = return $ ResponseBuilder status200 [] $ fromString "hello" main :: IO () main = run 8080 server
動かす。
% ghci Main.hs ...略 *Main> main
クライアント側。
% curl -i http://localhost:8080/ HTTP/1.1 200 OK Server: Warp/1.3.0 Transfer-Encoding: chunked hello
おわり。
ここで、Responseの型がこんななので、
import qualified Network.HTTP.Types as H import qualified Data.Conduit as C data Response = ResponseFile H.Status H.ResponseHeaders FilePath (Maybe FilePart) | ResponseBuilder H.Status H.ResponseHeaders Builder | ResponseSource H.Status H.ResponseHeaders (C.Source (C.ResourceT IO) (C.Flush Builder)) deriving Typeable
今回はとりあえず文字列を返すということでResponseBuilderを使ってみましたが、ファイルをそのまま返したりもできるみたいです。
server :: Application server _ = return $ ResponseFile status200 [] "index.html" Nothing
さっきと同様に、データを準備して動かす。
% echo 明日は晴れるでしょう > index.html % curl -i http://localhost:8080/ HTTP/1.1 200 OK Server: Warp/1.3.0 Content-Length: 31 明日は晴れるでしょう
ファイル用意せずにやるとちゃんと404 NotFoundが返ってきます。賢いですね。
レスポンスヘッダにContent-typeくらい書けよという気もしますが。
あとは定義の通り、ConduitのSourceを返すこともできる。
それと、わざわざWarpでサーバ立てなくてもいいように、Waiだけをテストするためのパッケージもあります。
Network.Wai.Test
さすが気が利くというか、ありがたいことです。