🏗️More into requests

Maps were way too expensive to work with. Linear search was the real winner.

Key-value storage

Headers, parameters, dynamic path wildcards, cookies — almost every key-value storage uses the same underlying implementation — *kv.Storage. Which in fact makes life a bit easier: the same set of methods is available everywhere. Here's a quick demo of what we can actually do with the storage:

func myHandler(req *http.Request) *http.Response {
    if !req.Headers.Has("some-header") {
        return http.Error(req, status.ErrForbidden)
    }
    
    header, found := req.Headers.Lookup("some-header")
    // or:
    header = req.Headers.Value("some-header")
    // get all the header values:
    values := slices.Collect(req.Headers.Values("some-header"))
    // get all the headers names contained:
    keys := slices.Collect(req.Headers.Keys())
    // headers object will be re-used on a next request.
    // To use it after current request is processed,
    // you must clone it:
    headersCopy := req.Headers.Clone()
    // ...and a plenty of others, whose names are pretty
    // self-describing. Sorry:(
} 

There's slices.Collect() used on some calls. That's because otherwise we would need to store and handle countless buffers, so instead iterators are returned.

Hijacking

Hijacking a connection returns the net.Client, which wraps the connection, and an error. An error is returned, because during the hijacking the request's body is drained automatically. The reason hjiacking returns the net.Client is, it implements a pushback - extra data read from the connection is stored back in order to be read the next time.

func hijacker(request *http.Request) *http.Response {
    client, err := request.Hijack()
    if err != nil {
        return http.Error(request, err)
    }
    
    pending := client.Pending()
    conn := client.Conn()
    ...
    
    return nil
}

Please, note that there is no need to close the connection. It'll be closed automatically as the handler exits.

Last updated