An HTTP interaction always starts with a client sending a request,
optionally some data (e.g., a POST body); and then the server
responds with a response and optionally some data (e.g. the
requested document). Requests and responses have some data associated
with them: for requests, this is a method (e.g. GET), a target
(e.g. /index.html), and a collection of headers
(e.g. User-agent:demo-clent). For responses, it’s a status code
(e.g. 404 Not Found) and a collection of headers.

Of course, as far as the network is concerned, there’s no such thing
as “requests” and “responses” – there’s just bytes being sent from
one computer to another. Let’s see what this looks like, by fetching
https://httpbin.org/xml:

If you try to reproduce these examples interactively, then you’ll
have the most luck if you paste them in all at once. Remember we’re
talking to a remote server here – if you type them in one at a
time, and you’re too slow, then the server might give up on waiting
for you and close the connection. One way to recognize that this
has happened is if response_data comes back as an empty string,
or later on when we’re working with h11 this might cause errors
that mention ConnectionClosed.

So that’s, uh, very convenient and readable. It’s a little more
understandable if we print the bytes as text:

Here we can see the status code at the top (200, which is the code for
“OK”), followed by the headers, followed by the data (a silly little
XML document). But we can already see that working with bytes by hand
like this is really cumbersome. What we need to do is to move up to a
higher level of abstraction.

This is what h11 does. Instead of talking in bytes, it lets you talk
in high-level HTTP “events”. To see what this means, let’s repeat the
above exercise, but using h11. We start by making a TLS connection
like before, but now we’ll also import h11, and create a
h11.Connection object:

Why doesn’t h11 go ahead and send the bytes for you? Because it’s
designed to be usable no matter what socket API you’re using –
doesn’t matter if it’s synchronous like this, asynchronous,
callback-based, whatever; if you can read and write bytes from the
network, then you can use h11.

In this case, we’re not quite done yet – we have to send another
event to tell the other side that we’re finished, which we do by
sending an EndOfMessage event:

Of course, it turns out that in this case, the HTTP/1.1 specification
tells us that any request that doesn’t contain either a
Content-Length or Transfer-Encoding header automatically has a
0 length body, and h11 knows that, and h11 knows that the server knows
that, so it actually encoded the EndOfMessage event as the
empty string:

In [19]: end_of_message_bytes_to_sendOut[19]: b''

But there are other cases where it might not, depending on what
headers are set, what message is being responded to, the HTTP version
of the remote peer, etc. etc. So for consistency, h11 requires that
you always finish your messages by sending an explicit
EndOfMessage event; then it keeps track of the details of
what that actually means in any given situation, so that you don’t
have to.

Finally, we have to read the server’s reply. By now you can probably
guess how this is done, at least in the general outline: we read some
bytes from the network, then we hand them to the connection (using
Connection.receive_data()) and it converts them into events
(using Connection.next_event()).