yunomuのブログ

趣味のこと

Wai+WarpのWebサーバを拡張する

頂いたラムを飲みながらお送りしております。

で、前の続きです。
前の:Wai + WarpでWebサーバを作る - yunomuのブログ

Waiのマニュアルを見てるとちょっと面白い型があって
Network.Wai

type Application = Request -> ResourceT IO Response
type Middleware = Application -> Application

Applicationは前回出てきたとおりで、今回は下のMiddleware型。
Applicationを取ってApplicationを返すということで、多分引数として受け取ったApplicationに機能追加したり入力や出力を加工したりするために使うんでしょう。マニュアルにもGZIP encodingに使うとか書いてあるし。
でも役割的にMiddlewareっていうよりこれWrapperだよねぇと思わなくもない。

ということで、今回は前回作った超適当なWebサーバに機能を追加して遊んでみようと思います。
基本は前回のこれ。

{-# LANGUAGE OverloadedStrings #-}

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 = do
    run 8080 server

これに新機能を追加する。入出力の加工の方がわかりやすいかもしれないんだけど、ここではとりあえずHTTPの新しいメソッドを定義してみます。

{-# LANGUAGE OverloadedStrings #-}

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"

middleware :: Middleware
middleware app request = do
    if requestMethod request == "MOGE"
      then return $ ResponseBuilder status200 [] $ fromString res
      else app request
  where
    res = show $ pathInfo request

main :: IO ()
main = run 8080 $ middleware server

MOGEメソッドを作った。
機能は、ようはECHOです。パスをそのまま返す。メソッド名もECHOにすればよかった……。

Applicationの時もそうだったけど、今回のMiddlewareもやっぱりなんか型がわかりづらくて、

middleware :: Middleware

というのはtypeを展開すると要するに

middleware :: Application -> Request -> ResourceT IO Response

という事になります。

やってることは見ての通り、メソッドがMOGEだったらechoして、そうじゃなかったらWAIに任せるという感じです。
動作確認は、curlではできないのでtelnetでやります。

% telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
MOGE harukasanmoe HTTP/1.0

HTTP/1.0 200 OK
Server: Warp/1.3.0

["harukasanmoe"]Connection closed by foreign host.

つつがなく成功しているようですね。

ちなみにWarpはHTTPのバージョン指定をしないとちゃんと動かないみたいです。勝手に0.9だと解釈してくれたりはしない。律儀な奴だ。
けどバージョン番号の部分は"3.0"でも"aaa"でも”^D"でもいいみたいで、なんか適当に0.9だか1.0だかだと判断して返してくれます。ただ1.1にするとちゃんとKeepAliveが働く。なんとなくパーサ書くのを中途半端に面倒臭がった形跡が見え隠れする。HTTPの仕様をそんなに詳しく知らないのでなんとも言えないけども。
あ、MOGEメソッドもバージョン番号を1.1にするとちゃんと1.1相当に動きます。話のわかる奴だ。
ということで、これでWebDAVの実装も余裕ですね。いや全然余裕じゃないっていうか、Warpの半分を書き換えた上でそれ以上の機能を追加する羽目になるような気がしますけども。

あとは、Network.Wai.Testを使ったりなんたり、というのは面倒くさいのでコードの参照で。
exercises/wai-example at master · yunomu/exercises · GitHub
あくまでもWaiのテストであって、Warpのテストではない点は注意です。Wai的にオッケーなら未知の形式のメソッドでも普通に通っちゃいますので。