10

問題

Haskell と Pipes ライブラリを使用して単純な Web サーバーを実装しようとしています。循環トポロジーまたはダイヤモンド トポロジーはパイプでは不可能であることを理解しましたが、私がしようとしているのはそうであると思いました。したがって、私の望ましいトポロジは次のとおりです。

                                 -GET--> handleGET >-> packRequest >-> socketWriteD
                                 |
socketReadS >-> parseRequest >-routeRequest
                                 |
                                 -POST-> handlePOST >-> packRequest >-> socketWriteD

チェーンで使用するタイプがありますHTTPRequest RequestLine Headers Message。は、ソケットからバイトを受け取り、Attoparsec を使用してバイトをオブジェクトに解析する に転送します。次に、実装する HTTP メソッドの数に応じて、パイプが少なくとも 2 回、場合によってはそれ以上分岐するようにします。各関数は、アップストリームからオブジェクトを受信し、オブジェクトをに転送する必要があります。これにより、HTTPResponse オブジェクトが で送信できるようにまとめられます。HTTPResponse StatusLine Headers MessagesocketReadSparseRequestHTTPRequesthandle<method>HTTPRequestHTTPResponsepackRequestByteStringsocketWriteS

次のコードは、GHC に型を推論させるかどうかを型チェックrouteRequest'''します (私のものは少しずれているようです)。ただし、 の後に何も実行されていないようparseRequestです。誰でも理由を理解するのを手伝ってもらえますか?

コード

routeRequest分岐を処理する必要がある次のコードがあります。

routeRequest''' ::
    (Monad m, Proxy p1, Proxy p2, Proxy p3)
    => () -> Consumer p1 HTTPRequest (Pipe p2 HTTPRequest HTTPRequest (Pipe p3 HTTPRequest HTTPRequest m)) r
routeRequest''' () = runIdentityP . hoist (runIdentityP . hoist runIdentityP) $ forever $ do
    httpReq <- request ()
    let method = getMethod httpReq
    let (URI uri) = getURI httpReq
    case method of
      GET -> lift $ respond httpReq
      POST -> lift $ lift $ respond httpReq

routeRequest'' = runProxyK $ routeRequest''' <-< unitU
routeRequest' socket = runProxyK $ raiseK (p4 socket <-< handleGET) <-< routeRequest''
routeRequest socket = (p4 socket <-< handlePOST) <-< (routeRequest' socket)

handleGETそしてhandlePOST、そのように実装されています:

handleGET :: Proxy p => () -> p () HTTPRequest r ByteString IO r
handleGET () = runIdentityP $ do
    httpReq <- request ()
    let (URI uri) = getURI httpReq
    lift $ Prelude.putStrLn "GET"
    respond $ B.append (B.pack "GET ") uri


handlePOST :: Proxy p => () -> p () HTTPRequest r ByteString IO r
handlePOST () = runIdentityP $ do
    httpReq <- request ()
    let (URI uri) = getURI httpReq
    lift $ Prelude.putStrLn "POST"
    respond $ B.append (B.pack "POST ") uri

プロキシの略記は次のとおりです。

p1 socket = socketReadS 32 socket
p2 = parseRequestProxy 
p4 socket = socketWriteD socket

最後に、次のように全体を実行します。

main = serveFork (Host "127.0.0.1") "8080" $
    \(socket, remoteAddr) -> do
        ret <- runProxy $ runEitherK $ p1 socket >-> printD >-> p2 >-> printD  >-> routeRequest socket 
        Prelude.putStrLn $ show ret

の型シグネチャparseRequestProxyは次のとおりです。

parseRequestProxy
  :: (Monad m, Proxy p) =>
     ()
     -> Pipe
          (EitherP Control.Proxy.Attoparsec.Types.BadInput p)
          ByteString
          HTTPRequest
          m
          r

編集

ソースコードのリポジトリはこちら。加工はしておりませんので、ご利用は自己責任でお願いします。https://bitbucket.org/Dwilson1234/haskell-web-server/overview

4

2 に答える 2

8

ダイアモンド トポロジを処理できないと最初に言ったとき、私は間違っていました。ArrowChoiceのようなインターフェイスを使用してこれを行うための賢明な方法を後で発見し、とコンビネータpipes-3.2.0の形式でソリューションを含めました。それがどのように機能するかを説明します:leftDrightD

プロキシ トランスフォーマーをネストする代わりに、結果をLeftorでラップします。Right

routeRequest ::
    (Monad m, Proxy p)
    => () -> Pipe p HTTPRequest (Either HTTPRequest HTTPRequest) m r
routeRequest () = runIdentityP $ forever $ do
    httpReq <- request ()
    let method = getMethod httpReq
    let (URI uri) = getURI httpReq
    respond $ case method of
      GET  -> Left  httpReq
      POST -> Right httpReq

次に、各ハンドラーを各ブランチに選択的に適用し、ブランチをマージできます。

routeRequest >-> leftD handleGET >-> rightD handlePOST >-> mapD (either id id)
    :: (Monad m, Proxy p) => () -> Pipe p HTTPRequest ByteString IO r

2 つ以上のブランチがある場合はEither、s をネストする必要がありますが、これは機能の制限にすぎませんArrowChoice

于 2013-04-25T03:53:58.130 に答える
1

私はあなたのコードを実行していませんが、問題を見つけたと思います。

routeRequest'' = runProxyK $ routeRequest''' <-< unitU

routeRequest'''提供するものが何もない unitU からのデータを要求しているため、ハングします。

:t runProxy $ unitU >-> printD

チェックを入力しますが、何も実行されません。

データが間違ったレベルのモナド トランスフォーマーに送信されているようです。 に流れているデータは に流れているrouteRequestはずrouteRequest'''です。モナドトランスフォーマーの間違ったレベルに流れ込むデータは、すべてを型チェックするために型シグネチャを残す必要がある可能性があります。型シグネチャを使用すると、上流から来るrouteRequestことが期待()されます。おそらく、型シグネチャがないと、ポリモーフィックになることが許可されます。

「パイプを閉じる」ことができるというあなたの定義では、タイプシグネチャがないrouteRequest場合でも構築を許可しない unitD を使用することで、それが呼ばれるものだと思います。routeRequest'''

于 2013-04-25T03:24:06.737 に答える