We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to continue.

This lesson's interactive features are locked, please to keep using them

Add to Parse

All our tests are passing! Now let's hook it up to the state machine and watch it parse the full set of headers.

Assignment

Back in the request package, we're going to update our (r *Request) parse method to parse the headers in addition to the request line. Let's start with some new tests.

  1. // Test: Standard Headers
    reader := &chunkReader{
        data:            "GET / HTTP/1.1\r\nHost: localhost:42069\r\nUser-Agent: curl/7.81.0\r\nAccept: */*\r\n\r\n",
        numBytesPerRead: 3,
    }
    r, err := RequestFromReader(reader)
    require.NoError(t, err)
    require.NotNil(t, r)
    assert.Equal(t, "localhost:42069", r.Headers["host"])
    assert.Equal(t, "curl/7.81.0", r.Headers["user-agent"])
    assert.Equal(t, "*/*", r.Headers["accept"])
    
    // Test: Malformed Header
    reader = &chunkReader{
        data:            "GET / HTTP/1.1\r\nHost localhost:42069\r\n\r\n",
        numBytesPerRead: 3,
    }
    r, err = RequestFromReader(reader)
    require.Error(t, err)
    

There's a big change here that can be tricky: parsing the headers may need to happen successfully multiple times, unlike the request line, which we always parse in one go. This is also true of the request body, which we'll cover later. So, because both the headers and the body will need to support multiple "parses" for each "read", I did a little refactor.

I did not change my .parse method signature, but I did move a lot of the functionality to a new method. My .parse now keeps track of the totalBytesParsed, and then starts a loop:

totalBytesParsed := 0
for r.state != requestStateDone {
    n, err := r.parseSingle(data[totalBytesParsed:])
    // ...

Again, I'm doing this because the header parsing may need to happen multiple times for the same chunk of data because we can have multiple headers in the same chunk. So, I moved the switch/case logic that handles the actual parsing based on the state and moved it into a new nested method called parseSingle.

When you're done parsing all the headers, set the state to requestStateDone and return the total number of bytes parsed, just like before.

Run and submit the CLI tests.