I believe we can merge this now as it is. If we want to add more guidance later we should open a separate issue and another PR.
This issue came up during the discussion of issue . We say that ACK or ACK_MP frames can be send over any path, however, maybe we should discuss some guidance and trade-offs. E.g. sending on the same path as the packets received also for RTT measurements; using the shortest delay path might have some performance benefits...?
I agree. We should add some guidance here. We did some experiment regarding this issue. Our finding is returning ACK on the shortest path is helpful. But the improvement is only more significant when the path rtt ratio is larger than 4:1.


Fix issue
In section 3, we have However, the error code does not exist (anymore), and I'm not sure we need such a specific error code anyway. Why not raising a in such case?
It's reasonable to use TRANSPORTPARAMETERERROR. Submitted PR for this issue.


as this is already stated normatively in RFC900.
Currently the draft say: However, we should not use normative language in this draft but just point to RFC9000 instead as things are defined normatively there already. Btw. this reference should point to section 13.3 ("Upon detecting losses, a sender MUST take appropriate congestion control action.") and 9.4 ("Packets sent on the old path MUST NOT contribute to congestion control or RTT estimation for the new path.") of RFC9000 specifically and maybe also to RFC9002 directly.
LGTMLGTM


Not sure to understand why the last part of the sentence is an issue. The use of zero-length connection IDs is problematic anyway in setups with NATs, for example.
I agree that in many deployments you want at least one CID from the client to server. Multiple packet numbers as currently defined however requires CIDs in both direction; there is separate issue to discuss that. The next sentence also further explains that there might be other deployments though, e.g. in constraint networks, that might also benefit from saving the bits of the CID. This one sentence is of course only a very brief summary of the pros and cons of the different approach and more discussion is needed in the group. So I don't expect to have this part in the draft like this when we finally publish it as RFC. Is there anything in the issue that we need to address now? Or can we close this issue?
Thanks, Mirja. That's what I expected, but I do still think more elaboration is needed for this text to be useful. I would suggest to make this modification and leave the discussion of pros/cons to be adequately documented in Section 7: OLD: This proposal supports the negotiation of either the use of one packet number space for all paths or the use of separate packet number spaces per path. While separate packet number spaces allow for more efficient ACK encoding, especially when paths have highly different latencies, this approach requires the use of a connection ID. Therefore use of a single number space can be beneficial in highly constrained networks that do not benefit from exposing the connection ID in the header. While both approaches are supported by the specification in this version of the document, the intention for the final publication of a multipath extension for QUIC is to choose one option in order to avoid incompatibility. More evaluation and implementation experience is needed to select one approach before final publication. Some discussion about pros and cons can be found here: URL URL NEW: This proposal supports the negotiation of either the use of one packet number space for all paths or the use of separate packet number spaces per path. While both approaches are supported by the specification in this version of the document, the intention for the final publication of a multipath extension for QUIC is to choose one option in order to avoid incompatibility. More evaluation and implementation experience is needed to select one approach before final publication. Some discussion about pros and cons can be found here: URL URL
You are suggesting to strike out the sentences that try to summarize the differences, "While separate packet number spaces allow for more efficient ACK encoding, especially when paths have highly different latencies, this approach requires the use of a connectionID. Therefore use of a single number space can be beneficial in highly constrained networks that do not benefit from exposing the connection ID in the header." I think that's a good idea. The sentences are a short summary of the arguments presented in the powerpoint, which are not limited to "highly constrained networks". For example, the Chrome browser, by default, uses NULL CID. I may be placing words in the mouth of Google developers, but I believe their goal is to reduce the overhead of the QUIC header compared to TCP/TLS. This is related to a general quest for performance, not to network constraints.


NAME you wrote: That sounds like you want to restrict the multipath extension to only one server address (but potentially different paths from different clients address)...? I think this restriction is unnecessary. Yes we don't specify a way to learn server addresses but that doesn't means that the client doesn't know (e.g. based on higher layer information) multiple addresses of the server. What should we restrict that?
However, can we maybe merge this PR as it is for now and you open a separate issue for your additional point NAME ?
Mirja, That was my understanding of the sentences in the PR. Maybe I got it wrong. I think that we should discuss the role of preferred address in section 3 where we explain how paths can be opened.
Fine, let's do it this way.
RFC9000 defines a list of transport parameters. 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f 0x10 [QUICWG] These transport parameters apply to all the paths that compose a Multipath QUIC connection. We should probably confirm this in the mpquic draft to avoid any misinterpretation (e.g. for maxidletimeout or ack delays). There are two transport parameters that could warrant some discussion: disableactivemigration : This transport parameter is incompatible with enablemultipath. An endpoint that receives both this transport parameter and a non zero enablemultipath parameter MUST treat this as a connection error of type MPCONNECTIONERROR and close the connection. preferredaddress: RFC9000, section 9.2.1 contains the following: Once the handshake is confirmed, the client SHOULD select one of the two addresses provided by the server and initiate path validation (see Section 8.2). A client constructs packets using any previously unused active connection ID, taken from either the preferredaddress transport parameter or a NEWCONNECTION_ID frame. As soon as path validation succeeds, the client SHOULD begin sending all future packets to the new server address using the new connection ID and discontinue use of the old server address. If path validation fails, the client MUST continue sending all future packets to the server's original IP address. For multipath, do we want to be stronger than SHOULD select one ? Can a client create news paths towards the initial address or only to one of the preferred addresses ? Should the client use both addresses (IPv4 and IPv6) if non-empty ?
Good point about "disableactivemigration". I would read it as also disabling multipath. We do not need to say that "transport parameters apply to all the paths that compose a Multipath QUIC connection". We are already inheriting that property from compatibility with path migration in RFC 9000. Multipath support is an extension to QUIC v1, not a new protocol, so we do not need to specify all the defaults. Same goes for the preferred address -- meaning does not change just because the nodes negotiate multipath. I would avoid debating "which server addresses the client can try" in the base multipath document. If we want the server to signal plausible addresses to the client, let's have a specific document for that.
I would maybe add a sentence that this draft does not change the meaning of preferred_address and if that address could also be used for multipath is out of scope for this extension.
Regarding disableactivemigration: this cannot be a connection error, as you can also send preferred address and after migration to the preferred address you can still use migration or multipath (if negotiated). I think the definition in RFC9000 is still valid; it means you can't send any packets from another address than the one used in the handshake. Which effectively means disabling multipath as well as Christian noted (if not moved to another preferred address). I guess we could explicitly note that this draft does not change the definition of disableactivemigration. For your reference this is what RFC9000 says:
I created a PR in . Is this sufficient to address this issue or do we need to say more? NAME ?
The proposed PR sounds good to me, with the slight addition I mentioned.


This seems fine to me, but doesn't seem to include URL
NAME after trying to work that text in it became clear (to me) that a better outcome was modify the existing text we have about anonymity set reduction. So I went with that approach rather than open the can of consistency worms that NAME alluded to.
Let's say HPKE public key A was valid from 2022-07-01 to 2022-07-02. The client encapsulates a request X using pubkey A, but buffers it because of network issues, in order to retry later. Now it rotates and public key B is valid as of 2022-07-03. X encapsulated using pubkey A would not be decryptable by pubkey B. So if you receive X on 2022-07-03, then you know that X was generated between 2022-07-01 and 2022-07-02.
I'm not inclined to add this. "Don't do that" seems pretty obvious to me, especially when we recommend that requests include a Date.
While this advice seems fine, I agree that it's not really necessary. It also brings up more questions to me — what stops any layer below the OHTTP layer or an intermediary from arbitrarily buffering messages? Maybe just have a note that the key config that's used as well as explicit date markings can leak the time that the request was generated?
This seems like a reasonable way to deal with the problem. Liveness information can leak in a number of places, including through noting which public key was used. It might also leak via the date. Maybe this can become part of the differential treatment section?
That sounds good to me
I think that while this text is true, I believe that it is also unnecessary. Discussion of key liveness are dealt with in the key-consistency draft, so this is really about requests that are transmitted well after they are constructed. If we start to document that, there are lots of other things that start to come up.
I agree, but the content I'm proposing here relates to how liveness affects differential treatment. We've already noted a few key ways in which this can happen, so it seems pretty harmless to me to include one more. I don't think we need to say anything more about how to deal with liveness or how it pertains to consistency. I'll propose some text to make this more clear. No more than one sentence.


Section 5 details HTTP usage. It seems it's technically correct but the section closes with a sequence of paragraphs that jump back and forth between statements about how an Oblivious Gateway Resource treats requests or responses. Perhaps these could be consolidated to present requests first consistently. I.e. swap the final two paragraphs around


We debated this at some length. This defines an optional-to-use signaling method for the gateway so that it can inform the client when the key configuration is not acceptable. Clients can use this as a prompt to refresh their configuration.
(Originally posted by NAME in URL): Any lifetime information that comes with a key is helpful, but in practice is insufficient for clients to drive key updates. Key rotation may not occur on a fixed schedule and it may not occur synchronously (some gateway instances might not get the key at the right time. This means that clients cannot distinguish between failures due to configuration mismatch or request tampering (or whatever else might cause the gateway to reply without an encapsulated response). If the client can't distinguish then the client's recovery implementation is a lot more complicated. Does it always try to forcibly update its configuration? Does it implement some sort of backoff? Can a relay abuse the client's recovery implementation to somehow put the client into a state where it is stuck with an old configuration? It seems vastly simpler to just be clear that the configuration is wrong as a signal to the client that it's time to update. This was done in ODoH with a 401 response code. proposed a new problem statement, which I think is the right way to deal with this. Yes, there are potential privacy problems based on automated updates, but those depend on the client's consistency story and is therefore not something new a signal would introduce.
This looks great -- thanks!


From NAME Can we (additionally or in replacement) point to the "HPKE KEM Identifiers" IANA registry created from this table instead? Same for this and "HPKE AEAD Identifiers" registry.


Why is it that media type registration is a journey of discovery each and every time? It's like the coastline paradox: the closer you get, the more detail resolves. Also, each requirement makes sense in isolation, but if you need an entire RFC to explain it, maybe something has gone wrong.
no strong feelings on this one.


Corrected to mention response freshness, with a new citation Added note about timing of a retry revealing RTT Corrected figure title grammar
Nice addition on RTT timing


Following up from URL It seems like the key configuration lists a set of potential HPKE algorithms that the client can use. The section URL does say "a selected combination of KDF, identified by kdfid, and AEAD, identified by aeadid", but it's easy to miss the selected word and even then its not apparent as to what the selection is from. I think it could be made clearer that the client can choose from a list of HPKE algorithms specified in the Key Configuration.


and . Given (either assuming a fixed key ID, or looking up the KEM based on key ID), I think this is a nice simplification. It also authenticates more, even if redundant. (The kemID is already part of the HPKE key schedule.) I implemented this change and it's straightforward.
Currently, an encapsulated request carries a key ID which maps to a KEM key that determines the size of the encapsulated public key in a request. This means that code parsing a request needs to know the ID -> KEM mapping in order to parse things correctly, which is somewhat annoying. I propose we add KEMID to the request so that this mapping isn't needed to just parse the message. As a bonus, that would also fold in the KEMID explicitly into the AAD alongside the KDFID and AEADID.
We currently length-prefix it, but we don't have to. Think about this a little.
Both the public key and encapsulated secret are fixed length. I think making these fixed length on the wire is good. However, it does require the parsing code to know the corresponding size, which depends on the KEM. This requires there to be a function to map the kem_id to the corresponding size(s). NAME do you have such an API in your implementation? I have one in mine, which would make this pretty straightforward to implement.


note: when merged.
We should probably make it clear what the application can do here (SPA for servers; maybe intentional migration for clients?) I'm not sure if migration is transparent to client applications or not.
The client application might have some knowledge if it's behind a NAT or not and as such might want to indicate to use Conn ID or not. We can make that more explicit in the text.
I did some small edits on the text in the connection migration section but NAME maybe you had something more in mind and want to propose more text?
This whole section should then also be moved to after the Conn ID section.
I like the move of text to the manageability draft. If it were me, I'd move "Server-Generated Connection ID" as well, as I don't think apps need to worry about it, but it's not a big deal. I think adding text about server preferred address is probably more important for this draft.
Added some more text in
NAME we think the current state of the draft addresses this issue; could you check and close this issue if so?


note: don't merge yet, still need to make the pass on applicability but working on the CI issue at the moment
done. mergeable if ok.
There are a few instances that might be read as normative statements. Is that really wise? Examples: cannot? Maybe this isn't something that this document needs to say. It could just say that fallback to unencrypted TCP might not be a desirable outcome. There is also a bunch of "MUST"-level statements in the 0-RTT section, but I've proposed a rewrite that (I hope) eliminates them.
Fixed in : removed all 2119 language.
Not clear if these documents should use normative language at all. The applicability statement also sometimes uses normative language to recommend the implementation of certain interfaces; I don't think this is the right document for that but as we also don't have another document, we should decide if we want to do that or not!
Manageability is a user's guide to the wire image, as such there's no sense to normative language (though I suppose some might like to add MUST NOT observe packets). Since applicability is in part a set of recommendations to application designers, normative language might make sense there... but the document is informational, so normative language here has to reference or be trivially derived from normative language in one of the base drafts. To simplify things, I'd just scrub all the SHOULDs, MAYs, and MUSTs.


addresses
"QUIC's linkability resistance ensures that a deliberate connection migration is accompanied by a change in the connection ID and necessitate that connection ID aware DDoS defense system must have the same information about connection IDs as the load balancer {{?I-D.ietf-quic-load-balancers}}. This may be complicated where mitigation and load balancing environments are logically separate." This is insufficient. Even the load balancer is only able to identify the end server, not the connection.
On a related note, the suggestion to use the "first 8 bytes" of a server's connection ID is somewhat strange. Why not observe the CIDL it's using in long headers, or just have common configuration?
How about if we just remove any wording about load balancers here? Like this maybe:
On the 8 bytes: I actually not sure where this comes from, however, I assume that the thinking was that using the first 8 bytes might be good enough to avoid collisions if you don't know the connection ID length. But I guess we can remove it or add some more explanation... any thoughts?
If you read the Retry Services section of QUIC-LB, you'll see that there is no dependence on the Connection ID encoding at all. It is a common format for Retry Tokens that allows servers to (at a minimum) extract the original CIDs necessary for verification. Certainly, no device should drop packets simply because the CID has changed, which is maybe what you're trying to say here? But I'm not sure that this is a DDoS thing.
The point is when a DDoS attack is on-going you want to make sure that on-going connections keep running while new connections are handled more carefully. So you have to be flow aware. However, maybe it's acceptable if connectivity breaks during a DDoS when the 5 tuple changes...? Otherwise you would need to be CID aware.
The VN section suggests that observers not use the version number to detect QUIC. Would it be futile to ask them to admit QUIC versions they don't recognize rather than drop them?
Probably.
Not sure. Depends on the function you implement. I guess there could be security functions that decide to only allow certain versions... not sure if that is justified but I don't think we can stop it.
Any further opinions here?
We can stop it with greasing, I guess. IMO the draft should specify best practices that don't break extensibility and if people don't follow it it's on them. That's the approach I took in QUIC-LB.
The best practice is to not block any unknown traffic as blocking hinders evolution. However, I guess there might be security reasons why some will not do that. I guess allowing for certain versions of QUIC is better than blocking UDP entirely...?
+1... I don't think that putting text in this document saying "don't wholesale block versions of QUIC you don't understand" will actually change the behavior of the boxes that will ship this behavior, since drop-unknown is taken as a security best practice. But I still think it's worth putting that advice in the document nonetheless, if only to have "I told you so" in an RFC. (I do still tend to think that we'll see only two behaviors emerge in the wild: (1) permit anything with a QUIC wire image since there's not much to grab on to there (and rely on the LB implementation to eat DoS hiding behind that wire image) and (2) block anything that smells like QUIC (because it is easy to implement in the default case and it forces fallback to TCP, which already has lots of running security monitoring code).)
will work up a PR for us to discuss
In manageability: "Integrity Protection of the Wire Image {#wire-integrity} As soon as the cryptographic context is established, all information in the QUIC header, including information exposed in the packet header, is integrity protected. Further, information that was sent and exposed in handshake packets sent before the cryptographic context was established are validated later during the cryptographic handshake. Therefore, devices on path cannot alter any information or bits in QUIC packet headers, since alteration of header information will lead to a failed integrity check at the receiver, and can even lead to connection termination." This isn't precise. The Initial packet is not meaningfully integrity-protected, but the contents of the CRYPTO frame (and the connection IDs) are. An on-path attacker could, for instance, change the ClientHello's Packet Number to from '0' to '1' and then adjust the ACK frame from '1' to '0'. (Why would one do this?)
Is the ACK frame in the case not encrypted...?
The Initial is decryptable by anyone, and so could be rewritten.
Okay. What do we want to write here now?
how about "URL alter any information or bits in QUIC packet headers, since alteration..." changing to: "URL alter any information or bits in QUIC packet headers, except specific parts of Initial packets, since alteration..."
WFM. I can change that as soon as your PR landed (or you add it to your PR).


Merged... with the neurons I have left on 22 December, this seems broadly true. I'm not sure we should be too prescriptive about load balancing in this doc, though -- in part because I suspect the challenge of handling DDoS attacks using QUIC traffic will lead to divergent innovation in the LB space in the near term...
So if the 5-tuple changes because of NAT, the connection ID might actually not change. So in case of DDoS you might want to look at the connection ID to at cover those cases. For DDoS you usually don't need a hard guarantee to track all flows (if you block a flow incorrectly, it has to reconnect), but where you can track the flow when the 5-tuple changes, it might actually help. That is what this paragraph is/was about.
"QUIC's linkability resistance ensures that a deliberate connection migration is accompanied by a change in the connection ID and necessitate that connection ID aware DDoS defense system must have the same information about connection IDs as the load balancer {{?I-D.ietf-quic-load-balancers}}. This may be complicated where mitigation and load balancing environments are logically separate." This is insufficient. Even the load balancer is only able to identify the end server, not the connection.
On a related note, the suggestion to use the "first 8 bytes" of a server's connection ID is somewhat strange. Why not observe the CIDL it's using in long headers, or just have common configuration?
How about if we just remove any wording about load balancers here? Like this maybe:
On the 8 bytes: I actually not sure where this comes from, however, I assume that the thinking was that using the first 8 bytes might be good enough to avoid collisions if you don't know the connection ID length. But I guess we can remove it or add some more explanation... any thoughts?
If you read the Retry Services section of QUIC-LB, you'll see that there is no dependence on the Connection ID encoding at all. It is a common format for Retry Tokens that allows servers to (at a minimum) extract the original CIDs necessary for verification. Certainly, no device should drop packets simply because the CID has changed, which is maybe what you're trying to say here? But I'm not sure that this is a DDoS thing.
The point is when a DDoS attack is on-going you want to make sure that on-going connections keep running while new connections are handled more carefully. So you have to be flow aware. However, maybe it's acceptable if connectivity breaks during a DDoS when the 5 tuple changes...? Otherwise you would need to be CID aware.
This PR removes the problems I cited in the issue. Ship it. I wonder if we should hit the point in the last sentence a little harder. If you want to support connection migration, there's nothing you can do in this space except the Retry Service stuff in QUIC-LB, is there?


This text in is incorrect. An endpoint can abruptly close (reset) egress data, but it can only request that its peer abruptly close ingress (using STOP_SENDING).
is there a missing "not"? Do you mean: s/An endpoint can abruptly close (reset) egress data/An endpoint cannot abruptly close (reset) egress data/ ? Anyway, yes, we need to fix that!


Issue: “and use of the UDP port number corresponding to the TCP port already registered for the application is RECOMMENDED”. I agree with the intent, but here an RFC2119 keyword is used, but is this intended, and can this please cite the RFC that says this rather than just stating this.
Similarly noted in
PR removes normative language and says "usually ... appropriate" instead. Is that okay? Or should the wording be stronger?


The proposed text looks OK to me.
The ID said: “blocking or other interference by network elements such as firewalls” I think this language could be seen as having a little attitude that could be avoided. I suggest replacing “interference”, and instead say that this can change the forwarding policy. This also extends it to include action from QoS policies and service level agreements. “by network elements such as firewalls that rely on the port number for application identification.” I’d prefer “use” to “rely” … because they might well use other heuristics in future if they do prove more useful to classify sessions/packets.
Is the wording in better/good?


process assumes that the set of versions that a server supports is fixed. This complicates the process for deploying new QUIC versions or disabling old versions when servers operate in clusters. This functionality was removed from -transport, and appears to refer to text in draft-ietf-quic-version-negotiation, but that draft isn't referenced at all.
I can add a reference to draft-ietf-quic-version-negotiation and make clear that this is an extension and not part of the base spec.


Some section refs weren't linked to the sections; some objects were defined multiple ways; one reference wasn't actually cited in the text.
Thanks a lot!


This paragraph is redundant with the previous paragraph, however, not sure I removed the right one now...


This goes to the end of "Server Name Indication" Some changes are attempts at clarifications, but I could have incorrectly clarified something, so feel free to reject changes.


From PR : I'd also like to see something like : /In this section, we discuss those aspects of the QUIC transport protocol that/This section discusses those aspects of the QUIC transport protocol that/ /Here, we are concerned primarily with the unencrypted part of QUIC's wire image {{WIRE-IMAGE}}, which we define as the information available in the packet/This section is concerned primarily with the unencrypted part of QUIC's wire image {{WIRE-IMAGE}}, defined as the information available in the packet/ and /least four datagrams we'll call "Client Initial",/least four datagrams, labelled "Client Initial",/ and / 0-RTT data may also be seen in the Client Initial datagram, as shown in /When the client uses 0-RTT connection resumption, the Client Initial datagram can also carry 0-RTT data, as shown in/ and /typically around 10 packets/typically to around 10 packets/ and /of a connection after one of one endpoint/of a connection after an endpoint/ I'd prefer more like: /Though the guidance there holds, a particularly unwise behavior admits a handful of UDP packets and then makes a decision as to whether or not to filter later packets in the same connection. QUIC applications are encouraged to fail over to TCP if early packets do/ ... and I'll close PR .
“QUIC exposes some information to the network in the unencrypted part of the header, either before the encryption context is established or because the information is intended to be used by the network.” Could this benefit form a cross-reference to manageability, where this topic is also discussed from a network perspective?
Three places where the text might avoid confusion when read in future years: 1) “Further downgrades to older TLS versions than used in QUIC, which is 1.3, “ … will it always be 1.3, or should this be “in QUIC [REF] is TLS 1.3” 2) “Currently, QUIC only provides fully reliable stream transmission, which means that prioritization of retransmissions will be beneficial in most cases, by filling in gaps and freeing up the flow control window. “ I'm not so happy with currently here: Is this the best way to say this, might it be better to say this rather than even hinting that future QUIC might only be reliable: “When a QUIC endpoint provides provides fully reliable stream transmission, prioritization of retransmissions will be beneficial in most cases, by filling in gaps and freeing up the flow control window. “ 3) I’d prefer to see a ref here, since this something that the WG might later decide to work upon: “Currently QUIC only supports failover cases [Quic-Transport].”
That mega paragraph has a bunch of good concepts, but it's all run together into a single blob, which makes it a bit hard to follow.


Okay I made another pass on the text based on NAME comments, however, it didn't get any short but maybe it's slight more clear now. Please re-review! (Also I moved some paragraphs around to improve the text flow; so the PR now looks bigger than it is. So we also need to update the section title now, which is "Port Selection and Application Endpoint Discovery"?)
Applicability Sec 8: >Note that HTTP/3's ALPN token ("h3") identifies not only the version of the application protocol, but also the version of QUIC >itself; this approach allows unambiguous agreement between the endpoints on the protocol stack in use. I was going to start a discussion about this at IETF 111. I think we should think hard about this; if there are a lot of versions with small differences to the application, this could result in a ridiculous cloud of RFCs.
Is there a change to make to this document in WGLC, or is this a note to be careful in the future?
I personally don't like this linkage, and think the ALPN should just define the application. While linking the version does smooth out version negotiation, it can result in an explosion in the ALPN registry. I'll start a thread on the list about this, since I suppose it can't wait for the discussion at 111 that I was planning.
The point this was discussed already at length and the manageability document is just reflecting what has been decided for h3. So if at all you'd need to raise an issue on the h3 draft but I guess that's also too late.
I disagree that the text of the H3 draft mandates this: That is a long way from saying "roll a new ALPN for each QUIC version". Maybe everyone tacitly agrees we should roll a new ALPN, and I'm in the rough. But I'd like to confirm that.
See quicwg/base-drafts
How about rewording the sentence to reflect reality, roughly Since QUIC v1 (RFC9000) deferred defining a version negotiation mechanism. HTTP/3 (RFC-to-be-whatever) deferred deciding how it would map to other QUIC versions. HTTP/3 requires QUIC v1 and defines the ALPN token ("h3") to only apply to that version. This solves a versioning problem at the cost of future flexibility. Application protocol mappings written for QUIC v1 or other QUIC versions may be able to benefit from version negotiation mechanisms that did not exist during the development of HTTP/3 (RFC-to-be-whatever). Coupling QUIC version to ALPN tokens is one approach but not the only approach.
I believe the text already says that this doesn't have to be applied by other application. The cited text is really only meant to described what was decided from h3 as an example. We can definitely reword this for reflect the consensus more clearly as proposed by Lucas.
Lucas, I would welcome your proposal as kicking the can down the road and not enshrining an approach that I think is bad. I would also like to continue the ALPN/versioning discussion, but it doesn't have to block the applicability draft.
I can make it shorter. Maybe not as short as NAME would have it, but still shorter :)


hopefully addresses issue
I don't think "the same port number as would be used for the same application over TCP" is necessary general-purpose guidance. I would argue for something more like:
Copying from URL There's no requirement that servers use a particular UDP port for HTTP/QUIC or any other QUIC traffic. Using Alt-Svc, the server is able to pick and advertise any port number and a compliant client will handle it just fine. That's already the case, and isn't part of this issue. updates the HTTP draft to highlight this, increasing the odds that implementations will test and support that case. This issue is to track that it might actually be desirable from a privacy standpoint for servers to pick arbitrary port numbers and perhaps even rotate them periodically (though that requires coordination with their Alt-Svc advertisements and lifetimes, which could be challenging) in order to make it more difficult for a network observer to classify traffic (and therefore more difficult to ossify). On the other hand, as we're wrestling with in each of these privacy/manageability debates, removing easy network visibility into the likely protocol by using arbitrary port numbers means that middleboxes will probably resort to other means of attempting to identify protocols and potentially doing it badly, which could result in even worse ossification. (E.g. indexing into the TLS ClientHello to find the ALPN list, then panicking on a different handshake protocol.) There's further discussion on the original issue, but this belongs in the ops drafts, not the protocol drafts.
this kind-of-but-not-really-duplicates , which I'll go ahead and close.
Actually, it's an exact duplicate, given that it points to the same issue. ;-) My bad.
NAME can you check if PR addresses this issue sufficiently? At least for the applicability doc...
Now also PR for the manageability statement.
and are a (ready for -03) treatment of this; however, we should consider if we want to say more, e.g. such as general guidance / recommendations for using ALPN in QUIC mappings, for instance? Keeping this open to track.
created new issue for ALPN


URL , can we be more specific here about the control signal we are talking about is QUIC protocol control signal not really other kind of control signal that the network might have? also here it is compared with TCP, should it also mention TCP with TLS?
I added the term "transport-layer" in PR . Does that help. I don't think TLS needs to be mentioned here.


hi NAME NAME NAME PTAnotherL, reviewed the thread and I do think an additional 9065 ref here would be helpful.


Could the first 1200-byte QUIC packet be used to suspect a QUIC connection establishment ? [MK] Yes, I think if you look at the first packet as a whole you can be reasonably sure that you look at a quic packet. I guess this is an assumption throughout the whole document, as section 2.4 says: "New QUIC connections are established using a handshake, which is distinguishable on the wire and contains some information that can be passively observed." [MK] Do we need to say more? EV> hummm I guess not even if the text in 2.4 is rather generic while the point in 3.1 is really specific. Perhaps adding which handshake properties are 'distinguishable' already in section 2.4 ?
proposes to fix this with a forward reference.


"Here we assume a bidirectional observer", let's also state that there is no section about unidirectional observer ?
Not sure if we add something because I think it okay to state assumptions...
"unless noted" states that we'll note when something is unidirectionally observable, I'm reviewing the language now to see if we missed any...


Tries to I resisted the urge to reference the QUIC-LB draft, which isn't adopted. The major substantive change is that I removed the prohibition on non-routing information in the CID. I'm happy to discuss this if people have object.
LGTM. will go ahead and land this, then fix up the lint issues.
NAME missed this during the inconsistency, will fix in working copy
(this is done)
BTW, this was meant to address
Moving URL to the ops repo.
see also -- in any case the text that is there now is not great.
NAME can you please review the existing text (orginially provided by NAME and state what else is needed? Please have also a look at issue
This is a bit OBE, but I think there might still be value in discussing how a server implementer should think about connection IDs. Fortunately, NAME has been doing a bunch of work on this in his load balancer draft. I would suggest that we leave this issue open, and either point to the LB draft or use text from it.
fixed this
Write section to provide guidance; maybe in the same section than guidance in port selection in general


Address .
The section on "Passive network performance measurement and troubleshooting" is probably a bit too pessimistic. It's probably worth pointing out that one part of network troubleshooting -- "find out which box to blame" -- is substantially easier. Thanks to integrity protection, most middleboxes, can drop, delay/rate limit, and corrupt, and that's about it. These are fairly easy behaviors to diagnose. I've had some conversations with customers afraid of not having their TCP headers anymore, and I believe this fear is largely misguided. If people agree this belongs in the draft, I can file a PR.
I agree the wording is very pessimistic. However, I don't think your observation belongs in this section because it's about measurement of performance metrics. There is a sentence in the introduction: "Given QUIC is an end-to-end transport protocol, all information in the protocol header, even that which can be inspected, is is not meant to be mutable by the network, and is therefore integrity-protected to the extent possible." Maybe it makes sense to add another sentence right there to also emphasis the positive effect of that, like: "While more limited information compared to TCP is visible to the network, integrity protection can also make troubleshooting easier as none of the nodes on the network path can modify the transport layer information."
LGTM, will fix Jana's suggestion manually.One nit, but LGTM. The nit is to avoid ambiguity that might arise from "transport" -- one might read that QUIC integrity-protects UDP headers as well, since those are technically transport.


NAME -- you mentioned wanting to have a more balanced view of zero RTT in the applicability draft: is this something like what you had in mind?
I'll go ahead and merge this to have one less editor's note in the ietf-00 submission; file an issue if this doesn't address the "thinking in 0rtt" point adequately.
Jana noted at the interim in Paris that we should point out that applications need to be re-thought slightly to get the benefits of zero RTT. Add a little text to discuss this and why it's worth the effort, before we go straight into the dragons.


Clean-up after URL As the content-from-edhoc branch seems to be the most up-to-date one (in which the parts changed are introduced in the first place), this builds on , and only the latest commit is actually relevant.
Compared to the original bstridentifier mapping, this is a different int-to-bstr mapping, but one that's most likely efficient to implement. I have not made any statement on the efficiencies, but some guidance we could give is that from a coding density point of view, numeric Cx should only be used with -24..23; for all other values, doing it in a bstr is just as good and saves a byte in OSCORE. (In fact we may want to make that normative, so that only the single-byte case needs to be implemented where this needs explicit implementation).


This prescribes explicitly one of the possible ways that the response is sent, and states the (hopefully) obvious about accepting new requests. The alternative to "MUST NOT be encrypted" would be "MUST be encrypted and MUST carry an explicit IV"; even both would work, but clients need to know what they need to be prepared for. I'd be fine with either of the two (or three) versions of the request encryption, but not stating anything might result in incompatible implementations where one only works this and one only works that way. Being explicit about (to us) obvious things like not responding with an implied IV or not accepting Repeat as Class U (or in an unencrypted request that might then clear the way for any replays) might not be necessary from a specification point of view, but could avoid implementation errors I see very vividly before my inner eye.
Come to think of it (from the considerations in URL), we have to go the other way and say that the response "MUST be encrypted, and MUST use an explicit partial IV". As we want to use this for HTTP too, this is by far the easier option; otherwise, we'd have to additionally specify how the option is transported in HTTP, so a HC proxy can convert it, and then the HC proxy would need to know whether it's transporting OSCORE (so it'd forward the Repeat option for the HTTP client to include it in a new encrypted request) or whether it's working for an OSCORE-unaware client that would not be equipped to answer the Repeat challenge (so it'd repeat the known fresh (because HTTP) request with a Repeat option by itself without bothering the client).
Did I understand right, that you take back the proposal in the PR? Partial IV generated by the server is allowed in any response. With the resolution of issue : "if Partial IV present in the message -> use that Partial IV" Hence the replay draft can specify that replay syncing MUST use the server generated PIV, in which case it will be encrypted. I probably missed the issue?
Yes; I'll still need to update the PR to say the desired. We don't need to change anything about mechanism, but we should be explicit that the server MUST reply with an encrypted Repeat and a generated partial IV.
Sorry, still not clear: OSCORE now allows the use of server-generated Partial IV. The processing for Repeat is defined in the RRT-draft. Why does anything else need to change in the OSCORE draft?
Wrote the promised changes as it's much easier to talk with concrete text; the PR is now updated as 81d47927. This doesn't change any mechanism (at least not in implementations that are based on the current text and written in full awareness of all the relevant considerations). It is just a text change that prescribes what will be obvious to some implementors but not to others. (Especially since it's not so obvious as that I would have gotten it right in the first attempt.)
(This issue takes some context from issue ). The current draft states that replay protection violation triggers a 5.03 with Max-Age 0 (Section 6.4). While this is a practical solution to issue (which is not mitigated by encrypting the code because FETCH is still safe), it could still be caught by a proxy that'd try to be smart about it (and would, AFAICT, be within its rights). I suggest that the response code be 4.01 Unauthorized with Max-Age=0. The semantics of Unauthorized are "SHOULD NOT repeat the request without first improving its authentication status to the server", and while it's a bit of a stretch to say that increasing the sequence number is sufficient to improve the authentication status, the Max-Age gives a good hint ("Come on, try again right away, don't look for other keys"), and clients can use the presence of the Max-Age option as a discriminator to tell "Sorry, no such context" apart from "Retry with a new seqno".
We also just identified this response code as not appropriate. The proposal is good, unless it results in overloading the semantics of Max-Age=0. There is already at least one requirement when Max-Age has to be zero: RFC7252, Section 5.6.1: "If an origin server wishes to prevent caching, it MUST explicitly include a Max-Age Option with a value of zero seconds." Is there a setting where the the server would not want the response 4.01 to be cached even though it is not a replay? (Obviously we have to differentiate between Inner and Outer 4.01, but is that enough?)
I don't know. I briefly thought about using an Echo here, but an HTTP proxy would react to that as a CoAP proxy would to 5.03,Max-Age=0. (See comments 2ff). I did give that some thought, and considering the Echo option thought about whether the server should just reply with an encrypted ,Max-Age=0 (or Echo=short). I decided against suggesting that because responding to a possibly fraudulent message with an owned partial IV means that any adversary that has one valid-but-used message can thus drive any server into partial-IV exhaustion.
By the way, just FYI, that Max-Age=0 with 5.03 means "service unavailable, retry in 0 seconds", which is wrong (and not what we want). (see: URL)
The current text where there MAY be a Max-Age of 0 both on "4.01, Security context not found" and on "4.01, Security context not found" means that a client can not know any more which is the case (and it should not look into the message). I see the reason for the MAY set Max-Age for most error cases in , but a client needs to keep those apart (otherwise it'd need to so something like try 3 times with a new PIV, and if the server still responds the same, assume that it doesn't know the KID). I suggest that for those two responses, the Max-Age values are enforced as "MUST NOT set a Max-Age option" and "MUST set the Max-Age option to 0", or find any other discriminator, but an implementation should be able to tell them apart.
Granted, with the current setup of POST and FETCH there are much fewer cases where replay protection errors can happen (they'd only legally happen under particular package loss situations when starting an observation via a proxy that does not support observation but does do optimizations for safe requests), but I still think that there'd be more clarity if there were a discriminant between the two error cases.
The document, as of now, specifies that Partial IV SHOULD NOT be present in responses without observe. (This refer to: https://core-URL ) The processing for protecting and verifying responses, as of now, specifies that: if Observe -> use the Partial IV in the response if not Observe -> use Partial IV of the request Should this part be changed to: if Partial IV present in the message -> use that Partial IV if not present -> use Partial IV of the request
Need to read text, but this reflects what I wanted to see.
Sorry NAME when you say "this" do you mean That's not currently included (so no text about it yet). The text right now is:
I was talking about the should change to
Section 5 - You have defined a 'sid' and said that it could be placed in the unprotected field, however you have also said that the unprotected field must be empty. These statements are contradictory
As per Christian Amsüss' message from 2017-01-05 there still seems to be confusing text in that section.
Answering both to NAME and NAME (URL) The point here is: when defining the 'sid' as a new COSE Header parameter, there is no reason related to security that would make this a "MUST be placed in the protected bucket", so instead it is a MAY. But when used in OSCOAP, the 'sid' is used in a way that it MUST be in the protected part, so the unprotected SHALL be empty. The 'sid' is analogous to 'kid' from this point of view, it is defined as a COSE parameter that MAY be unprotected, but is used protected.
Multicast removed in branch v-02 Hello OSCOAP authors, some more questions came up: 5.2 gives the context for the Encstructure as "Encrypted", while cose-msg-24 would set a "Encrypt0". Why does that differ here? states that the '"unprotected" field [...] SHALL be empty', but above states that sid 'can be placed in the unprotected headers bucket'. How do those statements go together? How are sequence numbers (which are sorted integers) mapped to partial IVs? Which endianness is used? (The padding direction follows from cose-msg-24 3.1). I'd assume (and implement) that big-endian is used because it aligns naturally with COSE's left-padding, but as I see it, that does not strictly follow from the draft. I concluded that the tag is always the last bytes of the ciphertext from example A.4, but did not find text in the spec to support that; what made me especially unsure about this is that the COSEEncrypt0 definition of cose-msg-24 5.2 only mentions the ciphertext there, which may be nil. Best regards Christian To use raw power is to make yourself infinitely vulnerable to greater powers. -- Bene Gesserit axiom


This PR should (hopefully) conclude the main rewrite of Observe which was triggered by the security analysis and additional reviews of Observe. It should (hopefully) now be clear what part of the Observe related processing can be omitted in case Observe is not supported or in case intermediaries are not used. A new appendix E is covering the latter case.


Solves issue (from review comments by NAME : URL)
Section 4.1.3.1 - I am unclear why an OSCORE error response can be cached by an OSCORE unaware intermediate would be cached, but a success message is never going to be cached. Based on this, I don't plan to set an outer Max-Age option.
Closed in 84403c4c4 Section 4.1.3.1 - I am unclear why an OSCORE error response can be cached by an OSCORE unaware intermediate would be cached, but a success message is never going to be cached. Based on this, I don't plan to set an outer Max-Age option. In section 5.4 you have the text requestpiv: contains the value of the 'Partial IV' in the COSE object of the request (see Section 5), with one exception: in case of protection or verification of Observe cancellations, the requestpiv contains the value of the 'Partial IV' in the COSE object of the corresponding registration (see Section 4.1.3.5.1). I am unclear how/why this is different for observations. A cancelation is a message, so the IV is the request. A re-registration is a request message, any response would correspond to that request. The only interesting question has to do with updating the MID on a re-registration but not how PIVs work for this field. Section 6.1 - Is a registry needed for the leading byte of compression? Behavior if bits 0, 1, or 2 is set in the flags byte on decode? Section C - given the way that my system is implemented, it would be nice if the outputs included the first full IV to be used for both the sender and the recipient. That would allow for a test that the combination of ids and common ivs is done correctly. In my case I do not have the shared IV available for testing as I immediately or in the id.


Solves issue (from review comments by NAME : URL)
In section 5.4 you have the text requestpiv: contains the value of the 'Partial IV' in the COSE object of the request (see Section 5), with one exception: in case of protection or verification of Observe cancellations, the requestpiv contains the value of the 'Partial IV' in the COSE object of the corresponding registration (see Section 4.1.3.5.1). I am unclear how/why this is different for observations. A cancelation is a message, so the IV is the request. A re-registration is a request message, any response would correspond to that request. The only interesting question has to do with updating the MID on a re-registration but not how PIVs work for this field.
Another case to make sure that works Client - please cancel my observation Server - I don't have an observation for you, but sure I am willing to tell you I canceled it. What request PIV would I use in the AAD?
Yes, the verification of cancellation would fail since the server does not have the initial registration's partial iv saved, if it does not have the observation stored. So OSCORE would fail, which is not right. We have reverted to previous version (request_piv is not exception for cancellations) in c94314321 , but we want to add some security considerations about Observe and cancellations before closing this issue. Section 4.1.3.1 - I am unclear why an OSCORE error response can be cached by an OSCORE unaware intermediate would be cached, but a success message is never going to be cached. Based on this, I don't plan to set an outer Max-Age option. In section 5.4 you have the text requestpiv: contains the value of the 'Partial IV' in the COSE object of the request (see Section 5), with one exception: in case of protection or verification of Observe cancellations, the requestpiv contains the value of the 'Partial IV' in the COSE object of the corresponding registration (see Section 4.1.3.5.1). I am unclear how/why this is different for observations. A cancelation is a message, so the IV is the request. A re-registration is a request message, any response would correspond to that request. The only interesting question has to do with updating the MID on a re-registration but not how PIVs work for this field. Section 6.1 - Is a registry needed for the leading byte of compression? Behavior if bits 0, 1, or 2 is set in the flags byte on decode? Section C - given the way that my system is implemented, it would be nice if the outputs included the first full IV to be used for both the sender and the recipient. That would allow for a test that the combination of ids and common ivs is done correctly. In my case I do not have the shared IV available for testing as I immediately or in the id.


UTF-8 über alles.
Abstract: Explain that OSCOAP works across intermediaries and over any layer.Intro: Explain that OSCOAP works for Observe and Blockwise.Intro: Move details on replay and freshness to some other section.Terminology: Explain the references and add COSE.


, and
This goes against Jim's wish to have things in bits rather than bytes... But HKDF uses octets and to combine the two parameters as wanted by Ludwig, they need the same unit.
Fixed in branch v-02
OSCOAP refers to HKDF [RFC5869], but it not clearly defined how the parameters map to HKDF-Extract and HKDF-Expand. E.g. IKM. Salt should be made optional.
In section 3.2.1. outlen is defined as "the key/IV size of the AEAD algorithm" and outputlength as "is the size of the AEAD key/IV in bytes encoded as an 8-bit unsigned integer" Isn't that essentially the same?
I just created a v-02 branch for the next update. I'll make an update as soon as possible trying to address this.
Fixed in brach v-02


This Partly addresses
Clarify that maximum sequence number is algorithm dependant and define a formula for it.
May be simpler to just have a single structure for the externalaad. externalaad = [ ver : uint, code : uint, alg : int, id : bstr, seq : bstr, ? tag-previous-block : bstr ]
the "context" parameter, which has value "Encrypted" This should be "Encrypt0", but even better OSCOAP should just point to draft-ietf-cose-msg that already specifies this.
-00 Section 3.2.3 - You are getting ahead of yourself. The only way that the identifiers would not be unique would be if the sender and recipient collided. I do not know that this would be a problem and therefore talking about it makes no sense. I am assuming that this is supposed to have something to do with the multicast work that I have not looked at. If so it does not belong here but there.
Agree. I'll delete all text on group communication from the document.
Multicast removed in branch v-02


This is an important security consideration for the re-encryption case, which doesn't appear to be covered by the current text.
Salt could be the same, but key definitely needs to be different. Requiring both to be different is best, of course.


First, these documents can now refer to the published QUIC RFCs. While doing that, let's just replace all [REF] with {{REF}}, which works better for our tooling. Lastly, lets brush up the cross references between documents a bit.
I checked this locally and everything was good. It's easy enough to fix up later if I did break something.
qlog alreaady refers to the -34 drafts. Nominally this change should be easy. Although some section references might need double checking.
lgtm, assuming that the build tool correctly sets the links to the RFCs


Inappropriate use of MAY, lack of MUST [NOT]. Thanks to Russ Housley for schooling me on the correct usage.


This uses kramdown tricks to make real links for the section references to RFC 9001 and uses a nicer label for those.


, , , , , .
Just a style thing (and something we should try an capture in a QUIC style guide).
There a bit of inconsistency through the document. Personally I prefer the QUIC version N style.
is there a missing word here?
Nope, extra word, thanks
I find this awkward to read in isolation, it requires converting the RFC 9000 types definition format into a different encoding to realize there is a change. A clearer alternative could be "All version 2 Long Packet Types are different: Initial: 0b01 0-RTT: 0b10 Handshake 0b11 Retry: 0b00
Starting with This might be a matter of style but to me the term "changes" imply that you're modifying the original, not creating something new. How about we term these as differences instead? The the opening paragraph of Section 3 could be something like "QUIC version 2 endpoints MUST implement the QUIC version 1 specification as described in ], ], and ]. However, the following differences apply in version 2."
This might have been important earlier in the document lifetime but it seems to stick out now.
This could be misread to imply the salt is universally known and universally applicable, which I don't think is your intention. In fact, I find the construction of this entire paragraph a bit awkward. Can we rephrase this for better clarity? Something like "In QUIC version 1, Initial packets are encrypted with the version-specific salt as described in Section 5.2 of [QUIC-TLS]. Protecting Initial packets in this way allows observers to inspect their contents, which includes the TLS Clent Hello or Server Hello messages. Again, there is the potential for middleboxes to ossify on the 1 key derivation and packet formats."
Couple of nits but otherwise LGTM


Though this would likely be monstrously inadvisable, I am fairly confident that I could construct an interoperable (or at least deployable) extension to QUIC that works in QUICv1 but not QUICv2 or vice versa. It's OK to establish expectations, but some softening of the statement might help.
This seems very accurate


here (URL) it is better that we call it QUIC version 1 not just QUIC. As later it is referred as QUIC version 1.here (URL) I would add reference to VN draft instead of calling it "that document"here (URL) i would add "packet" after "Initial" just to be clear, as we have now a initial version in VN draft.
Thanks, fixed here URL


As requested, all new version number, salts, etc. for QUICv2. Verification of the test vectors would be especially valuable.
Woof. Thank you very much for quickly checking, and sorry I botched every part of that. All fixed now, I hope.
The new test vectors works well with my QUIC implementation in Haskell.
Thanks Kazu!
WFM. Not validated with a real build, but a script. See URL for the script that I used.


A dynamic table entry that has not been acknowledged by the decoder must not be evicted by the encoder, even if there has never been any references to it. This is already alluded to in the current spec at URL by the phrase "acknowledged by the decoder", but since "acknowledged" is used interchangeably for acknowledging the insertion instruction and acknowledging references, a reader could understand this to mean "all references acknowledged". In addition, at three other places eviction of entries with unacknowledged references are banned, with unambigously no ban on eviction of unacknowledged entries with no references. I argue that more explicit language would be beneficial. This issue has already been discussed at URL and below. Note that avoiding this kind of corruption could also be achieved by banning an encoder from inserting entries that it does not immediately reference, but this is probably way too restrictive. For example, an encoder might wish to achieve best latency and speculatively improve future compression ratio at the expense of potentially wasting current bandwidth by encoding header fields as literals while inserting them at the same time for potiential future use. This is already alluded to in the spec at multiple places. Below is a specific scenario when this issue might cause corruption: Suppose MaxEntries is 5. Suppose that there is a fresh connection, with an empty dynamic table, and there is a header list to be sent that has two header fields which fit in the dynamic table together. Suppose one encoder implementation inserts these two header fields into the dynamic table, and encodes the header list using two Indexed Header Field instructions, referring to absolute indices 1 and 2. This header block will have an abstract Largest Reference value of 2, which is encoded as 3 on the wire. A spec compliant decoder should have no difficultly correctly decoding this header block. Suppose another encoder implementation starts by adding 10 unique entries, all different from the two header fields in the header list to be sent, to the dynamic table, but does not emit references to any of them. Of course in the end only the last couple will stay in the dynamic table, the rest will be evicted. This can be considered spec compliant depending on how one interprets the spec, see above. Suppose that after this, this encoder implementation encodes our header list by inserting both header fields into the dynamic table, with absolute indices 11 and 12, and using two Indexed Header Field instructions on the request stream. The abstract Largest Reference value is 12, which is encoded as 3 on the wire since 2 * MaxEntries == 10. Now suppose that out of the 12 instructions on the encoder stream, only the first two arrive before all the request stream data are received. The remaining 10 instructions on the encoder stream are delayed on the wire. Now a spec compliant decoder sees the same header block on the request stream, bit by bit, as above, and two insertions on the encoder stream. It will emit dynamic table entires 1 and 2, which are different from the encoded header list.
You should really create a new issue for these things. Because this comment relates only to the issue, not the proposed fix. I haven't read the proposed changes, though I will if you think that there is a fix in there that is worth keeping after this. Anyhow, the example is invalid here: Because: And you can only ever fit 5 entries in that table.
That's exactly the issue: "it has first been acknowledged by the decoder" is ambigous. "Acknowledged" is being used throughout the text with two different meanings, most often to refer to references, not insertions. The ban on evicting an entry the insertion of which has not been acknowledged, that you are quoting, only occurs once out of four different locations talking about when an entry can be evicted. The other three places clearly only ban evicting an entry with unacknowledged references (note the use of the same word), giving the impression that acknowledgement of the insertion is not necessary before eviction. Since the design is correct if you read it right, but wording is not as clear as I think it should be, I consider this issue editorial. The change I propose in this PR is meant to be an improvement on clarity.
OK, having read the changes, I agree with them. They consolidate the fix by creating two conditions that cause an entry to be "blocking".
Rebased on . Please let me know if anybody can come up with a term better than "blocking" which is confusing given that streams can be "blocked".
NAME Thank you for your feedback.
Thanks for this. I don't have a better name for blocking entries right now, but I will keep thinking on it as I agree it could confuse people even more.
This looks reasonable.


Not sure if this is enough, but it enacts Brad's suggested fix.
Yup, that's what I had in mind.
Why is this limited to being prior to the push stream being created, given that you specify how the server handles it if it has created the stream.
I don't read this as saying that you have to avoid it. The point is that you also have to reset/STOPSENDING on the stream if it exists, so there is no real point to using CANCELPUSH in addition.
Sounds like the intent is that CANCELPUSH should be used between the client receiving a PUSHPROMISE and before receiving the stream (i.e. when it is ambiguous as to whether the stream has been created by the server from the clients perspective). I think replacing "created" with "received" would make this more clear.


Follow-up from
Consider the following sequence of packets being sent In our old loss recovery code (before unifying TLP and RTO, ), we used the following logic for determining if an RTO was not spurious, which would then lead to collapse of the congestion window: In the example above would be packet 14. This means that to count as a verified RTO, an ACK must acknowledge 15 or 16, but not any packets smaller or equal than 14. With , we now collapse the congestion window when is larger than 2 and we receive an ACK which leads us to declare any packet lost. In the example above this means any ACK received after sending packet 15 which declares a packet lost would suffice. Even worse, this logic already triggers if we receive the ACK immediately after sending 15, i.e. when the peer didn't even have a chance to receive it. This seems a lot more aggressive than what we had before before. Is this intentional?
This is unintentional. I belIeve Jana is intending to fix this in PR , so please take a look at that.
If I understand correctly, is a fix for and only makes the description of the constant and the pseudo code consistent.
I believe the text is now correct, but the pseudocode is still incorrect in the way you describe. I think we should introduce a largestsentbefore_congestion variable as a replacement, but I'm open to other solutions.
I'm removing design. I consider this a bug fix, but still very important.
See which I believe fixes the text.
I've been using to mark things that will result in changes that affect implementations. This would seem to fit that description. Whether you consider it worth discussing is different. If it isn't worth discussing, maybe it will just be fixed by and everything will be fine without discussion in Tokyo. WIthout the label, this won't make the change log, and that might be a problem.
Fair point, I think fixes the text, but I'll re-label this as design to ensure it's included in the changelogs.
The spurious RTO detection calls an RTO spurious if any unacked packets sent prior to the RTO are acked after the RTO. The implication here is that any packet that was received but not acked can cause the RTO to be labeled spurious, even if there were more packets that were in fact lost. This is not always right. Consider the following example: Packets 5, 6, 7 are sent Packet 5 is received, but 6 and 7 are dropped Ack of 5 is dropped TLPs (packets 8 and 9) are sent and dropped RTO (packet 10) is sent and received ACK acks 10 and 5 Since 5 was sent prior to RTO, RTO is marked spurious. A crisp definition of spurious RTO may be useful to continue. Here's what I think is sensible: A spurious RTO is an unnecessary RTO. If the RTO timer had not gone off, things would have been fine. Based on this definition, clearly the RTO was necessary in the example, and therefore not spurious. We need to change the mechanism for detecting spurious RTOs, and I'll propose the following. An RTO is spurious if an ack is received after the RTO fires that acks only packets sent prior to the RTO. In other words, RTO is spurious if ack is received after the RTO, and (instead of , as the draft currently states).
This was recently changed because Lars ran into an issue() where the RTO'd packet was acknowledged for the first time in the same ACK as the pre-RTO packets. In that case, I think it was due to an overly aggressive MinRTO and some pretty coarse grained timers, but I think we want to declare that RTO spurious, since everything would have been fine if it hadn't been sent?
NAME : does not change the semantics substantively of what was in the draft. The intent was always to declare spurious RTO if (packet prior to RTO was newly acked). I'm proposing that we change that to (no packet sent after RTO was newly acked).
In light of , I think this is OBE?
Yes, this is OBE. Closing.


This keeps things simple.
I have no idea how that happened. Forced it.
Are we ok with a server sending both a VN packet and a Retry in response to a single UDP datagram?
NAME Does that happen? IIUC VN is sent only when there's a version mismatch. Retry is sent only when there's no version mismatch.
NAME A client could coalesce two packets of different versions. As I've said on the issue, I still feel uneasy with the fact that a client could coalesce around 70 packets into a single datagram, and I'd prefer more rigid rules to solve this problem.
NAME I'd argue that a client can't do that, because the draft states "Senders MUST NOT coalesce QUIC packets for different connections into a single UDP datagram", OTOH I agree that there's nothing that requires the server to drop following QUIC packets with different versions within a combined datagram. As I stated on URL, I (still) think the approach in this PR would require having minimal state. Would you mind elaborating (possibly on the issue) why you think differently?
Sure, the issue here comes from multi-threaded processing, and reusing packet buffers. I have one thread (or rather, one go-routine) that reads UDP datagrams from the network, splits the coalesced packets and parses the unencrypted part of the header, and then passes the QUIC packets to their respective connections (which are running in a separate go-routine each). In order to reduce allocations, I reuse the buffers I read my packets into. When handling coalesced packets, this means I have to wait until all coalesced parts have been processed before reusing the buffer. Therefore, I can't just parse the first coalesced packet, pass it on for handling in another threat, and then continue parsing. This would create a race condition, since the handling of the packet might be executed before the parsing is finished, and the packet buffer would be returned already. So I have to split the coalesced packets first, and then handle them. This wouldn't be a problem at all if a coalesced packet just had a few parts, as it's supposed to be. But since we're maximizing flexibility for no reason, an attacker might send me datagram consisting of around 70 packets, which I would all have to completely parse.
NAME Thank you for elaborating. I think that we should be dispatching each UDP datagram (instead of each QUIC packet) to the respective connections. That's how load balancers are expected to work, and what you are implementing is essentially a load balancer. The benefit of dispatching at the datagram level is that you'd have less things to do on the receiving thread. I understand your point on flexibility. However, in my view, the flexibility is not only how you coalesce, but the redundancy in all the long header fields. We have deliberately chosen the design because it is clear and easy to analyze (especially in regard to encryption). My preference goes to retaining the clarity by avoiding introducing restrictions.
I think this PR addresses the amplification problem directly, by tying the number of response datagrams to the number of incoming datagrams, so I am in favor of this PR as a spec solution. NAME : quic-go can still choose to drop datagrams with a large (> 3?) coalesced packets, which might help you (slightly). I agree with NAME also that since all coalesced packets are supposed to go to the same connection, the implementation would be better aligned if it were to pass the datagram along to the connection instead of the individual packets... but that's an impl choice, and it's certainly not the only way to do this.
I'm merging this. Please reopen the issue if you think this ought to be discussed further, especially in Tokyo.


So a friendly pointer to detail that could be easily misinterpreted or missed? It seems like a table footnote is required but I'm not sure how to do that in Markdown.
While each frame's description indicates the stream type(s) on which it is allowed, there isn't one unified place to indicate which frames are expected on which streams. QPACK subdivides the instruction space per stream, which has the nice property that you can't put something on the wrong stream. While that's overkill for H3 (too dramatic a departure from H2, multiple IANA registries, etc.), having a table to indicate which frames are permitted on which streams would be helpful.
I like the table idea for both. Want a PR?
Text always welcome.
I'll submit two PRs in next day or so.
Minor nit; otherwise good. Given that we have two frames that fall into this camp, might it be worth doing something like "(*) = only as the first frame"?


This is my stab at adding the indication discussed in .
Lgtm. An minor Improvement might be to highlight in the new sentence that "specific guidance is provided in the relevant section" but that might be obvious. I'm happy without anything further.


What data can and cannot be retried safely?
That's not something that QUIC knows. HTTP does though. As advice, I don't think that this needs much more expansion, but I'd be happy to take a PR.
I'm not so sure this is a HTTP-only issue. Unlike TLS 1.3, there is QUIC data being sent under encryption, and that might have side effects.
LGTM, one nit. ("using applications" is fine, it's just a bit awkward.)


This change does two things: Clarify that if UDP datagram contains Initial packets, it must be at least 1200 bytes in size; and Rule out padding the UDP datagram with junk (the "padding packets" part) as NAME suggested earlier. Issue is only about (1). Not that I am against (2), but this is just so that we are clear.
I think that the reinterpretation of the text here has introduced the second problem NAME mentions. This also has the unfortunate consequence of disallowing small acknowledgment datagrams (for HelloRetryRequest in particular). This might need to be more carefully phrased.
As NAME said, this is an editorial change, so I'm going to merge. The substantive one is in NAME
NAME -- you're right, but I think this was unintentional earlier. I think if we want to make a case for allowing junk, that should be discussed separately. The proposed text captures what I believe our intent was.
states: This seemingly says that the first UDP datagram that has two coalesced packets, Initial and 0-RTT, can be smaller than 1200 bytes. I don't believe that's the intention.
Agreed, I believe the "only" needs to removed unless I'm missing some other intent.
I don't think that is sufficient. The intent of this was to allow the client to send Initial packets that acknowledge server Initial packets without padding to 1200.
Given PADDING is supposed to count towards bytes in flight, but it's not ack-eliciting, a special case for ACK-only packets makes sense. I'd be inclined to specify ACK-only packets as the exception, opposed to basing it on receiving an ACK of Handshake packets or some other mechanism.
As stated in URL, I prefer to stop requiring the use of full-sized packets once the client observes a response from server. Reasons: QUIC stacks would have the logic to change rules based on if the peer's address has been validated due to the 3-packet rule on the server side. It's just about using the same logic for padding on the client side. Implementations that prefer to pad all the Initial packets it sends can do so anyways. Regardless of what we decide, the server side logic would be indifferent; i.e. create new connection context only when observing a full-sized Initial packet.
tl;dr: we should always pad to 1200 for UDP datagrams that contain Initial packets. The reason we pad to 1200 is to provide a server with plenty of inbound bytes so that it can send outbound bytes prior to path validation. Padding to 1200 has sub-optimal performance in certain cases. In particular, it makes datagrams containing a simple ACK enormous. So the original thought was to try to find a case where we could allow for smaller datagrams to be sent. I think that in both alternative designs we have a problem with the interaction with our DoS defense. And while those problems might only exist in the abstract for now, I think that the best answer is to pad to 1200 always. (I will suggest rewording the text separately.) An ACK-only Initial packet is sent at the tail of the Initial phase. That is the obvious candidate for consideration here. But there are other cases where small Initial packets might be sent. The looser requirement NAME suggests is less than ideal for HelloRetryRequest. A client can receive a HelloRetryRequest and be forced to send another ClientHello. We can't assume here that the server is using the HelloRetryRequest to validate the client address (though it certainly can). If the ClientHello message is small, the number of bytes that a server can send is still limited. If the client doesn't pad, the server can still count the original ClientHello toward its budget, but it doesn't get significant additional budget from the client. And that budget doesn't increase when packets are lost. So it could be that the server is stuck with 3x whatever it gets in ACKs, which is not a lot. It's worse if you consider the possibility of the ServerHello being enormous (hello PQ crypto). At this point, the server won't have validated the client address and so is still limited in what it can send. Ideally, we want the packets to be big so that the server gets more budget for sending and isn't limited to sending tiny packets. But if all the client sends is small ACK-only packets (under either proposed amendment, either what NAME or NAME describe), the increased allowance is very small. Based on this, I would suggest that we reluctantly say that all Initial-carrying datagrams need to be 1200 bytes or more. For the case of the ACK at the tail, most of the designs proposed for key discard will result in that packet not being sent. If it is sent, there are plenty of cases where it will be a packet with Initial (ACK), Handshake (ACK + ...Finished) and 1-RTT data, so the requirement is met without wasting bytes at all. But for ACK of big ServerHello messages or HelloRetryRequest, the overhead is regrettably necessary.
SGTM. I agree this greatly incentivizes good coalescing, but otherwise I don't see any downsides. And simple is good. Even when suggesting the special case for ACK-only packets, it felt like a premature optimization.
Works for me. It's a corner case anyways. Unless HRR is involved, client will not send an Initial in response to the server's Initial, assuming we decide to continue dropping the Initial keys when the client receives a handshake packet.
A potential friendly amendment, but then we should proceed. I would say after confirming with chairs, because there was a pretty strong design decision involved here, but technically this is only an editorial change.


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.


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.


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.


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.


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...


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


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.


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.


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