CodeRigor

FREE LESSON

Requests, responses & transport failures

Separate the request, HTTP response and absence of a response while inspecting honest offline exchanges.

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.

One request, distinct observations
start / 3
Structured fixtures preserve whether a response was observed; an error status is still a response.
One request, distinct observations
Structured fixtures preserve whether a response was observed; an error status is still a response.

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:

Python
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 0

Body 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.

Bytes before text
start / 2
UTF-8 character counts and byte counts answer different questions.
Bytes before text
UTF-8 character counts and byte counts answer different questions.

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.

Watch & read

Free lectures, talks and readings that cover this skill from another angle. All resources →

Focus on request/response roles, methods and status lines. The graded inspector uses supplied Python records and does not execute the browser or network exchanges shown in the video.