如何通过管道将类型化的流程传递给wai-conduit的responseSource?

时间:2019-09-03 04:46:11

标签: haskell conduit haskell-warp typed-process

我想让warp运行一个进程,然后对该进程的输出进行响应。假定输出大于服务器的RAM。加载整个输出,然后响应不是一种选择。我以为我可以使用类似的方法来完成此任务

withProcessWait_ (setStdout createSource "cat largefile") (pure . responseSource ok200 [] . getStdout)

但是responseSource使用ConduitT i (Flush Builder) IO (),而createSource使用ConduitT i ByteString m ()。我不知道如何将ByteString管道转换为Flush Builder管道。


因此,我设计了一种似乎可行的解决方案,但遗憾的是它的定义不太简单:

responseProcess :: Status -> ResponseHeaders -> ProcessConfig in out err -> Response
responseProcess s hs cfg = responseStream s hs $ \send flush ->                                                                                                                                
     withProcessWait_ (setStdout createPipe cfg) $ \p@(getStdout -> h) ->
         let loop = do
             bs <- hGetSome h defaultChunkSize
             unless (BS.null bs) (send (byteString bs) *> flush *> loop)
         in loop *> hClose h 

。即使我可以用mkStreamSpec包裹起来进行修饰来修饰它,这也是必要的吗?还是我缺少一种更简单的方法?


编辑:对解决方案的评论:

intersperseC让我一起使用ChunkFlush。这样就解决了Flush Builder / ByteString的转换问题。我尚未对其进行测试,但是它看起来不错,并且我相信它已经被使用了。

但是,我发现了

withProcessWait_ (setStdout createSource "cat largefile") $ \p ->
    responseSource ok200 [] (getStdout p .| mapC (Chunk . byteString) .| intersperseC Flush)

太早关闭进程句柄。因此,我需要自己管理管道:使用createPipe而不是createSource。但这意味着我需要在最后调用hClose,这意味着我需要一个返回IO ()的响应处理程序; (responseRaw除外)是唯一的responseStream,它使用StreamingBody作为管道的替代。因此,我得出结论,我需要原始的解决方案,并且Conduit不能用于流式处理。如果不正确,请随时纠正。

1 个答案:

答案 0 :(得分:0)

responseSource具有类型

  

responseSource ::状态-> ResponseHeaders->源IO(刷新   生成器)->响应

并且Flush的定义是

  

数据刷新a =块a |冲洗

也就是说,类型Flush Builder的值可以是Builder或指示 warp 刷新输出流的命令。

Builder来自binary软件包。它基本上是一个字节块的表示形式,已针对有效串联进行了优化。可以使用fromByteString函数从ByteString构造它。

知道了这一点,并使用导管中的mapC,我们可以定义此适配器:

adapter :: :: Monad m => ConduitT ByteString (Flush Builder) m ()
adapter = mapC (Chunk . fromByteString) 

但是有一个问题,适配器从不刷新。但是我们可以通过intersperseC来插入命令:

adapter :: :: Monad m => ConduitT ByteString (Flush Builder) m ()
adapter = mapC (Chunk . fromByteString) .| intersperseC Flush

如果我们不想在每个块之后都刷新怎么办?也许我们可以使用chunksOfCE对字节块进行分组,然后再将其转换为Flush值。