-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make it possible to guarantee that final DATA frame is marked end-of-stream #2
Conversation
The idea of this version is that we can extend it further without having to keep introducing new functions all the time: we can just extend the record, without breaking backwards compatibility.
This avoids this `error` case: ```haskell LZero -> error "fillBufBuilder: LZero" ``` but more importantly, paves the way for the next commit.
We were doing the same thing in all cases: compute the `room` as the minimum of the buffer size and the available window size. Now we just specify this `room` directly.
This refactored version improves separation of concerns: * (Only) `fillStreamBodyGetNext.loop` deals with reading the next `StreamingChunk` from the queue * (Only) `runStreamingChunk` deals with the various kinds of `StreamingChunk`s * (Only) `runStreamingBuilder` deals with the various ways that a `Builder` can can terminate. This obsoletes the need for the `Leftover` data type. This refactoring is semantics preserving, but paves the way for the next commit.
See justification in the code comments. After the previous refactoring, this change is now very clean.
This finally is really the purpose of all of these changes; of course, we'll have to modify `http2` to actually make use of this new function.
This PR breaks |
Do you mean "break" as in: it needs to be updated because it doesn't compile anymore, or do you mean you updated it and the tests break? The latter would be worrying. If the former, there are three changes that you need to make:
- Next datPayloadLen reqflush mnext <- curr datBuf datBufSiz lim
+ Next datPayloadLen reqflush mnext <- curr datBuf (min datBufSiz lim)
- atomically $ writeTBQueue tbq (StreamingBuilder b)
+ atomically $ writeTBQueue tbq (StreamingBuilder b Nothing)
- let push b = atomically $ writeTBQueue tbq (StreamingBuilder b)
- flush = atomically $ writeTBQueue tbq StreamingFlush
- finished = atomically $ writeTBQueue tbq $ StreamingFinished (decCounter mgr)
+ decrementedCounter <- newIORef False
+ let decCounterOnce = do
+ alreadyDecremented <- atomicModifyIORef decrementedCounter $ \b -> (True, b)
+ unless alreadyDecremented $ decCounter mgr
+ let iface = OutBodyIface {
+ outBodyUnmask = unmask
+ , outBodyPush = \b -> atomically $ writeTBQueue tbq (StreamingBuilder b Nothing)
+ , outBodyPushFinal = \b -> atomically $ writeTBQueue tbq (StreamingBuilder b (Just decCounterOnce))
+ , outBodyFlush = atomically $ writeTBQueue tbq StreamingFlush
+ }
+ finished = atomically $ writeTBQueue tbq $ StreamingFinished decCounterOnce
incCounter mgr
- strmbdy unmask push flush `finally` finished
+ strmbdy iface `finally` finished I think that should be it. Let me know if you'd like me to take a look instead. |
The former. Thank you for the diffs. I will try to fix |
Ok, perfect. Thanks @kazu-yamamoto ! Have a great weekend :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
http3
is also ready.
Merged. |
Thanks @kazu-yamamoto ! Pleasure working with you as always :) |
## 0.3.0 * Breaking change: fillFileBodyGetNext takes Sentinel instead of IO () to close files on time. ## 0.2.1 * Add outBodyCancel to OutBodyIface [#11](kazu-yamamoto/http-semantics#11) * Documentation improvement. [#10](kazu-yamamoto/http-semantics#10) [#11](kazu-yamamoto/http-semantics#11) ## 0.2.0 * Introduce `responseStreamingIface` [#9](kazu-yamamoto/http-semantics#9) ## 0.1.2 * Avoid buffer overflow in fillBufBuilderOne [#4](kazu-yamamoto/http-semantics#4) ## 0.1.1 * Avoid buffer overflow in runStreamingBuilder [#3](kazu-yamamoto/http-semantics#3) ## 0.1.0 * Make it possible to guarantee that final DATA frame is marked end-of-stream. [#2](kazu-yamamoto/http-semantics#2) ## 0.0.1 * Defining getResponseBodyChunk'. [#1](kazu-yamamoto/http-semantics#1)
Prior to this PR, when the thread constructing the streaming body of a request (that is, the argument to
requestStreaming
orrequestStreamingUnmask
) terminated without callingflush
, the final DATA frame would usually be marked as end-of-stream, but there was no guarantee that this would happen. Specifically, this depended on whether theStreamingFinished
chunk would be enqueued before some other thread interrupted (perhaps because of a window update):https://github.com/kazu-yamamoto/http2/blob/398a5c5284421399a6b91a12734a2293a78c0a1b/Network/HTTP2/Client/Run.hs#L206-L211
If this chunk happened to be enqueued a bit later, the result would be that the constructed DATA frame would not be marked as end-of-stream, but instead the stream would terminate with a separate empty data frame. Unfortunately, this confuses some servers, so we need a way to have more control over this.
This PR (along with a companion small PR for
http2
) achieves this goal in a few steps:requestStreamingIface
. I was worried that we'd end up with a whole family of functionsrequestStreaming
,requestStreamingUnmask
,requestStreamingThisThatOrTheOther
; so to avoid this,requestStreamingIface
instead provides the callback with a record of functions, which we can add new features to at will without breaking backwards compatibility and without having to invent new names.StreamingChunk
, splitfillBufBuilder
(see comments inline for the commits), simplifyDynaNext
(again, comments in the commit), and refactorfillStreamBodyGetNext
(see commit). These changes just pave the way for the "real" change in the next commit.StreamingBuilder
to indicate that thisBuilder
is the end-of-stream. This is the direct send equivalent of what we did for receive in Mark final chunk as final #1.outBodyPushFinal
toOutBodyIface
, which can then be populated inhttp2
, making use of the new argument toStreamingBuilder
.