@@ -39,6 +39,7 @@ module Network.Wai.Handler.Warp
3939 , InvalidRequest (..)
4040#if TEST
4141 , takeHeaders
42+ , readInt
4243#endif
4344 ) where
4445
@@ -81,6 +82,8 @@ import Blaze.ByteString.Builder.Char8 (fromChar, fromShow)
8182import Data.Monoid (mappend, mconcat)
8283import Network.Sendfile
8384
85+import qualified System.PosixCompat.Files as P
86+
8487import Control.Monad.IO.Class (liftIO)
8588import qualified Timeout as T
8689import Data.Word (Word8)
@@ -296,17 +299,29 @@ hasBody s req = s /= (H.Status 204 "") && requestMethod req /= "HEAD"
296299sendResponse :: T.Handle
297300 -> Request -> H.HttpVersion -> Socket -> Response -> IO Bool
298301sendResponse th req hv socket (ResponseFile s hs fp mpart) = do
299− Sock.sendMany socket $ L.toChunks $ toLazyByteString $ headers hv s hs False
302+ (hs', cl) <-
303+ case (readInt `fmap` lookup "content-length" hs, mpart) of
304+ (Just cl, _) -> return (hs, cl)
305+ (Nothing, Nothing) -> do
306+ cl <- P.fileSize `fmap` P.getFileStatus fp
307+ return (("Content-Length", B.pack $ show cl):hs, fromIntegral cl)
308+ (Nothing, Just part) -> do
309+ let cl = filePartByteCount part
310+ return (("Content-Length", B.pack $ show cl):hs, fromIntegral cl)
311+ Sock.sendMany socket $ L.toChunks $ toLazyByteString $ headers hv s hs' False
300312 if hasBody s req
301313 then do
302314 case mpart of
303− Nothing -> sendfile socket fp EntireFile (T.tickle th)
315+ Nothing -> sendfile socket fp PartOfFile {
316+ rangeOffset = 0
317+ , rangeLength = cl
318+ } (T.tickle th)
304319 Just part -> sendfile socket fp PartOfFile {
305320 rangeOffset = filePartOffset part
306321 , rangeLength = filePartByteCount part
307322 } (T.tickle th)
308323 T.tickle th
309− return $ lookup "content-length" hs /= Nothing
324+ return True
310325 else return True
311326sendResponse th req hv socket (ResponseBuilder s hs b)
312327 | hasBody s req = do
@@ -497,3 +512,8 @@ checkCR bs pos =
497512 then p
498513 else pos
499514{-# INLINE checkCR #-}
515+
516+-- Note: This function produces garbage on invalid input. But serving an
517+-- invalid content-length is a bad idea, mkay?
518+readInt :: S.ByteString -> Integer
519+readInt = S.foldl' (\x w -> x * 10 + fromIntegral w - 48) 0