At its core HTTP/2 is still a request oriented protocol. A client sends the server a request, the server generates a response and sends it back (server push is obviously an exception to this). This means that it maps cleanly to HTTP/1.1 semantics, so in many cases the application code that is processing a request does not need to know which version of HTTP the wire protocol is using.
A request is started by a client sending a
HEADERS frame to open a stream. As you would expect from the name this contains normal HTTP request headers, however it also contains the following pseudo headers:
The request method
The request path
The request scheme. Usually either
Similar to the HTTP/1.1
Host: header, this contains the authority portion of the target URI (e.g. http://example.com)
These pseudo headers must be present and the first headers in the frame. After this the
HEADERS frame can contain any number of request headers. If the number of request headers exceeds the maximum frame size then the client can immediately send
CONTINUATION frames with additional headers. The last frame containing headers will have the
END_HEADERS flag set, which tells the remote endpoint that there is no more headers.
The request can also contain data (e.g. POST requests). If the request has no data the initial
HEADERS frame will have the
END_STREAM flag set, which tells the server there is no data. Otherwise the server will expect the client to send any number of
DATA frames, with the
END_STREAM flag set on the last one.
When the server is ready to send a response sequence of frames is similar to when the client sends a request, a single
HEADERS frame, followed by optional
CONTINUATION frames if the headers do not fit in a single frame, followed by
DATA frames for the entity body, however in this case the only pseudo header field is
:status, which carries the response status code.
Note that because of the framed structure of HTTP/2 it is no longer necessary (in fact explicitly forbidden) to use the chunked transfer encoding that HTTP/1.1 uses for entities of unknown lengths. If the content length is known it is still recommended to send it in a
Content-Length header, even though it is no longer required for the client to know when the request is done it makes for a more user friendly experience (as if this is not present it is not possible for a client to display download progress).