github.com/karrick/gogetter

gogetter wraps http.Client to allow composing http.Client's Get functionality


License
MIT
Install
go get github.com/karrick/gogetter

Documentation

gogetter

Small Go library for wrapping http.Client's Get method.

Usage

Documentation is available via GoDoc.

Description

One of the strength's of Go's interface system is that it allows easy composability of functionality through interfaces. What happens if you want to slightly modify the behavior of http.Client's Get method? In Go you would create a composing function that has an identical API that can be inserted between http.Client and the code that requires this additional functionality.

Gogetter is a collection of small wrappers that can be composed to provide additional standard fucnctionality around the http.Client's Get method.

Supported Use Cases

While the objects in this library allow easy wrapping of http.Client, don't forget that you can customize the base http.Client instance for your application. For instance, the below example configures each http.Client instance to attempt to re-use connections with the Keep-Alive header, and provide a request timeout.

    // convert remote address to Getter
    func addr2getter(addr string) gogetter.Getter {
        return &http.Client{
            Transport: &http.Transport{
                MaxIdleConnsPerHost: int(maxConns),
            },
            Timeout: time.Duration(timeout),
        }
    }
Prefixer

Especially when using a round-robin collection of servers, the final URL used by http.Client's Get method must be modified by prefixing the URL with the address of the remote machine. That's where Prefixer becomes handy.

Recall that Getters wrap each other, and the base Getter is simply an http.Client instance, configured as your appliction requires.

    // convert remote address to Getter
    func addr2getter(addr string) gogetter.Getter {
        return &gogetter.Prefixer{
            Prefix: fmt.Sprintf("http://%s/some/resource/route?", addr),
            // NOTE: customize http.Client as desired:
            Getter: &http.Client{
                Transport: &http.Transport{
                    MaxIdleConnsPerHost: int(maxConns),
                },
                Timeout: time.Duration(timeout),
            },
        }
    }
RoundRobin

Perhaps you'd like your application to round-robin HTTP GET calls to a collection of different servers providing this interface when there is no actual load balancer configured. RoundRobin will send successive Get invocations to different underlying http.Client instances.

    func addrs2getter(addrs []string) (gogetter.Getter, error) {
        if len(hostnames) == 0 {
            return nil, fmt.Errorf("cannot create Getter without at least one server address")
        }

        var getter gogetter.Getter

        if len(hostnames) == 1 {
            getter = addr2getter(hostnames[0])
        } else {
            rr := &gogetter.RoundRobin{}
            for _, hostname := range hostnames {
                rr.Getters = append(rr.Getters, addr2getter(hostname))
            }
            getter = rr
        }

        return getter, nil
    }
Retrier

Perhaps you'd like to conditionally retry failed HTTP GET calls for certain network or connection errors automatically. Normally your application would check the error type, and if the error type was temporary, it would re-issue the GET method. The Retrier allows your application to encode your retry business logic into a function, and this library will retry those errors for you.

    func addrs2getter() {
        // (as above)

        // set 'retryCount' to a positive number if you'd like to retry on errors; set it to 0 to
        // send queries only once
        retryCount := 10
        if retryCount > 0 {
            getter = &gogetter.Retrier{
                Getter:     getter,
                RetryCount: retryCount,
            }
        }

        return getter, nil
    }
Failer

This wrapper is designed for testing purposes to similate random failed http.Get invocations by returning an error. You would never use this in production code.

    ...

    getter = &Failure{Frequency: 0.9, Getter: getter}

    ...