on master help sign in
Merge branch 'bugfix/accept-error-handling' of https://github.com/helium/wai into helium-bugfix/accept-error-handling
19a939eMichael Snoyman
warp/Network/Wai/Handler/Warp.hs+17
@@ -70,12 +70,14 @@ module Network.Wai.Handler.Warp (
70 , setSlowlorisSize
70 , setSlowlorisSize
71 , setHTTP2Disabled
71 , setHTTP2Disabled
72 , setLogger
72 , setLogger
73+ , setGracefulShutdownTimeout
73 -- ** Getters
74 -- ** Getters
74 , getPort
75 , getPort
75 , getHost
76 , getHost
76 , getOnOpen
77 , getOnOpen
77 , getOnClose
78 , getOnClose
78 , getOnException
79 , getOnException
80+ , getGracefulShutdownTimeout
79 -- ** Exception handler
81 -- ** Exception handler
80 , defaultOnException
82 , defaultOnException
81 , defaultShouldDisplayException
83 , defaultShouldDisplayException
@@ -244,6 +246,12 @@ getOnClose = settingsOnClose
244getOnException :: Settings -> Maybe Request -> SomeException -> IO ()
246getOnException :: Settings -> Maybe Request -> SomeException -> IO ()
245getOnException = settingsOnException
247getOnException = settingsOnException
246
248
249+-- | Get the graceful shutdown timeout
250+--
251+-- Since 3.2.8
252+getGracefulShutdownTimeout :: Settings -> Maybe Int
253+getGracefulShutdownTimeout = settingsGracefulShutdownTimeout
254+
247-- | A code to install shutdown handler.
255-- | A code to install shutdown handler.
248--
256--
249-- For instance, this code should set up a UNIX signal
257-- For instance, this code should set up a UNIX signal
@@ -347,6 +355,15 @@ setLogger :: (Request -> H.Status -> Maybe Integer -> IO ())
347 -> Settings -> Settings
355 -> Settings -> Settings
348setLogger lgr y = y { settingsLogger = lgr }
356setLogger lgr y = y { settingsLogger = lgr }
349
357
358+-- | Set the graceful shutdown timeout. A timeout of `Nothing' will
359+-- wait indefinitely, and a number, if provided, will be treated as seconds
360+-- to wait for requests to finish, before shutting down the server entirely.
361+--
362+-- Since 3.2.8
363+setGracefulShutdownTimeout :: Maybe Int
364+ -> Settings -> Settings
365+setGracefulShutdownTimeout time y = y { settingsGracefulShutdownTimeout = time }
366+
350-- | Explicitly pause the slowloris timeout.
367-- | Explicitly pause the slowloris timeout.
351--
368--
352-- This is useful for cases where you partially consume a request body. For
369-- This is useful for cases where you partially consume a request body. For
warp/Network/Wai/Handler/Warp/Run.hs+20−16
@@ -13,7 +13,6 @@ module Network.Wai.Handler.Warp.Run where
13import Control.Applicative ((<$>))
13import Control.Applicative ((<$>))
14#endif
14#endif
15import Control.Arrow (first)
15import Control.Arrow (first)
16−import Control.Concurrent (threadDelay)
17import qualified Control.Concurrent as Conc (yield)
16import qualified Control.Concurrent as Conc (yield)
18import Control.Exception as E
17import Control.Exception as E
19import Control.Monad (when, unless, void)
18import Control.Monad (when, unless, void)
@@ -23,6 +22,8 @@ import Data.Char (chr)
23import "iproute" Data.IP (toHostAddress, toHostAddress6)
22import "iproute" Data.IP (toHostAddress, toHostAddress6)
24import Data.IORef (IORef, newIORef, readIORef, writeIORef)
23import Data.IORef (IORef, newIORef, readIORef, writeIORef)
25import Data.Streaming.Network (bindPortTCP)
24import Data.Streaming.Network (bindPortTCP)
25+import Foreign.C.Error (Errno(..), eCONNABORTED)
26+import GHC.IO.Exception (IOException(..))
26import Network (sClose, Socket)
27import Network (sClose, Socket)
27import Network.Socket (accept, withSocketsDo, SockAddr(SockAddrInet, SockAddrInet6), setSocketOption, SocketOption(..))
28import Network.Socket (accept, withSocketsDo, SockAddr(SockAddrInet, SockAddrInet6), setSocketOption, SocketOption(..))
28import qualified Network.Socket.ByteString as Sock
29import qualified Network.Socket.ByteString as Sock
@@ -44,7 +45,7 @@ import qualified Network.Wai.Handler.Warp.Timeout as T
44import Network.Wai.Handler.Warp.Types
45import Network.Wai.Handler.Warp.Types
45import Network.Wai.Internal (ResponseReceived (ResponseReceived))
46import Network.Wai.Internal (ResponseReceived (ResponseReceived))
46import System.Environment (getEnvironment)
47import System.Environment (getEnvironment)
47−import System.IO.Error (isFullErrorType, ioeGetErrorType)
48+import System.Timeout (timeout)
48
49
49#if WINDOWS
50#if WINDOWS
50import Network.Wai.Handler.Warp.Windows
51import Network.Wai.Handler.Warp.Windows
@@ -218,7 +219,7 @@ acceptConnection set getConnMaker app counter ii0 = do
218 -- ensure that no async exception is throw between the call to
219 -- ensure that no async exception is throw between the call to
219 -- acceptNewConnection and the registering of connClose.
220 -- acceptNewConnection and the registering of connClose.
220 void $ mask_ acceptLoop
221 void $ mask_ acceptLoop
221− gracefulShutdown counter
222+ gracefulShutdown set counter
222 where
223 where
223 acceptLoop = do
224 acceptLoop = do
224 -- Allow async exceptions before receiving the next connection maker.
225 -- Allow async exceptions before receiving the next connection maker.
@@ -242,17 +243,14 @@ acceptConnection set getConnMaker app counter ii0 = do
242 ex <- try getConnMaker
243 ex <- try getConnMaker
243 case ex of
244 case ex of
244 Right x -> return $ Just x
245 Right x -> return $ Just x
245− Left e -> do
246+ Left e -> do
247+ let eConnAborted = getErrno eCONNABORTED
248+ getErrno (Errno cInt) = cInt
249+ if ioe_errno e == Just eConnAborted
250+ then acceptNewConnection
251+ else do
246− settingsOnException set Nothing $ toException e
252+ settingsOnException set Nothing $ toException e
247− if isFullErrorType (ioeGetErrorType e) then do
248− -- "resource exhausted (Too many open files)" may
249− -- happen by accept(). Wait a second hoping that
250− -- resource will be available.
251− threadDelay 1000000
252− acceptNewConnection
253− else
254− -- Assuming the listen socket is closed.
255− return Nothing
253+ return Nothing
256
254
257-- Fork a new worker thread for this connection maker, and ask for a
255-- Fork a new worker thread for this connection maker, and ask for a
258-- function to unmask (i.e., allow async exceptions to be thrown).
256-- function to unmask (i.e., allow async exceptions to be thrown).
@@ -498,5 +496,11 @@ setSocketCloseOnExec _ = return ()
498setSocketCloseOnExec socket = F.setFileCloseOnExec $ fromIntegral $ fdSocket socket
496setSocketCloseOnExec socket = F.setFileCloseOnExec $ fromIntegral $ fdSocket socket
499#endif
497#endif
500
498
501−gracefulShutdown :: Counter -> IO ()
499+gracefulShutdown :: Settings -> Counter -> IO ()
502−gracefulShutdown counter = waitForZero counter
500+gracefulShutdown set counter =
501+ case settingsGracefulShutdownTimeout set of
502+ Nothing ->
503+ waitForZero counter
504+ (Just seconds) ->
505+ void (timeout (seconds * microsPerSecond) (waitForZero counter))
506+ where microsPerSecond = 1000000
warp/Network/Wai/Handler/Warp/Settings.hs+6
@@ -101,6 +101,11 @@ data Settings = Settings
101 -- ^ A log function. Default: no action.
101 -- ^ A log function. Default: no action.
102 --
102 --
103 -- Since 3.X.X.
103 -- Since 3.X.X.
104+ , settingsGracefulShutdownTimeout :: Maybe Int
105+ -- ^ An optional timeout to limit the time (in seconds) waiting for
106+ -- a graceful shutdown of the web server.
107+ --
108+ -- Since 3.2.8
104 }
109 }
105
110
106-- | Specify usage of the PROXY protocol.
111-- | Specify usage of the PROXY protocol.
@@ -135,6 +140,7 @@ defaultSettings = Settings
135 , settingsSlowlorisSize = 2048
140 , settingsSlowlorisSize = 2048
136 , settingsHTTP2Enabled = True
141 , settingsHTTP2Enabled = True
137 , settingsLogger = \_ _ _ -> return ()
142 , settingsLogger = \_ _ _ -> return ()
143+ , settingsGracefulShutdownTimeout = Nothing
138 }
144 }
139
145
140-- | Apply the logic provided by 'defaultOnException' to determine if an
146-- | Apply the logic provided by 'defaultOnException' to determine if an