如何使用Snap框架实现数据流?

时间:2014-11-18 09:38:38

标签: haskell streaming haskell-snap-framework

我想使用Snap服务器实现大数据流(双向)。为了探索可能性,我创建了一个具有两个端点的示例程序 - 读取和写入。有一个非常简单的内部缓冲区,其中包含一个ByteString,写入端点的任何内容都会出现在读取端点中。 (目前没有办法如何终止流,但这对于此目的来说没问题。)

{-# LANGUAGE OverloadedStrings #-}                                           
import Control.Applicative                                                   
import Control.Concurrent.MVar.Lifted                                        
import Control.Monad                                                         
import Data.ByteString (ByteString)                                          
import Blaze.ByteString.Builder (Builder, fromByteString)                    
import Data.Enumerator                                                       
import qualified Data.Enumerator.List as E                                   
import Data.Enumerator.Binary (enumFile, iterHandle)                         
import Snap.Core                                                             
import Snap.Http.Server                                                      

main :: IO ()                                                                
main = do                                                                    
  buf <- newEmptyMVar                                                        
  quickHttpServe (site buf)                                                  

site :: MVar ByteString -> Snap ()                                           
site buf =                                                                   
    route [ ("read", modifyResponse (setBufferingMode False                  
                                     . setResponseBody (fromBuf buf)))       
          , ("write", runRequestBody (toBuf buf))                            
          ]                                                                  

fromBuf :: MVar ByteString -> Enumerator Builder IO a                        
fromBuf buf = E.repeatM (liftM fromByteString $ takeMVar buf)                

toBuf :: MVar ByteString -> Iteratee ByteString IO ()                        
toBuf buf = E.mapM_ (putMVar buf)

然后我在不同的终端运行

curl http://localhost:8000/read >/dev/nul

dd if=/dev/zero bs=1M count=100 | \
  curl --data-binary @- http://localhost:8000/write

但写入部分失败,异常转义为顶层:读取的字节数太多。这显然是TooManyBytesReadException的一个实例,但我无法找到抛出的位置。写入少量数据(如1MB)可以按预期工作。

我的问题是:

  1. 在何处/如何修正阅读限制?
  2. 这会流数据,而不会在内存中加载整个POST请求吗?如果没有,如何解决?

2 个答案:

答案 0 :(得分:2)

如果您向"application/x-www-form-urlencoded"添加不是/write的任何内容类型,它将会有效,例如:

dd if=/dev/zero bs=1M count=100 | \
  curl -H "Content-Type: application/json" --data-binary @- http://localhost:8000/write
Snap中的

This bit做了类似

的事情
if contentType == Just "application/x-www-form-urlencoded" then readData maximumPOSTBodySize
  where
    maximumPOSTBodySize = 10*1024*1024

x-www-form-urlencoded是curl的默认设置。

答案 1 :(得分:2)

要跟进上一个答案:因为application/x-www-form-urlencoded类型的表单非常常见,因为Snap会为您自动解码它们并将它们放入请求中的参数映射中。该想法在精神上类似于例如来自PHP的$_POST

然而,由于这些映射被读入RAM,天真地解码这些数据的无限量将允许攻击者通过向其发送任意数量的此输入直到堆耗尽来简单地DoS服务器。出于这个原因,snap-server限制了它愿意以这种方式阅读的数据量。