@@ -92,7 +92,6 @@ import Data.Typeable (Typeable)
9292
9393import Control.Monad.Trans.Resource (ResourceT, runResourceT)
9494import qualified Data.Conduit as C
95−import qualified Data.Conduit.Binary as CB
9695import qualified Data.Conduit.List as CL
9796import Data.Conduit.Blaze (builderToByteString)
9897import Control.Exception.Lifted (throwIO)
@@ -116,7 +115,7 @@ import qualified Network.HTTP.Types as H
116115import qualified Data.CaseInsensitive as CI
117116import System.IO (hPutStrLn, stderr)
118117import ReadInt (readInt64)
119−
118+import qualified Data.IORef as I
120119
121120#if WINDOWS
122121import Control.Concurrent (threadDelay)
@@ -322,17 +321,32 @@ parseRequest' port (firstLine:otherLines) remoteHost' src = do
322321 | otherwise = ("", rpath')
323322 let heads = map parseHeaderNoAttr otherLines
324323 let host = fromMaybe host' $ lookup "host" heads
325− let len =
324+ let len0 =
326325 case lookup "content-length" heads of
327326 Nothing -> 0
328327 Just bs -> readInt bs
329328 let serverName' = takeUntil 58 host -- ':'
330329 -- FIXME isolate takes an Integer instead of Int or Int64. If this is a
331330 -- performance penalty, we may need our own version.
332− rbody <- C.prepareSource $
333− if len == 0
334− then mempty
335− else src C.$= CB.isolate len
331+ rbody <-
332+ if len0 == 0
333+ then return mempty
334+ else do
335+ -- We can't use the standard isolate, as its counter is not
336+ -- kept in a mutable variable.
337+ lenRef <- liftIO $ I.newIORef len0
338+ let isolate = C.Conduit $ return $ C.PreparedConduit push close
339+ push bs = do
340+ len <- liftIO $ I.readIORef lenRef
341+ let (a, b) = S.splitAt len bs
342+ len' = len - S.length a
343+ liftIO $ I.writeIORef lenRef len'
344+ return $ if len' == 0
345+ then C.Finished (if S.null b then Nothing else Just b) (if S.null a then [] else [a])
346+ else C.Producing push close [a]
347+ close = return []
348+ psrc <- C.prepareSource $ src C.$= isolate
349+ return $ C.Source $ return psrc
336350 return Request
337351 { requestMethod = method
338352 , httpVersion = httpversion
@@ -345,7 +359,7 @@ parseRequest' port (firstLine:otherLines) remoteHost' src = do
345359 , requestHeaders = heads
346360 , isSecure = False
347361 , remoteHost = remoteHost'
348− , requestBody = C.Source $ return rbody
362+ , requestBody = rbody
349363 , vault = mempty
350364 }
351365
@@ -515,7 +529,7 @@ sendResponse th req conn r = sendResponse' r
515529 , C.conduitClose = close
516530 }
517531
518− push x = return $ C.Producing [chunkedTransferEncoding x]
532+ push x = return $ C.Producing push close [chunkedTransferEncoding x]
519533 close = return [chunkedTransferTerminator]
520534
521535parseHeaderNoAttr :: ByteString -> H.Header
@@ -529,23 +543,24 @@ parseHeaderNoAttr s =
529543 in (CI.mk k, rest')
530544
531545connSource :: Connection -> T.Handle -> C.PreparedSource IO ByteString
532−connSource Connection { connRecv = recv } th = C.PreparedSource
533− { C.sourcePull = do
534− bs <- liftIO recv
535− if S.null bs
536− then return C.Closed
537− else do
538− when (S.length bs >= 2048) $ liftIO $ T.tickle th
539− return (C.Open bs)
540− , C.sourceClose = return ()
541− }
546+connSource Connection { connRecv = recv } th =
547+ src
548+ where
549+ src = C.PreparedSource
550+ { C.sourcePull = do
551+ bs <- liftIO recv
552+ if S.null bs
553+ then return C.Closed
554+ else do
555+ when (S.length bs >= 2048) $ liftIO $ T.tickle th
556+ return (C.Open src bs)
557+ , C.sourceClose = return ()
558+ }
542559
543560-- | Use 'connSendAll' to send this data while respecting timeout rules.
544561connSink :: Connection -> T.Handle -> C.Sink B.ByteString IO ()
545−connSink Connection { connSendAll = send } th = C.Sink $ return C.SinkData
546− { C.sinkPush = push
547− , C.sinkClose = close
548− }
562+connSink Connection { connSendAll = send } th =
563+ C.Sink $ return $ C.SinkData push close
549564 where
550565 close = liftIO (T.resume th)
551566 push x = do
@@ -553,7 +568,7 @@ connSink Connection { connSendAll = send } th = C.Sink $ return C.SinkData
553568 T.resume th
554569 send x
555570 T.pause th
556− return C.Processing
571+ return (C.Processing push close)
557572 -- We pause timeouts before passing control back to user code. This ensures
558573 -- that a timeout will only ever be executed when Warp is in control. We
559574 -- also make sure to resume the timeout after the completion of user code
@@ -616,7 +631,7 @@ takeHeaders =
616631
617632takeHeadersPush :: THStatus
618633 -> ByteString
619− -> ResourceT IO (THStatus, C.SinkResult ByteString [ByteString])
634+ -> ResourceT IO (C.SinkStateResult THStatus ByteString [ByteString])
620635takeHeadersPush (THStatus len _ _ ) _
621636 | len > maxTotalHeaderLength = throwIO OverLargeHeader
622637takeHeadersPush (THStatus len lines prepend) bs =
@@ -624,7 +639,7 @@ takeHeadersPush (THStatus len lines prepend) bs =
624639 -- no newline. prepend entire bs to next line
625640 Nothing -> do
626641 let len' = len + bsLen
627− return (THStatus len' lines (prepend . S.append bs), C.Processing)
642+ return $ C.StateProcessing $ THStatus len' lines (prepend . S.append bs)
628643 Just nl -> do
629644 let end = nl
630645 start = nl + 1
@@ -638,8 +653,8 @@ takeHeadersPush (THStatus len lines prepend) bs =
638653 if start < bsLen
639654 then do
640655 let rest = SU.unsafeDrop start bs
641− return (undefined, C.Done (Just rest) lines')
642− else return (undefined, C.Done Nothing lines')
656+ return $ C.StateDone (Just rest) lines'
657+ else return $ C.StateDone Nothing lines'
643658 -- more headers
644659 else do
645660 let len' = len + start
@@ -648,7 +663,7 @@ takeHeadersPush (THStatus len lines prepend) bs =
648663 then do
649664 let more = SU.unsafeDrop start bs
650665 takeHeadersPush (THStatus len' lines' id) more
651− else return (THStatus len' lines' id, C.Processing)
666+ else return $ C.StateProcessing $ THStatus len' lines' id
652667 where
653668 bsLen = S.length bs
654669 mnl = S.elemIndex 10 bs