Original text:
the message header (see RFC7230, Section 3.2), sent as a single HEADERS frame (see frame-headers), <del> the payload body (see RFC7230, Section 3.3), sent as a series of DATA frames (see frame-data), </del>  <del> optionally, one HEADERS frame containing the trailer-part, if present (see RFC7230, Section 4.1.2). </del>  A server MAY send one or more PUSH_PROMISE frames (see frame-push- promise) before, after, or interleaved with the frames of a response message.  These PUSH_PROMISE frames are not part of the response; see server-push for more details.  The HEADERS and PUSH_PROMISE frames might reference updates to the QPACK dynamic table.  While these updates are not directly part of the message exchange, they must be received and processed before the The "chunked" transfer encoding defined in Section 4.1 of RFC7230 MUST NOT be used. <del> If a DATA frame is received before a HEADERS frame on a either a request or push stream, the recipient MUST respond with a connection error of type HTTP_UNEXPECTED_FRAME (errors). Trailing header fields are carried in an additional HEADERS frame following the body.  Senders MUST send only one HEADERS frame in the trailers section; receivers MUST discard any subsequent HEADERS frames. </del> A response MAY consist of multiple messages when and only when one or more informational responses (1xx; see RFC7231, Section 6.2) precede a final response to the same request.  Non-final responses do not contain a payload body or trailers.  An HTTP request/response exchange fully consumes a bidirectional QUIC stream.  After sending a request, a client MUST close the stream for sending.  Unless using the CONNECT method (see the-connect-method), A push stream is indicated by a stream type of "0x01", followed by the Push ID of the promise that it fulfills, encoded as a variable- length integer.  The remaining data on this stream consists of HTTP/3 <del> frames, as defined in frames, and fulfills a promised server push. Server push and Push IDs are described in server-push. </del>  Only servers can push; if a server receives a client-initiated push stream, this MUST be treated as a connection error of type

Reviewer comments:
Clarify HTTP message definition. Require the connection to be closed upon receipt of frames in incorrect order. Refer to HTTP messages in push stream section. This addresses URL
Good catch. I added a paragraph to explicitly allow unknown frames to the message exchange section. I also modified the wording in the push section to be more permissive of interleaved frames ("by" instead of "consists of").
LGTM
According to URL, a bidirectional HTTP stream can have at least one HEADERS frame (exactly one if the stream is client-initiated), zero or more DATA frames, then zero or one HEADERS frame, in this order. Handling the receipt of frames that does not conform to this requirement is inconsistent: (1) If a DATA frame is received before any other frame, that's an error: (2) If the trailing HEADERS frame is followed by another HEADERS frame, that is to be discarded: (3) There is no guidance on how to handle a DATA frame after the trailing headers. I suggest that every violation to this requirement should be an error. (I do not feel strongly whether a connection error like (1) is handled right now, or a stream error. The implementation I am contributing to will promote most stream errors to connection errors regardless.) Note that (2) comes from the technical limitation that header blocks needed to be processed, which is not the case any longer with QPACK. It just seems like that when the requirement to decode the header block was removed at URL, adding the requirement to signal an error was not in scope of that commit.
I agree with NAME . If you read that requirement a certain way, then any use of trailers will blow up the connection :) I think we could rewrite this text to be clearer about when DATA frames are expected to be received.
Good points. Let's continue tightening down the screws. :-)
Closed in .
This text was already somewhat lacking by not discussing the possibility of extension frames being interleaved; stating that the push stream consists of "responses" rather than "frames" extends this shortcoming to the push stream section. I'm open to fixing this in a separate PR, if you prefer, though.

Revised text:
the message header (see RFC7230, Section 3.2), sent as a single HEADERS frame (see frame-headers),  <ins> optionally, the payload body, if present (see RFC7230, Section 3.3), sent as a series of DATA frames (see frame-data), </ins>  <ins> optionally, trailing headers, if present (see RFC7230, Section 4.1.2), sent as a single HEADERS frame. </ins> A server MAY send one or more PUSH_PROMISE frames (see frame-push- promise) before, after, or interleaved with the frames of a response message.  These PUSH_PROMISE frames are not part of the response; see server-push for more details. <ins> Frames of unknown types (extensions), including reserved frames (frame-grease) MAY be sent on a request or push stream before, after, or interleaved with other frames described in this section. </ins> The HEADERS and PUSH_PROMISE frames might reference updates to the QPACK dynamic table.  While these updates are not directly part of the message exchange, they must be received and processed before the The "chunked" transfer encoding defined in Section 4.1 of RFC7230 MUST NOT be used.  A response MAY consist of multiple messages when and only when one or more informational responses (1xx; see RFC7231, Section 6.2) precede a final response to the same request.  Non-final responses do not contain a payload body or trailers. <ins> If an endpoint receives an invalid sequence of frames on either a request or a push stream, it MUST respond with a connection error of type HTTP_UNEXPECTED_FRAME (errors).  In particular, a DATA frame before any HEADERS frame, or a HEADERS or DATA frame after the trailing HEADERS frame is considered invalid. </ins> An HTTP request/response exchange fully consumes a bidirectional QUIC stream.  After sending a request, a client MUST close the stream for sending.  Unless using the CONNECT method (see the-connect-method), A push stream is indicated by a stream type of "0x01", followed by the Push ID of the promise that it fulfills, encoded as a variable- length integer.  The remaining data on this stream consists of HTTP/3  <ins> frames, as defined in frames, and fulfills a promised server push by zero or more non-final HTTP responses followed by a single final HTTP response, as defined in request-response.  Server push and Push IDs are described in server-push. </ins> Only servers can push; if a server receives a client-initiated push stream, this MUST be treated as a connection error of type