@@ -25,17 +25,20 @@ import Network.Wai.Handler.Warp.Conduit
2525import Network.Wai.Handler.Warp.ReadInt
2626import Network.Wai.Handler.Warp.Types
2727import Prelude hiding (lines)
28+import qualified Network.Wai.Handler.Warp.Timeout as Timeout
2829
2930-- FIXME come up with good values here
3031maxTotalHeaderLength :: Int
3132maxTotalHeaderLength = 50 * 1024
3233
33−parseRequest :: Connection -> Port -> SockAddr
34+parseRequest :: Connection
35+ -> Timeout.Handle
36+ -> Port -> SockAddr
3437 -> Source (ResourceT IO) ByteString
3538 -> ResourceT IO (Request, IO (ResumableSource (ResourceT IO) ByteString))
36−parseRequest conn port remoteHost' src1 = do
39+parseRequest conn timeoutHandle port remoteHost' src1 = do
3740 (src2, headers') <- src1 $$+ takeHeaders
38− parseRequest' conn port headers' remoteHost' src2
41+ parseRequest' conn timeoutHandle port headers' remoteHost' src2
3942
4043handleExpect :: Connection
4144 -> H.HttpVersion
@@ -53,13 +56,14 @@ handleExpect conn hv front (x:xs) = handleExpect conn hv (front . (x:)) xs
5356
5457-- | Parse a set of header lines and body into a 'Request'.
5558parseRequest' :: Connection
59+ -> Timeout.Handle
5660 -> Port
5761 -> [ByteString]
5862 -> SockAddr
5963 -> ResumableSource (ResourceT IO) ByteString -- FIXME was buffered
6064 -> ResourceT IO (Request, IO (ResumableSource (ResourceT IO) ByteString))
61−parseRequest' _ _ [] _ _ = throwIO $ NotEnoughLines []
62−parseRequest' conn port (firstLine:otherLines) remoteHost' src = do
65+parseRequest' _ _ _ [] _ _ = throwIO $ NotEnoughLines []
66+parseRequest' conn timeoutHandle port (firstLine:otherLines) remoteHost' src = do
6367 (method, rpath', gets, httpversion) <- parseFirst firstLine
6468 let (host',rpath)
6569 | S.null rpath' = ("", "/")
@@ -97,7 +101,16 @@ parseRequest' conn port (firstLine:otherLines) remoteHost' src = do
97101 , requestHeaders = heads
98102 , isSecure = False
99103 , remoteHost = remoteHost'
100− , requestBody = rbody
104+ , requestBody = do
105+ -- Timeout handling was paused after receiving the full request
106+ -- headers. Now we need to resume it to avoid a slowloris
107+ -- attack during request body sending.
108+ liftIO $ Timeout.resume timeoutHandle
109+ -- As soon as we finish receiving the request body, whether
110+ -- because the application is not interested in more bytes, or
111+ -- because there is no more data available, pause the timeout
112+ -- handler again.
113+ addCleanup (const $ liftIO $ Timeout.pause timeoutHandle) rbody
101114 , vault = mempty
102115#if MIN_VERSION_wai(1, 4, 0)
103116 , requestBodyLength =