@@ -115,7 +115,7 @@ import Data.List (foldl')
115import Control.Monad (forever, when)
115import Control.Monad (forever, when)
116import qualified Network.HTTP.Types as H
116import qualified Network.HTTP.Types as H
117import qualified Data.CaseInsensitive as CI
117import qualified Data.CaseInsensitive as CI
118−import System.IO (hPutStrLn, stderr)
118+import System.IO (hPrint, stderr)
119import ReadInt (readInt64)
119import ReadInt (readInt64)
120import qualified Data.IORef as I
120import qualified Data.IORef as I
121import Data.String (IsString (..))
121import Data.String (IsString (..))
@@ -198,7 +198,7 @@ bindPort p s = do
198 theBody addr =
198 theBody addr =
199 bracketOnError
199 bracketOnError
200 (Network.Socket.socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr))
200 (Network.Socket.socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr))
202 (\sock -> do
202 (\sock -> do
203 setSocketOption sock ReuseAddr 1
203 setSocketOption sock ReuseAddr 1
204 bindSocket sock (addrAddress addr)
204 bindSocket sock (addrAddress addr)
@@ -511,6 +511,7 @@ sendResponse th req conn r = sendResponse' r
511 needsChunked hs = isChunked' && not (hasLength hs)
511 needsChunked hs = isChunked' && not (hasLength hs)
512 isKeepAlive hs = isPersist && (isChunked' || hasLength hs)
512 isKeepAlive hs = isPersist && (isChunked' || hasLength hs)
513 hasLength hs = isJust $ lookup "content-length" hs
513 hasLength hs = isJust $ lookup "content-length" hs
514+ sendHeader = connSendMany conn . L.toChunks . toLazyByteString
515 sendResponse' :: Response -> ResourceT IO Bool
516 sendResponse' :: Response -> ResourceT IO Bool
516 sendResponse' (ResponseFile s hs fp mpart) = liftIO $ do
517 sendResponse' (ResponseFile s hs fp mpart) = liftIO $ do
@@ -523,17 +524,17 @@ sendResponse th req conn r = sendResponse' r
523 (Nothing, Just part) -> do
524 (Nothing, Just part) -> do
524 let cl = filePartByteCount part
525 let cl = filePartByteCount part
525 return $ addClToHeaders cl
526 return $ addClToHeaders cl
526− connSendMany conn $ L.toChunks $ toLazyByteString $
527− headers version s lengthyHeaders False
527+ let headers' = headers version s lengthyHeaders
528−
528+ sendHeader $ headers' False
529 T.tickle th
529 T.tickle th
531− if not (hasBody s req) then return isPersist else do
530+ if hasBody s req then do
532 case mpart of
531 case mpart of
533 Nothing -> connSendFile conn fp 0 cl (T.tickle th)
532 Nothing -> connSendFile conn fp 0 cl (T.tickle th)
534 Just part -> connSendFile conn fp (filePartOffset part) (filePartByteCount part) (T.tickle th)
533 Just part -> connSendFile conn fp (filePartOffset part) (filePartByteCount part) (T.tickle th)
535 T.tickle th
534 T.tickle th
536 return isPersist
535 return isPersist
538 addClToHeaders cl = (("Content-Length", B.pack $ show cl):hs, fromIntegral cl)
539 addClToHeaders cl = (("Content-Length", B.pack $ show cl):hs, fromIntegral cl)
@@ -544,10 +545,7 @@ sendResponse th req conn r = sendResponse' r
544 T.tickle th) body
545 T.tickle th) body
545 return (isKeepAlive hs)
546 return (isKeepAlive hs)
546 | otherwise = liftIO $ do
547 | otherwise = liftIO $ do
550− $ headers' False
548+ sendHeader $ headers' False
551 T.tickle th
549 T.tickle th
552 return isPersist
550 return isPersist
@@ -559,8 +557,16 @@ sendResponse th req conn r = sendResponse' r
559 `mappend` chunkedTransferTerminator
557 `mappend` chunkedTransferTerminator
560 else headers' False `mappend` b
558 else headers' False `mappend` b
562− sendResponse' (ResponseSource s hs bodyFlush) =
560+ sendResponse' (ResponseSource s hs bodyFlush)
563− response
561+ | hasBody s req = do
562+ let src = CL.sourceList [headers' needsChunked'] `mappend`
563+ (if needsChunked' then body C.$= chunk else body)
564+ src C.$$ builderToByteString C.=$ connSink conn th
565+ return $ isKeepAlive hs
566+ | otherwise = liftIO $ do
567+ sendHeader $ headers' False
565 body = fmap (\x -> case x of
571 body = fmap (\x -> case x of
566 C.Flush -> flush
572 C.Flush -> flush
@@ -568,26 +574,12 @@ sendResponse th req conn r = sendResponse' r
568 headers' = headers version s hs
574 headers' = headers version s hs
569 -- FIXME perhaps alloca a buffer per thread and reuse that in all
575 -- FIXME perhaps alloca a buffer per thread and reuse that in all
570 -- functions below. Should lessen greatly the GC burden (I hope)
576 -- functions below. Should lessen greatly the GC burden (I hope)
572− | not (hasBody s req) = liftIO $ do
574− $ L.toChunks $ toLazyByteString
577− return (checkPersist req)
580− CL.sourceList [headers' needsChunked'] `mappend`
581− (if needsChunked' then body C.$= chunk else body)
582− src C.$$ builderToByteString C.=$ connSink conn th
583− return $ isKeepAlive hs
584 needsChunked' = needsChunked hs
577 needsChunked' = needsChunked hs
585 chunk :: C.Conduit Builder IO Builder
578 chunk :: C.Conduit Builder IO Builder
586 chunk = C.Conduit
579 chunk = C.Conduit
587 { C.conduitPush = push
580 { C.conduitPush = push
588 , C.conduitClose = close
581 , C.conduitClose = close
591 conduit = C.Conduit push close
583 conduit = C.Conduit push close
592 push x = return $ C.Producing conduit [chunkedTransferEncoding x]
584 push x = return $ C.Producing conduit [chunkedTransferEncoding x]
593 close = return [chunkedTransferTerminator]
585 close = return [chunkedTransferTerminator]
@@ -695,7 +687,7 @@ defaultSettings = Settings
695 Just x -> go x
687 Just x -> go x
696 Nothing ->
688 Nothing ->
697 when (go' $ fromException e) $
689 when (go' $ fromException e) $
698− hPutStrLn stderr $ show e
690+ hPrint stderr e
699 , settingsTimeout = 30
691 , settingsTimeout = 30
700 , settingsIntercept = const Nothing
692 , settingsIntercept = const Nothing
701 , settingsManager = Nothing
693 , settingsManager = Nothing