4.3.24 · D4Computer Networks

Exercises — HTTP - 1.1 — methods, status codes, headers, persistent connections

2,569 words12 min readBack to topic

Level 1 — Recognition

Goal: can you name and classify the pieces?

Exercise 1.1

Look at this response start line and identify the three fields and the status class:

HTTP/1.1 404 Not Found
Recall Solution 1.1

A response start line has the shape HTTP/1.1 SP status-code SP reason-phrase, where SP is a single space (defined at the top of this page).

  • Field 1 — version: HTTP/1.1
  • Field 2 — status code: 404
  • Field 3 — reason phrase: Not Found

The first digit is the class. Here the first digit is 4, so the class is 4xx = client error — the resource you asked for doesn't exist. The server itself is fine.

Exercise 1.2

For each method, write Safe? (Y/N) and Idempotent? (Y/N): GET, POST, PUT, DELETE, HEAD.

Recall Solution 1.2

Recall: safe = no server state change; idempotent = doing it times leaves the same final state as doing it once.

Method Safe? Idempotent?
GET Y Y
HEAD Y Y
PUT N Y
DELETE N Y
POST N N

Note every safe method is automatically idempotent (if you change nothing, repeating changes nothing). The reverse is false: PUT changes state yet is idempotent.

Exercise 1.3

Which of these headers is mandatory in every HTTP/1.1 request, and why? User-Agent, Host, Content-Length, Accept.

Recall Solution 1.3

Host is mandatory. One server IP can host many domains (virtual hosting — see Virtual hosting), so the request must say which site it targets. The other three are optional. Content-Length only appears when there is a body.


Level 2 — Application

Goal: apply the message format and status rules to concrete inputs.

Exercise 2.1

Write a complete, correct raw HTTP/1.1 GET request for https://shop.io/cart with a user agent of curl/8.0 and accepting JSON. Use literal \r\n markers.

Recall Solution 2.1

Structure: METHOD SP request-target SP HTTP/1.1, then header lines, then a blank line.

GET /cart HTTP/1.1\r\n
Host: shop.io\r\n
User-Agent: curl/8.0\r\n
Accept: application/json\r\n
\r\n

The final lone \r\n marks "headers finished." A GET has no body, so the message ends there. The scheme (https) and host go into the Host header, not the request-target — the target is just the path /cart.

Exercise 2.2

A server sends this response on a persistent connection. How many bytes of body must the client read, and how does it know the response is over?

HTTP/1.1 200 OK\r\n
Content-Type: text/plain\r\n
Content-Length: 5\r\n
\r\n
Hello
Recall Solution 2.2

The client reads exactly 5 bytes of body (H, e, l, l, o).

On a persistent connection the socket does not close after the response, so "read until EOF" is impossible. The Content-Length: 5 header tells the client precisely where this response ends and where the next one on the same socket begins. This framing is the whole reason persistence needs a length header (or chunked encoding).

Exercise 2.3

Match each situation to the best status code: (a) User requests a page that was permanently moved to a new URL. (b) Cached copy is still fresh; nothing changed since last fetch. (c) User is not logged in and tries a protected page. (d) The server threw an unhandled exception.

Recall Solution 2.3
  • (a) → 301 Moved Permanently (3xx redirect).
  • (b) → 304 Not Modified (3xx; conditional GET succeeded — see Caching, ETag and conditional GET).
  • (c) → 401 Unauthorized (4xx client error — authentication needed).
  • (d) → 500 Internal Server Error (5xx server error).

Split rule: 4xx = "you messed up," 5xx = "I messed up." (c) is on the client's side (no credentials); (d) is the server's fault.


Level 3 — Analysis

Goal: reason about why the system behaves as it does; compute costs.

Exercise 3.1

A page needs resources. RTT ms. Using the parent's toy latency model compute , , and the milliseconds saved.

The figure below plots both models as grows. The orange curve () climbs twice as fast as the teal curve (), because HTTP/1.0 pays a fresh handshake for every resource while HTTP/1.1 pays only one handshake and then one RTT per resource. The plum bar at is the vertical gap between the two curves — that gap is the 440 ms you compute below, and it widens by exactly one RTT for each extra resource.

Figure — HTTP - 1.1 — methods, status codes, headers, persistent connections
Recall Solution 3.1

Substitute , RTT ms. Cross-check with the closed form: saving ms. ✓ This is exactly the height of the plum bar in the figure above.

The saving comes from doing one TCP handshake (see TCP three-way handshake) instead of 12, thanks to the persistent connection.

Exercise 3.2

On the same link (, RTT ms), suppose pipelining lets the client fire all requests without waiting, so the whole batch costs about (one handshake RTT + one RTT for the pipelined burst). How much faster is pipelined 1.1 than serial 1.1 from 3.1, and what flaw still remains?

Recall Solution 3.2

Serial 1.1 was ms, so pipelining saves ms more.

The remaining flaw is head-of-line blocking: replies must come back in order on one TCP stream, so one slow response stalls every response queued behind it. This is exactly the limitation HTTP-2 multiplexing removes.

Exercise 3.3

Explain why a response with no Content-Length and no Transfer-Encoding: chunked header forces the connection to close after it, even in HTTP/1.1.

Recall Solution 3.3

Without a length header or chunked framing, the only way the client can know the body ended is to see the socket close (EOF) — the old HTTP/1.0 rule. But EOF means the TCP connection is gone. So the server cannot keep the connection open: there'd be no way to tell where this body stops and the next response starts. Therefore an unframed response implies Connection: close. Persistence and framing are inseparable.


Level 4 — Synthesis

Goal: build correct, complete messages/flows from requirements.

Exercise 4.1

Design a raw HTTP/1.1 request that creates a new user by sending the JSON body {"name":"Ada"} to api.dev/users. Include exactly the headers required for the server to parse the body correctly on a persistent connection. State the byte count you put in the length header.

Recall Solution 4.1

Creating a resource by submitting data = POST (not idempotent, not safe — repeating it makes duplicate users). The body is {"name":"Ada"}. Count the bytes one character at a time (no spaces exist in the payload): { " n a m e " : " A d a " } = 14 characters, 14 bytes (all are single-byte ASCII).

POST /users HTTP/1.1\r\n
Host: api.dev\r\n
Content-Type: application/json\r\n
Content-Length: 14\r\n
\r\n
{"name":"Ada"}

Content-Type tells the server how to interpret the bytes; Content-Length: 14 tells it how many to read before the next request on the persistent socket.

Exercise 4.2

A client wants to refresh a cached page only if it changed. Its cached copy carried ETag: "v9". Write (a) the conditional request and (b) the two possible responses (changed vs unchanged), naming the status code in each. For the "changed" case use the new body Hi Ada! and compute its Content-Length.

Recall Solution 4.2

(a) Conditional request uses If-None-Match carrying the stored ETag:

GET /page HTTP/1.1\r\n
Host: site.io\r\n
If-None-Match: "v9"\r\n
\r\n

(b) Two outcomes:

  • Unchanged — server's current ETag is still "v9":
    HTTP/1.1 304 Not Modified\r\n
    ETag: "v9"\r\n
    \r\n
    
    No body — client reuses its cache. This saves bandwidth (the point of Caching, ETag and conditional GET).
  • Changed — new ETag, full body sent. The body is Hi Ada!; count it: H i (space) A d a ! = 7 bytes (the space is a real character in this body, unlike the JSON above). So Content-Length: 7:
    HTTP/1.1 200 OK\r\n
    ETag: "v10"\r\n
    Content-Length: 7\r\n
    \r\n
    Hi Ada!
    

Level 5 — Mastery

Goal: integrate the full stack and reason at the limits/edge cases.

Exercise 5.1

A browser opens https://blog.io fresh (cold cache). List, in order, the round-trip-costing steps before the first byte of HTML arrives, naming the relevant vault topics. Then say which of these HTTP/1.1's persistent connection actually eliminates on the second request.

Recall Solution 5.1

Cold-start sequence for an HTTPS page:

  1. DNS resolution — turn blog.io into an IP (~1 RTT, often cached).
  2. TCP three-way handshake — 1 RTT before any data can flow.
  3. TLS handshake — set up encryption (1–2 RTT for TLS 1.2; ~1 for TLS 1.3).
  4. TCP slow start — congestion window starts small, so early responses are throughput-limited even after the handshakes.
  5. HTTP GET request → first byte of HTML (1 RTT).

On the second request over the same persistent connection, HTTP/1.1 eliminates steps 2, 3, and 4-restart — the TCP handshake, the TLS handshake, and a fresh slow-start ramp are all reused. That reuse is the entire value of persistence. DNS (step 1) is separate and cached independently.

Exercise 5.2

Edge case: a server on a persistent connection sends a response with both Content-Length: 20 and Transfer-Encoding: chunked. Per the spec, which wins, and why is sending both dangerous?

Recall Solution 5.2

When both are present, Transfer-Encoding: chunked wins and Content-Length must be ignored. Chunked framing carries its own explicit end marker (a zero-length final chunk), so the length is redundant.

Why dangerous: if a front-end proxy honors Content-Length while the back-end honors Transfer-Encoding (or vice versa), they disagree on where the message ends. An attacker can smuggle a hidden request into the "leftover" bytes — this is HTTP request smuggling. The fix: never send both; strip one at the proxy.

Exercise 5.3 (capstone)

Two designs move 30 small resources over a 100 ms-RTT link. Design A = HTTP/1.0 (new connection each). Design B = HTTP/1.1 persistent + serial. Compute both times with the toy model, the saving, and then explain in one sentence what a third design (HTTP/2 multiplexing) would improve that Design B still cannot.

Recall Solution 5.3

, RTT ms. Design B still sends requests serially on one ordered stream, so a slow resource blocks the queue (head-of-line blocking). HTTP-2 multiplexing interleaves independent streams on the single connection, so no one response can stall the rest.


Recall Self-test wrap-up

Which level tested framing on persistent connections? ::: Level 2 (Ex 2.2) and Level 5 (Ex 5.2). What is the closed-form RTT saving of persistent vs 1.0? ::: . Byte count of the body {"name":"Ada"}? ::: 14 bytes. What survives pipelining but dies under HTTP/2 multiplexing? ::: Head-of-line blocking.