[英]Haskell Pipes and Branching
我正在嘗試使用Haskell和Pipes庫實現一個簡單的Web服務器。 我現在明白管道不可能使用循環或鑽石拓撲,但我認為我想要的是。 因此我想要的拓撲結構是:
-GET--> handleGET >-> packRequest >-> socketWriteD
|
socketReadS >-> parseRequest >-routeRequest
|
-POST-> handlePOST >-> packRequest >-> socketWriteD
我有HTTPRequest RequestLine Headers Message
和HTTPResponse StatusLine Headers Message
鏈中使用的HTTPResponse StatusLine Headers Message
類型。 socketReadS
從套接字中獲取字節並將它們轉發給parseRequest
,后者使用Attoparsec將字節解析為HTTPRequest
對象。 然后,我希望管道分支至少兩次,可能更多,具體取決於我實現的HTTP方法數量。 每個handle<method>
函數都應該從上游接收HTTPRequest
對象,並將HTTPResponse
對象轉發到packRequest
,它簡單地將HTTPResponse對象打包在准備與socketWriteS
一起發送的ByteString
。
如果我讓GHC推斷出routeRequest'''
的類型,那么下面的代碼類型routeRequest'''
(我的某種方式似乎有些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
我原來說你無法處理鑽石拓撲時錯了。 后來我發現了一種使用ArrowChoice
類接口的合理方法,並以leftD
和rightD
組合器的形式包含了leftD
pipes-3.2.0
中的解決方案。 我會解釋它是如何工作的:
您可以使用Left
或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
如果有兩個以上的分支,那么你將不得不窩Either
S,而這僅僅是如何限制ArrowChoice
工作。
我沒有運行你的代碼,但我認為我發現了一個問題。
routeRequest'' = runProxyK $ routeRequest''' <-< unitU
routeRequest'''
正在請求來自unitU的數據,該數據沒有供應,因此它會掛起。
:t runProxy $ unitU >-> printD
將鍵入檢查但沒有運行。
看起來數據被發送到monad變換器的錯誤級別,流入routeRequest
數據應該流入routeRequest'''
。 流入monad變換器的錯誤級別的數據可能導致您需要保留類型簽名以獲取要檢查的所有內容。 使用類型簽名routeRequest
期望a ()
來自上游,我敢打賭,沒有類型簽名,它被允許是多態的。
在你對routeRequest
的定義中,你可以“關閉管道”,我認為這就是所謂的,通過使用unitD,即使在routeRequest'''
沒有類型簽名時也會禁止你的構造。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.