From af0b0cbece91a8f5c9d1f10b6bf1547a20f1b0aa Mon Sep 17 00:00:00 2001 From: Mike Bishop Date: Thu, 17 Feb 2022 16:37:53 -0500 Subject: [PATCH 1/4] Rearrange and rename to align with updated HTTP/2 spec --- rfc9114.md | 252 +++++++++++++++++++++++++++-------------------------- 1 file changed, 129 insertions(+), 123 deletions(-) diff --git a/rfc9114.md b/rfc9114.md index 2187a1dd37..35dbb0f942 100644 --- a/rfc9114.md +++ b/rfc9114.md @@ -418,9 +418,9 @@ origin can indicate that it is not authoritative for a request by sending a 421 of HTTP}}. -# HTTP Request Lifecycle +# Expressing HTTP Semantics in HTTP/3 {#http-request-lifecycle} -## HTTP Message Exchanges {#request-response} +## HTTP Message Framing {#request-response} A client sends an HTTP request on a request stream, which is a client-initiated bidirectional QUIC stream; see {{request-streams}}. A client MUST send only a @@ -440,7 +440,8 @@ response following a final HTTP response MUST be treated as malformed An HTTP message (request or response) consists of: -1. the header section, sent as a single HEADERS frame (see {{frame-headers}}), +1. the header section, including message control data, sent as a single HEADERS + frame (see {{frame-headers}}), 2. optionally, the content, if present, sent as a series of DATA frames (see {{frame-data}}), and @@ -506,8 +507,81 @@ responses at their discretion for other reasons. If the server sends a partial or complete response but does not abort reading the request, clients SHOULD continue sending the body of the request and close the stream normally. +### Request Cancellation and Rejection {#request-cancellation} + +Once a request stream has been opened, the request MAY be cancelled by either +endpoint. Clients cancel requests if the response is no longer of interest; +servers cancel requests if they are unable to or choose not to respond. When +possible, it is RECOMMENDED that servers send an HTTP response with an +appropriate status code rather than cancelling a request it has already begun +processing. + +Implementations SHOULD cancel requests by abruptly terminating any directions of +a stream that are still open. To do so, an implementation resets the sending +parts of streams and aborts reading on the receiving parts of streams; see +{{Section 2.4 of QUIC-TRANSPORT}}. + +When the server cancels a request without performing any application processing, +the request is considered "rejected". The server SHOULD abort its response +stream with the error code H3_REQUEST_REJECTED. In this context, "processed" +means that some data from the stream was passed to some higher layer of software +that might have taken some action as a result. The client can treat requests +rejected by the server as though they had never been sent at all, thereby +allowing them to be retried later. + +Servers MUST NOT use the H3_REQUEST_REJECTED error code for requests that were +partially or fully processed. When a server abandons a response after partial +processing, it SHOULD abort its response stream with the error code +H3_REQUEST_CANCELLED. + +Client SHOULD use the error code H3_REQUEST_CANCELLED to cancel requests. Upon +receipt of this error code, a server MAY abruptly terminate the response using +the error code H3_REQUEST_REJECTED if no processing was performed. Clients MUST +NOT use the H3_REQUEST_REJECTED error code, except when a server has requested +closure of the request stream with this error code. -### Field Formatting and Compression {#header-formatting} +If a stream is cancelled after receiving a complete response, the client MAY +ignore the cancellation and use the response. However, if a stream is cancelled +after receiving a partial response, the response SHOULD NOT be used. Only +idempotent actions such as GET, PUT, or DELETE can be safely retried; a client +SHOULD NOT automatically retry a request with a non-idempotent method unless it +has some means to know that the request semantics are idempotent +independent of the method or some means to detect that the original request was +never applied. See {{Section 9.2.2 of HTTP}} for more details. + +### Malformed Requests and Responses {#malformed} + +A malformed request or response is one that is an otherwise valid sequence of +frames but is invalid due to: + +- the presence of prohibited fields or pseudo-header fields, +- the absence of mandatory pseudo-header fields, +- invalid values for pseudo-header fields, +- pseudo-header fields after fields, +- an invalid sequence of HTTP messages, +- the inclusion of uppercase field names, or +- the inclusion of invalid characters in field names or values. + +A request or response that is defined as having content when it contains a +Content-Length header field ({{Section 6.4.1 of HTTP}}), +is malformed if the value of a Content-Length header field does not equal the +sum of the DATA frame lengths received. A response that is defined as never +having content, even when a Content-Length is present, can have a non-zero +Content-Length field even though no content is included in DATA frames. + +Intermediaries that process HTTP requests or responses (i.e., any intermediary +not acting as a tunnel) MUST NOT forward a malformed request or response. +Malformed requests or responses that are detected MUST be treated as a stream +error ({{errors}}) of type H3_MESSAGE_ERROR. + +For malformed requests, a server MAY send an HTTP response indicating the error +prior to closing or resetting the stream. Clients MUST NOT accept a malformed +response. Note that these requirements are intended to protect against several +types of common attacks against HTTP; they are deliberately strict because being +permissive can expose implementations to these vulnerabilities. + + +### HTTP Fields {#header-formatting} HTTP messages carry metadata as a series of key-value pairs called "HTTP fields"; see {{Sections 6.3 and 6.5 of HTTP}}. For a listing of registered HTTP @@ -535,12 +609,46 @@ connection-specific header fields as discussed in {{Section 7.6.1 of HTTP}}, or their messages will be treated by other HTTP/3 endpoints as malformed ({{malformed}}). -#### Pseudo-Header Fields +#### Field Compression + +{{QPACK}} describes a variation of HPACK that gives an encoder some control over +how much head-of-line blocking can be caused by compression. This allows an +encoder to balance compression efficiency with latency. HTTP/3 uses QPACK to +compress header and trailer sections, including the control data present in the +header section. + +To allow for better compression efficiency, the "Cookie" field ({{!RFC6265}}) +MAY be split into separate field lines, each with one or more cookie-pairs, +before compression. If a decompressed field section contains multiple cookie +field lines, these MUST be concatenated into a single byte string using the +two-byte delimiter of 0x3b, 0x20 (the ASCII string "; ") before being passed +into a context other than HTTP/2 or HTTP/3, such as an HTTP/1.1 connection, or a +generic HTTP server application. + +#### Header Size Constraints + +An HTTP/3 implementation MAY impose a limit on the maximum size of the message +header it will accept on an individual HTTP message. A server that receives a +larger header section than it is willing to handle can send an HTTP 431 (Request +Header Fields Too Large) status code ({{?RFC6585}}). A client can discard +responses that it cannot process. The size of a field list is calculated based +on the uncompressed size of fields, including the length of the name and value +in bytes plus an overhead of 32 bytes for each field. + +If an implementation wishes to advise its peer of this limit, it can be conveyed +as a number of bytes in the SETTINGS_MAX_FIELD_SECTION_SIZE parameter. An +implementation that has received this parameter SHOULD NOT send an HTTP message +header that exceeds the indicated size, as the peer will likely refuse to +process it. However, an HTTP message can traverse one or more intermediaries +before reaching the origin server; see {{Section 3.7 of HTTP}}. Because +this limit is applied separately by each implementation that processes the +message, messages below this limit are not guaranteed to be accepted. + +## HTTP Control Data Like HTTP/2, HTTP/3 employs a series of pseudo-header fields, where the field name begins with the ':' character (ASCII 0x3a). These pseudo-header fields -convey the target URI, the method of the request, and the status code for the -response. +convey message control data (see {{Section 6.2 of HTTP}}). Pseudo-header fields are not HTTP fields. Endpoints MUST NOT generate pseudo-header fields other than those defined in this document; however, an @@ -559,6 +667,8 @@ fields. Any request or response that contains a pseudo-header field that appears in a header section after a regular header field MUST be treated as malformed ({{malformed}}). +### Request Pseudo-Header Fields + The following pseudo-header fields are defined for requests: ":method": @@ -621,7 +731,10 @@ An HTTP request that omits mandatory pseudo-header fields or contains invalid values for those pseudo-header fields is malformed ({{malformed}}). HTTP/3 does not define a way to carry the version identifier that is included in -the HTTP/1.1 request line. +the HTTP/1.1 request line. HTTP/3 requests implicitly have a protocol version +of "3.0". + +### Response Pseudo-Header Fields For responses, a single ":status" pseudo-header field is defined that carries the HTTP status code; see {{Section 15 of HTTP}}. This pseudo-header @@ -629,115 +742,8 @@ field MUST be included in all responses; otherwise, the response is malformed (see {{malformed}}). HTTP/3 does not define a way to carry the version or reason phrase that is -included in an HTTP/1.1 status line. - -#### Field Compression - -{{QPACK}} describes a variation of HPACK that gives an encoder some control over -how much head-of-line blocking can be caused by compression. This allows an -encoder to balance compression efficiency with latency. HTTP/3 uses QPACK to -compress header and trailer sections, including the pseudo-header fields present -in the header section. - -To allow for better compression efficiency, the "Cookie" field ({{!RFC6265}}) -MAY be split into separate field lines, each with one or more cookie-pairs, -before compression. If a decompressed field section contains multiple cookie -field lines, these MUST be concatenated into a single byte string using the -two-byte delimiter of 0x3b, 0x20 (the ASCII string "; ") before being passed -into a context other than HTTP/2 or HTTP/3, such as an HTTP/1.1 connection, or a -generic HTTP server application. - -#### Header Size Constraints - -An HTTP/3 implementation MAY impose a limit on the maximum size of the message -header it will accept on an individual HTTP message. A server that receives a -larger header section than it is willing to handle can send an HTTP 431 (Request -Header Fields Too Large) status code ({{?RFC6585}}). A client can discard -responses that it cannot process. The size of a field list is calculated based -on the uncompressed size of fields, including the length of the name and value -in bytes plus an overhead of 32 bytes for each field. - -If an implementation wishes to advise its peer of this limit, it can be conveyed -as a number of bytes in the SETTINGS_MAX_FIELD_SECTION_SIZE parameter. An -implementation that has received this parameter SHOULD NOT send an HTTP message -header that exceeds the indicated size, as the peer will likely refuse to -process it. However, an HTTP message can traverse one or more intermediaries -before reaching the origin server; see {{Section 3.7 of HTTP}}. Because -this limit is applied separately by each implementation that processes the -message, messages below this limit are not guaranteed to be accepted. - -### Request Cancellation and Rejection {#request-cancellation} - -Once a request stream has been opened, the request MAY be cancelled by either -endpoint. Clients cancel requests if the response is no longer of interest; -servers cancel requests if they are unable to or choose not to respond. When -possible, it is RECOMMENDED that servers send an HTTP response with an -appropriate status code rather than cancelling a request it has already begun -processing. - -Implementations SHOULD cancel requests by abruptly terminating any directions of -a stream that are still open. To do so, an implementation resets the sending -parts of streams and aborts reading on the receiving parts of streams; see -{{Section 2.4 of QUIC-TRANSPORT}}. - -When the server cancels a request without performing any application processing, -the request is considered "rejected". The server SHOULD abort its response -stream with the error code H3_REQUEST_REJECTED. In this context, "processed" -means that some data from the stream was passed to some higher layer of software -that might have taken some action as a result. The client can treat requests -rejected by the server as though they had never been sent at all, thereby -allowing them to be retried later. - -Servers MUST NOT use the H3_REQUEST_REJECTED error code for requests that were -partially or fully processed. When a server abandons a response after partial -processing, it SHOULD abort its response stream with the error code -H3_REQUEST_CANCELLED. - -Client SHOULD use the error code H3_REQUEST_CANCELLED to cancel requests. Upon -receipt of this error code, a server MAY abruptly terminate the response using -the error code H3_REQUEST_REJECTED if no processing was performed. Clients MUST -NOT use the H3_REQUEST_REJECTED error code, except when a server has requested -closure of the request stream with this error code. - -If a stream is cancelled after receiving a complete response, the client MAY -ignore the cancellation and use the response. However, if a stream is cancelled -after receiving a partial response, the response SHOULD NOT be used. Only -idempotent actions such as GET, PUT, or DELETE can be safely retried; a client -SHOULD NOT automatically retry a request with a non-idempotent method unless it -has some means to know that the request semantics are idempotent -independent of the method or some means to detect that the original request was -never applied. See {{Section 9.2.2 of HTTP}} for more details. - -### Malformed Requests and Responses {#malformed} - -A malformed request or response is one that is an otherwise valid sequence of -frames but is invalid due to: - -- the presence of prohibited fields or pseudo-header fields, -- the absence of mandatory pseudo-header fields, -- invalid values for pseudo-header fields, -- pseudo-header fields after fields, -- an invalid sequence of HTTP messages, -- the inclusion of uppercase field names, or -- the inclusion of invalid characters in field names or values. - -A request or response that is defined as having content when it contains a -Content-Length header field ({{Section 6.4.1 of HTTP}}), -is malformed if the value of a Content-Length header field does not equal the -sum of the DATA frame lengths received. A response that is defined as never -having content, even when a Content-Length is present, can have a non-zero -Content-Length field even though no content is included in DATA frames. - -Intermediaries that process HTTP requests or responses (i.e., any intermediary -not acting as a tunnel) MUST NOT forward a malformed request or response. -Malformed requests or responses that are detected MUST be treated as a stream -error ({{errors}}) of type H3_MESSAGE_ERROR. - -For malformed requests, a server MAY send an HTTP response indicating the error -prior to closing or resetting the stream. Clients MUST NOT accept a malformed -response. Note that these requirements are intended to protect against several -types of common attacks against HTTP; they are deliberately strict because being -permissive can expose implementations to these vulnerabilities. +included in an HTTP/1.1 status line. HTTP/3 responses implicitly have a protocol +version of "3.0". ## The CONNECT Method {#connect} @@ -836,12 +842,12 @@ treat receipt of a push stream as a connection error of type H3_ID_ERROR references a Push ID that is greater than the maximum Push ID. The Push ID is used in one or more PUSH_PROMISE frames ({{frame-push-promise}}) -that carry the header section of the request message. These frames are sent on -the request stream that generated the push. This allows the server push to be -associated with a client request. When the same Push ID is promised on multiple -request streams, the decompressed request field sections MUST contain the same -fields in the same order, and both the name and the value in each field MUST be -identical. +that carry the control data and header fields of the request message. These +frames are sent on the request stream that generated the push. This allows the +server push to be associated with a client request. When the same Push ID is +promised on multiple request streams, the decompressed request field sections +MUST contain the same fields in the same order, and both the name and the value +in each field MUST be identical. The Push ID is then included with the push stream that ultimately fulfills those promises; see {{push-streams}}. The push stream identifies the Push ID of From 7f63bb03dd803c7ce9fcab6cbe2a8b5476a2a73c Mon Sep 17 00:00:00 2001 From: Mike Bishop Date: Thu, 17 Feb 2022 16:45:31 -0500 Subject: [PATCH 2/4] Move Fields up a level --- rfc9114.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rfc9114.md b/rfc9114.md index 35dbb0f942..aea81438ea 100644 --- a/rfc9114.md +++ b/rfc9114.md @@ -581,7 +581,7 @@ types of common attacks against HTTP; they are deliberately strict because being permissive can expose implementations to these vulnerabilities. -### HTTP Fields {#header-formatting} +## HTTP Fields {#header-formatting} HTTP messages carry metadata as a series of key-value pairs called "HTTP fields"; see {{Sections 6.3 and 6.5 of HTTP}}. For a listing of registered HTTP From acd3adf758c4dc8e8c3e040f619a7d8385d231f0 Mon Sep 17 00:00:00 2001 From: Mike Bishop Date: Tue, 22 Feb 2022 15:16:32 -0500 Subject: [PATCH 3/4] Minor tweaks --- rfc9114.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfc9114.md b/rfc9114.md index aea81438ea..277d86cf61 100644 --- a/rfc9114.md +++ b/rfc9114.md @@ -648,10 +648,10 @@ message, messages below this limit are not guaranteed to be accepted. Like HTTP/2, HTTP/3 employs a series of pseudo-header fields, where the field name begins with the ':' character (ASCII 0x3a). These pseudo-header fields -convey message control data (see {{Section 6.2 of HTTP}}). +convey message control data; see {{Section 6.2 of HTTP}}. Pseudo-header fields are not HTTP fields. Endpoints MUST NOT generate -pseudo-header fields other than those defined in this document; however, an +pseudo-header fields other than those defined in this document. However, an extension could negotiate a modification of this restriction; see {{extensions}}. From eecbcb1e978b3e849312fc225b46db45f8d2f651 Mon Sep 17 00:00:00 2001 From: Mike Bishop Date: Tue, 22 Feb 2022 15:20:30 -0500 Subject: [PATCH 4/4] Fix section nesting --- rfc9114.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rfc9114.md b/rfc9114.md index 5259987668..0079b4607c 100644 --- a/rfc9114.md +++ b/rfc9114.md @@ -676,7 +676,7 @@ connection-specific header fields as discussed in {{Section 7.6.1 of HTTP}}, or their messages will be treated by other HTTP/3 endpoints as malformed ({{malformed}}). -#### Field Compression +### Field Compression {{QPACK}} describes a variation of HPACK that gives an encoder some control over how much head-of-line blocking can be caused by compression. This allows an @@ -692,7 +692,7 @@ two-byte delimiter of 0x3b, 0x20 (the ASCII string "; ") before being passed into a context other than HTTP/2 or HTTP/3, such as an HTTP/1.1 connection, or a generic HTTP server application. -#### Header Size Constraints +### Header Size Constraints An HTTP/3 implementation MAY impose a limit on the maximum size of the message header it will accept on an individual HTTP message. A server that receives a