@@ -18,7 +18,7 @@ import Data.Maybe (isNothing)
1818import Data.Word (Word8, Word32)
1919import Foreign.Ptr (Ptr, plusPtr)
2020import Foreign.Storable (poke)
21−import Network.HPACK (setLimitForEncoding)
21+import Network.HPACK (setLimitForEncoding, toHeaderTable)
2222import Network.HTTP2
2323import Network.HTTP2.Priority (isEmptySTM, dequeueSTM, Precedence)
2424import Network.Wai
@@ -122,16 +122,16 @@ frameSender ctx@Context{outputQ,controlQ,connectionWindow,encodeDynamicTable}
122122 Nothing -> return ()
123123 Just siz -> setLimitForEncoding siz encodeDynamicTable
124124
125− output (ONext strm curr mtbq tell) off0 lim = do
125+ output out@(Output strm _ _ tell getH2D (ONext curr)) off0 lim = do
126126 -- Data frame payload
127127 let !buf = connWriteBuffer `plusPtr` off0
128128 !siz = connBufferSize - off0
129129 Next datPayloadLen mnext <- curr buf siz lim
130− off <- fillDataHeader strm off0 datPayloadLen mnext tell
131− maybeEnqueueNext strm mtbq mnext tell
130+ off <- fillDataHeader strm off0 datPayloadLen mnext tell getH2D
131+ maybeEnqueueNext out mnext
132132 return off
133133
134− output (ORspn strm rspn ii tell) off0 lim = do
134+ output out@(Output strm rspn ii tell getH2D ORspn) off0 lim = do
135135 -- Header frame and Continuation frame
136136 let !sid = streamNumber strm
137137 !endOfStream = case rspn of
@@ -149,32 +149,32 @@ frameSender ctx@Context{outputQ,controlQ,connectionWindow,encodeDynamicTable}
149149 let payloadOff = off + frameHeaderLength
150150 Next datPayloadLen mnext <-
151151 fillFileBodyGetNext conn ii payloadOff lim path mpart
152− off' <- fillDataHeader strm off datPayloadLen mnext tell
153− maybeEnqueueNext strm Nothing mnext tell
152+ off' <- fillDataHeader strm off datPayloadLen mnext tell getH2D
153+ maybeEnqueueNext out mnext
154154 return off'
155155 RspnBuilder _ _ builder -> do
156156 -- Data frame payload
157157 let payloadOff = off + frameHeaderLength
158158 Next datPayloadLen mnext <-
159159 fillBuilderBodyGetNext conn ii payloadOff lim builder
160− off' <- fillDataHeader strm off datPayloadLen mnext tell
161− maybeEnqueueNext strm Nothing mnext tell
160+ off' <- fillDataHeader strm off datPayloadLen mnext tell getH2D
161+ maybeEnqueueNext out mnext
162162 return off'
163163 RspnStreaming _ _ tbq -> do
164164 let payloadOff = off + frameHeaderLength
165165 Next datPayloadLen mnext <-
166166 fillStreamBodyGetNext conn payloadOff lim tbq strm
167− off' <- fillDataHeader strm off datPayloadLen mnext tell
168− maybeEnqueueNext strm (Just tbq) mnext tell
167+ off' <- fillDataHeader strm off datPayloadLen mnext tell getH2D
168+ maybeEnqueueNext out mnext
169169 return off'
170170
171− output (OPush strm ths rspn ii tell pid) off0 lim = do
171+ output out@(Output strm _ _ _ _ (OPush ths pid)) off0 lim = do
172172 -- Creating a push promise header
173173 -- Frame id should be associated stream id from the client.
174174 let !sid = streamNumber strm
175175 len <- pushPromise pid sid ths off0
176176 off <- sendHeadersIfNecessary $ off0 + frameHeaderLength + len
177− output (ORspn strm rspn ii tell) off lim
177+ output out{ outputType = ORspn } off lim
178178
179179 output _ _ _ = undefined -- never reach
180180
@@ -183,9 +183,12 @@ frameSender ctx@Context{outputQ,controlQ,connectionWindow,encodeDynamicTable}
183183 if isClosed state then
184184 return off
185185 else case out of
186− OWait strm' rsp ii wait -> do
186+ Output _ _ _ wait _ OWait -> do
187187 -- Checking if all push are done.
188− let out' = ORspn strm' rsp ii (return ())
188+ let out' = out {
189+ outputHook = return ()
190+ , outputType = ORspn
191+ }
189192 forkAndEnqueueWhenReady wait outputQ out' mgr
190193 return off
191194 _ -> case mtbq of
@@ -252,12 +255,11 @@ frameSender ctx@Context{outputQ,controlQ,connectionWindow,encodeDynamicTable}
252255
253256 {-# INLINE maybeEnqueueNext #-}
254257 -- Re-enqueue the stream in the output queue.
255− maybeEnqueueNext :: Stream -> Maybe (TBQueue Sequence)
256− -> Maybe DynaNext -> IO () -> IO ()
257− maybeEnqueueNext _ _ Nothing _ = return ()
258− maybeEnqueueNext strm mtbq (Just next) tell = enqueueOutput outputQ out
258+ maybeEnqueueNext :: Output -> Maybe DynaNext -> IO ()
259+ maybeEnqueueNext _ Nothing = return ()
260+ maybeEnqueueNext out (Just next) = enqueueOutput outputQ out'
259261 where
260− !out = ONext strm next mtbq tell
262+ !out' = out { outputType = ONext next }
261263
262264 {-# INLINE sendHeadersIfNecessary #-}
263265 -- Send headers if there is not room for a 1-byte data frame, and return
@@ -269,21 +271,36 @@ frameSender ctx@Context{outputQ,controlQ,connectionWindow,encodeDynamicTable}
269271 flushN off
270272 return 0
271273
272− fillDataHeader strm off datPayloadLen mnext tell = do
274+ fillDataHeader strm@Stream{streamWindow,streamNumber}
275+ off datPayloadLen mnext tell getH2D = do
273276 -- Data frame header
274− let !sid = streamNumber strm
277+ mh2d <- getH2D
278+ let (!trailers,!noTrailers) = case http2dataTrailers <$> mh2d of
279+ Nothing -> ([], True)
280+ Just ts -> (ts, null ts)
275281 !buf = connWriteBuffer `plusPtr` off
276282 !off' = off + frameHeaderLength + datPayloadLen
277− !done = isNothing mnext
278− flag | done = setEndStream defaultFlags
279− | otherwise = defaultFlags
280− fillFrameHeader FrameData datPayloadLen sid flag buf
281− when done $ do
283+ !noMoreBody = isNothing mnext
284+ flag | noMoreBody && noTrailers = setEndStream defaultFlags
285+ | otherwise = defaultFlags
286+ fillFrameHeader FrameData datPayloadLen streamNumber flag buf
287+ off'' <- handleEndOfBody noMoreBody off' noTrailers trailers
288+ atomically $ modifyTVar' connectionWindow (subtract datPayloadLen)
289+ atomically $ modifyTVar' streamWindow (subtract datPayloadLen)
290+ return off''
291+ where
292+ handleTrailers True off0 _ = return off0
293+ handleTrailers _ off0 trailers = do
294+ (ths,_) <- toHeaderTable trailers
295+ kvlen <- headerContinue streamNumber ths True off0
296+ sendHeadersIfNecessary $ off0 + frameHeaderLength + kvlen
297+ handleEndOfBody True off0 noTrailers trailers = do
298+ off1 <- handleTrailers noTrailers off0 trailers
282299 void $ tell
283300 closed ctx strm Finished
284− atomically $ modifyTVar' connectionWindow (subtract datPayloadLen)
285− atomically $ modifyTVar' (streamWindow strm) (subtract datPayloadLen)
286− return off'
301+ return off1
302+ handleEndOfBody False off0 _ _ = return off0
303+
287304
288305 pushPromise pid sid ths off = do
289306 let !offsid = off + frameHeaderLength