build-an-http-client / lesson-12.md
Lesson 12 · Building a request

Serialize the whole request

You have a request line and a header block; today you join them into the complete bytes of a request with no body - the full message a server reads before any content.

The goal

Serialize a full request - request line then header block - to exact bytes ending in the blank line.

Start here - the target
TO DO
Scenario: Writing a complete bodyless request
Givena GET request for "http://example.com/a" with its Host header derived
Whenthe whole request is serialized
Thenthe bytes are exactly "GET /a HTTP/1.1\r\nHost: example.com\r\n\r\n"
Andthe message ends in the blank line "\r\n\r\n" that separates headers from a (here empty) body
Background

A complete request is just the two pieces you already have stacked together: the request line, then the header block. Because the header block ends in the blank line, the whole message already terminates correctly - GET /a HTTP/1.1\r\n, then Host: example.com\r\n, then the closing \r\n. That trailing blank line is the universal HTTP boundary between headers and body; a server reads headers until it sees it.

This is the client’s first genuinely usable output: hand it a URL and it produces the exact bytes a server expects to receive. It carries no body yet - the next lesson attaches one and slots its length into the headers - but the framing is already complete and correct, which is what makes the byte-for-byte tests possible.

Make it work
// a full request is: request line, then the header block.
// the header block already ends in the blank line, so this is
// just a concatenation of the two pieces you built.
func (r *Request) Bytes() []byte {
// requestLine() + header.serialize()
}
CheckpointDONE
A request serializes to its complete wire bytes, terminated by the blank line. Commit and stop here.