Question:
As discussed on Slack, currently the text makes no mention of the case where the PUSHPROMISE arrives behind (some of) the data on the push stream itself. However, it does mention this situation for the DUPLICATEPUSH frame. This proposed change recommends buffering pushed data for an unknown PUSHID and using flow control to prevent this from being a problem. I am unsure if more text is needed (e.g., what happens if the PUSHID is never resolved (which would mean a misbehaving server)), but I think the proposed change should suffice. This is also related to issue . Thanks to NAME for his help.
LGTM. Non-normative language seems to be the best form of guidance here
the push id that links a push stream to a push promise is now an unframed varint that comes right after the stream preface. this is right now the only unframed data (except made for the unidirectional stream prefaces) in the specs and I am proposing to make that a frame as well for consistency and to simplify implementation and allow code reuse. motivation: the specs already define frames that only carry a single varint (mostly push related) max push id duplicate push cancel push and goaway! here instead we decided to leave it unframed just because we can since it is at the beginning of the stream cost: two extra bytes per push stream the text will need to specify that on a push stream this new frame (PUSH_ID ?) MUST be the first one, otherwise it is a protocol error. I am happy to put up a PR, in case we reach consensus
The varint type and length of a frame is also unframed. So you are asking to trade one unframed varint for two. Is your assertion that this is unique and difficult code to write, or that the unframed content is somehow difficult to manage?
This seems to be going back to some of the early discussion we had on a thread I can't find. I'm not against discussing things again because we have more experience. However, I don't see a compelling reason for the change. My implementation needs to parse unframed data because of the stream header and for QPACK streams. I think this change might make my implementation more complex. Reading multiple varints requires more work: more checks to see if the stream has enough data, more checks to ensure only 1 frame sent at a specific time (this is the same as SETTINGS)
NAME those are in the frame header -- so they are part of the frame. I am not sure what your point is here then. Here I am assuming that an implementation has generic code to parse frames, and that parsing anything that is not part of a frame requires special handling. While this is implementation specific and there can definitely be counter examples, from an abstraction and consistency point of view I see no reason for doing it the way we are doing it now. (If we discount the unidirectional stream preface) I believe there should be two type of streams: framed (control, request, and push streams) and unframed (QPACK, and potentially extension streams). The push stream right now is a hybrid, that only becomes a framed stream after receiving that first PUSH_ID. If I am recalling correctly when we discussed potentially having DATA frames with zero-length to transition to unframed data, it was also considered a bit unusual. Here I am arguing that we are sort of doing the reverse here where we have an unframed stream that then transitions to framed. I think this generates complexity. And really the benefit we get is saving two bytes and one frame type. NAME I do vaguely remember that discussion and not 'wasting' frame types may have been one of the arguments when we made that decision, but that is not a problem now that we have a 62bit space for frame types.
I don't care about the cost, I care about things like having to deal with PUSH_ID (or whatever) frames in places other than the start of a push stream. And I don't believe that it is difficult to take the special-case code for pulling a varint off a partially-available stream and use that for push ID. We've not had too much trouble with that so far in our implementation.
it is definitely not difficult, but it is a special case and as any special case forces an increased complexity in the possible codepaths, even if not that big in this case. I am making my argument for the sake of consistency mostly, and for the fact that I believe it would also help to better define error handling. With the current text, if I receive something bogus on a push stream (after the preface) I'll have to keep the stream around (because the push stream may come out of order) waiting for a PUSH_PROMISE with that ID that may never come. So I believe that framing could help error handling as well. I do not have super strong feelings about this proposal but at the same time I'd like us to try and recall what the motivations are for the current text and for making this special since it was a while back and personally I can't recall what they were.
This sounds like the case NAME is looking at. I'd argue this is a good reason for the current design, frame processing should only take place once the PUSH_PROMISE is received. But I'd like to understand how framing could help avoid errors.
I was not trying to solve the same problem as here, but I was mentioning the fact that I believe framing would slightly help in a subset of cases. Take a server with a buggy push implementation, rather than a malicious one. Something that doesn't send the PUSHID at all. On the client you'll start seeing a flow of data and you can't do anything better than parsing the first few bytes of that as a PUSHID, and basically see random IDs. With Framing, if a server forgets to send the PUSHID, it is clear at the receiver that it is a protocol error. That being said there are many other classes of bugs that may trigger the same behavior even with frames, so this is minor. NAME now, that seems like an implementation dependent claim :) as says (and I like it) a client should buffer any data after receiving the PUSHID and before it has a PUSH PROMISE for it. and that is sufficient. no mention of frames there.
Right now in draft 19 we have: Parse varint - stream type Parse varint - push ID Parse varint - frame type (HEADERS). The assertion then is, if step 2 gets skipped by the sender, the receiver will parse the HEADERS frame type as a varint expecting it to be the push ID (value 0x1, with no restriction on min or max encoding). It will then parse the HEADERS frame length as a varint expecting it to be the HEADERS frame type (length is unlikely to be 1). Doesn't the client just blow up at that point?
It was just an example. And as I said I don't feel strongly about the error handling nit. But if you must, yes if you assume headers is the first frame after the pushid then yes you would read 0x1 which is conveniently a server initiated bidirectional stream which is not allowed anyway, so you could reject it immediately. But I find this just fortuitous. For example, one could make a counter argument: why are we framing SETTINGS at all given that it is the first message on the control stream ? We also defined it quite clearly in this table what to do with it [Certain frames can only occur as the first frame of a particular stream type; these are indicated in Table 1 with a (1).] URL Do you think there is zero value in doing that for the PUSHID as well? I personally think there is some value. Anyway, I think we are diverting this the wrong route. All I am looking for here is a good explanation for why it is defined like it is now, and I am failing to find it. As far as the implementation goes it makes little difference, and I guess it is more of a consistency problem for me. That naked varint on the wire triggers my OCD :) So instead of going back and forth like this, perhaps NAME can shed some light on the good reasons why it is this way.
+1 to levelling up, I'll take acknowledge my guilt here. But before I move on: If your at the stage of parsing QUIC STREAM frame payload, you should already know exactly what the stream ID is. Some historic points I remember: In Paris MT presented some ideas for unidirecitonal streams - URL This relates to issue URL and PR URL Push and control streams were identified then. And we were happy with reserving specific stream IDs for certain things. All non-reserved streams were server push streams. Subsequent data on the stream only makes logical sense to handle if you know what push it relates to, so those bytes go first. Roll forward some, QPACK design requires additional streams so those get reserved to. Around Kista, the topic of unidirectional stream types comes up. Without types, we make uncoordinated unidirecitonal stream extensions quite a difficult thing. So add more bytes at the start of stream and remove reservations. The SETTINGS frame can contain a set of parameters, indeed it SHOULD contain at least one GREASE setting. A single SETTINGS frame that describes the length of all parameters is quite useful, you know when the peer has finished sending all of them. I don't see as much value for PUSH_PROMISE because you're just framing a single integer that describes its own length already.
oh gosh yeah, I confused PUSH_ID with a stream id. scratch that completely
One can imagine a scenario where we'd want to place something before the SETTINGS frame on the control stream. On the other hand, it's less likely we'd ever want anything other than the Push ID to begin the push stream.
Discussed in London; no.
Seems like a good editorial improvement.

for responses in request-response.  Due to reordering, data on a push stream can arrive before the corresponding PUSH_PROMISE, in which case both the associated client request and the pushed request <del> headers are unknown.  Clients which receive a new push stream with an </del> <ins> headers are unknown.  Clients that receive a new push stream with an </ins> as-yet-unknown Push ID can buffer the stream data in expectation of the matching PUSH_PROMISE.  A client can use stream flow control (see section 4.1 of QUIC-TRANSPORT) to limit the amount of data a server


Question:
Saying it "terminates a stream" dangerously makes it sound like it has the same meaning as HTTP/2, which is false.
Thanks for all these little patches Martin, they are a real help.

19.4. An endpoint uses a RESET_STREAM frame (type=0x04) to abruptly <del> terminate a stream. </del> <ins> terminate the sending part of a stream. </ins> After sending a RESET_STREAM, an endpoint ceases transmission and retransmission of STREAM frames on the identified stream.  A receiver


Question: The transport draft states the following (Section 4.4): This verbiage, introduced in PR , is ambiguous. Byte offset may refer to the Offset field in the STREAM frame; in this case, the definition above is incorrect. What we want to say is that the final size is one higher than the offset of the byte with the largest offset sent on the stream. If no bytes were sent, the final size is zero.

The final size is the amount of flow control credit that is consumed by a stream.  Assuming that every contiguous byte on the stream was sent once, the final size is the number of bytes sent.  More <del> generally, this is one higher than the largest byte offset sent on the stream. </del> <ins> generally, this is one higher than the offset of the byte with the largest offset sent on the stream, or zero if no bytes were sent. </ins> For a stream that is reset, the final size is carried explicitly in a RESET_STREAM frame.  Otherwise, the final size is the offset plus the


Question:
No point in levying requirements on them.
Section 8.2 says: However, this directly contradicts TLS 1.3 URL, which says: In order to say this, this document would need to Update 8446, and even then it doesn't make sense because the presumptive point of this rule is to detect attempts to negotiate QUIC with non-QUIC TLS endpoints, which don't need to conform to this RFC (and may predate it).
I don't think QUIC can tell anyone what do with regard to any protocol that is not QUIC. And since QUIC v1 carries its own TLS record wrapping it makes even less sense.
I agree with the problem statement, however I wonder if this opens a session ticket obtained using TLS13-over-TCP to be used in QUIC. Is that what we want?
NAME why would this text have an impact on that? Couldn't I negotiate TLS 1.3 over TCP w/o extension and then send the resume over QUIC w/ extension?
The point here is to insist on the alert if the extension is understood. This wasn't intended to supersede 8446.
NAME My bad. I was confused with the earlier versions of the QUIC-TLS draft that had the transport_parameters extension in the session tickets.
NAME I think you could phrase that, but is it useful, given that we can't rely on it?
Probably not. I'm OK with culling the text.
This might not be possible if a cloud load balancer only forwards QUIC with TLS and terminates TLS otherwise.

Endpoints MUST NOT send this extension in a TLS connection that does not use QUIC (such as the use of TLS with TCP defined in TLS13).  A <del> fatal unsupported_extension alert MUST be sent if this extension is received when the transport is not QUIC. </del> <ins> fatal unsupported_extension alert MUST be sent by an implementation that supports this extension if the extension is received when the transport is not QUIC. </ins> 8.3.


Question:
Makes two minor fixes in the GOAWAY text, both editorial: Correctly references the MAXSTREAMS frame instead of the MAXSTREAM_ID frame Notes that servers need not (and cannot) send a GOAWAY if the client has used up all the possible stream IDs; the client simply won't be making any more requests.
This is a whatever...

maximum value for a Push ID that the server can use in a PUSH_PROMISE frame.  Consequently, this also limits the number of push streams that the server can initiate in addition to the limit set by the QUIC <del> MAX_STREAM_ID frame. </del> <ins> MAX_STREAMS frame. </ins> The MAX_PUSH_ID frame is always sent on the control stream.  Receipt of a MAX_PUSH_ID frame on any other stream MUST be treated as a


Question:
Makes two minor fixes in the GOAWAY text, both editorial: Correctly references the MAXSTREAMS frame instead of the MAXSTREAM_ID frame Notes that servers need not (and cannot) send a GOAWAY if the client has used up all the possible stream IDs; the client simply won't be making any more requests.
This is a whatever...

of the connection.  An endpoint that completes a graceful shutdown SHOULD use the HTTP_NO_ERROR code when closing the connection. <ins> If a client has consumed all available bidirectional stream IDs with requests, the server need not send a GOAWAY frame, since the client is unable to make further requests. </ins> 6.3. An HTTP/3 implementation can immediately close the QUIC connection at


Question:
Technically design, but barely.
Originally posted by NAME in URL There are three reasonable places to blow up: When you decide to stop reading a critical stream (i.e. when STOPSENDING is sent) When you discover the peer has stopped reading a critical stream (i.e. when STOPSENDING is received) When you discover the peer has closed a critical stream (when RESETSTREAM or FIN is received) My inclination would be to advise against FIN/SS/R_S on a critical stream and mandate connection termination on receipt of any of them.
Completely agree. This seems like the simplest and most obvious solution.
SGTM mike
Note that this PR does NOT address , just a comment that came up there.
Re-reading this, I think the existing text is pretty good; it just needs a little reframing. The existing requirement is to error out "If the control stream is closed at any point," without reference to whether it was closed because the peer send a FIN/RESET on their control stream or you obeyed a STOP_SENDING on yours. I think I'll add a requirement not to request closure of a control stream; do you think we need more elucidation here?
nits, but LGTM

permitted; receipt of a second stream which claims to be a control stream MUST be treated as a connection error of type HTTP_WRONG_STREAM_COUNT.  The sender MUST NOT close the control <del> stream.  If the control stream is closed at any point, this MUST be treated as a connection error of type HTTP_CLOSED_CRITICAL_STREAM. </del> <ins> stream, and the receiver MUST NOT request that the sender close the control stream.  If either control stream is closed at any point, this MUST be treated as a connection error of type HTTP_CLOSED_CRITICAL_STREAM. </ins> A pair of unidirectional streams is used rather than a single bidirectional stream.  This allows either peer to send data as soon


Question:
Skirts the editorial/design line. Previously, DUPLICATEPUSH referred to a Push ID in a previously-sent PUSHPROMISE frame; that frame would have caused a connection error on arrival. This says that the error should be sent regardless of which frame arrives first. May need reconciling with .
Two questions: Should DUPLICATEPUSH respect max push ID limit? PUSHPROMISE says the following: provided in a MAXPUSHID frame (Section 4.2.8) and MUST NOT use the same Push ID in multiple PUSHPROMISE frames What should a client do if it sees a DUPLICATEPUSH for an ID of a PUSHPROMISE that it never received? This could be caused by a race condition, so it seems sensible to allow this to clients to support this without error. We should probably document that like we did for other racy things. E.g. a Push ID that has not yet been mentioned by a PUSHPROMISE frame.
(2) is covered in : For (1), yes it should -- the DUPLICATE might show up first, but the implication is that the ID has already been used in a PUSHPROMISE frame. If that would be invalid, we should be explicit that the DUPLICATEPUSH is also invalid.
SGTM
I think that we have a frame name problem, otherwise, this is fine.I think some wires got crossed in the suggested changes. Your comment points out that this might need reconciliation with but it seems to borrow the text directly from that PR. The outcome is that this PR is inconsistent with itself because at line the same error with PUSHPROMISE causes a MALFORMEDFRAME. I think the best path is to make this change be a MALFORMED_FRAME error and reconcile on if the group want that change.

The DUPLICATE_PUSH frame carries a single variable-length integer that identifies the Push ID of a resource that the server has <del> previously promised (see frame-push-promise). </del> <ins> previously promised (see frame-push-promise), though that promise might not be received before this frame.  A server MUST NOT use a Push ID that is larger than the client has provided in a MAX_PUSH_ID frame (frame-max-push-id).  A client MUST treat receipt of a DUPLICATE_PUSH that contains a larger Push ID than the client has advertised as a connection error of type HTTP_MALFORMED_FRAME. </ins> This frame allows the server to use the same server push in response to multiple concurrent requests.  Referencing the same server push


Question:
LGTM
S 5.2. I don't believe this is possible on UNIX. What system call do I use to generate RST? Neither close() nor shutdown() does it.
You set SO_LINGER to {1, 0}, then close.
I don't think there's a question here about the -http draft, is there?
The question is if it's possible on standard OSes. Sounds like it is on Unix. How about Windows? NAME
This is the same requirement as in HTTP/2. A "MUST (but we know you probably won't)" ?
Well, that was a joke in 6919, so probably we shouldn't do that.
This is not that. We know that some implementations use TCP half-closed in HTTP. Some even rely on it to the point that things break if you don't do it. Not that things don't break for those people, but they have a narrow set of peers that they interact with. Of course, whether that set of endpoints is ALSO the same set of endpoints using CONNECT is hard to know.
Note that this particular issue is not about half-closed. In half-closed, you generate a FIN and then keep receiving data. This is about sending an RST.
Perhaps the text should be "SHOULD", and if the platform doesn't allow it, to "close the connection".
There's risk with that too, though. We encountered issues where the TCP connection was closed cleanly instead of aborted, and the client interpreted a truncated response as the (cacheable) response. It gets ugly. If you can't force a RST on the other side, you perhaps shouldn't be implementing CONNECT.
Discussed in Tokyo; MUST close the connection, SHOULD close with a RST.

A TCP connection error is signaled with QUIC RESET_STREAM frame.  A proxy treats any error in the TCP connection, which includes receiving a TCP segment with the RST bit set, as a stream error of <del> type HTTP_CONNECT_ERROR (http-error-codes).  Correspondingly, a proxy MUST send a TCP segment with the RST bit set if it detects an error with the stream or the QUIC connection. </del> <ins> type HTTP_CONNECT_ERROR (http-error-codes).  Correspondingly, if a proxy detects an error with the stream or the QUIC connection, it MUST close the TCP connection.  If the underlying TCP implementation permits it, the proxy SHOULD send a TCP segment with the RST bit set. </ins> 4.3.


Question: Use 'on a control stream' instead of 'on either control stream'. This is consistent with the changes made by .

bytes associated with an HTTP request or response payload. DATA frames MUST be associated with an HTTP request or response.  If <del> a DATA frame is received on either control stream, the recipient MUST </del> <ins> a DATA frame is received on a control stream, the recipient MUST </ins> respond with a connection error (errors) of type HTTP_WRONG_STREAM. 7.2.2.


Question:
in what I believe is the simplest way possible -- point to 8164 and require that be successfully retrieved prior to sending any scheme other than .
It would be good if it could be clarified what will happen if someone needs/tries to send a HTTP request with an http:// uri over H3. I primarily see this to occur when you have a case where you have an HTTP proxy that receives an HTTP request and then have an HTTP/3 connection with the authorative server. If the expectation is that all requests are upgraded by the HTTP/3 sending side, then that needs to be made explicit.
I might propose this comes down to a discovery problem for the proxy. What method(s) do you envisage the proxy used to decide to make an HTTP/3 connection to the authorative server? This seems related to the discussion in HTTP WG - URL If Alt-Svc is being used, the flow might be that the proxy attempts to make a cleartext HTTP/1.1 connection to the authoritative server and does an upgrade, as described in RFC 7838, to HTTP/3. In which case I don't think any action is required. If the proxy wants to opportunistically upgrade via some happy eyeballs approach described on URL, we might want to provide guidance that either 1) it shouldn't be done for client requests with http:// or 2) that it should always be done.
This is another variant of -- HTTP/2 and HTTP/3 explicitly include the scheme and the authority with all requests, so the server knows what it's being asked for. The question is how the client decides that a QUIC endpoint is authoritative for this particular URL. RFC 7838 describes how an https://-schemed request might come to be sent to an HTTP/3 endpoint and the resolution of the issue was to ask the HTTP WG to define other ways. Adding RFC 8164 as well, an http://-schemed resource might also be requested over HTTP/3; it's up to the HTTPbis group whether they want to enable a direct upgrade there as well. I don't currently see any text required for this, but if you have suggestions, I'm happy to review or expand the discussion to the list.
I'd rather duplicate this to and rely on for this. We were not very careful in that document to avoid mention of the specific HTTP version, but the technique is perfectly portable between versions. We only really intended to put HTTP/1.x off limits.
Perhaps "not very careful" was careful enough -- it says "such as HTTP/2," and mandates that the protocol needs a way to carry the scheme. HTTP/3 does, so we're covered. Do we think a reference to 8164 is warranted in the section about finding an HTTP/3 endpoint?
By "not very careful" I was mainly referring to the title :) As for the text, I don't know. It will depend on what else it says.
Discussed in London; reference 8164 and require the same check when sending a scheme other than "https."

HTTP/3 endpoint is authoritative for other origins without an explicit signal. <ins> Prior to making requests for an origin whose scheme is not "https," the client MUST ensure the server is willing to serve that scheme. If the client intends to make requests for an origin whose scheme is "http", this means that it MUST obtain a valid "http-opportunistic" response for the origin as described in RFC8164 prior to making any such requests.  Other schemes might define other mechanisms. </ins> A server that does not wish clients to reuse connections for a particular origin can indicate that it is not authoritative for a request by sending a 421 (Misdirected Request) status code in


Question:
I went with "Default" because that and "Initial Value" now have specific, related (but distinct) usages in the context of pre-SETTINGS settings. Swapping them seems like it would create unnecessary confusion.
Yeah that makes sense on a reread of section 7.2.5.2
Not a new issue but: RFC 7540 has a column for . I think it makes sense that HTTP/3 does similar. Originally posted by NAME in URL

An optional reference to a specification that describes the use of the setting. <ins> The value of the setting unless otherwise indicated.  SHOULD be the most restrictive possible value. </ins> The entries in the following table are registered by this document. Additionally, each code of the format "0x1f * N + 0x21" for integer


Question:
PR had a mistake in it, and some very confusing language. The rules for generating stateless resets was all mixed up in some complementary rules about generating packets that might trigger stateless resets. This attempts to split out the rules for generating stateless resets (minimum size is 5 + 16 = 21), and for generating packets that might trigger a stateless reset (min_cid + 22).
Do we need discussion on the fixed QUIC header bit in the triggering packet and outgoing reset packet?
NAME if you can lay out what you think the security concerns are in another issue, that would helpful. I realize that there are plenty of compromises in this design, which make this less than ideal. But first I'd like to try to understand where you think this is falling short.
Do we need discussion on the fixed QUIC header bit in the triggering packet and outgoing reset packet? Originally posted by NAME in URL
Maybe I'm misunderstanding the question, but isn't the spec is completely clear on that? A stateless reset has the first bit unset and the second bit set.
Agreed, I think this is already clear and I don't see any need to change it.
Thanks both. I just wanted to check and didn't feel like I had the brain cells to answer that. I don't like relying on the image only, but the text says "two fixed bits", and stateless reset - unlike Version Negotiation - is version-specific. So I'm closing with no action.
I believe this PR addresses the stated issue, which is that the text is unclear. However, I am still concerned about the security properties of this design.

To entities other than its intended recipient, a stateless reset will appear to be a packet with a short header.  For the stateless reset <del> to appear as a valid QUIC packet and be smaller than the received packet, the Unpredictable Bits field needs to include at least 46 bits of data (or 6 bytes, less the two fixed bits).  To ensure the stateless reset packet is not smaller than other packets received on the connection, an endpoint SHOULD also ensure the total packet length is at least the minimum chosen CID length plus 22 bytes.  22 bytes allows for 1 type byte, 4 packet number and data bytes, 16 bytes for AEAD expansion, and an extra byte to allow the peer to send a smaller stateless reset than the packet it receives. The Stateless Reset Token corresponds to the minimum expansion of the packet protection AEAD.  More unpredictable bytes might be necessary if the endpoint could have negotiated a packet protection scheme with a larger minimum AEAD expansion. An endpoint SHOULD NOT send a stateless reset that is significantly larger than the packet it receives.  Endpoints MUST discard packets that are too small to be valid QUIC packets.  With the set of AEAD functions defined in QUIC-TLS, packets that are smaller than 21 bytes are never valid. </del> <ins> to appear as a valid QUIC packet, the Unpredictable Bits field needs to include at least 38 bits of data (or 5 bytes, less the two fixed bits). A minimum size of 21 bytes does not guarantee that a stateless reset is difficult to distinguish from other packets if the recipient requires the use of a connection ID.  To prevent a resulting stateless reset from being trivially distinguishable from a valid packet, all packets sent by an endpoint SHOULD be padded to at least 22 bytes longer than the minimum connection ID that the endpoint might use.  An endpoint that sends a stateless reset in response to packet that is 43 bytes or less in length SHOULD send a stateless reset that is one byte shorter than the packet it responds to. These values assume that the Stateless Reset Token is the same as the minimum expansion of the packet protection AEAD.  Additional unpredictable bytes are necessary if the endpoint could have negotiated a packet protection scheme with a larger minimum expansion. An endpoint MUST NOT send a stateless reset that is three times or more larger than the packet it receives to avoid being used for amplification.  reset-looping describes additional limits on stateless reset size. Endpoints MUST discard packets that are too small to be valid QUIC packets.  With the set of AEAD functions defined in QUIC-TLS, packets that are smaller than 21 bytes are never valid. </ins> Endpoints MUST send stateless reset packets formatted as a packet with a short header.  However, endpoints MUST treat any packet ending


Question:
PR had a mistake in it, and some very confusing language. The rules for generating stateless resets was all mixed up in some complementary rules about generating packets that might trigger stateless resets. This attempts to split out the rules for generating stateless resets (minimum size is 5 + 16 = 21), and for generating packets that might trigger a stateless reset (min_cid + 22).
Do we need discussion on the fixed QUIC header bit in the triggering packet and outgoing reset packet?
NAME if you can lay out what you think the security concerns are in another issue, that would helpful. I realize that there are plenty of compromises in this design, which make this less than ideal. But first I'd like to try to understand where you think this is falling short.
Do we need discussion on the fixed QUIC header bit in the triggering packet and outgoing reset packet? Originally posted by NAME in URL
Maybe I'm misunderstanding the question, but isn't the spec is completely clear on that? A stateless reset has the first bit unset and the second bit set.
Agreed, I think this is already clear and I don't see any need to change it.
Thanks both. I just wanted to check and didn't feel like I had the brain cells to answer that. I don't like relying on the image only, but the text says "two fixed bits", and stateless reset - unlike Version Negotiation - is version-specific. So I'm closing with no action.
I believe this PR addresses the stated issue, which is that the text is unclear. However, I am still concerned about the security properties of this design.

endpoint that occasionally uses different connection IDs might introduce some uncertainty about this. <del> Finally, the last 16 bytes of the packet are set to the value of the Stateless Reset Token. </del> This stateless reset design is specific to QUIC version 1.  An endpoint that supports multiple versions of QUIC needs to generate a stateless reset that will be accepted by peers that support any


Question:
This is implied in some spots, but never stated with normative language.
Under "Handshakes and New Paths", it says "Data at Initial encryption MUST be retransmitted before Handshake data and data at Handshake encryption MUST be retransmitted before any ApplicationData". I moved that down to this section, since I think it fits slightly better here.
In terms of packet size, this is similar to what the old Handshake text recommended and what PTO should have said all along, as it aligns well with TCP's TLP and RTO behavior.
The previous text read like it only described retransmission of lost data, and this now reads like it only describes retransmission in PTOs. Are both intended? Is there a restriction anywhere that retransmitted data must not be sent at a higher encryption level than it was originally sent, or similar?
All of this text is in the PTO section, so only applies to PTO. I think transport is usually the best place to talk about where data is retransmitted, but it might be worth an editorial note that data is never retransmitted at a different packet number space than the original one.
Thanks for opening other issues, NAME I'm going to merge this, since I think it's a net betterment over what we have. We can continue the other discussions on the other issues/PRs.
At the New York QUIC interim, we dealt with the problem of the server being blocked by the amplification factor by making the client arm the PTO until it knew the server had validated the path, even if it had nothing to send. From the recovery : When reviewing URL, I realized that if we allowed the server to send a PING-only packet when the PTO fired and the amplification factor had been reached, then that would elicit a padded Initial ACK frame in response, which also fixes the issue. NAME though that was a simpler solution than the current approach and this is a bit of an edge case, so simple is probably good. The original issue was
In a strict sense, this means that a client can use N bytes to trigger the server to send 3N + epsilon bytes, but this fits in nicely with Probes generally not being subject to sending limits. It is somewhat gross but more elegant that a client timer with nothing in flight.
I think this might be concerning, though I could well be wrong. Consider the case of an attacker acting as a client, using the IP address of a victim as the source address. The attacker can spoof an Initial packet that carries a Client Hello and another Initial packet that carries an ACK for the server's Initial packet that carried the Server Hello. An attacker can build the correct second packet assuming that the server uses the DCID of the client's Initial packet as it's CID (or the CID is zero length), and that the server's first packet number can be guessed. If that ACK contains a delay that indicate to the server that the RTT is very small (e.g., 1ms), then the PTO could be as small as that value. Then, if the server's connection timeout is say 1000ms, the server might send 1000 packets to the victim before dropping the connection. Am I missing something?
PTO always uses exponential backoff, so if the first PTO was 1ms, that'd be ~10 PTOs, not 1000. In terms of total bytes sent, 10 packets with a 1 byte payload are still smaller than a single full-sized packet.
Isn't this concern why we designed the Retry mechanism in the first place? If we are concerned about address spoofing attacks, the simple solution might be to generalize retry. As in, "if the server can predict that its first flight will contain more than 3 times the amount of data received with the client's Initial packets, then it SHOULD verify the source address using Retry."
My concern is that the current anti-deadlock mechanism is fairly awkward and rarely needed, which makes me concerned it will be incorrectly implemented. By sending a PING only packet, the server is essentially informing the client it has something to send, but can't send it due to the amplification limit. The deadlock happens when the client's Initial is acknowledged, but the client doesn't receive the server's Initial, so it can't decrypt any handshake packets. In this state, the client has nothing to send. It can happen even if the server's first flight fits into 3 packets. But it's expected that typically servers will bundle their ACK of the client's Initial with the server Initial, and this will be rare in practice.
NAME Fair. Due to the risk of amplification attacks, the server is probably not willing to retransmit Initial packets on timer. In that case, the current spec is a bit too lose. It should say that "If the sender is not willing to retransmit Initial packets due to protection against amplification attacks, it MUST NOT acknowledge the client's initial packet." So basically, either the server verifies the client's address with the Retry/Token mechanism, or it MUST NOT acknowledge client initial packets before continuity can be verified by obtaining an ACK from the client.
That's an interesting suggestion, but I think it require some non-trivial changes to the server's logic in addition to not sending ACKs? Something similar to what's described in . It may be worth opening up an issue for allowing the server to not arm a timer until the path has been validated, because I think that's a wider issue than just this PR?
I agree that having the server repeat a PING on timer may also work, and that the ACK of the randomly chosen Server's CID would make a reasonable address ownership test. ACK of the PING would naturally unlock any transmission that is pending address ownership. Kazuho objects that this his hackable if the chosen Server's CID is guessable. We don't have a "non-guessable" requirement in the spec. In that case, the natural fix is the challenge/response mechanism.
NAME Ah. Thank you for pointing that out. Though it seems that the amount of the data that the server can send could go like 4x the amount of data that the client has sent rather than the current 3x. To paraphrase, I think what we might be trading here is the initial amount of data the server can send vs. the capability of sending PINGs. My view is that the former is a scarce resource, and that I'd prefer having the more space in the former even if requires us to have some complexity.
NAME An Initial ping is maybe 45 bytes. So 10 pings makes the amplification ~3.4. I was under the impression that the 3 multiplier was semi-arbitrary, so I'm unconcerned about a fractional increase.
I have a couple of thoughts. First, this change is not necessarily an overall simplification. It makes things slightly simpler at the client, but it adds about as much code to the server. Additional code has to be added at the server to the PTO response to only send a PING and not a retransmission when the path is not yet validated. Second, I think it does open up an interesting attack vector that is currently absent. NAME notes that there is amplification, but I don't think byte amplification is the interesting one here, since the total load is going to be < 1 MTU. The interesting amplification here is packet amplification, which is a useful attack since UDP and packet processing costs at endpoints / clients is a relatively high cost. And you can get a fair number of packets back from the server for spoofing a couple of packets, in spite of the exponential backoff. I'll grant that the attack is a bit of a stretch, and does require that the attacker guess the server's CID and packet number. Though, as NAME pointed out to me offline, we allow the server to use a zero-length CID. I don't think this change is necessary. And on balance, I don't think this change is a good one.
I do like the idea of sending a PING frame from the server as a signal to the client that it is blocked. I think we can suggest that as an optimization that a clever server might implement under the 3x limit, or when it reaches the 3x limit.
NAME While endpoints can do that, I am not sure if it is a good idea to endorse such a behavior in the specification. The reason we have so far been fine with having the 3x limit being applied until the Handshake packets are exchanged is because we assume that the size of ServerHello would not be that large compared to the size of the ClientHello, and therefore that the chance of hitting 3x limit before path validation is minimal. It's a hack. If we are to address the corner cases that the server hits after sending 3x amount of data, then I think we should fix the hack than putting another hack (i.e. the PING that signals 3x limit) above the existing hack. I mean, if we think that defining a way that allows the server to unblock the 3x limit is important, we should be considering allowing the use of PATHCHALLENGE / PATHRESPONSE frames in Initial packets.
I believe this is simpler to implement assuming one is implementing both a client and server. I already have special case code to send a PING when there are bytes in flight but nothing to send, so this is very similar. I agree it's not necessary, but I find the simplification appealing, and when we first discussed this problem, no one suggested this solution, so I thought it was worth writing up. If we keep the existing text that the client has to keep sending, I'm not sure doing this on the server side is a valuable optimization, so I'd prefer to stick with one mechanism or the other. To me, the main negative is the server sending extra datagrams. And maybe that's enough to stick with what we have.
When a PTO fires, the endpoint is supposed to try and retransmit something if there is data to retransmit. In this case, you'd need to special-case it so that the server doesn't in fact retransmit but sends a PING instead. That's what I meant by adding more code.
It also SHOULD send a PING only packet if there are bytes in flight but nothing to send.
Not new data, any data. This was deliberate. See Section 5.3.1: "When the PTO timer expires, and there is new or previously sent unacknowledged data, it MUST be sent." The point of this text was to send data if available, new or previously sent. In the case that we are discussing the server is supposed to send a PING, but it might have data to send.
Discussed in ZRH. NAME to decide if he wants to pursue this, potentially discuss with NAME and/or NAME
I realized there is one major benefit of keeping the existing design, which is the client is guaranteed to have at least one RTT measurement in this circumstance, whereas the server may not. For this reason, I'm inclined to close this with no action unless others feel strongly otherwise.
NAME came up with the same point simultaneously, so let's close this with no action.
There are multiple mentions of spurious retransmission in the recovery draft, but no text on what detecting this may require from an implementation. Maybe there should be? "Spuriously declaring packets as lost leads to unnecessary retransmissions and may result in degraded performance due to the actions of the congestion controller upon detecting loss. Implementations that detect spurious retransmissions and increase the reordering threshold in packets or time MAY choose to start with smaller initial reordering thresholds to minimize recovery latency." From comments on
Some discussion of implementation strategies here would absolutely be helpful; it doesn't obviously fall out of Quinn's current architecture.
NAME and NAME indicated my effort at adding more text wasn't that useful, so I'm closing this.
As of URL, if the client's Initial is lost, there is no required behavior that leads to it being retransmitted. Because the client has received no ACKs, no is set, and no loss is detected, leading to the client only sending probe packets, which are not required to bear any particular data ("A probe packet MAY carry retransmitted unacknowledged data") so long as they are ACK-eliciting. The server, having no connection state, cannot do anything with these packets. Quinn probes are currently a single PING frame (plus padding, when appropriate) for simplicity, so implementing the new logic immediately broke our tests. If recovery logic now requires probe packets to bear specific data to function correctly in certain circumstances, this should be specified explicitly.
I also hit this issue too. After several hours tries and error, I came to the conclusion that implementation should mark the in-flight Initial packet lost in this case, and resend it. Draft-22 has explicit statement that implementation should not mark in-flight crypto packet lost, but draft-23 does not have it anymore. BTW, draft-23 does not allow PING in Initial or Handshake packet.
Why not retransmit the data without declaring the in-flight packet lost?
I think it is rather implementation detail to internally mark in-flight packet lost but keep it in order to catch late ACK.
There's a related problem where if the server's first flight goes missing, the only response the new logic has is to send a probe at unspecified, i.e. highest known, encryption level. The server will generally have 1-RTT keys at this point, so it sends a 1-RTT probe, which the client, not having received anything prior, cannot decrypt and drops. As above, this was previously handled by explicitly tracking whether any data was in-flight, but that got removed.
The packets should not be marked as lost, but data needs to be retransmitted before sending a PING. I sent out a PR , feel free to make further suggestions there.
I'm marking this design because it adds some normative language, though I'll note that it doesn't change what I originally intended the text to say.

delay SHOULD NOT be considered an RTT sample. Until the server has validated the client's address on the path, the <del> amount of data it can send is limited, as specified in Section 8.1 of QUIC-TRANSPORT.  Data at Initial encryption MUST be retransmitted before Handshake data and data at Handshake encryption MUST be retransmitted before any ApplicationData data.  If no data can be sent, then the PTO alarm MUST NOT be armed until data has been received from the client. </del> <ins> amount of data it can send is limited to three times the amount of data received, as specified in Section 8.1 of QUIC-TRANSPORT.  If no data can be sent, then the PTO alarm MUST NOT be armed. </ins> Since the server could be blocked until more packets are received from the client, it is the client's responsibility to send packets to


Question:
This is implied in some spots, but never stated with normative language.
Under "Handshakes and New Paths", it says "Data at Initial encryption MUST be retransmitted before Handshake data and data at Handshake encryption MUST be retransmitted before any ApplicationData". I moved that down to this section, since I think it fits slightly better here.
In terms of packet size, this is similar to what the old Handshake text recommended and what PTO should have said all along, as it aligns well with TCP's TLP and RTO behavior.
The previous text read like it only described retransmission of lost data, and this now reads like it only describes retransmission in PTOs. Are both intended? Is there a restriction anywhere that retransmitted data must not be sent at a higher encryption level than it was originally sent, or similar?
All of this text is in the PTO section, so only applies to PTO. I think transport is usually the best place to talk about where data is retransmitted, but it might be worth an editorial note that data is never retransmitted at a different packet number space than the original one.
Thanks for opening other issues, NAME I'm going to merge this, since I think it's a net betterment over what we have. We can continue the other discussions on the other issues/PRs.
At the New York QUIC interim, we dealt with the problem of the server being blocked by the amplification factor by making the client arm the PTO until it knew the server had validated the path, even if it had nothing to send. From the recovery : When reviewing URL, I realized that if we allowed the server to send a PING-only packet when the PTO fired and the amplification factor had been reached, then that would elicit a padded Initial ACK frame in response, which also fixes the issue. NAME though that was a simpler solution than the current approach and this is a bit of an edge case, so simple is probably good. The original issue was
In a strict sense, this means that a client can use N bytes to trigger the server to send 3N + epsilon bytes, but this fits in nicely with Probes generally not being subject to sending limits. It is somewhat gross but more elegant that a client timer with nothing in flight.
I think this might be concerning, though I could well be wrong. Consider the case of an attacker acting as a client, using the IP address of a victim as the source address. The attacker can spoof an Initial packet that carries a Client Hello and another Initial packet that carries an ACK for the server's Initial packet that carried the Server Hello. An attacker can build the correct second packet assuming that the server uses the DCID of the client's Initial packet as it's CID (or the CID is zero length), and that the server's first packet number can be guessed. If that ACK contains a delay that indicate to the server that the RTT is very small (e.g., 1ms), then the PTO could be as small as that value. Then, if the server's connection timeout is say 1000ms, the server might send 1000 packets to the victim before dropping the connection. Am I missing something?
PTO always uses exponential backoff, so if the first PTO was 1ms, that'd be ~10 PTOs, not 1000. In terms of total bytes sent, 10 packets with a 1 byte payload are still smaller than a single full-sized packet.
Isn't this concern why we designed the Retry mechanism in the first place? If we are concerned about address spoofing attacks, the simple solution might be to generalize retry. As in, "if the server can predict that its first flight will contain more than 3 times the amount of data received with the client's Initial packets, then it SHOULD verify the source address using Retry."
My concern is that the current anti-deadlock mechanism is fairly awkward and rarely needed, which makes me concerned it will be incorrectly implemented. By sending a PING only packet, the server is essentially informing the client it has something to send, but can't send it due to the amplification limit. The deadlock happens when the client's Initial is acknowledged, but the client doesn't receive the server's Initial, so it can't decrypt any handshake packets. In this state, the client has nothing to send. It can happen even if the server's first flight fits into 3 packets. But it's expected that typically servers will bundle their ACK of the client's Initial with the server Initial, and this will be rare in practice.
NAME Fair. Due to the risk of amplification attacks, the server is probably not willing to retransmit Initial packets on timer. In that case, the current spec is a bit too lose. It should say that "If the sender is not willing to retransmit Initial packets due to protection against amplification attacks, it MUST NOT acknowledge the client's initial packet." So basically, either the server verifies the client's address with the Retry/Token mechanism, or it MUST NOT acknowledge client initial packets before continuity can be verified by obtaining an ACK from the client.
That's an interesting suggestion, but I think it require some non-trivial changes to the server's logic in addition to not sending ACKs? Something similar to what's described in . It may be worth opening up an issue for allowing the server to not arm a timer until the path has been validated, because I think that's a wider issue than just this PR?
I agree that having the server repeat a PING on timer may also work, and that the ACK of the randomly chosen Server's CID would make a reasonable address ownership test. ACK of the PING would naturally unlock any transmission that is pending address ownership. Kazuho objects that this his hackable if the chosen Server's CID is guessable. We don't have a "non-guessable" requirement in the spec. In that case, the natural fix is the challenge/response mechanism.
NAME Ah. Thank you for pointing that out. Though it seems that the amount of the data that the server can send could go like 4x the amount of data that the client has sent rather than the current 3x. To paraphrase, I think what we might be trading here is the initial amount of data the server can send vs. the capability of sending PINGs. My view is that the former is a scarce resource, and that I'd prefer having the more space in the former even if requires us to have some complexity.
NAME An Initial ping is maybe 45 bytes. So 10 pings makes the amplification ~3.4. I was under the impression that the 3 multiplier was semi-arbitrary, so I'm unconcerned about a fractional increase.
I have a couple of thoughts. First, this change is not necessarily an overall simplification. It makes things slightly simpler at the client, but it adds about as much code to the server. Additional code has to be added at the server to the PTO response to only send a PING and not a retransmission when the path is not yet validated. Second, I think it does open up an interesting attack vector that is currently absent. NAME notes that there is amplification, but I don't think byte amplification is the interesting one here, since the total load is going to be < 1 MTU. The interesting amplification here is packet amplification, which is a useful attack since UDP and packet processing costs at endpoints / clients is a relatively high cost. And you can get a fair number of packets back from the server for spoofing a couple of packets, in spite of the exponential backoff. I'll grant that the attack is a bit of a stretch, and does require that the attacker guess the server's CID and packet number. Though, as NAME pointed out to me offline, we allow the server to use a zero-length CID. I don't think this change is necessary. And on balance, I don't think this change is a good one.
I do like the idea of sending a PING frame from the server as a signal to the client that it is blocked. I think we can suggest that as an optimization that a clever server might implement under the 3x limit, or when it reaches the 3x limit.
NAME While endpoints can do that, I am not sure if it is a good idea to endorse such a behavior in the specification. The reason we have so far been fine with having the 3x limit being applied until the Handshake packets are exchanged is because we assume that the size of ServerHello would not be that large compared to the size of the ClientHello, and therefore that the chance of hitting 3x limit before path validation is minimal. It's a hack. If we are to address the corner cases that the server hits after sending 3x amount of data, then I think we should fix the hack than putting another hack (i.e. the PING that signals 3x limit) above the existing hack. I mean, if we think that defining a way that allows the server to unblock the 3x limit is important, we should be considering allowing the use of PATHCHALLENGE / PATHRESPONSE frames in Initial packets.
I believe this is simpler to implement assuming one is implementing both a client and server. I already have special case code to send a PING when there are bytes in flight but nothing to send, so this is very similar. I agree it's not necessary, but I find the simplification appealing, and when we first discussed this problem, no one suggested this solution, so I thought it was worth writing up. If we keep the existing text that the client has to keep sending, I'm not sure doing this on the server side is a valuable optimization, so I'd prefer to stick with one mechanism or the other. To me, the main negative is the server sending extra datagrams. And maybe that's enough to stick with what we have.
When a PTO fires, the endpoint is supposed to try and retransmit something if there is data to retransmit. In this case, you'd need to special-case it so that the server doesn't in fact retransmit but sends a PING instead. That's what I meant by adding more code.
It also SHOULD send a PING only packet if there are bytes in flight but nothing to send.
Not new data, any data. This was deliberate. See Section 5.3.1: "When the PTO timer expires, and there is new or previously sent unacknowledged data, it MUST be sent." The point of this text was to send data if available, new or previously sent. In the case that we are discussing the server is supposed to send a PING, but it might have data to send.
Discussed in ZRH. NAME to decide if he wants to pursue this, potentially discuss with NAME and/or NAME
I realized there is one major benefit of keeping the existing design, which is the client is guaranteed to have at least one RTT measurement in this circumstance, whereas the server may not. For this reason, I'm inclined to close this with no action unless others feel strongly otherwise.
NAME came up with the same point simultaneously, so let's close this with no action.
There are multiple mentions of spurious retransmission in the recovery draft, but no text on what detecting this may require from an implementation. Maybe there should be? "Spuriously declaring packets as lost leads to unnecessary retransmissions and may result in degraded performance due to the actions of the congestion controller upon detecting loss. Implementations that detect spurious retransmissions and increase the reordering threshold in packets or time MAY choose to start with smaller initial reordering thresholds to minimize recovery latency." From comments on
Some discussion of implementation strategies here would absolutely be helpful; it doesn't obviously fall out of Quinn's current architecture.
NAME and NAME indicated my effort at adding more text wasn't that useful, so I'm closing this.
As of URL, if the client's Initial is lost, there is no required behavior that leads to it being retransmitted. Because the client has received no ACKs, no is set, and no loss is detected, leading to the client only sending probe packets, which are not required to bear any particular data ("A probe packet MAY carry retransmitted unacknowledged data") so long as they are ACK-eliciting. The server, having no connection state, cannot do anything with these packets. Quinn probes are currently a single PING frame (plus padding, when appropriate) for simplicity, so implementing the new logic immediately broke our tests. If recovery logic now requires probe packets to bear specific data to function correctly in certain circumstances, this should be specified explicitly.
I also hit this issue too. After several hours tries and error, I came to the conclusion that implementation should mark the in-flight Initial packet lost in this case, and resend it. Draft-22 has explicit statement that implementation should not mark in-flight crypto packet lost, but draft-23 does not have it anymore. BTW, draft-23 does not allow PING in Initial or Handshake packet.
Why not retransmit the data without declaring the in-flight packet lost?
I think it is rather implementation detail to internally mark in-flight packet lost but keep it in order to catch late ACK.
There's a related problem where if the server's first flight goes missing, the only response the new logic has is to send a probe at unspecified, i.e. highest known, encryption level. The server will generally have 1-RTT keys at this point, so it sends a 1-RTT probe, which the client, not having received anything prior, cannot decrypt and drops. As above, this was previously handled by explicitly tracking whether any data was in-flight, but that got removed.
The packets should not be marked as lost, but data needs to be retransmitted before sending a PING. I sent out a PR , feel free to make further suggestions there.
I'm marking this design because it adds some normative language, though I'll note that it doesn't change what I originally intended the text to say.

ack-eliciting packets, to avoid an expensive consecutive PTO expiration due to a single lost datagram. <del> It is possible that the sender has no new or previously-sent data to send.  As an example, consider the following sequence of events: new </del> <ins> When the PTO timer expires, and there is new or previously sent unacknowledged data, it MUST be sent.  Data that was previously sent with Initial encryption MUST be sent before Handshake data and data previously sent at Handshake encryption MUST be sent before any ApplicationData data. It is possible the sender has no new or previously-sent data to send. As an example, consider the following sequence of events: new </ins> application data is sent in a STREAM frame, deemed lost, then retransmitted in a new packet, and then the original transmission is <del> acknowledged.  In the absence of any new application data, a PTO timer expiration now would find the sender with no new or previously- sent data to send. When there is no data to send, the sender SHOULD send a PING or other ack-eliciting frame in a single packet, re-arming the PTO timer. </del> <ins> acknowledged.  When there is no data to send, the sender SHOULD send a PING or other ack-eliciting frame in a single packet, re-arming the PTO timer. </ins> Alternatively, instead of sending an ack-eliciting packet, the sender MAY mark any packets still in flight as lost.  Doing so avoids


Question:
Use the correct ABNF grammar to express repetition and fix the Reserved Frame Type link.
There is some baggage with this parameter - URL People were a bit grumpy with changing their parser 18 months ago, I think they might be even grumpier now that we have even more client implementations in the wild. What is the benefit of this change? I think we need an issue for due process and discussion.
Oh joy. This caused me to go look at our parser in Chrome, and realized we're still doing the old (pre 1097) quic=;quic= format. So Chrome has work to do no matter how we resolve this. That being said, I'd really rather not have two different ways to do this, so +1 to NAME 's comment on the motivation.
I can remove the additional unquoted token change and keep this PR purely as editorial fix. Before the PR, the current text does not have correct ABNF grammar. I added the unquoted token variant since it is permitted (but not required) by the grammar at URL Since HTTP/3 over QUIC is not the only user of this Alt-Svc header, I can imagine that generic parsers are written such that they already understand the unquoted token value. Thus, adding support for this should not be an additional burden? I have no strong opinion here. The additional CPU cost of handling both unquoted and quoted forms should be neglible. Disallowing the unquoted form is a restriction of the RFC 7838 grammar. Should I bring this to the list and/or create a new issue?
Let's create a new issue. You're correct that non-HTTP/3 implementations might need to handle this - hence the original issue which related back to structured headers (an attempt at defining some best practice in this regard -URL). Part of the problem is not CPU cost of DQUOTEs but expressing "a list of versions" and making sure that implementations know how to do that. Alt-Svc defines the parameter syntax but not how to handle repetition of parameters.
I removed the single quote change, this should be a purely editorial fix now. I'll create a new issue for the unquoted parameter value. The "list of versions" situation does not change, if you only support one version you would write: If you support multiple, you would still use one parameter, but you have to quote it since a comma is not valid in a .
According to URL, the Alt-Svc parameter value can be unquoted: This means that this can be written: instead of only: The current text in URL forbids the unquoted variant which is an additional constraint on the RFC 7838 grammar. Should unquoted parameter values also be permitted? Note that this does not change the situation when multiple versions are supported since an unquoted "token" type is not allowed to contain commas. See also the discussion in .
RFC 7838 allows unquoted strings, but there's nothing preventing new specifications of Alt-Svc parameters from being more restrictive. I'm not sure the added flexibility of allowing unquoted versions buys us much here, and it comes at an implementation cost.
If alt-svc can be specified in configuration files, it is likely to result in interop issues if unquoted is not permitted.
I support allowing unquoted token for alt-svc quic parameter. quic=1 and quic="1" are semantically the same, and there is no reason to disallow unquoted token. Moreover, requiring quoted-string means that we cannot use generic alt-svc parser because such parser can parse quic=1 without an error. You need to write your own parser to detect that the DQUOTE is really there. If you think that detecting it is not necessary, then requiring quoted-string is not necessary in the first place.
People and implementers will be surprised (and do wrong) if the rules and constraints of RFC7838 aren't kept.
To highlight my earlier point, on slack the following was posted The issue appears to not be the unquoted ma=3600, but it does suggest we will see unquoted strings in the wild, and URL is said to also use unquoted ma=3600.
But that's the ma= field, not the quic= field, right? (This issue is about the quic= field, I think)
As I said elsewhere, the current text in draft-23 stems from a Structured Headers issue related to wanting to advertise multiple versions of QUIC. URL Alt-Svc isn't a structured one but if it were (e.g. if someone wanted to write a generic Structured Header parser) then there was a danger that the rules preventing dictionary key repetition would have prevented an endpoint sending multiple parameters e.g. . To mitigate the risk, the quic parameter was changed to a DQUOTED comma-separated list as part of URL This issue addresses the correctness within the Alt-Svc spec but does it reopen the can related to Structured Headers?
I think that the problem here is independent from whether structured headers should be applied or not. - Structured header issue: duplicate keys. - Invalid grammar according to RFC 7838 since "token" does not include commas. - valid grammar according to both RFC 7838 and the QUIC description. However, if only a single version has to be advertised, then the quoting is not necessary: - valid grammar according to both RFC 7838 and the QUIC description. - valid grammar according to both RFC 7838, but QUIC currently forbids it. In I proposed to fix the grammar (editorial issue). However, at the same time I could also make the grammar match RFC 7838 (the last bullet point above). NAME I would mind not making the semantics more restrictive (e.g. restricting the values of the QUIC parameter to hexadecimal only), but restricting the parser grammar would impose extra restrictions on those who have to generate the header. It is conceivable that implementations will continue accepting the RFC 7838 grammar, and end up accepting while others would reject it and accept only. By making sure that the grammars align, I hope that this becomes a non-issue. So far NAME NAME and NAME seems to support this proposal and NAME is not sure. Can this be considered consensus? :smiley:

versions are four-byte sequences with no additional constraints on format.  Leading zeros SHOULD be omitted for brevity. <del> Syntax: </del> <ins> Syntax of the "quic" parameter value: </ins> Where multiple versions are listed, the order of the values reflects the server's preference (with the first value being the most


Question: The current text was unclear to me: how does one enforce a MUST on TLS? The updated text hopefully clarifies that it's legal for a server to send a NewSessionTicket without the "early_data" extension - that indicates that the server supports resumption but not 0-RTT, as it does in TLS.

4.5. <del> In order to be usable for 0-RTT, TLS MUST provide a NewSessionTicket message that contains the "early_data" extension with a max_early_data_size of 0xffffffff; the amount of data which the client can send in 0-RTT is controlled by the "initial_max_data" transport parameter supplied by the server.  A client MUST treat receipt of a NewSessionTicket that contains an "early_data" extension with any other value as a connection error of type PROTOCOL_VIOLATION. </del> <ins> To communicate their willingness to process 0-RTT data, servers send a NewSessionTicket message that contains the "early_data" extension with a max_early_data_size of 0xffffffff; the amount of data which the client can send in 0-RTT is controlled by the "initial_max_data" transport parameter supplied by the server.  Servers MUST NOT send the "early_data" extension with a max_early_data_size set to any value other than 0xffffffff.  A client MUST treat receipt of a NewSessionTicket that contains an "early_data" extension with any other value as a connection error of type PROTOCOL_VIOLATION. </ins> A client that wishes to send 0-RTT packets uses the "early_data" extension in the ClientHello message of a subsequent handshake (see


Question:
NAME The text in the PR adopts 1 PTO as a suggestion, as I am not sure if there is a point in that value being the exact limit. If it needs to be, we can rephrase the text to "MUST retire the corresponding connection IDs in one PTO and send corresponding RETIRECONNECTIONID frames."
I'm a bit worried that a stateless reset from a retired CID could be used as an attack vector by simply delaying packets en-route. However, if the reset is only effective on CIDs that are no longer recognised that shouldn't be a problem.
At the moment, states, quote: Upon receipt, the peer SHOULD retire the corresponding connection IDs and send the corresponding RETIRECONNECTIONID frames in a timely manner. Failing to do so can cause packets to be delayed, lost, or cause the original endpoint to send a stateless reset in response to a connection ID it can no longer route correctly. As the text uses "SHOULD" rather than a "MUST", some view it as an optional requirement to switch to using a new connection ID in such a situation. Others think that it is a mandatory requirement for an endpoint to switch to a new connection ID. If we are to consider the latter behavior to be the case, I think the text should better be changed to "MUST retire the corresponding connection IDs and send the corresponding RETIRECONNECTIONID frames in a timely manner".
My personal take is that it would be nice if the processing of NEWCONNECTIONID frame and RETIRECONNECTIONID frame can be an optional feature. QUIC is already very complex, and it would be beneficial to have some features being optional, in sense that endpoints can simply discard certain frames when not implementing certain features. IIUC, at the moment, these two frames are optional for an endpoint that do not support migration. OTOH, from the robustness viewpoint of the protocol, it does make sense to change the text to MUST. We might have already crossed the river by introducing the Retire Prior To field. The third option is to add a TP that indicates if an endpoint supports retirement of connection IDs...
There was a lot of discussion when the Retire Prior To feature was added. At the time, I felt it pretty clear that it could not be optional from our server's perspective. I agree the sentence with the SHOULD, if read in isolation, could be interpreted as making the feature as optional. But the sentence immediately following it makes it pretty clear that it's not option. I understand the desire to make things optional for simplicity's sake, but a primary point of this feature, when it was added, was that is was required. Making it optional now would, IMO, defeat the purpose of the feature.
My understanding is that this is optional in the sense GOAWAY is optional. At some point, the connection is likely to terminate abruptly if you don't follow the request.
That's one way to look at it. The difference here is that we're not trying to kill the connection with Retire Prior To.
There was definitely lots of discussion around the requirement level here when this was added (see ), NAME asked: There was good discussion following that ( and scroll down). My understanding of was that the endpoint being asked to retire a CID really SHOULD retire it or else the peer may stop responding to that CID. After the endpoint requesting retirement gives up on a CID and forgets about it, packets will either arrive with that CID or they will not. It has two choices, it can choose to process them if they arrive with the wrong CID, or it can drop them. The enforcement mechanism here is to stop processing the "bad" CIDs, not close the connection, especially as you take into account potentially delayed-but-valid packets. I think the current stance of "really SHOULD stop using such a CID, but the other endpoint needs to be careful about enforcing that" is the right one, if there's a wording update that would help with understanding there, then it would be great to update the wording as an editorial issue.
Whatever the answer here, the SHOULD was chosen because "timely manner" isn't defined in a testable fashion. If someone wants to argue for MUST, then we need a firmer definition of the reaction.
And to agree with NAME the squishiness here only exists up until the point that this is "do this or else". We should make it clearer that the cost of non-compliance is losing the connection. Not always, but a endpoint is entitled to cut and run if their peer doesn't comply.
NAME Thank you for the explanation and the links. They help a lot in understanding how we ended up here. NAME Yeah we already state that "an endpoint MAY discard a connection ID for which retirement has been requested once an interval of no less than 3 PTO has elapsed since an acknowledgement is received for the NEWCONNECTIONID frame requesting that retirement." So I think it's testable. Considering that we have a MAY on the side that initiates the retirement, I think we can have a MUST on the receiver side (assuming that we want to).
That almost works, except that PTO isn't a shared variable. It's subjective and so endpoints might disagree on where this line is. At 3PTO, that's probably not a disagreement worth having, but you are effectively recommending instant compliance if you set the bar that low, as packet loss on any packet sent that attempts to comply might cause it to take up to 3PTO to get through.
IIRC, the base of the SHOULD was that we didn't have a specific timeout anyone felt comfortable enforcing in the absence of shared timers between peers. We've had a general principle that requirements you can't tell whether the peer violated are probably SHOULDs instead of MUSTs. As Nick says, the SHOULD is intended to refer to doing so promptly, not to doing so at all.
While I agree that we have tried to make MUST requirements testable, I am not certain if it is a good idea to avoid use of MUST when a requirement cannot be tested (or if doing so is a good idea, per the definition of the keywords in RFC 2119). At least for this particular case, isn't it a requirement for an endpoint to drop the old connection IDs? Though I could well be wrong though in how the keywords are used in practice.
I'm fine saying MUST drop in a timely manner, and being explicit about what we mean by timely manner (3 PTO from the peer's perspective). Obviously, from the peer's perspective, that is enforceable. Locally though, it's obviously not as clear cut.
We need a PR. NAME do you think that you can do this? As discussed, should be that the receiving side will retire within one PTO. The sending side can enforce at 3PTO, by dropping the connection.
Discussed in Cupertino. NAME to quickly rev the PR to incorporate NAME suggestion.
NAME the delayed packets are handled by this text: If you've followed the requirement to retire as indicated, then a delayed packet triggering stateless reset can't hurt you.Looks fine to me. I'm also fine with Martin's suggestions as well.The endpoint here has always been going to do whatever it does, regardless of whether we said SHOULD or MUST, so this seems fine. I do like the direction for the other endpoint to wait 3PTO, that would be nice to include.

migrates away from a local address SHOULD retire all connection IDs used on that address once it no longer plans to use that address. <del> An endpoint can request that its peer retire connection IDs by sending a NEW_CONNECTION_ID frame with an increased Retire Prior To field.  Upon receipt, the peer SHOULD retire the corresponding connection IDs and send the corresponding RETIRE_CONNECTION_ID frames in a timely manner.  Failing to do so can cause packets to be delayed, lost, or cause the original endpoint to send a stateless reset in response to a connection ID it can no longer route </del> <ins> An endpoint can cause its peer to retire connection IDs by sending a NEW_CONNECTION_ID frame with an increased Retire Prior To field. Upon receipt, the peer MUST retire the corresponding connection IDs and send corresponding RETIRE_CONNECTION_ID frames.  Failing to retire the connection IDs within approximately one PTO can cause packets to be delayed, lost, or cause the original endpoint to send a stateless reset in response to a connection ID it can no longer route </ins> correctly. An endpoint MAY discard a connection ID for which retirement has been requested once an interval of no less than 3 PTO has elapsed since an acknowledgement is received for the NEW_CONNECTION_ID frame <del> requesting that retirement.  Subsequent incoming packets using that </del> <ins> requesting that retirement.  Until then, the endpoint SHOULD be prepared to receive packets that contain the connection ID that it has requested be retired.  Subsequent incoming packets using that </ins> connection ID could elicit a response with the corresponding stateless reset token.


Question:
At the moment, we state that a QUIC packet that contains frames other than ACK and PADDING (), but I wonder if that is true for CONNECTIONCLOSE frame. A CONNECTIONCLOSE frame never elicits an acknowledgment. IIUC, it is not meant to be governed by the congestion controller. Considering the aspects, I think we should add CONNECTION_CLOSE to the list of non-ACK-eliciting frames.
That also ensures the congestion controller never inadvertently blocks sending a CONNECTIONCLOSE, which is consistent with the intent. If we make this change, I believe APPLICATIONCLOSE needs to be on the list as well. Now that we discuss ack-eliciting in transport, I think we'd need to change both transport and recovery and mark this design. So I think this would be a slight improvement, but I an also imagine people claiming that CONNECITON_CLOSE is already special, so there's no need for a change.
APPLICATION_CLOSE is no more. This seems like a fine change.
This seems sensible.
Discussed in Cupertino. Obvious correction.
Seems right to me. Editorial tweaks suggested.

not ACK-only, and they are not acknowledged, declared lost, or abandoned along with old keys. <del> All frames besides ACK or PADDING are considered ack-eliciting. </del> <ins> All frames other than ACK, PADDING, and CONNECTION_CLOSE are considered ack-eliciting. </ins> Packets that contain ack-eliciting frames elicit an ACK from the receiver within the maximum ack delay and are called ack-eliciting


Question:
At the moment, we state that a QUIC packet that contains frames other than ACK and PADDING (), but I wonder if that is true for CONNECTIONCLOSE frame. A CONNECTIONCLOSE frame never elicits an acknowledgment. IIUC, it is not meant to be governed by the congestion controller. Considering the aspects, I think we should add CONNECTION_CLOSE to the list of non-ACK-eliciting frames.
That also ensures the congestion controller never inadvertently blocks sending a CONNECTIONCLOSE, which is consistent with the intent. If we make this change, I believe APPLICATIONCLOSE needs to be on the list as well. Now that we discuss ack-eliciting in transport, I think we'd need to change both transport and recovery and mark this design. So I think this would be a slight improvement, but I an also imagine people claiming that CONNECITON_CLOSE is already special, so there's no need for a change.
APPLICATION_CLOSE is no more. This seems like a fine change.
This seems sensible.
Discussed in Cupertino. Obvious correction.
Seems right to me. Editorial tweaks suggested.

performance of the QUIC handshake and use shorter timers for acknowledgement. <del> Packets that contain only ACK frames do not count toward congestion control limits and are not considered in-flight. </del> <ins> Packets containing frames besides ACK or CONNECTION_CLOSE frames count toward congestion control limits and are considered in- flight. </ins> PADDING frames cause packets to contribute toward bytes in flight without directly causing an acknowledgment to be sent.


Question:
At the moment, we state that a QUIC packet that contains frames other than ACK and PADDING (), but I wonder if that is true for CONNECTIONCLOSE frame. A CONNECTIONCLOSE frame never elicits an acknowledgment. IIUC, it is not meant to be governed by the congestion controller. Considering the aspects, I think we should add CONNECTION_CLOSE to the list of non-ACK-eliciting frames.
That also ensures the congestion controller never inadvertently blocks sending a CONNECTIONCLOSE, which is consistent with the intent. If we make this change, I believe APPLICATIONCLOSE needs to be on the list as well. Now that we discuss ack-eliciting in transport, I think we'd need to change both transport and recovery and mark this design. So I think this would be a slight improvement, but I an also imagine people claiming that CONNECITON_CLOSE is already special, so there's no need for a change.
APPLICATION_CLOSE is no more. This seems like a fine change.
This seems sensible.
Discussed in Cupertino. Obvious correction.
Seems right to me. Editorial tweaks suggested.

UDP datagram.  Multiple QUIC packets can be encapsulated in a single UDP datagram. <del> A QUIC packet that contains frames other than ACK and PADDING. These cause a recipient to send an acknowledgment (see sending- acknowledgements). </del> <ins> A QUIC packet that contains frames other than ACK, PADDING, and CONNECTION_CLOSE.  These cause a recipient to send an acknowledgment (see sending-acknowledgements). </ins> An entity that can participate in a QUIC connection by generating, receiving, and processing QUIC packets.  There are only two types


Question:
At the moment, we state that a QUIC packet that contains frames other than ACK and PADDING (), but I wonder if that is true for CONNECTIONCLOSE frame. A CONNECTIONCLOSE frame never elicits an acknowledgment. IIUC, it is not meant to be governed by the congestion controller. Considering the aspects, I think we should add CONNECTION_CLOSE to the list of non-ACK-eliciting frames.
That also ensures the congestion controller never inadvertently blocks sending a CONNECTIONCLOSE, which is consistent with the intent. If we make this change, I believe APPLICATIONCLOSE needs to be on the list as well. Now that we discuss ack-eliciting in transport, I think we'd need to change both transport and recovery and mark this design. So I think this would be a slight improvement, but I an also imagine people claiming that CONNECITON_CLOSE is already special, so there's no need for a change.
APPLICATION_CLOSE is no more. This seems like a fine change.
This seems sensible.
Discussed in Cupertino. Obvious correction.
Seems right to me. Editorial tweaks suggested.

Each endpoint advertises its own idle timeout to its peer.  An endpoint restarts any timer it maintains when a packet from its peer is received and processed successfully.  The timer is also restarted <del> when sending a packet containing frames other than ACK or PADDING (an ack-eliciting packet; see QUIC-RECOVERY), but only if no other ack- eliciting packets have been sent since last receiving a packet. Restarting when sending packets ensures that connections do not prematurely time out when initiating new activity. </del> <ins> when sending an ack-eliciting packet (see QUIC-RECOVERY), but only if no other ack-eliciting packets have been sent since last receiving a packet.  Restarting when sending packets ensures that connections do not prematurely time out when initiating new activity. </ins> The value for an idle timeout can be asymmetric.  The value advertised by an endpoint is only used to determine whether the


Question:
At the moment, we state that a QUIC packet that contains frames other than ACK and PADDING (), but I wonder if that is true for CONNECTIONCLOSE frame. A CONNECTIONCLOSE frame never elicits an acknowledgment. IIUC, it is not meant to be governed by the congestion controller. Considering the aspects, I think we should add CONNECTION_CLOSE to the list of non-ACK-eliciting frames.
That also ensures the congestion controller never inadvertently blocks sending a CONNECTIONCLOSE, which is consistent with the intent. If we make this change, I believe APPLICATIONCLOSE needs to be on the list as well. Now that we discuss ack-eliciting in transport, I think we'd need to change both transport and recovery and mark this design. So I think this would be a slight improvement, but I an also imagine people claiming that CONNECITON_CLOSE is already special, so there's no need for a change.
APPLICATION_CLOSE is no more. This seems like a fine change.
This seems sensible.
Discussed in Cupertino. Obvious correction.
Seems right to me. Editorial tweaks suggested.

13.2. Endpoints acknowledge all packets they receive and process.  However, <del> only ack-eliciting packets (see QUIC-RECOVERY) trigger the sending of an ACK frame.  Packets that are not ack-eliciting are only </del> <ins> only ack-eliciting packets cause an ACK frame to be sent within the maximum ack delay.  Packets that are not ack-eliciting are only </ins> acknowledged when an ACK frame is sent for other reasons. When sending a packet for any reason, an endpoint should attempt to


Question:
Some definitional work for terms that were poorly used. Note that "host" in the remaining uses is strictly interpreted in the RFC 1122 sense, so I left those alone. I considered sorting the terms section differently, and might still alphabetize that, but this isn't problematic just yet.
The word "host" is undefined. If it is equal to "endpoint", it should be replaced. Otherwise, "host" should" be defined in Sec 1.2. The word "address" sometime means "IP address" and "a pair of IP address and port" in other cases. Sec 1.2 should define "address" as "a pair of IP address and port". And "IP" should be added in front of "address" which means "IP address".
fixed this.
Thank you!

The endpoint accepting incoming QUIC connections. <ins> When used without qualification, the tuple of IP version, IP address, UDP protocol, and UDP port number that represents one end of a network path. </ins> An opaque identifier that is used to identify a QUIC connection at an endpoint.  Each endpoint sets a value for its peer to include in packets sent towards the endpoint.


Question:
Some definitional work for terms that were poorly used. Note that "host" in the remaining uses is strictly interpreted in the RFC 1122 sense, so I left those alone. I considered sorting the terms section differently, and might still alphabetize that, but this isn't problematic just yet.
The word "host" is undefined. If it is equal to "endpoint", it should be replaced. Otherwise, "host" should" be defined in Sec 1.2. The word "address" sometime means "IP address" and "a pair of IP address and port" in other cases. Sec 1.2 should define "address" as "a pair of IP address and port". And "IP" should be added in front of "address" which means "IP address".
fixed this.
Thank you!

associated with an existing connection, or - for servers - potentially create a new connection. <del> Hosts try to associate a packet with an existing connection.  If the packet has a non-zero-length Destination Connection ID corresponding to an existing connection, QUIC processes that packet accordingly. Note that more than one connection ID can be associated with a connection; see connection-id. </del> <ins> Endpoints try to associate a packet with an existing connection.  If the packet has a non-zero-length Destination Connection ID corresponding to an existing connection, QUIC processes that packet accordingly.  Note that more than one connection ID can be associated with a connection; see connection-id. </ins> If the Destination Connection ID is zero length and the packet <del> matches the local address and port of a connection where the host </del> <ins> matches the local address and port of a connection where the endpoint </ins> used zero-length connection IDs, QUIC processes the packet as part of that connection.