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