@@ -7,7 +7,6 @@ module Network.Wai.Handler.Warp.HTTP2.Sender (frameSender) where
7#if __GLASGOW_HASKELL__ < 709
7#if __GLASGOW_HASKELL__ < 709
8import Control.Applicative
8import Control.Applicative
10−import Control.Concurrent (forkIO)
11import Control.Concurrent.STM
10import Control.Concurrent.STM
12import qualified Control.Exception as E
11import qualified Control.Exception as E
13import Control.Monad (unless, void, when)
12import Control.Monad (unless, void, when)
@@ -58,22 +57,16 @@ unlessClosed ctx
58 let rst = resetFrame InternalError streamNumber
57 let rst = resetFrame InternalError streamNumber
59 connSendAll rst
58 connSendAll rst
61−checkWindowSize :: TVar WindowSize -> TVar WindowSize -> PriorityTree Output -> Output -> Priority -> (WindowSize -> IO ()) -> IO ()
60+getWindowSize :: TVar WindowSize -> TVar WindowSize -> IO WindowSize
62−checkWindowSize connWindow strmWindow outQ out pri body = do
61+getWindowSize connWindow strmWindow = do
62+ -- Waiting that the connection window gets open.
63 cw <- atomically $ do
63 cw <- atomically $ do
64 w <- readTVar connWindow
64 w <- readTVar connWindow
65 check (w > 0)
65 check (w > 0)
67+ -- This stream window is greater than 0 thanks to the invariant.
67 sw <- atomically $ readTVar strmWindow
68 sw <- atomically $ readTVar strmWindow
70− -- checkme: if connection is closed? GC throws dead lock error?
72− x <- readTVar strmWindow
76− body (min cw sw)
69+ return $ min cw sw
78frameSender :: Context -> Connection -> InternalInfo -> S.Settings -> IO ()
71frameSender :: Context -> Connection -> InternalInfo -> S.Settings -> IO ()
79frameSender ctx@Context{outputQ,connectionWindow}
72frameSender ctx@Context{outputQ,connectionWindow}
@@ -89,59 +82,60 @@ frameSender ctx@Context{outputQ,connectionWindow}
89 connSendAll initialFrame
82 connSendAll initialFrame
85+ -- ignoring the old priority because the value might be changed.
92− loop = dequeue outputQ >>= \(out, pri) -> switch out pri
86+ loop = dequeue outputQ >>= \(out, _) -> switch out
94 ignore :: E.SomeException -> IO ()
88 ignore :: E.SomeException -> IO ()
95 ignore _ = return ()
89 ignore _ = return ()
97− switch OFinish _ = return ()
91+ switch OFinish = return ()
98− switch (OGoaway frame) _ = connSendAll frame
92+ switch (OGoaway frame) = connSendAll frame
99− switch (OFrame frame) _ = do
93+ switch (OFrame frame) = do
100 connSendAll frame
94 connSendAll frame
102− switch out@(OResponse strm rsp aux) pri = unlessClosed ctx conn strm $ do
96+ switch (OResponse strm rsp aux) = unlessClosed ctx conn strm $ do
103− checkWindowSize connectionWindow (streamWindow strm) outputQ out pri $ \lim -> do
97+ lim <- getWindowSize connectionWindow (streamWindow strm)
104− -- Header frame and Continuation frame
98+ -- Header frame and Continuation frame
105− let sid = streamNumber strm
99+ let sid = streamNumber strm
106− endOfStream = case aux of
100+ endOfStream = case aux of
107− Persist{} -> False
101+ Persist{} -> False
108− Oneshot hb -> not hb
102+ Oneshot hb -> not hb
109− len <- headerContinue sid rsp endOfStream
103+ len <- headerContinue sid rsp endOfStream
110− let total = len + frameHeaderLength
104+ let total = len + frameHeaderLength
111− case aux of
105+ case aux of
112− Oneshot True -> do -- hasBody
106+ Oneshot True -> do -- hasBody
113− -- Data frame payload
107+ -- Data frame payload
114− (off, _) <- sendHeadersIfNecessary total
108+ (off, _) <- sendHeadersIfNecessary total
115− let payloadOff = off + frameHeaderLength
109+ let payloadOff = off + frameHeaderLength
116− Next datPayloadLen mnext <-
110+ Next datPayloadLen mnext <-
117− fillResponseBodyGetNext conn ii payloadOff lim rsp
111+ fillResponseBodyGetNext conn ii payloadOff lim rsp
112+ fillDataHeaderSend strm total datPayloadLen mnext
113+ maybeEnqueueNext strm mnext
115+ -- "closed" must be before "connSendAll". If not,
116+ -- the context would be switched to the receiver,
117+ -- resulting the inconsistency of concurrency.
118+ closed ctx strm Finished
120+ Persist sq tvar -> do
121+ (off, needSend) <- sendHeadersIfNecessary total
122+ let payloadOff = off + frameHeaderLength
123+ Next datPayloadLen mnext <-
124+ fillStreamBodyGetNext conn payloadOff lim sq tvar strm
125+ -- If no data was immediately available, avoid sending an
126+ -- empty data frame.
127+ if datPayloadLen > 0 then
118 fillDataHeaderSend strm total datPayloadLen mnext
128 fillDataHeaderSend strm total datPayloadLen mnext
119− maybeEnqueueNext strm mnext pri
121− -- "closed" must be before "connSendAll". If not,
122− -- the context would be switched to the receiver,
123− -- resulting the inconsistency of concurrency.
124− closed ctx strm Finished
126− Persist sq tvar -> do
127− (off, needSend) <- sendHeadersIfNecessary total
128− let payloadOff = off + frameHeaderLength
129− Next datPayloadLen mnext <-
130− fillStreamBodyGetNext conn payloadOff lim sq tvar
131− -- If no data was immediately available, avoid sending an
132− -- empty data frame.
133− if datPayloadLen > 0 then
134− fillDataHeaderSend strm total datPayloadLen mnext
136− when needSend $ flushN off
130+ when needSend $ flushN off
137− maybeEnqueueNext strm mnext pri
131+ maybeEnqueueNext strm mnext
139− switch out@(ONext strm curr) pri = unlessClosed ctx conn strm $ do
133+ switch (ONext strm curr) = unlessClosed ctx conn strm $ do
140− checkWindowSize connectionWindow (streamWindow strm) outputQ out pri $ \lim -> do
134+ lim <- getWindowSize connectionWindow (streamWindow strm)
141− -- Data frame payload
135+ -- Data frame payload
142− Next datPayloadLen mnext <- curr lim
136+ Next datPayloadLen mnext <- curr lim
143− fillDataHeaderSend strm 0 datPayloadLen mnext
137+ fillDataHeaderSend strm 0 datPayloadLen mnext
144− maybeEnqueueNext strm mnext pri
138+ maybeEnqueueNext strm mnext
147 -- Flush the connection buffer to the socket, where the first 'n' bytes of
141 -- Flush the connection buffer to the socket, where the first 'n' bytes of
@@ -183,12 +177,15 @@ frameSender ctx@Context{outputQ,connectionWindow}
183 canFitDataFrame total = total + frameHeaderLength < connBufferSize
177 canFitDataFrame total = total + frameHeaderLength < connBufferSize
185 -- Re-enqueue the stream in the output queue if more output is immediately
179 -- Re-enqueue the stream in the output queue if more output is immediately
186− -- available; do nothing otherwise. If the stream is not finished, it must
180+ -- available; do nothing otherwise.
181+ maybeEnqueueNext :: Stream -> Control DynaNext -> IO ()
182+ maybeEnqueueNext strm (CNext next) = do
183+ let out = ONext strm next
184+ enqueueOrSpawnTemporaryWaiter strm outputQ out
185+ -- If the streaming is not finished, it must already have been
187− -- already have been written to the 'TVar' owned by 'waiter', which will
186+ -- written to the 'TVar' owned by 'waiter', which will
188 -- put it back into the queue when more output becomes available.
187 -- put it back into the queue when more output becomes available.
189− maybeEnqueueNext :: Stream -> Control DynaNext -> Priority -> IO ()
188+ maybeEnqueueNext _ _ = return ()
190− maybeEnqueueNext strm (CNext next) = enqueue outputQ (ONext strm next)
191− maybeEnqueueNext _ _ = const $ return ()
194 -- Send headers if there is not room for a 1-byte data frame, and return
191 -- Send headers if there is not room for a 1-byte data frame, and return
@@ -282,13 +279,13 @@ fileStartEnd _ (Just part) =
283----------------------------------------------------------------
280----------------------------------------------------------------
285−fillStreamBodyGetNext :: Connection -> Int -> WindowSize -> TBQueue Sequence -> TVar Sync -> IO Next
282+fillStreamBodyGetNext :: Connection -> Int -> WindowSize -> TBQueue Sequence -> TVar Sync -> Stream -> IO Next
286fillStreamBodyGetNext Connection{connWriteBuffer,connBufferSize}
283fillStreamBodyGetNext Connection{connWriteBuffer,connBufferSize}
287− off lim sq tvar = do
284+ off lim sq tvar strm = do
288 let datBuf = connWriteBuffer `plusPtr` off
285 let datBuf = connWriteBuffer `plusPtr` off
289 room = min (connBufferSize - off) lim
286 room = min (connBufferSize - off) lim
290 (leftover, cont, len) <- runStreamBuilder datBuf room sq
287 (leftover, cont, len) <- runStreamBuilder datBuf room sq
291− nextForStream connWriteBuffer connBufferSize sq tvar leftover cont len
288+ nextForStream connWriteBuffer connBufferSize sq tvar strm leftover cont len
293----------------------------------------------------------------
290----------------------------------------------------------------
@@ -342,8 +339,8 @@ runStreamBuilder buf0 room0 sq = loop buf0 room0 0
342 Just SFlush -> return (LZero, True, total)
339 Just SFlush -> return (LZero, True, total)
343 Just SFinish -> return (LZero, False, total)
340 Just SFinish -> return (LZero, False, total)
345−fillBufStream :: Buffer -> BufSize -> Leftover -> TBQueue Sequence -> TVar Sync -> DynaNext
342+fillBufStream :: Buffer -> BufSize -> Leftover -> TBQueue Sequence -> TVar Sync -> Stream -> DynaNext
346−fillBufStream buf0 siz0 leftover0 sq tvar lim0 = do
343+fillBufStream buf0 siz0 leftover0 sq tvar strm lim0 = do
347 let payloadBuf = buf0 `plusPtr` frameHeaderLength
344 let payloadBuf = buf0 `plusPtr` frameHeaderLength
348 room0 = min (siz0 - frameHeaderLength) lim0
345 room0 = min (siz0 - frameHeaderLength) lim0
349 case leftover0 of
346 case leftover0 of
@@ -361,7 +358,7 @@ fillBufStream buf0 siz0 leftover0 sq tvar lim0 = do
361 void $ copy payloadBuf bs1
358 void $ copy payloadBuf bs1
362 getNext (LTwo bs2 writer) True room0
359 getNext (LTwo bs2 writer) True room0
364− getNext = nextForStream buf0 siz0 sq tvar
361+ getNext = nextForStream buf0 siz0 sq tvar strm
365 write writer1 buf room sofar = do
362 write writer1 buf room sofar = do
366 (len, signal) <- writer1 buf room
363 (len, signal) <- writer1 buf room
367 case signal of
364 case signal of
@@ -376,17 +373,18 @@ fillBufStream buf0 siz0 leftover0 sq tvar lim0 = do
376 let !total = sofar + len
373 let !total = sofar + len
377 getNext (LTwo bs writer) True total
374 getNext (LTwo bs writer) True total
379−nextForStream :: Buffer -> BufSize -> TBQueue Sequence -> TVar Sync
376+nextForStream :: Buffer -> BufSize -> TBQueue Sequence -> TVar Sync -> Stream
380 -> Leftover -> Bool -> BytesFilled
377 -> Leftover -> Bool -> BytesFilled
381 -> IO Next
378 -> IO Next
382−nextForStream _ _ _ tvar _ False len = do
379+nextForStream _ _ _ tvar _ _ False len = do
383 atomically $ writeTVar tvar SyncFinish
380 atomically $ writeTVar tvar SyncFinish
384 return $ Next len CFinish
381 return $ Next len CFinish
385−nextForStream buf siz sq tvar LZero True len = do
382+nextForStream buf siz sq tvar strm LZero True len = do
386− atomically $ writeTVar tvar $ SyncNext (fillBufStream buf siz LZero sq tvar)
383+ let out = ONext strm (fillBufStream buf siz LZero sq tvar strm)
384+ atomically $ writeTVar tvar $ SyncNext out
387 return $ Next len CNone
385 return $ Next len CNone
388−nextForStream buf siz sq tvar leftover True len =
386+nextForStream buf siz sq tvar strm leftover True len =
389− return $ Next len (CNext (fillBufStream buf siz leftover sq tvar))
387+ return $ Next len (CNext (fillBufStream buf siz leftover sq tvar strm))
391----------------------------------------------------------------
389----------------------------------------------------------------