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.
Serialize a full request - request line then header block - to exact bytes ending in the blank line.
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.
// 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()}