@@ -96,7 +96,7 @@ import Blaze.ByteString.Builder.HTTP
9696import Blaze.ByteString.Builder
9797 (copyByteString, Builder, toLazyByteString, toByteStringIO)
9898import Blaze.ByteString.Builder.Char8 (fromChar, fromShow)
99−import Data.Monoid (mappend, mconcat)
99+import Data.Monoid (mappend)
100100import Network.Sendfile
101101
102102import qualified System.PosixCompat.Files as P
@@ -211,12 +211,13 @@ serveConnection settings th onException port app conn remoteHost' = do
211211 serveConnection' :: ResourceT IO ()
212212 serveConnection' = do
213213 fromClient <- C.bufferSource $ sourceSocket th bytesPerRead conn
214− (len, env) <- parseRequest port remoteHost' fromClient
214+ env <- parseRequest port remoteHost' fromClient
215215 case settingsIntercept settings env of
216216 Nothing -> do
217217 -- Let the application run for as long as it wants
218218 liftIO $ T.pause th
219219 res <- app env
220+ -- FIXME flush the rest of the request body
220221 liftIO $ T.resume th
221222 keepAlive <- sendResponse th env conn res
222223 if keepAlive then serveConnection' else return ()
@@ -225,8 +226,8 @@ serveConnection settings th onException port app conn remoteHost' = do
225226 intercept fromClient conn
226227
227228parseRequest :: Port -> SockAddr
228− -> C.BSource IO S.ByteString
229− -> ResourceT IO (Integer, Request)
229+ -> C.BufferedSource IO S.ByteString
230+ -> ResourceT IO Request
230231parseRequest port remoteHost' src = do
231232 headers' <- takeHeaders src
232233 parseRequest' port headers' remoteHost' src
@@ -250,8 +251,8 @@ instance Exception InvalidRequest
250251parseRequest' :: Port
251252 -> [ByteString]
252253 -> SockAddr
253− -> C.BSource IO S.ByteString
254− -> ResourceT IO (Integer, Request)
254+ -> C.BufferedSource IO S.ByteString
255+ -> ResourceT IO Request
255256parseRequest' _ [] _ _ = throwIO $ NotEnoughLines []
256257parseRequest' port (firstLine:otherLines) remoteHost' src = do
257258 (method, rpath', gets, httpversion) <- parseFirst firstLine
@@ -270,21 +271,21 @@ parseRequest' port (firstLine:otherLines) remoteHost' src = do
270271 let serverName' = takeUntil 58 host -- ':'
271272 -- FIXME isolate takes an Integer instead of Int or Int64. If this is a
272273 -- performance penalty, we may need our own version.
273− rbody <- C.bufferSource $ src C.$= CB.isolate (fromIntegral len)
274− return (len, Request
275− { requestMethod = method
276− , httpVersion = httpversion
277− , pathInfo = H.decodePathSegments rpath
278− , rawPathInfo = rpath
279− , rawQueryString = gets
280− , queryString = H.parseQuery gets
281− , serverName = serverName'
282− , serverPort = port
283− , requestHeaders = heads
284− , isSecure = False
285− , remoteHost = remoteHost'
286− , requestBody = rbody
287− })
274+ rbody <- C.bufferSource $ src C.$= CB.isolate len
275+ return Request
276+ { requestMethod = method
277+ , httpVersion = httpversion
278+ , pathInfo = H.decodePathSegments rpath
279+ , rawPathInfo = rpath
280+ , rawQueryString = gets
281+ , queryString = H.parseQuery gets
282+ , serverName = serverName'
283+ , serverPort = port
284+ , requestHeaders = heads
285+ , isSecure = False
286+ , remoteHost = remoteHost'
287+ , requestBody = rbody
288+ }
288289
289290
290291takeUntil :: Word8 -> ByteString -> ByteString
@@ -427,41 +428,33 @@ sendResponse th req socket r = sendResponse' r
427428 `mappend` chunkedTransferTerminator
428429 else (headers' False) `mappend` b
429430
430− sendResponse' (ResponseStream res) =
431− res enumResponse
431+ sendResponse' (ResponseSource s hs body) =
432+ response
432433 where
433− enumResponse :: H.Status -> H.ResponseHeaders -> C.SinkM Builder IO Bool
434− enumResponse s hs =
435− response
436− where
437− headers' = headers version s hs
438− -- FIXME perhaps alloca a buffer per thread and reuse that in all functiosn below. Should lessen greatly the GC burden (I hope)
439− response
440− | not (hasBody s req) = do
441− liftIO $ Sock.sendMany socket
442− $ L.toChunks $ toLazyByteString
443− $ headers' False
444− return (checkPersist req)
445− | otherwise = C.SinkM $ do
446− let sink = builderToByteString C.=$ sinkSocket th (isKeepAlive hs) socket
447− CL.fromList [headers' needsChunked'] C.$$ sink
448− C.genSink $ chunk' sink
449− needsChunked' = needsChunked hs
450− chunk' sink = if needsChunked'
451− then chunk C.=$ sink
452− else sink
453− chunk :: C.ConduitM Builder IO Builder
454− chunk = C.ConduitM $ return $ C.Conduit
455− { C.conduitPush = push
456− , C.conduitClose = close
457− }
458−
459− go [] = id
460− go [x] = (chunkedTransferEncoding x:)
461− go xs = (chunkedTransferEncoding (mconcat xs):)
462−
463− push xs = return $ C.ConduitResult C.Processing $ go xs []
464− close xs = return $ C.ConduitResult [] $ go xs [chunkedTransferTerminator]
434+ headers' = headers version s hs
435+ -- FIXME perhaps alloca a buffer per thread and reuse that in all
436+ -- functions below. Should lessen greatly the GC burden (I hope)
437+ response
438+ | not (hasBody s req) = do
439+ liftIO $ Sock.sendMany socket
440+ $ L.toChunks $ toLazyByteString
441+ $ headers' False
442+ return (checkPersist req)
443+ | otherwise = do
444+ let src =
445+ CL.sourceList [headers' needsChunked'] `mappend`
446+ (if needsChunked' then body C.$= chunk else body)
447+ src C.$$ builderToByteString C.=$ sinkSocket th socket
448+ return $ isKeepAlive hs
449+ needsChunked' = needsChunked hs
450+ chunk :: C.Conduit Builder IO Builder
451+ chunk = C.Conduit $ return $ C.PreparedConduit
452+ { C.conduitPush = push
453+ , C.conduitClose = close
454+ }
455+
456+ push x = return $ C.Producing [chunkedTransferEncoding x]
457+ close = return [chunkedTransferTerminator]
465458
466459parseHeaderNoAttr :: ByteString -> H.Header
467460parseHeaderNoAttr s =
@@ -473,14 +466,14 @@ parseHeaderNoAttr s =
473466 else rest
474467 in (CI.mk k, rest')
475468
476−sourceSocket :: T.Handle -> Int -> Socket -> C.Source IO ByteString
477−sourceSocket th len socket = C.Source
469+sourceSocket :: T.Handle -> Int -> Socket -> C.PreparedSource IO ByteString
470+sourceSocket th len socket = C.PreparedSource
478471 { C.sourcePull = do
479472 bs <- liftIO $ Sock.recv socket len
480473 liftIO $ T.tickle th
481474 return $ if S.null bs
482− then C.SourceResult C.StreamClosed []
483− else C.SourceResult C.StreamOpen [bs]
475+ then C.Closed
476+ else C.Open bs
484477 , C.sourceClose = return ()
485478 }
486479
@@ -488,23 +481,19 @@ sourceSocket th len socket = C.Source
488481--separate package.
489482
490483sinkSocket :: T.Handle
491− -> ret
492484 -> Socket
493− -> C.SinkM B.ByteString IO ret
494−sinkSocket th ret sock = C.SinkM $ return $ C.SinkData
485+ -> C.Sink B.ByteString IO ()
486+sinkSocket th sock = C.Sink $ return $ C.SinkData
495487 { C.sinkPush = push
496488 , C.sinkClose = close
497489 }
498490 where
499− close xs = do
491+ close = do
500492 liftIO (T.resume th)
501− liftIO $ Sock.sendMany sock xs
502− liftIO (T.resume th)
503− return (C.SinkResult [] ret)
504− push [] = return $ C.Processing
505− push xs = do
493+ return ()
494+ push x = do
506495 liftIO $ T.resume th
507− liftIO $ Sock.sendMany sock xs
496+ liftIO $ Sock.sendAll sock x
508497 liftIO $ T.pause th
509498 return $ C.Processing
510499 -- We pause timeouts before passing control back to user code. This ensures
@@ -523,7 +512,7 @@ data Settings = Settings
523512 , settingsHost :: String -- ^ Host to bind to, or * for all. Default value: *
524513 , settingsOnException :: SomeException -> IO () -- ^ What to do with exceptions thrown by either the application or server. Default: ignore server-generated exceptions (see 'InvalidRequest') and print application-generated applications to stderr.
525514 , settingsTimeout :: Int -- ^ Timeout value in seconds. Default value: 30
526− , settingsIntercept :: Request -> Maybe (C.BSource IO S.ByteString -> Socket -> ResourceT IO ())
515+ , settingsIntercept :: Request -> Maybe (C.BufferedSource IO S.ByteString -> Socket -> ResourceT IO ())
527516 , settingsManager :: Maybe Manager -- ^ Use an existing timeout manager instead of spawning a new one. If used, 'settingsTimeout' is ignored. Default is 'Nothing'
528517 }
529518
@@ -550,7 +539,7 @@ defaultSettings = Settings
550539 go' (Just ThreadKilled) = False
551540 go' _ = True
552541
553−takeHeaders :: C.BSource IO ByteString -> ResourceT IO [ByteString]
542+takeHeaders :: C.BufferedSource IO ByteString -> ResourceT IO [ByteString]
554543takeHeaders src = do
555544 !x <- forceHead ConnectionClosedByPeer src
556545 takeHeaders' 0 id id x src
@@ -561,7 +550,7 @@ takeHeaders' :: Int
561550 -> ([ByteString] -> [ByteString])
562551 -> ([ByteString] -> [ByteString])
563552 -> ByteString
564− -> C.BSource IO ByteString
553+ -> C.BufferedSource IO ByteString
565554 -> ResourceT IO [ByteString]
566555takeHeaders' !len _ _ _ _ | len > maxTotalHeaderLength = throwIO OverLargeHeader
567556takeHeaders' !len !lines !prepend !bs src = do
@@ -590,7 +579,7 @@ takeHeaders' !len !lines !prepend !bs src = do
590579 if start < bsLen
591580 then {-# SCC "takeHeaders'.noMoreHeaders.yield" #-} do
592581 let !rest = {-# SCC "takeHeaders'.noMoreHeaders.yield.rest" #-} SU.unsafeDrop start bs
593− C.bsourceUnpull src [rest]
582+ C.bsourceUnpull src rest
594583 return lines'
595584 else return lines'
596585
@@ -606,7 +595,7 @@ takeHeaders' !len !lines !prepend !bs src = do
606595{-# INLINE takeHeaders' #-}
607596
608597forceHead :: InvalidRequest
609− -> C.BSource IO ByteString
598+ -> C.BufferedSource IO ByteString
610599 -> ResourceT IO ByteString
611600forceHead err src = do
612601 !mx <- src C.$$ CL.head