warp 3.0.9.3 → 3.0.10
raw patch · 4 files changed
+71/−9 lines, 4 filesdep +vault
Dependencies added: vault
Files
- ChangeLog.md +10/−0
- Network/Wai/Handler/Warp.hs +29/−1
- Network/Wai/Handler/Warp/Request.hs +29/−7
- warp.cabal +3/−1
ChangeLog.md view
@@ -1,3 +1,13 @@+## 3.0.10++* Fix [missing `IORef` tweak](https://github.com/yesodweb/wai/issues/351)+* 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](https://github.com/yesodweb/wai/issues/351).+* Add `pauseTimeout` function+ ## 3.0.9.3 * Don't serve a 416 status code for 0-length files [keter issue #75](https://github.com/snoyberg/keter/issues/75)
Network/Wai/Handler/Warp.hs view
@@ -17,6 +17,21 @@ --------------------------------------------------------- -- | A fast, light-weight HTTP server handler for WAI.+--+-- Note on slowloris timeouts: to prevent slowloris attacks, timeouts are used+-- at various points in request receiving and response sending. One interesting+-- corner case is partial request body consumption; in that case, Warp's+-- timeout handling is still in effect, and the timeout will not be triggered+-- again. Therefore, it is recommended that once you start consuming the+-- request body, you either:+--+-- * consume the entire body promptly+--+-- * call the 'pauseTimeout' function+--+-- For more information, see <https://github.com/yesodweb/wai/issues/351>.+--+-- module Network.Wai.Handler.Warp ( -- * Run a Warp server run@@ -73,6 +88,8 @@ , Port , InvalidRequest (..) , ConnSendFileOverride (..)+ -- * Per-request utilities+ , pauseTimeout -- * Connection , Connection (..) , socketConnection@@ -105,10 +122,12 @@ import Network.Wai.Handler.Warp.Timeout import Network.Wai.Handler.Warp.Types import Control.Exception (SomeException)-import Network.Wai (Request, Response)+import Network.Wai (Request, Response, vault) import Network.Socket (SockAddr) import Data.Streaming.Network (HostPreference) import Data.ByteString (ByteString)+import qualified Data.Vault.Lazy as Vault+import Data.Maybe (fromMaybe) -- | Port to listen on. Default value: 3000 --@@ -284,3 +303,12 @@ -- Since 3.0.5 setProxyProtocolOptional :: Settings -> Settings setProxyProtocolOptional y = y { settingsProxyProtocol = ProxyProtocolOptional }++-- | Explicitly pause the slowloris timeout.+--+-- This is useful for cases where you partially consume a request body. For+-- more information, see <https://github.com/yesodweb/wai/issues/351>+--+-- Since 3.0.10+pauseTimeout :: Request -> IO ()+pauseTimeout = fromMaybe (return ()) . Vault.lookup pauseTimeoutKey . vault
Network/Wai/Handler/Warp/Request.hs view
@@ -6,6 +6,7 @@ module Network.Wai.Handler.Warp.Request ( recvRequest , headerLines+ , pauseTimeoutKey ) where import qualified Control.Concurrent as Conc (yield)@@ -16,7 +17,6 @@ import qualified Data.ByteString.Unsafe as SU import qualified Data.CaseInsensitive as CI import qualified Data.IORef as I-import Data.Monoid (mempty) import qualified Network.HTTP.Types as H import Network.Socket (SockAddr) import Network.Wai@@ -30,6 +30,8 @@ import Network.Wai.Internal import Prelude hiding (lines) import Control.Monad (when)+import qualified Data.Vault.Lazy as Vault+import System.IO.Unsafe (unsafePerformIO) ---------------------------------------------------------------- @@ -62,7 +64,7 @@ te = idxhdr ! idxTransferEncoding handleExpect conn httpversion expect (rbody, remainingRef, bodyLength) <- bodyAndSource src cl te- rbody' <- timeoutBody th rbody+ rbody' <- timeoutBody remainingRef th rbody let req = Request { requestMethod = method , httpVersion = httpversion@@ -74,7 +76,9 @@ , isSecure = False , remoteHost = addr , requestBody = rbody'- , vault = mempty+ , vault = Vault.insert pauseTimeoutKey+ (Timeout.pause th)+ Vault.empty , requestBodyLength = bodyLength , requestHeaderHost = idxhdr ! idxHost , requestHeaderRange = idxhdr ! idxRange@@ -138,18 +142,31 @@ ---------------------------------------------------------------- -timeoutBody :: Timeout.Handle -> IO ByteString -> IO (IO ByteString)-timeoutBody timeoutHandle rbody = do+timeoutBody :: Maybe (I.IORef Int) -- ^ remaining+ -> Timeout.Handle+ -> IO ByteString+ -> IO (IO ByteString)+timeoutBody remainingRef timeoutHandle rbody = do isFirstRef <- I.newIORef True + let checkEmpty =+ case remainingRef of+ Nothing -> return . S.null+ Just ref -> \bs -> if S.null bs+ then return True+ else do+ x <- I.readIORef ref+ return $! x <= 0+ return $ do isFirst <- I.readIORef isFirstRef - when isFirst $+ when isFirst $ do -- Timeout handling was paused after receiving the full request -- headers. Now we need to resume it to avoid a slowloris -- attack during request body sending. Timeout.resume timeoutHandle+ I.writeIORef isFirstRef False bs <- rbody @@ -157,7 +174,8 @@ -- because the application is not interested in more bytes, or -- because there is no more data available, pause the timeout -- handler again.- when (S.null bs) (Timeout.pause timeoutHandle)+ isEmpty <- checkEmpty bs+ when isEmpty (Timeout.pause timeoutHandle) return bs @@ -247,3 +265,7 @@ checkCR bs pos = if pos > 0 && 13 == S.index bs p then p else pos -- 13 is CR where !p = pos - 1++pauseTimeoutKey :: Vault.Key (IO ())+pauseTimeoutKey = unsafePerformIO Vault.newKey+{-# NOINLINE pauseTimeoutKey #-}
warp.cabal view
@@ -1,5 +1,5 @@ Name: warp-Version: 3.0.9.3+Version: 3.0.10 Synopsis: A fast, light-weight web server for WAI applications. License: MIT License-file: LICENSE@@ -46,6 +46,7 @@ , wai >= 3.0 && < 3.1 , text , streaming-commons >= 0.1.10+ , vault >= 0.3 if flag(network-bytestring) Build-Depends: network >= 2.2.1.5 && < 2.2.3 , network-bytestring >= 0.1.3 && < 0.1.4@@ -143,6 +144,7 @@ , text , streaming-commons >= 0.1.10 , async+ , vault if flag(use-bytestring-builder) Build-Depends: bytestring < 0.10.2.0 , bytestring-builder