@@ -35,6 +35,7 @@ import qualified Network.Wai.Handler.Warp.FdCache as F
3535import qualified Network.Wai.Handler.Warp.FileInfoCache as I
3636import Network.Wai.Handler.Warp.HTTP2 (http2, isHTTP2)
3737import Network.Wai.Handler.Warp.Header
38+import Network.Wai.Handler.Warp.IORef
3839import Network.Wai.Handler.Warp.ReadInt
3940import Network.Wai.Handler.Warp.Recv
4041import Network.Wai.Handler.Warp.Request
@@ -63,7 +64,8 @@ socketConnection s = do
6364 connSendMany = Sock.sendMany s
6465 , connSendAll = sendall
6566 , connSendFile = sendFile s writeBuf bufferSize sendall
66− , connClose = close s >> freeBuffer writeBuf
67+ , connClose = close s
68+ , connFree = freeBuffer writeBuf
6769 , connRecv = receive s bufferPool
6870 , connRecvBuf = receiveBuf s
6971 , connWriteBuffer = writeBuf
@@ -262,6 +264,10 @@ fork :: Settings
262264 -> InternalInfo0
263265 -> IO ()
264266fork set mkConn addr app counter ii0 = settingsFork set $ \ unmask ->
267+ -- Allocate a new IORef indicating whether the connection has been
268+ -- closed, to avoid double-freeing a connection
269+ withClosedRef $ \ref ->
270+
265271 -- Run the connection maker to get a new connection, and ensure
266272 -- that the connection is closed. If the mkConn call throws an
267273 -- exception, we will leak the connection. If the mkConn call is
@@ -272,11 +278,14 @@ fork set mkConn addr app counter ii0 = settingsFork set $ \ unmask ->
272278 -- We grab the connection before registering timeouts since the
273279 -- timeouts will be useless during connection creation, due to the
274280 -- fact that async exceptions are still masked.
275− bracket mkConn closeConn $ \(conn, transport) ->
281+ bracket mkConn (\(conn, _) -> closeConn ref conn `finally` connFree conn)
282+ $ \(conn, transport) ->
276283
277284 -- We need to register a timeout handler for this thread, and
278− -- cancel that handler as soon as we exit.
279− bracket (T.registerKillThread (timeoutManager0 ii0)) T.cancel $ \th ->
285+ -- cancel that handler as soon as we exit. We additionally close
286+ -- the connection immediately in case the child thread catches the
287+ -- async exception or performs some long-running cleanup action.
288+ bracket (T.registerKillThread (timeoutManager0 ii0) (closeConn ref conn)) T.cancel $ \th ->
280289
281290 let ii1 = toInternalInfo1 ii0 th
282291 -- We now have fully registered a connection close handler
@@ -294,7 +303,11 @@ fork set mkConn addr app counter ii0 = settingsFork set $ \ unmask ->
294303 -- bracket with closeConn above ensures the connection is closed.
295304 when goingon $ serveConnection conn ii1 addr transport set app
296305 where
297− closeConn (conn, _transport) = connClose conn
306+ withClosedRef inner = newIORef False >>= inner
307+
308+ closeConn ref conn = do
309+ isClosed <- atomicModifyIORef' ref $ \x -> (True, x)
310+ unless isClosed $ connClose conn
298311
299312 onOpen adr = increase counter >> settingsOnOpen set adr
300313 onClose adr _ = decrease counter >> settingsOnClose set adr