将bytestring作为WAI HTTP服务器响应主体

时间:2018-05-03 10:58:49

标签: haskell bytestring haskell-wai haskell-warp haskell-streaming

我有一个基于BS.readFile的函数的值body :: BS.ByteString (ResourceT IO) ()。我想将该值作为Wai Application的响应主体进行传输。有一个帮助器,streamingResponse采用Stream (Of ByteString) IO r类型的值。我可以通过使用BS.toChunksBS.ByteString (ResourceT IO) ()转换为Stream (Of ByteString) (ResourceT IO) (),但它包含额外的ResourceT monad图层。将body传递给streamingResponse会让我:

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

我尝试了各种各样的东西,比如在runResourceT中包装东西,绑定和提升值等但是真的不知道如何继续。如果需要额外的上下文,Here是整个项目中的一行。

Update0

hoist runResourceT body似乎打字检查。有人还向我推荐了一个Haskell Pipes thread,这可能是一个非常相关的问题,并且可能暗示解决方案。

2 个答案:

答案 0 :(得分:1)

而不是readFilewithFile + hSetBinaryMode + Data.ByteString.Streaming.fromHandle是否足够?

fromHandle生成ByteString IO (),可以转换为Stream (Of ByteString) IO ()streamingResponse可以接受的streamingBody

存在将withFile包围操作放在何处的问题。根据{{​​3}},您可以用它包含Application - 构建函数的结果:

  

请注意,自WAI 3.0以来,此类型的结构是连续的   传递样式以允许适当的安全资源处理。这是   过去通过其他方式处理(例如,ResourceT)。

     

[...]

     

为了以异常安全的方式分配资源,您可以   在对responseStream的调用之外使用括号模式。

注意: streaming-bytestring 的文档说fromHandle在达到EOF时会自动关闭句柄。看一下实施,WAI documentation。您需要withFile才能正确关闭手柄。

答案 1 :(得分:1)

如果我们想要允许Stream中的ResourceT,我们可以不使用streaming-wai中的函数(仅适用于基于{Stream的{​​{1}} 1}})而是建立在network-waiIO之类的函数之上:

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的类型同义词,它将写回调和刷新回调作为参数,并使用它们来编写响应范围内的一些数据源。 (请注意,这些回调由WAI 提供,而不是由用户提供。)

在我们的案例中,数据来源是(Builder -> IO ()) -> IO () -> IO (),位于Stream。我们需要使用ResourceT解除写入和刷新回调(存在于IO中),并且还记得调用liftIO以在最后返回纯runResourceT个动作。

如果我们想在发出的字节串的累计长度达到某个限制之后才清除响应 ,该怎么办?

每次达到限制时,我们需要一个函数(此处未实现)来创建一个除法:

IO

然后我们可以在编写流之前使用intercalates插入每个组之间的刷新操作:

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