on master help sign in
Don't throw exception if keep-alive fails #618
commit89c5834ba80efd50a31a7cbad81a769056483f9d authorMichael Snoyman <michael@snoyman.com> dateWed 12 Apr 2017, 12:19 (10 years ago) parent98fc1d2
2 files changed+32−11
warp/Network/Wai/Handler/Warp/Request.hs+19−7 warp/Network/Wai/Handler/Warp/Run.hs+13−4
warp/Network/Wai/Handler/Warp/Request.hs+19−7
@@ -1,6 +1,7 @@
11{-# LANGUAGE BangPatterns #-}
22{-# LANGUAGE OverloadedStrings #-}
33{-# LANGUAGE CPP #-}
4+{-# LANGUAGE DeriveDataTypeable #-}
45{-# OPTIONS_GHC -fno-warn-deprecations #-}
56
67module Network.Wai.Handler.Warp.Request (
@@ -8,12 +9,14 @@ module Network.Wai.Handler.Warp.Request (
89 , headerLines
910 , pauseTimeoutKey
1011 , getFileInfoKey
12+ , NoKeepAliveRequest (..)
1113 ) where
1214
1315import qualified Control.Concurrent as Conc (yield)
14−import Control.Exception (throwIO)
16+import Control.Exception (throwIO, Exception)
1517import Data.Array ((!))
1618import Data.ByteString (ByteString)
19+import Data.Typeable (Typeable)
1720import qualified Data.ByteString as S
1821import qualified Data.ByteString.Unsafe as SU
1922import qualified Data.CaseInsensitive as CI
@@ -46,7 +49,8 @@ maxTotalHeaderLength = 50 * 1024
4649
4750-- | Receiving a HTTP request from 'Connection' and parsing its header
4851-- to create 'Request'.
49−recvRequest :: Settings
52+recvRequest :: Bool -- ^ first request on this connection?
53+ -> Settings
5054 -> Connection
5155 -> InternalInfo1
5256 -> SockAddr -- ^ Peer's address.
@@ -61,8 +65,8 @@ recvRequest :: Settings
6165 -- 'IndexedHeader' of HTTP request for internal use,
6266 -- Body producing action used for flushing the request body
6367
64−recvRequest settings conn ii1 addr src = do
65− hdrlines <- headerLines src
68+recvRequest firstRequest settings conn ii1 addr src = do
69+ hdrlines <- headerLines firstRequest src
6670 (method, unparsedPath, path, query, httpversion, hdr) <- parseHeaderLines hdrlines
6771 let idxhdr = indexRequestHeader hdr
6872 expect = idxhdr ! fromEnum ReqExpect
@@ -103,13 +107,21 @@ recvRequest settings conn ii1 addr src = do
103107
104108----------------------------------------------------------------
105109
106−headerLines :: Source -> IO [ByteString]
107−headerLines src = do
110+headerLines :: Bool -> Source -> IO [ByteString]
111+headerLines firstRequest src = do
108112 bs <- readSource src
109113 if S.null bs
110− then throwIO ConnectionClosedByPeer
114+ -- When we're working on a keep-alive connection and trying to
115+ -- get the second or later request, we don't want to treat the
116+ -- lack of data as a real exception. See the http1 function in
117+ -- the Run module for more details.
118+ then if firstRequest then throwIO ConnectionClosedByPeer else throwIO NoKeepAliveRequest
111119 else push src (THStatus 0 id id) bs
112120
121+data NoKeepAliveRequest = NoKeepAliveRequest
122+ deriving (Show, Typeable)
123+instance Exception NoKeepAliveRequest
124+
113125----------------------------------------------------------------
114126
115127handleExpect :: Connection
warp/Network/Wai/Handler/Warp/Run.hs+13−4
@@ -341,7 +341,7 @@ serveConnection conn ii1 origAddr transport settings app = do
341341 writeIORef istatus True
342342 leftoverSource src bs
343343 addr <- getProxyProtocolAddr src
344− http1 addr istatus src `E.catch` \e -> do
344+ http1 True addr istatus src `E.catch` \e -> do
345345 sendErrorResponse addr istatus e
346346 throwIO (e :: SomeException)
347347 where
@@ -402,8 +402,8 @@ serveConnection conn ii1 origAddr transport settings app = do
402402
403403 errorResponse e = settingsOnExceptionResponse settings e
404404
405− http1 addr istatus src = do
406− (req', mremainingRef, idxhdr, nextBodyFlush, ii) <- recvRequest settings conn ii1 addr src
405+ http1 firstRequest addr istatus src = do
406+ (req', mremainingRef, idxhdr, nextBodyFlush, ii) <- recvRequest firstRequest settings conn ii1 addr src
407407 let req = req' { isSecure = isTransportSecure transport }
408408 keepAlive <- processRequest istatus src req mremainingRef idxhdr nextBodyFlush ii
409409 `E.catch` \e -> do
@@ -412,7 +412,16 @@ serveConnection conn ii1 origAddr transport settings app = do
412412 settingsOnException settings (Just req) e
413413 -- Don't throw the error again to prevent calling settingsOnException twice.
414414 return False
415− when keepAlive $ http1 addr istatus src
415+
416+ -- When doing a keep-alive connection, the other side may just
417+ -- close the connection. We don't want to treat that as an
418+ -- exceptional situation, so we pass in False to http1 (which
419+ -- in turn passes in False to recvRequest), indicating that
420+ -- this is not the first request. If, when trying to read the
421+ -- request headers, no data is available, recvRequest will
422+ -- throw a NoKeepAliveRequest exception, which we catch here
423+ -- and ignore. See: https://github.com/yesodweb/wai/issues/618
424+ when keepAlive $ http1 False addr istatus src `E.catch` \NoKeepAliveRequest -> return ()
416425
417426 processRequest istatus src req mremainingRef idxhdr nextBodyFlush ii = do
418427 -- Let the application run for as long as it wants