@@ -17,7 +17,7 @@ import qualified Control.Concurrent as Conc (yield)
1717import Control.Exception as E
1818import qualified Data.ByteString as S
1919import Data.Char (chr)
20−import Data.IORef (IORef, newIORef, readIORef, writeIORef)
20+import Data.IORef (IORef, newIORef, readIORef, writeIORef, atomicModifyIORef')
2121import Data.Streaming.Network (bindPortTCP)
2222import Foreign.C.Error (Errno(..), eCONNABORTED)
2323import GHC.IO.Exception (IOException(..))
@@ -58,29 +58,17 @@ import Network.Socket (fdSocket)
5858#endif
5959
6060-- | Creating 'Connection' for plain HTTP based on a given socket.
61−socketConnection :: Settings -> Socket -> IO Connection
62−#if MIN_VERSION_network(3,1,1)
63−socketConnection set s = do
64−#else
65−socketConnection _ s = do
66−#endif
61+socketConnection :: Socket -> IO Connection
62+socketConnection s = do
6763 bufferPool <- newBufferPool
6864 writeBuf <- allocateBuffer bufferSize
6965 let sendall = Sock.sendAll s
70− isH2 <- newIORef False -- HTTP/1.x
7166 return Connection {
7267 connSendMany = Sock.sendMany s
7368 , connSendAll = sendall
7469 , connSendFile = sendFile s writeBuf bufferSize sendall
7570#if MIN_VERSION_network(3,1,1)
76− , connClose = do
77− h2 <- readIORef isH2
78− let tm = if h2 then settingsGracefulCloseTimeout2 set
79− else settingsGracefulCloseTimeout1 set
80− if tm == 0 then
81− close s
82− else
83− gracefulClose s tm `E.catch` \(E.SomeException _) -> return ()
71+ , connClose = gracefulClose s 5000
8472#else
8573 , connClose = close s
8674#endif
@@ -89,7 +77,6 @@ socketConnection _ s = do
8977 , connRecvBuf = receiveBuf s
9078 , connWriteBuffer = writeBuf
9179 , connBufferSize = bufferSize
92− , connHTTP2 = isH2
9380 }
9481
9582-- | Run an 'Application' on the given port.
@@ -151,7 +138,7 @@ runSettingsSocket set socket app = do
151138 setSocketCloseOnExec s
152139 -- NoDelay causes an error for AF_UNIX.
153140 setSocketOption s NoDelay 1 `E.catch` \(E.SomeException _) -> return ()
154− conn <- socketConnection set s
141+ conn <- socketConnection s
155142 return (conn, sa)
156143
157144 closeListenSocket = close socket
@@ -286,7 +273,10 @@ fork :: Settings
286273fork set mkConn addr app counter ii = settingsFork set $ \unmask ->
287274 -- Call the user-supplied on exception code if any
288275 -- exceptions are thrown.
289− handle (settingsOnException set Nothing) $
276+ handle (settingsOnException set Nothing) .
277+ -- Allocate a new IORef indicating whether the connection has been
278+ -- closed, to avoid double-freeing a connection
279+ withClosedRef $ \ref ->
290280 -- Run the connection maker to get a new connection, and ensure
291281 -- that the connection is closed. If the mkConn call throws an
292282 -- exception, we will leak the connection. If the mkConn call is
@@ -297,13 +287,21 @@ fork set mkConn addr app counter ii = settingsFork set $ \unmask ->
297287 -- We grab the connection before registering timeouts since the
298288 -- timeouts will be useless during connection creation, due to the
299289 -- fact that async exceptions are still masked.
300− bracket mkConn cleanUp (serve unmask)
290+ bracket mkConn (cleanUp ref) (serve unmask ref)
301291 where
302− cleanUp (conn, _) = connClose conn `finally` connFree conn
292+ withClosedRef inner = newIORef False >>= inner
293+
294+ closeConn ref conn = do
295+ isClosed <- atomicModifyIORef' ref $ \x -> (True, x)
296+ unless isClosed $ connClose conn
297+
298+ cleanUp ref (conn, _) = closeConn ref conn `finally` connFree conn
303299
304300 -- We need to register a timeout handler for this thread, and
305− -- cancel that handler as soon as we exit.
306− serve unmask (conn, transport) = bracket register cancel $ \th -> do
301+ -- cancel that handler as soon as we exit. We additionally close
302+ -- the connection immediately in case the child thread catches the
303+ -- async exception or performs some long-running cleanup action.
304+ serve unmask ref (conn, transport) = bracket register cancel $ \th -> do
307305 -- We now have fully registered a connection close handler in
308306 -- the case of all exceptions, so it is safe to once again
309307 -- allow async exceptions.
@@ -315,7 +313,8 @@ fork set mkConn addr app counter ii = settingsFork set $ \unmask ->
315313 -- above ensures the connection is closed.
316314 when goingon $ serveConnection conn ii th addr transport set app
317315 where
318− register = T.registerKillThread (timeoutManager ii) (connClose conn)
316+ register = T.registerKillThread (timeoutManager ii)
317+ (closeConn ref conn)
319318 cancel = T.cancel
320319
321320 onOpen adr = increase counter >> settingsOnOpen set adr
@@ -342,19 +341,10 @@ serveConnection conn ii th origAddr transport settings app = do
342341 istatus <- newIORef False
343342 if settingsHTTP2Enabled settings && h2 then do
344343 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.
352344 let recvN = wrappedRecvN th istatus (settingsSlowlorisSize settings) rawRecvN
353− sendBS x = connSendAll conn x >> T.tickle th
354345 -- fixme: origAddr
355346 checkTLS
356− setConnHTTP2 conn True
357− http2 conn transport ii origAddr settings recvN sendBS app
347+ http2 conn transport ii origAddr settings recvN app
358348 else do
359349 src <- mkSource (wrappedRecv conn th istatus (settingsSlowlorisSize settings))
360350 writeIORef istatus True