@@ -3,10 +3,8 @@
3{-# LANGUAGE OverloadedStrings #-}
3{-# LANGUAGE OverloadedStrings #-}
4{-# LANGUAGE PackageImports #-}
4{-# LANGUAGE PackageImports #-}
5{-# LANGUAGE PatternGuards #-}
5{-# LANGUAGE PatternGuards #-}
6−{-# LANGUAGE RecordWildCards #-}
7{-# LANGUAGE ScopedTypeVariables #-}
6{-# LANGUAGE ScopedTypeVariables #-}
8{-# LANGUAGE TupleSections #-}
7{-# LANGUAGE TupleSections #-}
9−{-# LANGUAGE ViewPatterns #-}
10{-# OPTIONS_GHC -fno-warn-deprecations #-}
8{-# OPTIONS_GHC -fno-warn-deprecations #-}
12module Network.Wai.Handler.Warp.Run where
10module Network.Wai.Handler.Warp.Run where
@@ -17,20 +15,15 @@ import qualified Control.Concurrent as Conc (yield)
17import Control.Exception as E
15import Control.Exception as E
18import qualified Data.ByteString as S
16import qualified Data.ByteString as S
19import Data.Char (chr)
17import Data.Char (chr)
20−import Data.IORef (IORef, newIORef, readIORef, writeIORef)
18+import Data.IORef (IORef, newIORef, readIORef, writeIORef, atomicModifyIORef')
21import Data.Streaming.Network (bindPortTCP)
19import Data.Streaming.Network (bindPortTCP)
22import Foreign.C.Error (Errno(..), eCONNABORTED)
20import Foreign.C.Error (Errno(..), eCONNABORTED)
23import GHC.IO.Exception (IOException(..))
21import GHC.IO.Exception (IOException(..))
24−import qualified Network.HTTP2 as H2
25import Network.Socket (Socket, close, accept, withSocketsDo, SockAddr(SockAddrInet, SockAddrInet6), setSocketOption, SocketOption(..))
22import Network.Socket (Socket, close, accept, withSocketsDo, SockAddr(SockAddrInet, SockAddrInet6), setSocketOption, SocketOption(..))
26−#if MIN_VERSION_network(3,1,1)
27−import Network.Socket (gracefulClose)
29import qualified Network.Socket.ByteString as Sock
23import qualified Network.Socket.ByteString as Sock
30import Network.Wai
24import Network.Wai
31import Network.Wai.Internal (ResponseReceived (ResponseReceived))
25import Network.Wai.Internal (ResponseReceived (ResponseReceived))
32import System.Environment (lookupEnv)
26import System.Environment (lookupEnv)
33−import qualified System.TimeManager as T
34import System.Timeout (timeout)
27import System.Timeout (timeout)
36import Network.Wai.Handler.Warp.Buffer
29import Network.Wai.Handler.Warp.Buffer
@@ -38,8 +31,7 @@ import Network.Wai.Handler.Warp.Counter
38import qualified Network.Wai.Handler.Warp.Date as D
31import qualified Network.Wai.Handler.Warp.Date as D
39import qualified Network.Wai.Handler.Warp.FdCache as F
32import qualified Network.Wai.Handler.Warp.FdCache as F
40import qualified Network.Wai.Handler.Warp.FileInfoCache as I
33import qualified Network.Wai.Handler.Warp.FileInfoCache as I
41−import Network.Wai.Handler.Warp.HTTP2 (http2)
34+import Network.Wai.Handler.Warp.HTTP2 (http2, isHTTP2)
42−import Network.Wai.Handler.Warp.HTTP2.Types (isHTTP2)
43import Network.Wai.Handler.Warp.Header
35import Network.Wai.Handler.Warp.Header
44import Network.Wai.Handler.Warp.Imports hiding (readInt)
36import Network.Wai.Handler.Warp.Imports hiding (readInt)
45import Network.Wai.Handler.Warp.ReadInt
37import Network.Wai.Handler.Warp.ReadInt
@@ -48,6 +40,7 @@ import Network.Wai.Handler.Warp.Request
48import Network.Wai.Handler.Warp.Response
40import Network.Wai.Handler.Warp.Response
49import Network.Wai.Handler.Warp.SendFile
41import Network.Wai.Handler.Warp.SendFile
50import Network.Wai.Handler.Warp.Settings
42import Network.Wai.Handler.Warp.Settings
43+import qualified Network.Wai.Handler.Warp.Timeout as T
51import Network.Wai.Handler.Warp.Types
44import Network.Wai.Handler.Warp.Types
@@ -58,38 +51,21 @@ import Network.Socket (fdSocket)
60-- | Creating 'Connection' for plain HTTP based on a given socket.
53-- | Creating 'Connection' for plain HTTP based on a given socket.
61−socketConnection :: Settings -> Socket -> IO Connection
54+socketConnection :: Socket -> IO Connection
62−#if MIN_VERSION_network(3,1,1)
63−socketConnection set s = do
65−socketConnection _ s = do
55+socketConnection s = do
67 bufferPool <- newBufferPool
56 bufferPool <- newBufferPool
68 writeBuf <- allocateBuffer bufferSize
57 writeBuf <- allocateBuffer bufferSize
69 let sendall = Sock.sendAll s
58 let sendall = Sock.sendAll s
70− isH2 <- newIORef False -- HTTP/1.x
71 return Connection {
59 return Connection {
72 connSendMany = Sock.sendMany s
60 connSendMany = Sock.sendMany s
73 , connSendAll = sendall
61 , connSendAll = sendall
74 , connSendFile = sendFile s writeBuf bufferSize sendall
62 , connSendFile = sendFile s writeBuf bufferSize sendall
75−#if MIN_VERSION_network(3,1,1)
78− let tm = if h2 then settingsGracefulCloseTimeout2 set
79− else settingsGracefulCloseTimeout1 set
83− gracefulClose s tm `E.catch` \(E.SomeException _) -> return ()
85 , connClose = close s
63 , connClose = close s
87 , connFree = freeBuffer writeBuf
64 , connFree = freeBuffer writeBuf
88 , connRecv = receive s bufferPool
65 , connRecv = receive s bufferPool
89 , connRecvBuf = receiveBuf s
66 , connRecvBuf = receiveBuf s
90 , connWriteBuffer = writeBuf
67 , connWriteBuffer = writeBuf
91 , connBufferSize = bufferSize
68 , connBufferSize = bufferSize
95-- | Run an 'Application' on the given port.
71-- | Run an 'Application' on the given port.
@@ -151,7 +127,7 @@ runSettingsSocket set socket app = do
151 setSocketCloseOnExec s
127 setSocketCloseOnExec s
152 -- NoDelay causes an error for AF_UNIX.
128 -- NoDelay causes an error for AF_UNIX.
153 setSocketOption s NoDelay 1 `E.catch` \(E.SomeException _) -> return ()
129 setSocketOption s NoDelay 1 `E.catch` \(E.SomeException _) -> return ()
154− conn <- socketConnection set s
130+ conn <- socketConnection s
155 return (conn, sa)
131 return (conn, sa)
157 closeListenSocket = close socket
133 closeListenSocket = close socket
@@ -192,15 +168,15 @@ runSettingsConnectionMakerSecure :: Settings -> IO (IO (Connection, Transport),
192runSettingsConnectionMakerSecure set getConnMaker app = do
168runSettingsConnectionMakerSecure set getConnMaker app = do
193 settingsBeforeMainLoop set
169 settingsBeforeMainLoop set
194 counter <- newCounter
170 counter <- newCounter
195− withII $ acceptConnection set getConnMaker app counter
171+ withII0 $ acceptConnection set getConnMaker app counter
197− withII action =
173+ withII0 action =
198 withTimeoutManager $ \tm ->
174 withTimeoutManager $ \tm ->
199 D.withDateCache $ \dc ->
175 D.withDateCache $ \dc ->
200 F.withFdCache fdCacheDurationInSeconds $ \fdc ->
176 F.withFdCache fdCacheDurationInSeconds $ \fdc ->
201 I.withFileInfoCache fdFileInfoDurationInSeconds $ \fic -> do
177 I.withFileInfoCache fdFileInfoDurationInSeconds $ \fic -> do
202− let ii = InternalInfo tm dc fdc fic
178+ let ii0 = InternalInfo0 tm dc fdc fic
203− action ii
179+ action ii0
205 !fdCacheDurationInSeconds = settingsFdCacheDuration set * 1000000
181 !fdCacheDurationInSeconds = settingsFdCacheDuration set * 1000000
206 !fdFileInfoDurationInSeconds = settingsFileInfoCacheDuration set * 1000000
182 !fdFileInfoDurationInSeconds = settingsFileInfoCacheDuration set * 1000000
@@ -229,9 +205,9 @@ acceptConnection :: Settings
229 -> IO (IO (Connection, Transport), SockAddr)
205 -> IO (IO (Connection, Transport), SockAddr)
230 -> Application
206 -> Application
231 -> Counter
207 -> Counter
232− -> InternalInfo
208+ -> InternalInfo0
234−acceptConnection set getConnMaker app counter ii = do
210+acceptConnection set getConnMaker app counter ii0 = do
235 -- First mask all exceptions in acceptLoop. This is necessary to
211 -- First mask all exceptions in acceptLoop. This is necessary to
236 -- ensure that no async exception is throw between the call to
212 -- ensure that no async exception is throw between the call to
237 -- acceptNewConnection and the registering of connClose.
213 -- acceptNewConnection and the registering of connClose.
@@ -258,7 +234,7 @@ acceptConnection set getConnMaker app counter ii = do
258 case mx of
234 case mx of
259 Nothing -> return ()
235 Nothing -> return ()
260 Just (mkConn, addr) -> do
236 Just (mkConn, addr) -> do
261− fork set mkConn addr app counter ii
237+ fork set mkConn addr app counter ii0
262 acceptLoop
238 acceptLoop
264 acceptNewConnection = do
240 acceptNewConnection = do
@@ -281,12 +257,15 @@ fork :: Settings
281 -> SockAddr
257 -> SockAddr
282 -> Application
258 -> Application
283 -> Counter
259 -> Counter
284− -> InternalInfo
260+ -> InternalInfo0
286−fork set mkConn addr app counter ii = settingsFork set $ \unmask ->
262+fork set mkConn addr app counter ii0 = settingsFork set $ \unmask ->
287 -- Call the user-supplied on exception code if any
263 -- Call the user-supplied on exception code if any
288 -- exceptions are thrown.
264 -- exceptions are thrown.
289− handle (settingsOnException set Nothing) $
265+ handle (settingsOnException set Nothing) .
266+ -- Allocate a new IORef indicating whether the connection has been
267+ -- closed, to avoid double-freeing a connection
268+ withClosedRef $ \ref ->
290 -- Run the connection maker to get a new connection, and ensure
269 -- Run the connection maker to get a new connection, and ensure
291 -- that the connection is closed. If the mkConn call throws an
270 -- that the connection is closed. If the mkConn call throws an
292 -- exception, we will leak the connection. If the mkConn call is
271 -- exception, we will leak the connection. If the mkConn call is
@@ -297,13 +276,22 @@ fork set mkConn addr app counter ii = settingsFork set $ \unmask ->
297 -- We grab the connection before registering timeouts since the
276 -- We grab the connection before registering timeouts since the
298 -- timeouts will be useless during connection creation, due to the
277 -- timeouts will be useless during connection creation, due to the
299 -- fact that async exceptions are still masked.
278 -- fact that async exceptions are still masked.
300− bracket mkConn cleanUp (serve unmask)
279+ bracket mkConn (cleanUp ref) (serve unmask ref)
281+ withClosedRef inner = newIORef False >>= inner
283+ closeConn ref conn = do
284+ isClosed <- atomicModifyIORef' ref $ \x -> (True, x)
285+ unless isClosed $ connClose conn
302− cleanUp (conn, _) = connClose conn `finally` connFree conn
287+ cleanUp ref (conn, _) = closeConn ref conn `finally` connFree conn
304 -- We need to register a timeout handler for this thread, and
289 -- We need to register a timeout handler for this thread, and
305− -- cancel that handler as soon as we exit.
290+ -- cancel that handler as soon as we exit. We additionally close
291+ -- the connection immediately in case the child thread catches the
292+ -- async exception or performs some long-running cleanup action.
306− serve unmask (conn, transport) = bracket register cancel $ \th -> do
293+ serve unmask ref (conn, transport) = bracket register cancel $ \th -> do
294+ let ii1 = toInternalInfo1 ii0 th
307 -- We now have fully registered a connection close handler in
295 -- We now have fully registered a connection close handler in
308 -- the case of all exceptions, so it is safe to once again
296 -- the case of all exceptions, so it is safe to once again
309 -- allow async exceptions.
297 -- allow async exceptions.
@@ -313,23 +301,23 @@ fork set mkConn addr app counter ii = settingsFork set $ \unmask ->
313 bracket (onOpen addr) (onClose addr) $ \goingon ->
301 bracket (onOpen addr) (onClose addr) $ \goingon ->
314 -- Actually serve this connection. bracket with closeConn
302 -- Actually serve this connection. bracket with closeConn
315 -- above ensures the connection is closed.
303 -- above ensures the connection is closed.
316− when goingon $ serveConnection conn ii th addr transport set app
304+ when goingon $ serveConnection conn ii1 addr transport set app
318− register = T.registerKillThread (timeoutManager ii) (connClose conn)
306+ register = T.registerKillThread (timeoutManager0 ii0)
307+ (closeConn ref conn)
319 cancel = T.cancel
308 cancel = T.cancel
321 onOpen adr = increase counter >> settingsOnOpen set adr
310 onOpen adr = increase counter >> settingsOnOpen set adr
322 onClose adr _ = decrease counter >> settingsOnClose set adr
311 onClose adr _ = decrease counter >> settingsOnClose set adr
324serveConnection :: Connection
313serveConnection :: Connection
325− -> InternalInfo
314+ -> InternalInfo1
327 -> SockAddr
315 -> SockAddr
328 -> Transport
316 -> Transport
329 -> Settings
317 -> Settings
330 -> Application
318 -> Application
332−serveConnection conn ii th origAddr transport settings app = do
320+serveConnection conn ii1 origAddr transport settings app = do
333 -- fixme: Upgrading to HTTP/2 should be supported.
321 -- fixme: Upgrading to HTTP/2 should be supported.
334 (h2,bs) <- if isHTTP2 transport then
322 (h2,bs) <- if isHTTP2 transport then
335 return (True, "")
323 return (True, "")
@@ -342,35 +330,22 @@ serveConnection conn ii th origAddr transport settings app = do
342 istatus <- newIORef False
330 istatus <- newIORef False
343 if settingsHTTP2Enabled settings && h2 then do
331 if settingsHTTP2Enabled settings && h2 then do
344 rawRecvN <- makeReceiveN bs (connRecv conn) (connRecvBuf conn)
332 rawRecvN <- makeReceiveN bs (connRecv conn) (connRecvBuf conn)
345− -- This thread becomes the sender in http2 library.
346− -- In the case of event source, one request comes and one
347− -- worker gets busy. But it is likely that the receiver does
348− -- not receive any data at all while the sender is sending
349− -- output data from the worker. It's not good enough to tickle
350− -- the time handler in the receiver only. So, we should tickle
351− -- the time handler in both the receiver and the sender.
352 let recvN = wrappedRecvN th istatus (settingsSlowlorisSize settings) rawRecvN
333 let recvN = wrappedRecvN th istatus (settingsSlowlorisSize settings) rawRecvN
353− sendBS x = connSendAll conn x >> T.tickle th
354 -- fixme: origAddr
334 -- fixme: origAddr
356− setConnHTTP2 conn True
357− http2 conn transport ii origAddr settings recvN sendBS app
335+ http2 conn ii1 origAddr transport settings recvN app
359 src <- mkSource (wrappedRecv conn th istatus (settingsSlowlorisSize settings))
337 src <- mkSource (wrappedRecv conn th istatus (settingsSlowlorisSize settings))
360 writeIORef istatus True
338 writeIORef istatus True
361 leftoverSource src bs
339 leftoverSource src bs
362 addr <- getProxyProtocolAddr src
340 addr <- getProxyProtocolAddr src
363 http1 True addr istatus src `E.catch` \e ->
341 http1 True addr istatus src `E.catch` \e ->
364− case () of
342+ case fromException e of
366− -- See comment below referencing
343+ -- See comment below referencing
367− -- https://github.com/yesodweb/wai/issues/618
344+ -- https://github.com/yesodweb/wai/issues/618
368− | Just NoKeepAliveRequest <- fromException e -> return ()
345+ Just NoKeepAliveRequest -> return ()
369− -- No valid request
346+ Nothing -> do
370− | Just (BadFirstLine _) <- fromException e -> return ()
372− _ <- sendErrorResponse (dummyreq addr) istatus e
347+ _ <- sendErrorResponse (dummyreq addr) istatus e
373− throwIO e
348+ throwIO e
376 getProxyProtocolAddr src =
351 getProxyProtocolAddr src =
@@ -413,6 +388,8 @@ serveConnection conn ii th origAddr transport settings app = do
414 decodeAscii = map (chr . fromEnum) . S.unpack
389 decodeAscii = map (chr . fromEnum) . S.unpack
391+ th = threadHandle1 ii1
416 shouldSendErrorResponse se
393 shouldSendErrorResponse se
417 | Just ConnectionClosedByPeer <- fromException se = False
394 | Just ConnectionClosedByPeer <- fromException se = False
418 | otherwise = True
395 | otherwise = True
@@ -421,7 +398,8 @@ serveConnection conn ii th origAddr transport settings app = do
421 status <- readIORef istatus
398 status <- readIORef istatus
422 if shouldSendErrorResponse e && status
399 if shouldSendErrorResponse e && status
401+ let ii = toInternalInfo ii1 0 -- dummy
424− sendResponse settings conn ii th req defaultIndexRequestHeader (return S.empty) (errorResponse e)
402+ sendResponse settings conn ii req defaultIndexRequestHeader (return S.empty) (errorResponse e)
425 else return False
403 else return False
427 dummyreq addr = defaultRequest { remoteHost = addr }
405 dummyreq addr = defaultRequest { remoteHost = addr }
@@ -429,8 +407,9 @@ serveConnection conn ii th origAddr transport settings app = do
429 errorResponse e = settingsOnExceptionResponse settings e
407 errorResponse e = settingsOnExceptionResponse settings e
431 http1 firstRequest addr istatus src = do
409 http1 firstRequest addr istatus src = do
432− (req, mremainingRef, idxhdr, nextBodyFlush) <- recvRequest firstRequest settings conn ii th addr src transport
410+ (req', mremainingRef, idxhdr, nextBodyFlush, ii) <- recvRequest firstRequest settings conn ii1 addr src
411+ let req = req' { isSecure = isTransportSecure transport }
433− keepAlive <- processRequest istatus src req mremainingRef idxhdr nextBodyFlush
412+ keepAlive <- processRequest istatus src req mremainingRef idxhdr nextBodyFlush ii
434 `E.catch` \e -> do
413 `E.catch` \e -> do
435 settingsOnException settings (Just req) e
414 settingsOnException settings (Just req) e
436 -- Don't throw the error again to prevent calling settingsOnException twice.
415 -- Don't throw the error again to prevent calling settingsOnException twice.
@@ -446,7 +425,7 @@ serveConnection conn ii th origAddr transport settings app = do
446 -- and ignore. See: https://github.com/yesodweb/wai/issues/618
425 -- and ignore. See: https://github.com/yesodweb/wai/issues/618
447 when keepAlive $ http1 False addr istatus src
426 when keepAlive $ http1 False addr istatus src
449− processRequest istatus src req mremainingRef idxhdr nextBodyFlush = do
428+ processRequest istatus src req mremainingRef idxhdr nextBodyFlush ii = do
450 -- Let the application run for as long as it wants
429 -- Let the application run for as long as it wants
451 T.pause th
430 T.pause th
@@ -460,7 +439,7 @@ serveConnection conn ii th origAddr transport settings app = do
460 -- send more meaningful error messages to the user.
439 -- send more meaningful error messages to the user.
461 -- However, it may affect performance.
440 -- However, it may affect performance.
462 writeIORef istatus False
441 writeIORef istatus False
463− keepAlive <- sendResponse settings conn ii th req idxhdr (readSource src) res
442+ keepAlive <- sendResponse settings conn ii req idxhdr (readSource src) res
464 writeIORef keepAliveRef keepAlive
443 writeIORef keepAliveRef keepAlive
465 return ResponseReceived
444 return ResponseReceived
466 case r of
445 case r of
@@ -514,19 +493,6 @@ serveConnection conn ii th origAddr transport settings app = do
515 return False
494 return False
517− checkTLS = case transport of
518− TCP -> return () -- direct
519− tls -> unless (tls12orLater tls) $ goaway conn H2.InadequateSecurity "Weak TLS"
520− tls12orLater tls = tlsMajorVersion tls == 3 && tlsMinorVersion tls >= 3
522−-- connClose must not be called here since Run:fork calls it
523−goaway :: Connection -> H2.ErrorCodeId -> ByteString -> IO ()
524−goaway Connection{..} etype debugmsg = connSendAll bytestream
526− einfo = H2.encodeInfo id 0
527− frame = H2.GoAwayFrame 0 etype debugmsg
528− bytestream = H2.encodeFrame einfo frame
530flushEntireBody :: IO ByteString -> IO ()
496flushEntireBody :: IO ByteString -> IO ()
531flushEntireBody src =
497flushEntireBody src =