An HTTP exchange starts with a client requesting an operation on a resource. The method gives the operation's semantics, the target identifies the resource, and request headers describe metadata or preferences. A server can answer with a status, response headers and possibly content. An API adds a particular application's promises: which targets exist, which data shapes it accepts and what each outcome means. Knowing HTTP does not tell you an unknown API's complete contract.
HTTPS carries HTTP through an authenticated, encrypted transport. DNS, TLS and connections introduce their own failures. This course starts after those mechanisms: every graded exercise consumes supplied data and performs no network requests. The distinction lets you test response-processing decisions exactly, without treating a made-up response as evidence that a server was contacted.
A received 404 is an HTTP response. A received 503 is also an HTTP response.
An empty 204 has a status and metadata even though it has no content. By
contrast, the fixture's response=None means no response was observed. Assigning
that absence a fictional status 500 would discard useful information. A real
transport failure also does not prove that a write operation never happened:
a server may apply it before the client loses the reply.
The build uses a small structured record rather than raw wire syntax. HTTP/1.1, HTTP/2 and HTTP/3 do not share one identical framing format, so inspecting these Python dictionaries does not implement a universal HTTP parser. The record keeps the request and observation separate:
exchange = {
"request": {"method": "GET", "target": "/books?tag=python"},
"response": {"status": 404, "body": b""},
}
response = exchange["response"]
print(response is None) # False: a response exists
print(response["status"], len(response["body"])) # 404 0Body storage is bytes. Text is one possible interpretation, chosen later
using a representation contract. len("café".encode("utf-8")) is five even
though the decoded text has four characters. Binary bytes need not be UTF-8
at all. Count supplied bytes without decoding them; this count is neither the
whole message's wire size nor a replacement for HTTP framing rules.
The inspector copies method and target into fresh summary dictionaries and
reports kind, status and byte count. It validates the supplied numeric
status range 100..599 without pretending every number in that range names a
registered status. It rejects boolean statuses explicitly because Python's
bool is a subclass of int. These checks enforce this fixture's contract;
application success comes in later lessons.
Before building, write three cases by hand: no response, a zero-byte 204 and a nonempty error response. Then add a multibyte UTF-8 body and arbitrary binary bytes. Use the checks to reason about what was observed, the bug hunts to catch collapsed states and wrong units, and the fill to practise the transformation before implementing the whole inspector.
The protocol vocabulary follows RFC 9110, HTTP Semantics. Its message model is broader than the deliberately small records used here.