This is based on the long set of negotiated changes added to . This adds the blacklist as Sam Hartman proposed at IETF 91.
Section 9.2.2 places restrictions on the ciphers that are acceptable for a http/2 connection that are different to the acceptable ciphers for a https connection that may be offered over the same handshake. To comply with 9.2.2, as server accepting an ALPN connection must either: a) influence the cipher selection to ensure an acceptable h2 cipher is selected; b) be informed of the cipher selected and if it is not acceptable then select http/1.1 instead of h2 as the protocol. Neither of these capabilities are required of a RFC7301 compliant implementation. Specifically there is no requirement for an ALPN extension to be able to influence cipher selection, nor is there a requirement for an ALPN to make the cipher that will be selected available to the protocol selection.
Even if ALPN does allow arbitrary selection of cipher/protocol pair, it is impossible for a server to select an acceptable cipher/protocol pair because clients interpretations of 9.2.2 will vary over time and available ciphers. If a server selects a cipher/protocol pair that it believes is h2 acceptable, but the client disagrees, then communication failure results. Thus the moment there are different interpretations of 9.2.2 in the client population it will be impossible for a single h2 server to accept connections from 100% of the population.
The server need only support the minimum requirements in 9.2.2 at the TLS layer, i.e. enable TLSECDHERSAWITHAES128GCM_SHA256 overall and make it a suitably high preference. If the 9.2.2 requirements are increased in the future, then that requires an ALPN bump too and thus it can be deployed without worry.
Note that I think discussion on this topic has moved beyond the specific concerns raised in this issue. This issue should probably be decomposed into: Should we be restricting TLS ciphers within the application protocol draft document The usage of the ALPN handshake is fragile as any differing interpretations of 9.2.2 against future ciphers can result in communication failure. The ALPN RFC does not require that a ALPN implementation provide functionality needed to implement 9.2.2, specifically either to a) use protocol selection to influence the cipher selection; or b) be informed of the cipher selected to influence protocol selection.
Ideally, no, the HTTP/2 spec should not be setting TLS details. HTTP/2 should simply require TLS 1.3, or at least a 1.2.1 with the required improvements done in the relevant spec. This, however, requires a level of coordination between working groups that isn't currently available. This section is just a hack to make a TLS 1.2-h2 variant that's the best that's currently attainable. It's a transitional spec until TLS 1.3 is eventually complete some time in the indeterminate future, just as HTTP/2 is likely to be a transitional spec until HTTP/3 gets written. The cleanest route would be to cut out sections 9.2.1 & 9.2.2 and make a new TLS 1.2.1 spec that satisfies those requirements, then separately publish that and require its usage here. (though, you'd have to come up with some way to handle the minor version number; maybe implement TLS 1.2.1 entirely as an extension) It's probably too late in the HTTP/2 development process to do this, unfortunately, though it would be a cleaner spec with less fiddly bits that can break interoperability.
Discuss on-list, please.
Actually, it's about ethics in game journalism.
Consensus to adopt .


Proposal for
Taking Mark's base, taking Herve's suggestions and removing some text. I think that this is good.
Folks in the Tor community point out that coalescing connections is a genuinely new vector for tracking users in HTTP/2. This should be pointed out in the Privacy Considerations subsection of Security Considerations, along with mitigations. While we're there, more specific text about mitigating HTTP/2-specific fingerprinting would be nice.
NAME I've marked this as Design, but I think we can prepare a proposal pretty easily. You want to take a crack at it, or should I do a pull?
I'm happy to take input. Since I'm basically offline this week, getting a start would help lots.
Marked as editor-ready; please consider list feedback.


Rather than just PROTOCOLERROR, allow REFUSEDSTREAM. REFUSED_STREAM is a more lenient error. It allows clients to retry requests. Servers might also set the GOAWAY stream number to a lower value if they upgraded.
"5.1.2 Stream Concurrency" says about SETTINGSMAXCONCURRENT_STREAMS: "Endpoints MUST NOT exceed the limit set by their peer. An endpoint that receives a HEADERS frame that causes their advertised concurrent stream limit to be exceeded MUST treat this as a stream error (Section 5.4.2)" But which stream error code should be used? Almost all other references to stream errors in the spec say "treated as a stream error of type XXX"
Also, if this is a stream error, what happens if the client has a CONTINUATION already in flight right behind the current-streams-violating HEADERS frame? The server would then return a stream error to the HEADERS frame, and then see the CONTINUATION frame that's not associated with an in-progress header block. I suppose a server should maintain enough state to remember that it sent a stream error on that stream ID and simply ignore any following CONTINUATION frames instead of returning a connection error?
The server knows that a continuation frame is coming as it is flagged in the HEADERS frame. Regardless, to ensure against corrupting the compression state, even if it is thrown out, all HEADERS/CONTINUATION data must go through the compression context unless you're closing the connection. This is even true for stream IDs that are not in a valid state (though I'd hope in those cases that the receiver would close the connection after immediately sending an error). , Brad Fitzpatrick EMAIL wrote:
NAME good point. My tests don't cover the case of a CONTINUATION having valuable compression context needed for future valid streams. I will update the test suite I'm building.
Discussed on-list; let's go with .


Two issues were raised about the use of the words "existing" and "current" to refer to streams. These were a little squishy, so I tightened the language a little. The text no longer uses either word to refer to streams, instead using stream states to describe where the relevant text applies and favouring "identified" as a fallback.
uses some under specified termonlogy: For example is for a PRIORITY frame which can be sent/received in the any state the same as in the following contexts: Can be translated to the stream's state? For example does it roughly mean ? If so then the usage in the PRIORITY frame section is a bit more confusing because a PRIORITY frame could be the first valid frame sent/received for a stream.
NAME - Thanks for the clarification!
NAME - I do have a question regarding your clarifications to the SETTING frame (specifically the SETTINGSINITIALWINDOWSIZE portion). It seems a bit confusing if a PRIORITY frame has been exchanged on an IDLE stream for the SETTINGSINITIALWINDOWSIZE not to apply to it. Consider the following example: 1) Stream is in the state (no frames exchanged) 2) A PRIORITY frame for stream is received. 3) A SETTINGS frame is received to update SETTINGSINITIALWINDOW_SIZE. ... 4) Stream transitions into the state. Is seems a bit counter intuitive that stream 's window will be the window size prior to step 3. Just imagine there are many more SETTINGS frames exchanged (and other events) before step URL stream 's window will be even more difficult to understand how it got to where it is.
That's a fair point. The current text is: From this, you might infer that a reserved or idle stream isn't affected. Implicit in here is that you don't actually have an active flow control window until streams are in those states. There is no sense in performing any accounting until you are actually sending, right? But then, it's only accounting and that 32-bit number might be allocated and used in some implementations. So, I think that this can be clarified, rather than rely on people only accounting in those states, the text might be better saying: Does that make more sense?
This does make more sense. On a related URL is the rational to prohibit WINDOWUPDATES on the IDLE streams when PRIORITY frames have been exchanged? It seems like if a stream is allowed to be put into the priority tree (via a PRIORITY message) and its window size controllable (via SETTINGS) then why would a WINDOWUPDATE also not be allowed? I look to the following statement and think that accounting may already be required to track the PRIORITY and SETTINGS updates. The WINDOW_UPDATE accounting seems like it is very similar (or may be close to identical depending on the implementation).
We want to strictly limit the amount of state that is maintained for non-open streams. Currently, that is limited to priority. Opening that to more state isn't really necessary. For WINDOWUPDATE, there is only the case where you want to open the flow control window immediately after opening a stream, which currently causes a potential stall on the remote/sender side. As specified, you can create a priority graph that is separate from the other stream state (flow control in particular). If we allowed WINDOWUPDATE in idle (or reserved), then you need to commit more state. We already acknowledge that priority information needs special handling outside of the key stream states, so the risk is both accounted for, and already present. The most important factor though is that priority is expendable. Under DoS, you can drop priority on the floor. You can't do the same for flow control. Therefore, any state commitment needs to be strictly bounded for that.
Right. So for these cases the peer creating the stream will have to wait to expand (or shrink) the window until after the stream has transitioned out of the IDLE state. So state may not be required however if a peer wants to change the window for that stream then state will be necessary to keep and act upon when the state transition occurs. I"m not sure I follow. WINDOW_UPDATE is allowed in reserved according to . Priority is an interesting portion of the spec. It almost seems like you could ignore priority all together and be compliant :)
Hmm, strange how you forget these things. Yes, you can send WINDOWUPDATE in "reserved (remote)". This allows a client to affect the flow control window of the server that has promised a push. This is something that I know Firefox uses so that it can resume stalled pushes when it knows that they are needed. No extra credits over the initial window size are given to pushed streams until they are recognized as necessary. You can't send WINDOWUPDATE in "reserved (local)". After all, the other side (the client) won't be sending anything on that stream. So none of this really affects processing of the setting :)
I think I agree with you in general (it is not necessary). I agree that minimizing state on IDLE streams seems like more of a driving force, and I also recognize that the peer can wait to send WINDOW_UPDATES until transitioning out of IDLE. I can also imaging a scenario that if an endpoint puts the node into a priority tree, it is possible they have an estimate of what they are going to be sending. It may then follow the theme of "a more flexible expressions of priority" (from ) to also be able to control the window of those streams (at a finer granularity than the SETTINGS frame). Anyways thanks for the discussion and clarification.
Editorial problem, I hope: The spec uses the phrase "current stream" in two places (both related to PRIORITY), but without any definition of what that means: 1) "5.1 Stream States: ... half closed (local) ... PRIORITY frames received in this state are used to reprioritize streams that depend on the current stream." 2) "6.3 Priority ... Note that this frame could arrive after processing or frame sending has completed, which would cause it to have no effect on the current stream." I don't have alternate wording proposals because I'm not exactly sure what those sentences are trying to say. Note that I'm only talking about "current stream" in the singular. I see no confusion with the use of "current streams" (plural) in "6.9.2 Initial Flow Control Window Size".
"subject stream" or "identified stream" would work effectively well. I'll take suggestions.
Both sound fine, but "identified stream" is already used often in the spec and "subject stream" is never used.
Excuse me for trolling this issue but I couldn't help but notice how evil the issue number is.


Hey so I have a bunch of typos I found and this is one, "an" –> "and" in the "Limits on Header Block Size" section


New safe methods can be defined. Does this mean that the client needs to respond with a stream error for methods not known to be safe? See URL
That would be the only logical conclusion. If the client cannot determine if the method is safe, it must assume that it is not.
Indeed. We just need to fix it.


The latter part is vestigal, from the time when gzip C-E was mandated. Remove it. Here, I'm less concerned.


Only if it makes sense to do so. It's a perfectly good word that has been spoiled by an unnecessary inclusion in some popular RFC (2119, I think).


There seems to be some disagreement about this, but I think that Roy is right. It's not critical how it is registered, since it shouldn't appear in a valid request anywhere, but it meets the criteria.
I really don't think it matters, so I'm fine with it either way. Theoretically, there may be some compat value in having it listed as safe/idempotent since an intermediary may be more likely to let such methods pass. But the time slice in which this registry has existed and HTTP/2 hasn't will be miniscule -- I expect any HTTP/2-unaware intermediary won't look at the registry values either.
To the extent that it matters (i.e., very little, as you point out), I think that the intermediary example weakens the case for safe+idempotent. I really don't want HTTP/2 to pass an intermediary without triggering some deliberate recognition; that has been the source of some very serious bugs in the past.


There were lots of small changes as a result of . The only change of note is URL, which adds text on what a receiver should expect and what they are obligated to do for streams in the "half closed (local)" state.


had a few actionable comments that seemed relatively easy to handle. In the absence of feedback from NAME and NAME I responded. Of note: the introductory text in URL about the choice to make the format inflexible.


Kathleen Moriarty's IESG review noted some confusion that I think derived from the use of the word "prohibited" in relation to the cipher suite black list.


Proposal to replace . I think that this is a little clearer this way. Also, it's strange that this was not added earlier, but then sometimes the most obvious things get missed. cc NAME
So I'm not seeing how the new sentence is any different than this one two paragraphs below: A receiver MUST terminate the connection with a connection error of type COMPRESSIONERROR if it does not decompress a header block. My chief concern (merely editorial) is that HPACK consistently uses the phrase "decoding error" whereas here we are using the phrase "decompress." I think it good to make clear that any and all decoding errors MUST result in connection errors of type COMPRESSIONERROR. Picky, I know. ;-)
I think that Martin's proposal tries to specifically address your concern. The sentence two paragraphs below address the case of an endpoint deciding for whatever reason not to decompress a header block: this also has to result in a COMPRESSION_ERROR. So I think that both are useful.
Ahhh - thanks NAME Makes perfect sense and I agree.


Expands section 7.1.3 to describes characteristics of header needing the protection offered by the never indexed literal representation.
I think that this looks good now.


Fixes for those things that were obviously busted. Which (I hope) addresses the comments, with one exception (Richard's discuss contained suggested edits that should clear that one up).


Multiple editorial commits....
Just an fyi... I have additional pending editorial edits, some of which are a bit more extensive. I will hold off committing them until this pull request is processed.
Ok, try now...
NAME - please don't do ANY reformatting in pull requests; we need to be able to precisely identify changes and their provenance. Thanks,


Moves considerations of defining an ALPN token for a non-TLS-based protocol to 3.1. Changes "token" to "protocol identifier" in 3.3.


LGTM
Merged by RFC editor.
This wasn't obvious, but all descriptions of padding reference DATA: However, this is subtly wrong, as NAME notes. The actual text should be:


This was confusing a few people who weren't clear if the prohibition on frame sending for CONTINUATION trumped this or not. This isn't a perfect fix, but it should be enough.
LGTM
Merged by RFC editor.


This is a minimal proposal for solving .
LGTM
Lgtm as well. On Mar 17, 2015 9:47 AM, "Martin Thomson" EMAIL wrote:


After some sleep, I've read through the section and noticed a few other minor inconsistencies. This alters the structure slightly, as well as replacing the first sentence with a more accurate one. Supercedes . .
+1, thanks
LGTM
Merged by RFC editor.
We are working on a HTTP/2 library and we are a bit confused and we would like to get it right. Is it correct that after a sender sent a frame with some to a receiver, the sender may still open new streams, even with stream identifiers higher than the sent in the frame? That is the frame and the only limit the ability of the receiver? If this is correct, and I believe it is, then I find the following sentence in the first paragraph at confusing. In my opinion it should say something of the like Thanks.
I am also intersted in getting clarity on this topic. It seems what the "spirit" of the is something like: Each endpoint maintains the goaway last stream id from its peer. When a goaway is received then no local streams can be created (i.e. streams can only transition to ), and all local streams with > from the goaway frame are closed (or as the spec says ). Remote streams can still be opened, and to ensure a graceful connection shutdown a goaway frame should be sent in response.
Not quite: Upon receipt of GOAWAY, new streams can be created, but with the following restrictions: those with stream identifiers higher than the will be ignored. Those with stream identifiers lower or equal are subject to the usual risk of loss, but they might be processed. Success is determined by what happens on the stream, of course.
NAME - Can you clarify the following from : This is what lead me to believe that streams can not be "created" after receiving a ?
Hmm. I'd forgotten that. That is in conflict with the graceful shutdown we've specified. I think that we need to take this to the list. This seems like a genuine bug.
Thanks for the quick response NAME Would you prefer to bring the issue to the list?
I'm just trying to formulate something.
hmm I see NAME As a follow up, does the in the GOAWAY have to be the identifier of a stream that actually exists or could it also just be a "random number"?
The intent was to say that the receiver of a GOAWAY can't create new streams higher than the value in GOAWAY. Let me check back in tomorrow; maybe fresh eyes will add perspective.
So the intention of the change is allow receiver of GOAWAY are still allowed to create stream lower or equal to received last-stream-id, accepting the fact that those streams are ignored by peer? As the graceful shutdown process as described in draft 17, server first sending GOAWAY with (2^31) - 1 starts to ignore any new streams created by peer and after some time (this ensures that GOAWAY is reached to the client), server sends another GOAWAY with updated last-stream-id where processing has occurred already.
NAME the intent of the change is to not change anything :) The process has been unclear, but the point is that if you receive GOAWAY with a last-stream-id greater than anything you have initiated, you can use what space remains for new streams. What that means is that you can issue new requests on that connection while you take the time to create a replacement connection. This was considered critical for . That means that 2^31-1 serves only as a warning of impending closure, though in theory it need not change any behaviour for the existing connection. I'd take steps to make a replacement connection though.
Thank you for clarification!
Here is a link to NAME doc from the mailing list which provides additional clarity to this issue URL
Latest PR is much more comfortable to me. Thanks!


LGTM
Merged by RFC editor.
Minor bug that needs to be tracked... I assume the text in section 6.2 needs to be modified to add "idle"?


Thanks for the PR NAME Should we move the issue discussion (URL) to this PR?
LGTM
NAME if you have comments, please let me know ASAP.
Merged by RFC editor.
has the following text: Draft 17 added the changed to . Can someone clarify why would streams in other states be excluded?
Those are the only states in which data (or DATA) can be sent.
Does your stream have to be eligible for data to be sent to have its windows manipulated? For example are IDLE streams intentionally excluded? Would this mean that the initial settings frame wouldn't apply to any streams? I think it may make sense to be able to alter window sizes of streams in other states because of dependencies/priorities and how the windows are distributed amongst the tree.
While it is true than an idle stream is affected, the text there is to highlight the fact that streams with active flow control windows (i.e., ones that might be in use) might be affected.
Is it possible to clarify this? I think there are a few options: If we are going to explicitly call out states: we should call out all states where it could possibly impact streams. If we are not going to explicitly call out states: Use a description which implies stream state which is defined elsewhere in the spec. Something like: Note this is just an example... The current text in the specification is in conflict with this clarification you have provided in this issue. It seems like the following sentence in the spec may be sufficient by itself?
NAME - Ping.
The inclusion of stream state in your clerical response leads me to another question related to the priority tree, stream state, and flow control windows. indicates that . The flow control window is a resource which can be divided amongst nodes in the priority tree. However the frame can be sent/received in all states, but the frame is more restrictive in its allowed states. This discrepancy makes controlling the resource (the window) difficult (i.e. providing a mechanism to avoid deadlock). Is there any way to clarify or allow the resource (the window) to be controlled adequately along with priority?
As far as it goes, yes, a server might allocate flow control window based on priority. But that only applies to the logic that an implementation uses to decide how to allocate credit to flow control windows, which is intentionally (and expressly) unspecified. I don't think that it needs a special call-out.
I agree that it doesn't need to be specifically called out that the window can be treated as a resource, and that this is an implantation detail. However calling out the stream states when referencing frames that impact the flow control window may be overly restrictive. For example the are not the only states in which a stream may be in the priority tree. Here are what I believe to be the relevant portions of the specification which relate to flow control window and stream state. seems to be very careful about stream state and doesn't necessarily limit to just is much more restrictive and declares that is not allowed in , or . For there is special mention that these frames must be ignored, or may be an error (if it arrives ). If the clarifications in this issue are applied then it seems like the impact on the flow control windows is further limited to .
BTW, I consider the state associated with having a stream in the priority tree to be necessarily separate from the state that is maintained for an "active stream" (i.e., one with active send or receive state). Lumping them all together exposes you to some potential attacks.
Can you be more specific (particularly about the )? I guess what I'm after is two fold: Consistency amongst the sections that discuss window update and stream state. Not necessarily preventing window_update being controlled when a stream is in the priority tree, or rational as to why it makes sense to do so.
The amount of state you commit to a stream that is active is potentially large (flow control windows, primarily). That is strictly bounded though by your value of the MAXCONCURRENTSTREAMS setting. If that state has to live on the same lifetime as stream priority, then it is not covered by the setting and you are potentially in a position to commit a much larger amount of state. You can (obviously) manage this by careful management of the transition to and from the active states, but I believe that it is better to instead have separate tracking of priority.
One way to implement this cleanly is to have a (small) priority state for streams that is created and managed by a garbage-collector. Active stream state can be a separate object that maintains a reference to the priority state (that might be nullable, or you could require that only closed or idle streams be exposed to the GC).
I think we are straying into implementation details a bit, and we are in agreement that the state could be managed, but in the scope of this issue....I re-read your PR and I am fine with it :)
NAME Does "old value" here means the old initial window size?
Yes


This makes it clearer that they can't be sent otherwise.
LGTM
Merged by RFC editor.
has the following clarification added in draft 17: Should this be instead of ? If not I think we have an inconsistency with which has the following for the section:
NAME - Ping.
This is merely a problem with perspective. As in, there is none. I can provide that thusly: Does that work?
This is an improvement. I'm not sure if we can avoid it but it is a bit confusing with respect to :
I don't think that's a problem. The (remote) peer isn't sending, but the (local) peer can.
Agreed. Ship it :) Thanks!


Same edits as before with additional tweaks based on feedback. I've adjusted the formatting on my edits to match the current styling as much as possible (must... resist... OCD... tendencies ;-) ...). Specific edits can be reviewed in the commit log.
Careful, you could end up with a new job if you keep this up.


A lot has happened to TLS since we did RFC 7540. This tries to capture that succinctly. It rolls in changes from RFC 8740.It mentions early data and RFC 8470.It updates references, citing TLS 1.3 rather than 1.2 for a generic TLS reference.
A few small changes are included in , which could be integrated easily.
I won't create the pull request just yet, but here are some proposed changes: URL NAME do you think that you could review these? I've paraphrased some of RFC 8740 and I want to make sure I haven't missed anything.
(The GitHub link did something silly, but I'm assuming 837d46a5ff9e23a62cb719f69be7f1d10bf8da80 is the right commit and just looked at that.) s/the handshake// Though I wonder if this sentence can just be omitted. I needed to address it in RFC8740 because RFC7540 didn't have a TLS version dispatch. But now the TLS 1.2 features section is explicitly mentioned as being 1.2-only, so that exception already doesn't apply. Otherwise LGTM. (I think the default values in server settings is implicit from the fact that we haven't received anything from the server yet, but whatever.)
Thanks. I've updated my branch to remove the renegotiation piece. I've updated that link (which I should have checked).


The thinking has evolved considerably since the original reference was made. This is the right way to refer to this concept.
So the re-request review button doesn't appear to work; NAME any objections to landing this with the new reference target?
The current reference is in the WHATWG "infra standard": URL To use this, we need to ask for the "tracking-vector" anchor to be made permanent.
URL filed. I have changes for a pull request .
I'm a bit surprised you chose infra; the fingerprinting guidance document in the W3C is much more complete. There are also tons of academic references that have better descriptions; to start, Eckersley's original paper.
I was looking for a direct replacement, which is what that is. I can cite URL if you think that is better. Or URL if that makes more sense. I never know which is better.
Probably the TR - whatever bibxml says, I'd think.
I'm going to go with something simpler. RFC 6973 has an adequate definition.
None whatsoever.


This is a first stab at . I've gone for a minimal change here, simply clarifying that only HEADERS and PUSHPROMISE frames have the effect of implicitly closing streams. While I was writing this I did have a question that may need to be raised on-list: do HEADERS and PUSHPROMISE only implicitly close streams if they are accepted? That is, if a client sends a HEADERS frame that triggers a stream error, does that HEADERS frame still implicitly close all streams with a lower stream ID that are in the "idle" state? This may also be worth clarifying.
Like it. Take the first, maybe take the second, and we can merge this one.


Thanks NAME ready for re-review I think.
Oh, you proposed changing "handling" to "reporting": did you want to do that pervasively in this document?
Let's take that up separately.
Ready for merge I think.
I agree, but as this is substantive, let's run this by the working group.
As there is a draft deadline, I will open an issue for my niggle, and ask for forgiveness rather than permission.
It's not clear to me from RFC 7540 how an endpoint should behave upon receiving a frame that is erroneous in more than one way. For example, suppose a server receives the following frame when it has stream id 1 in half-closed (remote) state: According to §4.2, the frame "MUST be treated as a connection error" and the server "MUST send an error code of FRAMESIZEERROR" (because the length of 4 "is too small to contain mandatory frame data", namely the weight and header block fragment fields); According to §5.1, the server "MUST respond with a stream error (Section 5.4.2) of type STREAMCLOSED" (because a frame "other than WINDOWUPDATE, PRIORITY, or RSTSTREAM" was received for a stream that is in "half-closed (remote)" state); and According to §5.3.1, the server "MUST treat this as a stream error (Section 5.4.2) of type PROTOCOLERROR" (because "a stream cannot depend on itself"). That is to say, according to the RFC, the server MUST (at very least) respond to this frame with one GOAWAY frame and two RSTSTREAM frames. However, sending all these frames would violate other requirements: According to §5.4.1, "after sending the GOAWAY frame for an error condition, the endpoint MUST close the TCP connection". This obviously prevents other frames from being sent. According to §5.1, sending a RSTSTREAM transitions the stream to closed state, in which "an endpoint MUST NOT send frames other than PRIORITY". This prevents any further RSTSTREAM frames from being sent for the same stream. In this particular case, it seems reasonable to me that the frame size error should halt further processing of the frame such that neither of the stream errors are even detected: after all, an incorrect frame size may indicate corruption (in which case the rest of the frame's data must also be suspect). But this is neither clear from the spec nor might other cases be so clear cut. I see three possibilities, with a reasonably strong leaning toward the first: Clarify that further processing of frames SHOULD (or MUST?) be halted upon encountering an error (I suspect this is what most implementations currently do, irrespective that the RFC says they "MUST" behave otherwise?). But then the order of processing becomes significant: is it right that this should be left as an implementation detail, or should it be specified? After all, the above frame might trigger a STREAMCLOSED stream error from one server and a FRAMESIZEERROR connection error from another, with very different consequences: how might these differences impact intermediaries, caching and retries? Better I think for the order to be specified. Define a precedence to errors, and mandate that that with highest precedence be sent (for example, connection errors might have higher precedence than stream errors). But then endpoints must continue processing frames they know to be erroneous simply to discover whether any higher precedence errors also exist (at least until an error of the highest possible precedence is encountered). Permit (require?) all errors arising from a single frame to be transmitted irrespective of any other errors the frame may also have triggered. Again, endpoints would then continue processing frames that they know to be erroneous—but the peer would then be fully informed of all problems in order that it is better able to recover.
I think the simplest explanation is that connection errors are those that suggest there's no way to (safely) continue attempting to parse anything. I suspect most implementations would find that this frame is not long enough to actually be a HEADERS frame and not attempt to parse it as a frame it cannot actually be. Thus, while a given implementation could possibly discover one of the stream errors first (thereby sending a RST_STREAM), once you discover the connection error, you're sending the GOAWAY and throwing the connection state away. The logical implementation is likely to find the connection error immediately and not read far enough to find the others.
I agree, but this is not specified. Is it okay to leave as an implementation detail, even if most implementations will do the "logical thing"? Also this was just one example of a frame with multiple errors... others may not be so straightforward.
The question we should ask is: Is there any behavior that would change that would cause it to not interoperate here? I suspect that, regardless of how one interprets this, processing must stop, and the client may or may not receive an error, thanks to the vagaries of packet-loss. If this requires any clarification, I'd pick the path of asserting that any connection error stops any other processing.
Actually I also found error handling quite tricky to do, especially regarding when and what to send in GOAWAY frames. One really needs to think in terms of error severity to implement a form of funnel so that more severe errors can still be emitted after less serious ones. One example that quickly comes to mind is that we can send a GOAWAY frame to indicate a graceful shutdown of a connection yet this one is not an error and may be followed by other GOAWAY reporting protocol violations. I also faced an annoying situation related to TCP where you can't send a GOAWAY frame due to a buffer full situation, and closing after this causes a reset to be emitted due to pending incoming data. So we actually have to enter an input drain state in error situations, which further adds to the implementation complexity.
QUIC has that I think h2 would benefit from.
See for some proposed text to address this. We probably need to workshop it a bit.
Thanks!


HTTP/1.1 to HTTP/2 Upgrade was never widely deployed and has no particular defenders. This change removes the text explaining and defining the mechanism. This change still requires discussion on the mailing list.
NAME I've returned the two IANA registry entries and pointed them backwards at RFC 7540, as the sections that define those fields were removed from this document. I'm not actually sure that's the right IETF process there. Happy to take guidance as to what I should be doing instead (e.g. putting in a pair of dummy sections saying that the field and token don't have semantics any longer and referring to those instead).
Ok, I've replaced the registrations with tombstones, this is ready for another go around.
It would be good to more explicitly say that the upgrade mechanism is NOT RECOMMENDED, both because we're encrypting everything and because of the it enables (even if they're mostly shoddy implementation, it's more surface area).
Sounds like a reasonable addition, but it might be better to track that with a separate change.
Happy to add that here or make a separate change, either way.
Looking into this a little more, I think that Section 10.3 () is probably where some extra warnings might be worthwhile. The reason I might prefer a separate change is that we can hit Upgrade, Transfer-Encoding, Content-Length, and other Connection fields at the same time if we do it right.
This has not been implemented, so can likely go. See the discussion on URL for more context.
I’m in favour of removing this. It’s clear that the ecosystem has not found a use for it.
As a co-author of this one, I agree as well. This resulted from a misunderstanding of the use of the preface that everyone happily uses in clear to distinguish between H1 and H2 (and hence serve as a much easier upgrade).
Seems to have support; does anyone have an issue with this?
As stated in today's meeting, this sounds good to me, but it's worth posting on the list.


After carefully reviewing the Netflix advisories, it was clear that the problem was that implementations didn't read the original text. The TCP window reduction attack in CVE-2019-9517 was not mentioned, and it is a clever exploit. The rest were clearly already documented. I took the opportunity to restructure things a little bit. The first few CVEs were about causing the victim to enqueue a bunch of frames and then exploit the fact that this queuing was badly inefficient. Those got a list of their own. I purposefully left CVE-2019-9516 out. Though I am sad that people didn't free() after malloc(), this is pretty much just a systematized memory leak and I don't feel like calling that out in an RFC. I've cited the disclosure page, so I think we're covered there anyway.
The spec includes language on this, but it turned out that many implementations failed to properly safeguard themselves. Clearer descriptions of problems based on this experience would be good.
I don’t think it hurts to tighten the text up here and add more examples, at least so new implementations are better able to learn from past mistakes. However, we should probably keep in mind that this cannot possibly be an exhaustive list of exhaustion attacks.
This is the Netflix write-up, which might be a good reference: URL I can only imagine citing a markdown file on GitHub will raise eyebrows, but it's not a bad summary of issues.
One minor suggestion, otherwise I’m happy with the new text.


Additionally, section 4.2.3 contains a lot of text that is similar to 4.2.2, but there may be a reason for that.
I'll try to remember to fix the content-length thing.


The previous text implied that a value of 1 was a problem, which was somewhat in tension with the default being 1 and servers mostly not sending the setting. So clearly what was being checked by client was the frame, not the value. This changes to checking the setting as it arrives, changes where that text lives. Also, this changes from "endpoint" to "client" or "server" as appropriate to match actual practice.
The definition of ENABLEPUSH implies that it can be set to 0 by a server, but the effect of that is meaningless as it results in treating PUSHPROMISE as an error. PUSH_PROMISE can't be sent by a client. Should we update the definition?
I don't think it's a good idea to start forbidding ENABLEPUSH = 0 from servers. It's clearly acceptable from the perspective of the RFC, and while it's a silly waste of bytes it doesn't introduce any protocol ambiguity. I do think we should clean up the text in § 6.5.2, which currently reads: However, Section 8.2 implies the default value for servers is 0: Arguably these two sections should be reconciled. In principle we could simply say that the value of ENABLEPUSH has no effect for servers, as the protocol forbids servers from pushing anyway, and remove the requirement that clients error.
I think that you mean clients are forbidden from pushing, but otherwise I would agree. The initial value for ENABLE_PUSH is defined as 1, which muddies things further. I think that we can change the definition to say that the setting has no effect when sent by a server and drop the connection error requirement.
Would we leave the connection error as an option?
I'm inclined to remove it as being inconsistent with reality. Though we might have to note that some implementations might do that (because they haven't updated) and so advise strongly against servers sending any value for this setting.
Is it inconsistent with reality? Presumably some implementations actually do police the MUST here.
Two things are in tension: policing probably happens in the sense that if a server sends ENABLEPUSH with any value (especially 1) a client might choke on it the initial value is 1, so those clients should probably choke on a connection preface from the server as well Removing the requirement that ENABLEPUSH result in an error avoids the second, but you need to keep a recommendation not to send it or you hit the first.
I suspect most implementations treat initial values somewhat specially, and while they enforce that they cannot receive a value of 1 from a server, they don’t prevent the server’s setting value having the value of 1.
This might be one worth circulating with the WG tbh, if only to get a sense of what existing clients actually do. We can also investigate putting together a test suite.
One minor language nit, otherwise seems great.


NAME says: It's not very widely used and many implementations either don't support it at all or don't support it in a way that improves end-user performance. This sparked a lively discussion at URL
I’m open to dropping this, but I think the case needs to be be made for what the burden is. It’s one of the easiest protocol features to just ignore: send SETTINGSENABLEPUSH=0 and then you can remove all awareness of PUSH_PROMISE from your state machine altogether.
Crazy thought: what if we made part of HTTP/2, and then factored server push into an extension?
Or what if we minted a new ALPN () and flipped the default value?
Or left it as is, since it is used outside of browsers. The browsers have an API problem that has not been solved, and it is unlikely that push will be effective there (except as a competitor to inlining) until that changes.
I have no particular objection to flipping the default under a new ALPN. Factoring it out to an extension is harder, but not impossible -- it's pretty embedded in the state machine, but we might be able to abstract that away. Removing it entirely seems challenging for several reasons. The purely process one is that RFC 8030 requires Server Push, and removing a feature used by a Standards Track RFC presumably requires deprecating that other RFC.
I should note that my preferred outcome here is "do nothing". I'm just spitballing ideas.
Flipping the default if we mint a new alpn seems sensible based on the comments above. This would also align with HTTP/3. Moving it to an extension is probably more work than it's worth, I'll admit. I wasn't aware of RFC 8030. Is that an actively used RFC?
Push is being used outside of browsers (curl supports it, once implemented for a particular company that uses/used it pretty widely across the globe), although I suspect that outside of browsers we also have no real means of figuring out exactly how much or little. Saying "do it in extension" will probably equal killing it, but I also think that removing it from the spec is more work than its worth and just toggling the default seems like the best middle ground.
We may want to be a bit relaxed about flipping the default. As the setting is client-only the default value never really exists on a connection: the client’s SETTINGS are the first frame it sends. The reason to flip the default is to save 6 bytes on all connections. This is not nothing, but it’s not a huge win either.
I agree that it's not worth changing anything here, except if we want to be sure to cause some confusion between implementations. The first frame sent is the one indicating support (or lack thereof) by the client. Maybe we should just add a paragraph in the PUSH section mentioning that 5 years after the relerase, PUSH has still got very low adoption among implementations and should not be assumed as a granted feature for protocols sitting on top of H2.
I think NAME suggestion of adding a note about deployment and not expecting wide support would be very helpful. My biggest concern with keeping it in the spec is that people will expect it's widely supported and its use is recommended for most use cases.
Feeling here seems to be supportive of no normative changes, but possibly some editorial additions clarifying the status of push. If you feel otherwise, please say so soon.
SGTM. FYI, Chrome is expected to drop support for HTTP/2 server push in the= near future and has never implemented push for HTTP/3.
NAME has it been announced somewhere? I can't find it.
Several popular web frameworks actively use Server Push including (mostly for assets) and (for API relations). Server Push is also (currently) supported by most browsers as well as by NGINX, Apache and Caddy. On the APIs field, , which aims to replace GraphQL-like document compounding is also gaining adoption, and rely on Server Push (even if it can fallback on preload Link headers and Early Hints too). I just published a benchmark showing how useful Server Push is for the specific use case of web APIs. Under certain conditions, relying on it can be 4x times faster than using compound documents: URL As pointed in martinthomson/http2v2, for these use cases, Server Push could be replaced by Early Hints or maybe by something using WebTransport, but these specs aren't currently implemented while Server Push is broadly available. Removing support for Server Push in Chrome will hurt all this use case. What's really missing with HTTP/2 Server Push is a way to prevent pushing resources already stored by the client. could have been a solution, but the work on it looks stopped.
URL
NAME suggested calling out the current status: lack of support in browser APIs for push, pointy edges in the usage, and the limitations in the spread of its usage. I think this is a reasonable editorial change.
NAME to draft a document to encourage users to set the value to 0 by default and move on.
Discussed at Feb 2021 interim; intent to document concerns / caveats, recommend sending setting = 0. NAME to PR.
LGTMVery nice. Thanks Mark.


In the spirit of the naming simplications of the recent HTTP changes, we can talk about settings in this revision. This aligns better with HTTP/3, though that still uses "settings parameters" (with that ugly double-plural). cc NAME
I mean, it's not too late to Auth48 the same change into H3.... Not quite, anyway.


From IANA: As we have the patient open already, it might be OK to change the registry name to follow modern conventions.
To clarify, IANA would like us to delete the word "Parameters" from the following text?


The current rules state that you need to include text in abstract and introduction. This will result in redundant entries for the specs with other changes that add 7540 references, but we can sort that out later.
Picking my battles more judiciously (as much as I'd like to fight this one, it's a low reward, high risk situation).
informatively, as they are obsoleted by this spec
Rules are boring. Be a rebel without a pause. Omit that text.


This required a little careful rewriting of text that only recently rewrote. Hopefully this retains the improvements of that rewrite.
Right now, we have a lot of 1.1-specific language. In theory, it should be possible to remove 1.1 references and use core HTTP semantics to describe the protocol elements. That would be much cleaner, but it could be tricky.
Nice. I think the retains the original meaning of the text well enough, and is as clear as it can be without ever needing to normatively invoke HTTP/1.1. Needle well threaded.


This encodes the conclusions from interim discussions: CR, LF, and NUL cannot appear anywhere in field names or values. SP and HTAB cannot appear at the start or end of field names or values. COLON cannot appear anywhere in a field name, except for the colon at the start of a pseudo-header field name. The strong requirements about validating fields according to ABNF has been replaced. The text is clearer about how the pieces fit together: it is HPACK that allows any octet, whereas HTTP/2 makes certain choices invalid.
This allows whitespace and control characters in field names. Is that intentional?
This was intentional, but it can be revised by expanding the set of characters we prohibit. I had imagined that this would not be different from HTTP/1.1 where "Foo\tBar: ?1" might parse, but is not valid in the same way that "Foo\Bar: ?1" is not. I thought that we had agreed that a close policing of the field name grammar was not required from HTTP/2 implementations.
NAME take another look. I've refactored your suggestion.
Yes.
NAME if you can suggest text that would address your remaining concerns, that would be good, but I think we're going to include this text in the next draft. Of course, there will be ample time to improve on this; we just want to make sure that we have an updated draft to discuss at the upcoming interim meeting.
NAME What I think is missing is a description of what this change is trying to achieve. The text identifies that there is a set of , but notes that that is a super-set of of . It then gives text description of what the set of is, but it is not clear exactly what the intent of that set is meant to be? The possibilities are: == union . But if this was the case then why not define it in terms of the HTTP ABNF? == union union . This seems a strange thing to do, but the motivation was originally given that this was describing what existing implementations do? If this is the case then it should be described as such. == intersection . I think this is what the text is actually striving for, but a) it would be good to say so; b) it is unclear to me why h2 needs a superset of option 1. above... if so it should be said why. If it is indeed option 3. then I assume that there are some fields in the set that are nether in the nor sets. What are those fields and what should an implementation do with them? We are told later in the update that if we are an intermediary then we can validate strictly against the HTTP ABNF and that if we don't we risk invalid fields (and by inference of the section title encapsulation attacks). But what do we do with those fields if we are not an intermediary? Do we pass them onto our applications and let them work it out that they are not members of ? There is nothing there that says we MAY treat such messages as malformed - so at the very least, it would be a good addition to the text to say that non-intermediaries can do stricter validation. So in short, I can see lots of text defining , but it is entirely unmotivated as to why it is > . Finally, to harp on my original complaint, I still do not understand why is defined in 4 bullet points of prose that have already confused some readers. There is a paragraph about how field names have to be lowercase and then the bullet points ignore that and define all visible characters except space (so if space is visible does that mean tab, CR, LF are also visible?) and except colon. Some simple ABNF would be so much better: Or better yet, just go with HTTP field names, but with names in lower-case: Edit: removed comment about ESC... I would looking at an ASCII table from before I was born!
No it doesn't. It gives a text description of a set of invalid H2 fields. The text does not say these are the only limitations on h2 field validity (noting that there is also an ABNF description that applies when -semantics applies), neither does it say that any field that doesn't contain any invalid characters as described in this section is valid. I do think the text could be clearer in saying that this is a non-exhaustive list of reasons why a header field may be invalid, but at the same time I'm reluctant to have this document spend too much time re-hashing reasons that exist in other specifications that are presumed to apply. This question is one for -semantics, not for HTTP/2. -semantics gives the answer to this: you MUST NOT emit (§ 2.2), but as a receiver you are cautioned that (§ 2.3): Additionally (§ 2.4): This gives you the permission you want: you are encouraged to parse the field defensively and you may attempt to recover, which implies that by default you may fail, and that -semantics does not specify exactly how you will handle these failures. HTTP/2 continues to grant your application that freedom. The specification does not bind you: you can handle this as you want.
OK True. Which is actually worse, as it leaves the set of undefined other than we know it is a subset of the compliment of the set of invalid fields define here. Also worse, as it means the document only vaguely defines validity. There is actually no definition of what is a valid h2-field. This reason could be used to say that there should be no additional invalidity check in this document. Just allow any hpack-valid field and leave the rest to be a matter of semantics. The fields excluded by this definition are only dangerous if forwarded, which is already covered by "Intermediary Encapsulation Attacks" section that suggests that full HTTP ABNF grammar should be applied (in addition to the vaguely defined exclusions of "HTTP Fields"). So I'm back to why is this document in-precisely defining valid/invalid h2-fields. It is not for intermediaries as there is a section in the document dedicated to that. It is not for locally interpreted HTTP as you say HTTP semantics should be applied. Why does this document say that a field name containing space is invalid, yet one containing a double quote is not? Neither are valid HTTP, both are valid hpack. There is no good reason I can see for either and whilst I don't know of any specific attack that either could be used for, I would not be surprised if both could be used to some evil ends. This vague definition is going to have a significant carbon footprint. Many Implementations will ultimately end up double validating field names: once to exclude the s in their h2 layer and then later in their semantic layer to include only the s. Double validation could be avoided if this document either didn't restrict field validity or precisely defined valid fields as a subset of s so that semantic layers could trust the fields received from their h2 layers as being valid HTTP. Maybe there is a reason for this double validation and the resulting CPU cycles, but I have yet to see why validity is only partially defined.
It suggests it but does not require it. The other text requires validation. Most intermediaries will not apply the full ABNF validation because a) it requires perfect understanding of the ABNF of all header fields, which they will not have, and b) even if they did, the -semantics ABNF is sufficiently costly to validate that they won't do it. Cheaper validation steps are more likely to be implemented. This document (and -semantics) is imprecisely defining them because a . The ABNF you're citing represents, fundamentally, guidance. If you want to reject things that don't conform to the ABNF you are free to do so (as -semantics says), but there is no document that obligates you to reject them. This is deliberate, because there is (IMO) absolutely no point in adding normative requirements that, if followed, would harm interoperability. In this case the document is calling out specific cases that MUST be rejected. Practically speaking the intention of this is to encode requirements from other messaging formats, such as -messaging, to ensure that HTTP/2 implementations do not encode header fields that cannot be safely translated to HTTP/1.1. This provides a lower bound on validation: so long as everybody does this, we can safely rely on HTTP/2 messages being represented in HTTP/1.1's framing. Note that this doesn't meant they'll be valid HTTP/1.1 messages, just that they are not going to parse incorrectly. With that in mind, the constraints added here are, in order: A cheap bitwise check that can be safely vectorised and applied to reject most invalid field names. This is sufficient to get close to the ABNF, but is cheaper. A check that reserves pseudo-headers and is a single-byte comparison. Three vectorizeable field-value checks (NUL, LF, CR) all ensuring valid HTTP/1.1 framing. 4 single-byte comparisons against the first byte of the field-value. This problem is solved by not doing that. The ABNF in -semantics, if enforced, is a strict superset of the requirements here except for (2). If your implementation will enforce the requirements in -semantics, you can choose to not enforce these ones at the h2 layer, knowing that the -semantics layer will cover you. The RFC does not bind your implementation, only your observable behaviour: if your implementation ends up rejecting header fields that meet these characteristics, it doesn't matter why you did it, only that you did.
I don't get that. We're still talking about field names, right?
No, all of this applies to names and values:
Well. Validating names and values are very different things. It would be best to clearly separate these topics.
I get it that there is a minimum standard being set. In section "HTTP Fields" it says that h2 implementations MUST treat as malformed any fields that violate any of the described conditions and an intermediary MUST NOT forward any such headers. Then again in section "Intermediary Encapsulation Attacks" is says the validations of the "HTTP Fields" MUST be implemented, which is kind of a duplication as they are already at MUST strength. I don't see any normative text that says that an implementation MAY or SHOULD validate against the HTTP ABNF and that if they do so then that is a superset of the conditions in section "HTTP Fields" I would very much prefer to see text in the "HTTP Fields" section that clearly says h2 implementations MAY validate against the HTTP ABNF and that any implementations that do not MUST validate fields against the following conditions. I'll have a try at this later today.... stand by....
NAME I'm reading your comments as supportive of the technical change, but a strong preference for a different way of presenting the information. I want to clarify that this change is about the mandatory validation that endpoints perform on fields. It looks like we all agree with stipulating a minimum amount of validation that applies to all implementations, especially those that would otherwise just forward fields without processing. Additionally, we all agree that additional validation is permitted, up to the point that knowledge of the semantics for the fields is applied in validating the values. Where we might disagree is in the way that these requirements are described. I've chosen to use words; NAME would prefer ABNF. As we agree on the technical substance and the question of presentation is an editorial decision in which Cory and I have discretion I'm going to merge this pull request. We would like to publish a revision ahead of the upcoming interim. If you disagree with this decision, especially the technical aspects, then I encourage you to open a new issue. One aspect that seems like it might worth discussing is whether we encourage additional validation rather than simply permitting it. That is, "SHOULD/MAY fully validate".
NAME that's a correct summation of my position. I would have preferred that some text for SHOULD/MAY fully validate to be resolved in this PR (with or without ABNF), but will open another issue.
: While most of the values that can be encoded will not alter header field parsing, carriage return (CR, ASCII 0xd), line feed (LF, ASCII 0xa), and the zero character (NUL, ASCII 0x0) might be exploited by an attacker if they are translated verbatim. Any request or response that contains a character not permitted in a header field value MUST be treated as malformed (Section 8.1.2.6). Valid characters are defined by the "field-content" ABNF rule in Section 3.2 of [RFC7230]. There are multiple issues here: it's confusing to say "HTTP/2 allows" when next they are actually forbidden with a MUST requirement; it might be better to say something like that the wire protocol in theory enables the transmission of these forbidden characters this puts a normative requirement on top of the base spec; but so does the revision of the core specs (see URL). We need to make sure that there are no conflicting requirements. It also should be checked whether the currently present requirements are actually implemented in UAs (and if they are not, what could be done about it)
It would presumably be more accurate to say that "HPACK allows", right?
HPACK also allows encoding a field line with an empty name. I'm not sure this needs to be mentioned explicitly or not.
Well, that's consistent with HTTP/1.1 :-)
I hate having to do this, but what is possible and what is permissible need to be more clearly separated.
I'm going to tag this as editorial: the possible/permissible split is clearly editorial. And we've decided to depend on the core docs, and whether or not it was clear before what was allowed or not, this is now very crisp in the core semantics. I want to wait until we get in before doing this though.
Discussed in the context of leading and trailing whitespace in -semantics. I think that we should be clearer here about a few things: CRLF and NUL are strictly prohibited leading and trailing WS are strictly prohibited Maybe loosen the requirement a little so that it is less strict about adherence to the ABNF That makes this non-editorial, I think.


The language doesn't properly acknowledge the possibility that some processing might have been performed before an error is detected. This could use some tweaking.
Proposal here: URL
Seems like a reasonable change.
Seems like a nice clear improvement


This is part of the fix for . This part removes the experimental range for frame types and settings. Also updates to use section references to RFC 8126, which replaces RFC 5226.
Nice clean change. No notes.


QUIC includes the following statement: I like this. (No accident. I wrote it.) Should HTTP/2 include a similar statement? We have PROTOCOL_ERROR, which has a definition compatible with this statement, so I guess we're already there on as far as allowing a generic code goes. Any sense in saying it explicitly?
Yeah, I think this is sensible text to include.
Reads clearly, captures the intent.


Not sure that this needs a citation.
URL describes how HTTP/2 can be used to remove some network-induced jitter from the timing measurements. If your server has timing side channels, then putting requests that can be compared in the same IP packet ensures that the server receives them at the same time. This doesn't help if timing differences are masked by server queuing, CDNs or load balancers, or response delivery, but it can help reduce measurement noise. Consider documenting this.
I'm reading this as editorial not normative (or at least not changing the wire protocol); seems uncontroversial.
Yeah, I'm not sure what else we can say here. There are risks here, there are not really many mitigations available.


Here is some draft text. In principle we don't need any of this at all: as this text shows, all of the relevant guidance is already present in both semantics and H2. I've elected to throw up some possible text, but if we wanted to say that some or all of it is entirely redundant with existing specifications then I'm happy to do so.
Upgrade, Transfer-Encoding, Content-Length, Connection, and anything listed in Connection MUST NOT be forwarded without modification. See also .
Note that this probably goes in
I have no personal knowledge of this issue, but it came up on the #whatwg IRC channel. URL describes Safari rejecting responses that contain the header field, while other browsers accept it. It'd be good to converge the major clients on a single behavior and, if that's not the behavior currently specified, update the spec to match.
To be clear, that's nginx being spectacularly broken by forwarding the header field. I'd characterise the philosophy of H2 as being strict with errors that endanger the whole connection -- and counts the server asking for an upgrade in a context where it doesn't make sense as qualifying as such. While I'd be surprised if folks wanted to relax the former (the strictness is a net win for H2), the latter might be worth revisiting, not because nginx is broken, but because H1's use of headers for connection-level mechanisms turned out to be so broken. I'm also not sure that aligning all clients to be so permissive is such a great idea; accommodating brokenness like this is a race to the bottom, and while it might be the only practical path when you have to interoperate with a large and extremely diverse pool of existing deployment (HTML), there aren't that many HTTP implementations that can move the needle. Interop might be better served by holding the line and forcing broken servers to address their problems.
Additionally, it seems broken for Apache to send the header when the connection between nginx and Apache is over TLS.
Filed .
Apache treats the effects of sending over TLS as a UA-side bug. :-(
Yeah, this looks bad. The spec is fairly unambiguous about the requirement to ignore Upgrade in one place. But it also prohibits the use of Connection, which implies that you can't use Upgrade. I think that we should fix this, potentially along with . I hope that we are able to convince NAME to change Apache here. There is no value in sending this value.
FYI analysis,


Brought over the QUIC text about flow control performance. cc NAME
Thank you Cory. After the first sentence, I'd like to add the following because I'm pretty certain it is easily overlooked, especially in gateways where there's basically no way to figure the really available room:
Under what circumstances can that happen?
NAME basically any time on the forward path: if you only count on the local buffer you're using to receive a stream, the window will always be ridiculously low, while you know that you have plenty of buffers on the other side (e.g. forwarding to H1, which is dedicated to your stream, you can count on the H1 buffers and system socket buffers). When forwarding to H2 you can theoretically at least count on min(streamwindow,connectionwindow,buffer room+system buffers), though it can be quite hard to figure. The problem with a multiplexed protocol (when doing fctl-over-fctl like H2 does with TCP) is that your per-stream buffers are always smaller than the connection-level buffers (that include the system's huge socket buffers), which are usually automatically tuned to accommodate the BDP. So in the end relying on stream buffers only implies you can never reach the BDP and will be forced to limit the single-stream bandwidth. This problem doesn't happen with other protocols like QUIC simply because there's no need in-order buffering in the system that you have to rely on. In the end, a reasonable approach consists in having a configurable margin on top of the per-stream buffer room to figure what to advertise, but we need to warn about the risks of advertising too large a window.
Hmm, sorry NAME but I don't see how that relates to the original note you gave. Where is there a head-of-line blocking issue? Having small stream window sizes prevents a single stream reaching full network performance, sure, but that isn't a head-of-line blocking problem. It's a window management problem. If you have multiple streams, all with small windows but all of which can make progress, you can in principal avoid any issues with window management. What am I missing that brings HOL blocking into play?
Sorry Cory if my explanation was not clear. What I meant is that when you start to use reasonable (i.e. small) windows for your streams, you immediately see the network performance become horrible, despite all the buffering capacity around (kernel etc), only because of the streams window. I've had users complain about 5 Mbps uploads for example with 64kB windows :-/ As such it's extremely tempting to overprovision the stream window to make it cover some of the extra buffers available around that you're almost certain exists (i.e. the output buffer for where that frame is going to be forwarded), but when starting to play with this you risk HOL because it's often very hard to be certain about really available room (i.e. if you forward that over H2 and several streams consider that same room as available etc). Window-in-window is a very well-known anti-pattern and we knew it was going to hit one way or another and we have to live with it here. For most use cases where a browser is the sole owner of a connection, it's trivial to advertise huge windows and deal with it this way (no real risk that the browser blocks itself). When coalescing multiple agents it becomes quite visible a problem. I hope I managed to make it clearer this time, otherwise do not hesitate to let me know :-)
Thanks Willy, this is definitely clarifying things. So let me try to verify my understanding in bullets. The default per-stream window size (64kB), and other small per-stream window sizes, has performance problems over fat links. This is true. It is not necessarily true if you have multiple streams in flight and your connection window is larger, but it is definitely true for single streams. To that end it is tempting to advertise larger stream windows, to improve throughput. However that window may not be available in the target logical stream, such that if the user actually consumes it your application would have to buffer it. Possible reasons for this include the TCP send buffer not having room, the target H2 connection window not having room, the target H2 stream window not having room, etc. So this is definitely a resource management issue. Presumably the reason there's an HOL issue here is that, in response to needing to buffer on the outbound side, you stop reading from the H2 connection. I can think of a few ways to manage this, but this isn't really the forum for that kind of engineering chat. :wink: Instead, I'll say that I think the current text covers this. Specifically, the text says "bigger windows improve performance, but you need to bear in mind the resource exhaustion concerns elsewhere in this document". Should we be saying anything else more specific?
Well, I only partially agree, in that it's not exactly a resource exhaustion in the pure sense of the term as there can be plenty of resources available (socket buffers everywhere), but more a matter of difficulty/complexity to reliably measure available resources at any instant (i.e. the TCP buffer you have on one side is not necessarily dedicated to a single stream, so what is doable during a POST upload can suddenly change before you even have the option to advertise a window size change). I agree that this could be discussed elsewhere in the doc in more details, but my point was to at least put a short warning like the one I proposed to raise awareness of implementors against the risk of pushing this setting too far because "it seems to work much better". For the record, in haproxy what we've done to mitigate this on the download side is to avoid mixing multiple client connections over a single outgoing H2 connection. This way a slow client cannot block a fast one and it remains possible to advertise slightly larger stream windows that more realistically take into account available room on the path. For uploads, users generally consider that an uploading browser doesn't perform other operations in parallel over the same connection so it's okay to increase the advertised stream as well. But all these mitigations resulted from experience, were not obvious and will not necessarily apply to other implementations, which is why I thought a small warning could be useful.
Great, so do you think the current text covers it?
Looks great like this, thank you :-)
Thanks for the constructive discussion. No doubt we missed something (that's a constant risk here), but this looks like this change is a useful one. We'll track further improvements with new issues as the opportunity arises.
H2 strongly suffers from head-of-line blocking when streams do not progress at the same spead, typically when they come from coalesced connections from different clients. Per-stream flow control is generally ineffective against this as practical window sizes matching a BDP often require impractical buffering between the intermediary and the client. As such we should mention in the recommendations that intermediaries that coalesce connections either have very large buffers or try to group streams from a same client connection together on a server connection. Suggested by NAME
I'm not sure that I'm following the problem statement very well, so I'm not sure what sort of recommendation would go with it. Is the problem that when aggregating requests from multiple clients you expose all of those clients - and all of the requests on the corresponding connections - to head-of-line blocking on the shared connection? That doesn't seem quite right to me - though the requests that share that connection might suffer, the way that a stalled request on one connection might affect other requests on a different connection is limited. If a request affects others, is it because it would consume shared resources like flow control window and connection-level flow control? Keeping per-stream flow control relatively small relative to the connection limits would be the way to avoid that exposure, would it not? That said, larger connection-level buffers might reduce contention, but in general more buffering just means more latency. NAME am I chasing the right idea? Maybe you can provide a paragraph on the problem and I can try to massage it into shape for the draft.
The issue is that the intermediary that coalesces many client connections into a single H2 connection cannot have infinite resources to buffer store the response data for these clients, yet will have to advertise a significantly large stream window to the other end to maintain an acceptable bandwidth. In practice it most often goes well because your advertised window is lower than what can be stored into system buffers. But once a client stalls, all the chain stalls, and you quickly end up in a situation where the advertised stream window is ahead of what you're really able to store. Note that looking at how TCP buffers are filled in front is not always effective. You may very well be using H2 on the front with other streams making it hard to figure how much you could really push, you could have SSL under it having its own buffers, you could be applying compression in the middle, also making it hard to figure how much the output gives you on input, etc. In haproxy we've finally adressed this by deciding that by default multiple clients are not coalesced over a server-facing H2 connection. We have multiple strategies of connection reuse so this remains configurable, but this was the best compromise we could find.
Probably that we could propose something like this: Also, please note that the same situation happens the other way around, with multiple uploads over a single H2 connection to multiple servers behind working at different speeds. It's generally less of an issue though, but the large windows necessary to permit fast POST uploads may very well block the retrieval of small objects by the same client within the same connection.
Is this just an over-commitment of flow control then? That is, you advertise a window of N when you only really have space for M < N? What is interesting is the implication here that you might be forced to advertise the larger window because if you don't you can't get decent throughput, but to do so you are betting that most of that capacity will be used by in-network state (data that is on the wire or in routers and whatnot, along with all the in-system buffers you don't see). Of course, if that stalls, all that stuff in those buffers will all be forced into your buffers. That can affect all connections that are somehow entangled with the affected stream. That means all requests on the same connection, but it could affect other connections. All that said, that's much more involved than your text here. You appear to be looking for something much simpler. We should have advice about the connection-level flow control credit being backed by real resources. You say stream, but the purpose of connection-level flow control is to limit resource allocation. While the stream-level values more directly determine stream-level throughput, the general idea is that they can be set with minimal regard to resource management if there is a good connection-level limit (the main consequence of not jointly managing these is a range of complicated priority issues). The current document is not very good about saying much about any of this. So any addition along these lines, including a version of that text, would be worth adding.
When flow-control is set to values that the endpoint will respect, it minimizes retransmits due to buffer overruns. Minimizing retransmits does not always guarantee highest bandwidth; The more conservative one is with respect to preventing retransmits or buffer overruns, the more probable it is that some resources will be unused. This is similar to the tradeoff space of congestion control, however the buffers here are visible to the endpoints.
The thing is that due to the window-in-window you don't even benefit from the outer buffering (including the wire) if you have too small stream windows. With a single stream (or even H1) this problem does not exist at all as TCP acks at the edge. Here it's different, the data need to be delivered to userland and wait for its turn before the window can open again. I've seen POST requests slow down to 1 Mbps where the equivalent in H1 were 20 times higher using the same buffer size and TCP stack. So users bump the advertised window to compensate for this horrible performance and face random trouble. And on the server side the issue is the same but less visible, it mostly appears when the coalescing gateway and the servers are located on different datacenters a few milliseconds apart. But even at 4 ms with 64k windows it's only 128 Mbps per stream, so you can be sure that users are quite tempted to increase this for certain use cases where they see their devices twiddling thumbs on their 10/40/100G NICs. This is where it is possible to use the whole pipe's length when it's reserved or all streams of a given connection. What I'm seeking is not to fix the protocol, it is how it is and we all knew about this limitation during its design, but rather to warn users against the risks and effects of not accurately calculating the advertised stream window size in case of combined streams.
By the way, one easy trap to fall into is to advertise a window corresponding to the buffer used at the connection level, while what matters is how much you can extract from this connection to be able to immediately parse subsequent frames. And often you have no precise ideas of how much space there is outside.
Let's try to pull something in, maybe copy from QUIC: URL


I haven't adjusted for vs. until that war is over. Note that I've explicitly added a format for the SETTINGS frame itself, which was missing.
+1 . This would make it easier to write extensions for both h2 and h3. Priorities draft suffers from the mixed presentation format right now.
OK, thanks for doing this NAME I merged this manually after fixing a few things up. From my commit: Use all caps for frame names Add a value for Type in frame definitions Change order of flag descriptions to match order in layout Make Exclusive field of PRIORITY optional Move PING ACK Flag to the right place Add SETTINGS ACK Flag Double-checking FTW. Of course, there's a good chance I also messed something up here. So feel free to check my work also.
I think the language about ordering is a bit weird, but it is clarified somewhat by the hex representations shortly after. I think those hex representations are doing the heavy lifting, as I’m not aware of any issue with interoperability caused by getting the flag indices wrong in practice. Nonetheless, if we’re really worried about it we could just delete “bit n” and replace it with “the x bit”, e.g. delete “bit 0” and replace it with “the bit”.


The validation for uppercase characters is no longer listed separately, but instead included with the minimal validation.
NAME : factored out non controversial changes from


See as an alternative.
Thanks everyone for your patience here. It's good to see this work out :)
Validating fields for characters has raised a concern where we don't have a clear delineation between framing and semantics. So the way that endpoints handle those cases is unclear. For requests in particular, the choice between resetting a stream and sending a 4xx response is unclear. It would be good if we could have more consistent overall behaviour here, but we have seen from the character composition issue (see ) that different implementations will enforce the rules at different layers. We probably need to allow for some flexibility. NAME has volunteered to help us navigate this somewhat nuanced issue.
I think this is reasonable. LGTM


new registry for fields; see
NAME removed upgrade and changed the registration of this header field to: Is that OK? Or would you prefer a complete registration update, as in: Field Name: HTTP2-Settings Status: Standard Ref.: Section 11.5 of RFC 7540 Comments: This field is obsoleted (this document).
That should be fine.


This is a rearrangement of the HTTP-related sections, in preparation for actually addressing 867.
This doesn't actually address the issue -- I came to a point where I realised this would add a bunch of new requirements and prose that would need heavy scrutiny, and if we want to finish soon, that may not be advisable (although maybe we can figure it out during WGLC). In the meantime, I think this rearrangement is much clearer than the original ordering -- see what you think.
The section talks about how to generate the various pseudo-header fields from a message, but not not how to create a message from the abstract constructs in HTTP semantics, such as the target URI. This includes security considerations around trusting the contents of . I'm happy to do a PR for this. Also, this is a weird paragraph: "HTTP/2 shares the same default port numbers" implies that it could choose to do otherwise; while that might have been an open question pre-core, I think this needs to be re-worded. Likewise, "requests for target resource URIs" is odd; is this meant to be "requests with target URIs"?
Yeah, most of this is just OLD. For instance, the "discovery" aspect really only relates to the need to use ALPN, but we have a better nomenclature now. If you are happy to do a PR, I'm more than happy to review rather than write.


RFC 4492 is obsolete. This is a TLS 1.2 section, so it is OK to refer to the TLS 1.2 document here.


URL is frame formats. I've chosen to put blank lines before and after both frames and the stream identifier part of the stream header. That should make this a lot clearer. NAME was right that this was a little hard to process without that. Hopefully this is a tiny bit better.
I think this helps. The only other clarification I can think of is to pull the flags out into a separate illustration, but let's see if this is sufficient first.
Bah I didn't respond at the right place. One day I'll understand github's interface. Or not :-) Both changes look good and possibly sufficient to me. Let's just wait a bit to see if anyone in the WG has any better proposal. Thanks for your quick reaction!
For the flags (and only the flags), what about trying this:
Should I add the octet values of flags? I would only do that for non-unused flags though.
I believe the prose text contains them, so there's at least a defensible reason for leaving them off.
They're indeed present but not trivially spottable. I have a dead-tree copy of 7540 here with them overlined in pink to spot them faster :-/ We could save a tree by noting them there :-). I agree with Martin that we can avoid the unused ones though, because what matters in tests is not to know the unused ones but rather to figure them as the complement of all known ones. I won't roll over the floor crying if that's rejected, but as an implementer I find that it significantly helps during debugging.
As I don't know how to comment in this format and I don't want to start inventing something (else), I think I'm going to take the coward's option.
What are the semantics of a blank line in this format? /me ducks


The Priorities section itself has been replaced with a discussion of why priorities aren't there, which is all well and good (). So I found this still being in the introduction a bit odd: If we're talking about the structure of the protocol, I'd think either we don't talk about a feature that's no longer in the protocol, or we explicitly mention it's been cut out.


This fixes issue .
I agree on 1 that there's no reason to exclude response pseudo-headers from this requirement. (I was worried about contradicting something regarding multiple interim responses, each of which needs a field, but each of those are in a separate field section, so a rule of no duplicates within a field section should work for pseudo-headers, both request and response.) However, I think that a general prohibition is a good thing. If a future (negotiated) extension wants to introduce a new pseudo-header that can be duplicated, it could override an otherwise general prohibition in this draft. If a future extension doesn't do that, it gets the more sane/safe default for free.
Hmm, yeah, I think I can see a justification for that argument. Given that adding new pseudo-headers needs to be negotiated anyway, I don't think that's a big deal. So I'm happy to go ahead with the change for (1) and skip (2).
NAME is "this" the current text, or revised text to address Cory's comment?
This pull request. I think that a blanket "at most once" is fine and what you have is enough. Future extensions can deal with the consequences of that if they want >1.
NAME Do you want the change to cover responses as well?
That would mean moving it up to the "HTTP Control Data" section. That is a good idea. I missed that this was under requests only.
I moved the language up to the HTTP Control Data section and slightly tweaked it.


The abstract says This is taken directly from RFC 7540, and seems a bit old hat given where we are at now.
Yeah, probably not needed any more. Propose text?


By removing the word negotiating and avoiding the actual identifiers, we can get the important point across
Maybe a hangover from trying to tear up the photos of the "http" URI and upgrade dance. But this seems to read oddly. On one hand: on the other: I don't have a solid suggestion to improve things.
How about we just remove "h2c" from the latter statement.
OK, that isn't going to fly. I just tried it. Pulling that string is unwise. We agreed to keep "prior knowledge" in the document, and the "h2c" ALPN token is part of that. The "h2c" upgrade token has been removed already. (Too subtle? Probably.)


The old text stated padding things were identical to DATA frames. That might not be entirely true. So just spell out the requirements expicitly.
I just realised RFC 7540 was always this way, but it doesn't seem right to me. The DATA frame definition includes normative requirements on sender and receiver using the field. I presume the HEADERS frame is supposed the treat padding the same but it isn't written down. edit: plus PUSH_PROMISE
Ah, it's stated in a separate paragraph at the end of each frame definition section. That's annoying but I guess its fine.
Yeah, so the short answer there is that they behave differently in some ways (at least with respect to flow control). If you think there’s a better way to structure the section I think you’re welcome to take a crack at it.
I didn't actually make the leap to flow control, that makes me more inclined to avoid the cross reference to DATA frames. I made to just make everything explicit at the cost of a few more lines of text.


thanks both, good suggestions
... and HTTP/2's positives too! protocol. However, the way HTTP/1.1 uses the underlying transport ([RFC7230], Section 6) has several characteristics that have a negative overall effect on application performance today. a time on a given TCP connection. HTTP/1.1 added request pipelining, but this only partially addressed request concurrency and still suffers from head-of-line blocking. Therefore, HTTP/1.0 and HTTP/1.1 clients that need to make many requests use multiple connections to a server in order to achieve concurrency and thereby reduce latency. I think time has shown these claims to not be great. How about cutting cruft and saying and then a tweak to a later paragraph in the intro to state that no one is perfect
It looks like you have a pull request that is dressed up as an issue :)
If you're receptive to change, I am willing to try and edit horrible XML
One change and I'm happy


At two points in the document, it refers to "previous versions of this document," which I take to mean RFC 7540, since we don't discuss differences from previous drafts when we publish the RFC. Is there a reason not to simply say "RFC 7540" in these places, similar to the discussion in 5.3?
Probably not.


Which changes to
The new reference is to the frame type (not the settings themselves), so I might have preferred the original, but I don't mind. The section is about more than the frame anyway.


We could try to make every statement precise. We could add "of the frames defined in this document" to a bunch of places. Or we could make a (better) blanket statement. That is what this does.
As discussed in , the current language of 5.1 prohibits all frames on closed streams other than the ones indicated. It is not clear whether the requirement in Section 5.5 constitutes "more specific guidance." My suggestion in the erratum was to scope "any frame" to "any frame defined in this document" where we might allow an extension frame to appear, since that was the minimal change to the RFC 7540 text, but the editors might choose a broader rewording.
So the text already says: "Frames of unknown types are ignored." right next to the guidance you refer to. Maybe we just need to be more direct about this.
Be careful Martin, you removed the sentence "Frames of unknown types are ignored" so I think we're losing one piece of information when looking at the patch. I should re-read it withiin its context to make sure we don't miss anything, but I preferred to warn.
NAME yes, that was a deliberate change. I replaced that text with a complete paragraph and a reference to the extensibility section, which already covers the rules for unknown frames clearly: "Implementations MUST discard frames that have unknown or unsupported types."
I think this is fine.


Going to let NAME have a look at this as well.
(Warning, I may have reached peak pedantry.) In Section 5.1, there are two references to frames which "contain" an ENDSTREAM flag. All HEADERS, CONTINUATION, and DATA frames contain this flag, which might be set or not. Most other references in the document are to the flag being set, which is more precise. There are also two references to frames "bearing" ENDSTREAM, which could be taken either way.
If you care enough to write the issue, maybe you also care enough to offer a pull request.


by copying the text from DATA
says: I think the intended reading is that when the parser reaches the point that only the FBF and the Padding remain, the length of Padding is given by Pad Length and the fragment is the balance. If Pad Length exceeds the remaining length of the frame, it's an error. There is a less-likely reading also allowed by the current phrasing, that Pad Length can't be more than half the remaining bytes (else Padding would exceed the size of the field block fragment). That's very unlikely to be the intended reading, but we should be clear here. I'd suggest simply taking the note from the end of DATA and putting it here verbatim.
LGTM, will let NAME take a read as well.


... or the analysis in the security considerations. We had to walk a more careful path when RFC 7540 was published, so we were careful to legitimize h2c. In the years since, things have changed and we can be more direct. This isn't a "don't use h2c", but more of a disclaimer as far as security goes.
Maybe a hangover from trying to tear up the photos of the "http" URI and upgrade dance. But this seems to read oddly. On one hand: on the other: I don't have a solid suggestion to improve things.
How about we just remove "h2c" from the latter statement.
OK, that isn't going to fly. I just tried it. Pulling that string is unwise. We agreed to keep "prior knowledge" in the document, and the "h2c" ALPN token is part of that. The "h2c" upgrade token has been removed already. (Too subtle? Probably.)


As stated in , it's misleading and contradictory. Better to concentrate on configuration for this, as that is the common usage.
Section 3.3 says the following about cleartext HTTP/2: I had previously conceived of Alt-Svc as requiring a certificate and TLS, but that's not strictly true. RFC 7838 says that a client MUST have reasonable assurances, and a TLS cert is one way to do that. It also says that the connection must have equal or better security properties, which implies you can't go from TLS to non-TLS. This statement suggests that it's valid to offer on an http:// URI if your server can accept direct-to-H2 connections, but doesn't explicitly say so. Is that the intended reading here? But RFC 7838 says: Perhaps that doesn't apply, because the alternative is the same server on the same TCP port. Still, I feel like this waves at Alt-Svc for a use which is, at best, under-specified; at worst, not actually possible.