@@ -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,24 +297,15 @@ 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 one again
308+ -- the case of all exceptions, so it is safe to once again
297 -- allow async exceptions.
309 -- allow async exceptions.
299 -- Call the user-supplied code for connection open and
311 -- Call the user-supplied code for connection open and
@@ -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,29 +413,25 @@ 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− when (shouldSendErrorResponse e && status) $ do
422+ if shouldSendErrorResponse e && status
400− let ii = toInternalInfo ii1 0 -- dummy
423+ then do
401− void $ 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 dummyreq addr = defaultRequest { remoteHost = addr }
427 dummyreq addr = defaultRequest { remoteHost = addr }
405 errorResponse e = settingsOnExceptionResponse settings e
429 errorResponse e = settingsOnExceptionResponse settings e
407 http1 firstRequest addr istatus src = do
431 http1 firstRequest addr istatus src = do
408− (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
409− let req = req' { isSecure = isTransportSecure transport }
410− keepAlive <- processRequest istatus src req mremainingRef idxhdr nextBodyFlush ii
433+ keepAlive <- processRequest istatus src req mremainingRef idxhdr nextBodyFlush
411 `E.catch` \e -> do
434 `E.catch` \e -> do
412− -- Call the user-supplied exception handlers, passing the request.
413− sendErrorResponse req istatus e
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.
416 return False
437 return False
@@ -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
@@ -433,30 +454,38 @@ serveConnection conn ii1 origAddr transport settings app = do
433 -- creating the request, we need to make sure that we don't get
454 -- creating the request, we need to make sure that we don't get
434 -- an async exception before calling the ResponseSource.
455 -- an async exception before calling the ResponseSource.
435 keepAliveRef <- newIORef $ error "keepAliveRef not filled"
456 keepAliveRef <- newIORef $ error "keepAliveRef not filled"
436− _ <- app req $ \res -> do
457+ r <- E.try $ app req $ \res -> do
437 T.resume th
458 T.resume th
438 -- FIXME consider forcing evaluation of the res here to
459 -- FIXME consider forcing evaluation of the res here to
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
467+ Right ResponseReceived -> return ()
468+ Left e@(SomeException _)
469+ | Just (ExceptionInsideResponseBody e') <- fromException e -> throwIO e'
471+ keepAlive <- sendErrorResponse req istatus e
472+ settingsOnException settings (Just req) e
473+ writeIORef keepAliveRef keepAlive
445 keepAlive <- readIORef keepAliveRef
475 keepAlive <- readIORef keepAliveRef
447 -- We just send a Response and it takes a time to
477 -- We just send a Response and it takes a time to
448 -- receive a Request again. If we immediately call recv,
478 -- receive a Request again. If we immediately call recv,
449− -- it is likely to fail and the IO manager works.
479+ -- it is likely to fail and cause the IO manager to do some work.
450− -- It is very costly. So, we yield to another Haskell
480+ -- It is very costly, so we yield to another Haskell
451 -- thread hoping that the next Request will arrive
481 -- thread hoping that the next Request will arrive
452 -- when this Haskell thread will be re-scheduled.
482 -- when this Haskell thread will be re-scheduled.
453 -- This improves performance at least when
483 -- This improves performance at least when
454 -- the number of cores is small.
484 -- the number of cores is small.
455 Conc.yield
485 Conc.yield
457− if not keepAlive then
487+ if keepAlive
458− return False
488+ then
460 -- If there is an unknown or large amount of data to still be read
489 -- If there is an unknown or large amount of data to still be read
461 -- from the request body, simple drop this connection instead of
490 -- from the request body, simple drop this connection instead of
462 -- reading it all in to satisfy a keep-alive request.
491 -- reading it all in to satisfy a keep-alive request.
@@ -482,6 +511,21 @@ serveConnection conn ii1 origAddr transport settings app = do
483 return False
512 return False
484 Nothing -> tryKeepAlive
513 Nothing -> tryKeepAlive
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
486flushEntireBody :: IO ByteString -> IO ()
530flushEntireBody :: IO ByteString -> IO ()
487flushEntireBody src =
531flushEntireBody src =