on master help sign in
Cleanup: handle trailers directly in sender.

Previously they were sent to the stream's waiter thread and then sent back to the queue. Now they are returned as part of 'Control' instead, and re-enqueued immediately.

commitdb9ae02141e6a1097627c073eb0adc822298167d authorAndrew Pritchard <awpr@google.com> dateSat 29 Aug 2015, 15:09 (11 years ago) parenta45c735
3 files changed+59−46
warp/Network/Wai/Handler/Warp/HTTP2/Sender.hs+40−34 warp/Network/Wai/Handler/Warp/HTTP2/Types.hs+13−6 warp/Network/Wai/Handler/Warp/HTTP2/Worker.hs+6−6
warp/Network/Wai/Handler/Warp/HTTP2/Sender.hs+40−34
@@ -119,8 +119,8 @@ frameSender ctx@Context{outputQ,connectionWindow}
119119 lim <- getWindowSize connectionWindow (streamWindow strm)
120120 -- Data frame payload
121121 Next datPayloadLen mnext <- curr lim
122− fillDataHeaderSend strm 0 datPayloadLen mnext
123− maybeEnqueueNext strm mnext
122+ fillDataHeaderSend strm 0 datPayloadLen
123+ dispatchNext strm mnext
124124 loop
125125 switch (OPush oldStrm push mvar strm s h aux) = do
126126 pushed <- unlessClosed conn oldStrm $ do
@@ -134,23 +134,26 @@ frameSender ctx@Context{outputQ,connectionWindow}
134134 sendResponse strm s h aux lim
135135 putMVar mvar pushed
136136 loop
137− switch (OTrailers strm []) = do
138− _ <- unlessClosed conn strm $ do
139− -- An empty data frame should be used to end the stream instead
140− -- when there are no trailers.
141− let flag = setEndStream defaultFlags
142− fillFrameHeader FrameData 0 (streamNumber strm) flag connWriteBuffer
143− closed strm Finished
144− flushN frameHeaderLength
145− loop
146137 switch (OTrailers strm trailers) = do
147138 _ <- unlessClosed conn strm $ do
148139 -- Trailers always indicate the end of a stream; send them in
149− -- consecutive header+continuation frames and end the stream.
150− builder <- hpackEncodeCIHeaders ctx trailers
151− off <- headerContinue (streamNumber strm) builder True
140+ -- consecutive header+continuation frames and end the stream. Some
141+ -- clients dislike empty headers frames, so end the stream with an
142+ -- empty data frame instead, as recommended by the spec.
143+ toFlush <- case trailers of
144+ [] -> frameHeaderLength <$ fillFrameHeader FrameData 0
145+ (streamNumber strm)
146+ (setEndStream defaultFlags)
147+ connWriteBuffer
148+ _ -> do
149+ builder <- hpackEncodeCIHeaders ctx trailers
150+ off <- headerContinue (streamNumber strm) builder True
151+ return (off + frameHeaderLength)
152+ -- 'closed' must be before 'flushN'. If not, the context would be
153+ -- switched to the receiver, resulting in the inconsistency of
154+ -- concurrency.
152155 closed strm Finished
153− flushN $ off + frameHeaderLength
156+ flushN toFlush
154157 loop
155158
156159 -- Send the response headers and as much of the response as is immediately
@@ -169,10 +172,10 @@ frameSender ctx@Context{outputQ,connectionWindow}
169172 -- If no data was immediately available, avoid sending an
170173 -- empty data frame.
171174 if datPayloadLen > 0 then
172− fillDataHeaderSend strm total datPayloadLen mnext
175+ fillDataHeaderSend strm total datPayloadLen
173176 else
174177 when needSend $ flushN off
175− maybeEnqueueNext strm mnext
178+ dispatchNext strm mnext
176179
177180
178181 -- Flush the connection buffer to the socket, where the first 'n' bytes of
@@ -223,16 +226,25 @@ frameSender ctx@Context{outputQ,connectionWindow}
223226 -- True if the connection buffer has room for a 1-byte data frame.
224227 canFitDataFrame total = total + frameHeaderLength < connBufferSize
225228
226− -- Re-enqueue the stream in the output queue if more output is immediately
227− -- available; do nothing otherwise.
228− maybeEnqueueNext :: Stream -> Control DynaNext -> IO ()
229− maybeEnqueueNext strm (CNext next) = do
229+ -- Take the appropriate action based on the given 'Control':
230+ -- - If more output is immediately available, re-enqueue the stream in the
231+ -- output queue.
232+ -- - If the output is over and trailers are available, send them now and
233+ -- end the stream.
234+ -- - If we've drained the queue and handed the stream back to its waiter,
235+ -- do nothing.
236+ --
237+ -- This is done after sending any part of the stream body, so it's shared
238+ -- by 'sendResponse' and @switch (ONext ...)@.
239+ dispatchNext :: Stream -> Control DynaNext -> IO ()
240+ dispatchNext strm (CNext next) = do
230241 let out = ONext strm next
231242 enqueueOrSpawnTemporaryWaiter strm outputQ out
232− -- If the streaming is not finished, it must already have been
233− -- written to the 'TVar' owned by 'waiter', which will
234− -- put it back into the queue when more output becomes available.
235− maybeEnqueueNext _ _ = return ()
243+ dispatchNext strm (CFinish trailers) = do
244+ let out = OTrailers strm trailers
245+ pri <- readIORef $ streamPriority strm
246+ enqueue outputQ out pri
247+ dispatchNext _ _ = return ()
236248
237249
238250 -- Send headers if there is not room for a 1-byte data frame, and return
@@ -244,18 +256,12 @@ frameSender ctx@Context{outputQ,connectionWindow}
244256 flushN total
245257 return (0, False)
246258
247− fillDataHeaderSend strm otherLen datPayloadLen mnext = do
259+ fillDataHeaderSend strm otherLen datPayloadLen = do
248260 -- Data frame header
249261 let sid = streamNumber strm
250262 buf = connWriteBuffer `plusPtr` otherLen
251263 total = otherLen + frameHeaderLength + datPayloadLen
252264 fillFrameHeader FrameData datPayloadLen sid defaultFlags buf
253− -- "closed" must be before "connSendAll". If not,
254− -- the context would be switched to the receiver,
255− -- resulting the inconsistency of concurrency.
256− case mnext of
257− CFinish -> closed strm Finished
258− _ -> return ()
259265 flushN total
260266 atomically $ do
261267 modifyTVar' connectionWindow (subtract datPayloadLen)
@@ -406,8 +412,8 @@ nextForStream :: InternalInfo -> Buffer -> BufSize -> TBQueue Sequence
406412 -> TVar Sync -> Stream -> Leftover -> Maybe Trailers
407413 -> BytesFilled -> IO Next
408414nextForStream _ _ _ _ tvar _ _ (Just trailers) len = do
409− atomically $ writeTVar tvar $ SyncFinish trailers
410− return $ Next len CNone
415+ atomically $ writeTVar tvar $ SyncFinish
416+ return $ Next len $ CFinish trailers
411417nextForStream ii buf siz sq tvar strm LZero Nothing len = do
412418 let out = ONext strm (fillBufStream ii buf siz LZero sq tvar strm)
413419 atomically $ writeTVar tvar $ SyncNext out
warp/Network/Wai/Handler/Warp/HTTP2/Types.hs+13−6
@@ -46,14 +46,21 @@ data Input = Input Stream Request
4646
4747----------------------------------------------------------------
4848
49−data Control a = CFinish
49+-- | The result of writing data from a stream's queue into the buffer.
50+data Control a = CFinish Trailers
51+ -- ^ The stream has ended, and the trailers should be sent.
5052 | CNext a
53+ -- ^ The stream has more data immediately available, and we
54+ -- should re-enqueue it when the stream window becomes open.
5155 | CNone
56+ -- ^ The stream queue has been drained and we've handed it off
57+ -- to its dedicated waiter thread, which will re-enqueue it when
58+ -- more data is available.
5259
5360instance Show (Control a) where
54− show CFinish = "CFinish"
55− show (CNext _) = "CNext"
56− show CNone = "CNone"
61+ show (CFinish _) = "CFinish"
62+ show (CNext _) = "CNext"
63+ show CNone = "CNone"
5764
5865type DynaNext = WindowSize -> IO Next
5966
@@ -102,8 +109,8 @@ data Sequence = SFinish Trailers
102109-- | A message from the sender to a stream's dedicated waiter thread.
103110data Sync = SyncNone
104111 -- ^ Nothing interesting has happened. Go back to sleep.
105− | SyncFinish Trailers
106− -- ^ The stream has ended; enqueue 'OTrailers' and quit.
112+ | SyncFinish
113+ -- ^ The stream has ended.
107114 | SyncNext Output
108115 -- ^ The stream's queue has been drained; wait for more to be
109116 -- available and re-enqueue the given 'Output'.
warp/Network/Wai/Handler/Warp/HTTP2/Worker.hs+6−6
@@ -217,14 +217,14 @@ waiter tvar sq strm outQ = do
217217 mx <- atomically $ do
218218 mout <- readTVar tvar
219219 case mout of
220− SyncNone -> retry
221− SyncNext out -> do
220+ SyncNone -> retry
221+ SyncNext out -> do
222222 writeTVar tvar SyncNone
223− return $ Right out
224− SyncFinish trailers -> return $ Left trailers
223+ return $ Just out
224+ SyncFinish -> return Nothing
225225 case mx of
226− Left trailers -> enqueueWhenWindowIsOpen outQ (OTrailers strm trailers)
227− Right out -> do
226+ Nothing -> return ()
227+ Just out -> do
228228 -- ensuring that the streaming queue is not empty.
229229 atomically $ do
230230 isEmpty <- isEmptyTBQueue sq