2014-11-18 1 views
3

Я хотел бы реализовать потоки больших данных (в обоих направлениях) с помощью 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 

Но сбой записи с исключение, спрятанное в toplevel: Слишком много байтов, прочитанных. Это, очевидно, пример TooManyBytesReadException, но я не мог найти, куда его бросили. Написание меньшего количества данных, таких как 1MB, работает так, как ожидалось.

Мои вопросы:

  1. Где/как исправить предел для чтения?
  2. Будет ли этот поток данных без загрузки всего запроса POST в память? Если нет, как это исправить?

ответ

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 

This bit в застёжкой делает что-то вроде

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

и x-www-form-urlencoded по умолчанию Curl в.

+0

Вероятно, «приложение/октет-поток» будет более подходящим типом контента. – nh2

1

Чтобы следить за предыдущим ответом: поскольку формы типа application/x-www-form-urlencoded настолько распространены, как удобная функция Snap автоматически декодирует их для вас и помещает их в карту параметров в запросе. Идея аналогична по духу, например, $_POST с PHP.

Однако, поскольку эти карты считываются в ОЗУ, наивно декодирование неограниченных количеств этих данных позволит злоумышленнику тривиально выполнять DoS-сервер, отправив произвольные количества этого ввода до выгрузки кучи. По этой причине snap-server ограничивает объем данных, которые он хочет читать таким образом.