簡體   English   中英

流字節串作為WAI HTTP服務器響應主體

[英]Streaming bytestring as WAI HTTP server response body

我有一個基於BS.readFile的函數的值body :: BS.ByteString (ResourceT IO) () 我想將該值作為Wai Application的響應主體進行流式處理。 有一個幫助器streamingResponse ,它采用Stream (Of ByteString) IO r類型的值。 我能到我的轉換BS.ByteString (ResourceT IO) ()Stream (Of ByteString) (ResourceT IO) ()通過使用BS.toChunks ,但它包含一個額外的ResourceT單子層。 body傳遞給streamingResponse給我:

Couldn't match type ‘ResourceT IO’ with ‘IO’
  Expected type: Stream (Of ByteString) IO ()
    Actual type: Stream (Of ByteString) (ResourceT IO) ()

我已經嘗試過各種方法,例如將它們包裝在runResourceT中,綁定和提升值等,但實際上不知道如何進行。 這里的在整個項目的行,如果需要額外的上下文。

更新0

hoist runResourceT body似乎要進行類型檢查。 有人還向我介紹了Haskell Pipes 線程 ,這可能是一個非常相關的問題,並且可能提示解決方案。

代替readFilewithFile + hSetBinaryMode + Data.ByteString.Streaming.fromHandle是否足夠?

fromHandle產生一個ByteString IO () ,可以將其轉換為streamingResponsestreamingBody可以接受的Stream (Of ByteString) IO ()

存在將withFile包圍操作放在何處的問題。 根據WAI文檔 ,您可以將Application構建功能的結果包裝在一起:

請注意,從WAI 3.0開始,此類型以連續傳遞樣式構造,以允許正確安全地處理資源。 過去是通過其他方式(例如ResourceT)來處理的。

[...]

為了以異常安全的方式分配資源,可以在對responseStream的調用之外使用括號模式。


注意: streaming- fromHandle文檔指出,到達EOF時fromHandle自動關閉句柄。 看一下實現, 情況似乎並非如此 您需要withFile才能正確關閉句柄。

如果我們想允許StreamResourceT ,則可以不使用stream-wai的功能(僅適用於基於IO Stream ),而可以在諸如network-wai的 responseStream之類的功能之上構建:

import           Control.Monad.Trans.Resource
import           Network.Wai                     
import           Streaming                   
import qualified Streaming.Prelude               as S
import           Data.ByteString.Builder (byteString, Builder)

streamingResponseR :: Stream (Of ByteString) (ResourceT IO) r
                   -> Status
                   -> ResponseHeaders
                   -> Response
streamingResponseR stream status headers =
    responseStream status headers streamingBody
    where
    streamingBody writeBuilder flush =
        let writer a =
                do liftIO (writeBuilder (byteString a))
                    -- flushes for every produced bytestring, perhaps not optimal
                   liftIO flush
         in runResourceT $ void $ S.effects $ S.for stream writer

streamingBody具有類型StreamingBody ,這實際上是一個功能的類型的同義詞(Builder -> IO ()) -> IO () -> IO ()需要一個寫入回調和沖洗回調作為參數,並使用它們來寫使用范圍內的某些數據源進行響應。 (請注意,這些回調是由WAI提供的 ,而不是由用戶提供的。)

在我們的例子中,數據源是StreamResourceTStream 我們需要使用liftIO來提升write和flush回調(位於IO ),還要記住調用runResourceT以在最后返回簡單的IO操作。


如果我們想沖洗后, 發出的字節串的累計長度達到一定極限的反應如何?

每次達到限制時,我們將需要一個函數(此處未實現)來創建除法:

breaks' :: Monad m 
        => Int 
        -> Stream (Of ByteString) m r 
        -> Stream (Stream (Of ByteString) m) m r
breaks' breakSize = undefined

然后,我們可以在編寫流之前使用intercalates在每組之間插入刷新動作:

streamingBodyFrom :: Stream (Of ByteString) (ResourceT IO) () 
                  -> Int 
                  -> StreamingBody
streamingBodyFrom stream breakSize writeBuilder flush =
    let writer a = liftIO (writeBuilder (byteString a))
        flusher = liftIO flush
        broken = breaks' breakSize stream
     in runResourceT . S.mapM_ writer . S.intercalates flusher $ broken

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM