on master help sign in
Disable timeouts as soon as request body is fully consumed.

This addresses the common case of a non-chunked request body. Previously, we would wait until a zero-length `ByteString` is returned, but that is suboptimal for some cases. For more information, see issue #351.

commit8655714bbb1bde6e24736ca712bc30134ad79be4 authorMichael Snoyman <michael@snoyman.com> dateFri 20 Mar 2015, 11:10 (12 years ago) parent3dd01eb
3 files changed+30−4
warp/ChangeLog.md+5 warp/Network/Wai/Handler/Warp.hs+8 warp/Network/Wai/Handler/Warp/Request.hs+17−4
warp/ChangeLog.md+5
@@ -1,6 +1,11 @@
11## 3.0.9.4
22
33* Fix [missing `IORef` tweak](https://github.com/yesodweb/wai/issues/351)
4+* Disable timeouts as soon as request body is fully consumed. This addresses
5+ the common case of a non-chunked request body. Previously, we would wait
6+ until a zero-length `ByteString` is returned, but that is suboptimal for some
7+ cases. For more information, see [issue
8+ 351](https://github.com/yesodweb/wai/issues/351).
49
510## 3.0.9.3
611
warp/Network/Wai/Handler/Warp.hs+8
@@ -17,6 +17,14 @@
1717---------------------------------------------------------
1818
1919-- | A fast, light-weight HTTP server handler for WAI.
20+--
21+-- Note on slowloris timeouts: to prevent slowloris attacks, timeouts are used
22+-- at various points in request receiving and response sending. One interesting
23+-- corner case is partial request body consumption; in that case, Warp's
24+-- timeout handling is still in effect, and the timeout will not be triggered
25+-- again. Therefore, it is recommended that once you start consuming the
26+-- request body, you consume the entire body promptly. For more information,
27+-- see <https://github.com/yesodweb/wai/issues/351>.
2028module Network.Wai.Handler.Warp (
2129 -- * Run a Warp server
2230 run
warp/Network/Wai/Handler/Warp/Request.hs+17−4
@@ -62,7 +62,7 @@ recvRequest settings conn ii addr src = do
6262 te = idxhdr ! idxTransferEncoding
6363 handleExpect conn httpversion expect
6464 (rbody, remainingRef, bodyLength) <- bodyAndSource src cl te
65− rbody' <- timeoutBody th rbody
65+ rbody' <- timeoutBody remainingRef th rbody
6666 let req = Request {
6767 requestMethod = method
6868 , httpVersion = httpversion
@@ -138,10 +138,22 @@ isChunked _ = False
138138
139139----------------------------------------------------------------
140140
141−timeoutBody :: Timeout.Handle -> IO ByteString -> IO (IO ByteString)
142−timeoutBody timeoutHandle rbody = do
141+timeoutBody :: Maybe (I.IORef Int) -- ^ remaining
142+ -> Timeout.Handle
143+ -> IO ByteString
144+ -> IO (IO ByteString)
145+timeoutBody remainingRef timeoutHandle rbody = do
143146 isFirstRef <- I.newIORef True
144147
148+ let checkEmpty =
149+ case remainingRef of
150+ Nothing -> return . S.null
151+ Just ref -> \bs -> if S.null bs
152+ then return True
153+ else do
154+ x <- I.readIORef ref
155+ return $! x <= 0
156+
145157 return $ do
146158 isFirst <- I.readIORef isFirstRef
147159
@@ -158,7 +170,8 @@ timeoutBody timeoutHandle rbody = do
158170 -- because the application is not interested in more bytes, or
159171 -- because there is no more data available, pause the timeout
160172 -- handler again.
161− when (S.null bs) (Timeout.pause timeoutHandle)
173+ isEmpty <- checkEmpty bs
174+ when isEmpty (Timeout.pause timeoutHandle)
162175
163176 return bs
164177