httpc

package module
v0.0.0-...-f966a2f Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 28, 2024 License: LGPL-2.1 Imports: 15 Imported by: 1

README

httpc

httpc is a simple http client that allows the consumer to build simple http calls and apply intelligent behavior driven backoffs.

GET example with JSON decoder

// server echos body and adds HTTP method name

type foo struct {
	Name   string
	Thing  string
	Method string
}

client := httpc.New(doer)

var fooResp foo
err := client.
    GET("/foo").
    Success(httpc.StatusOK()).
    Decode(httpc.JSONDecode(&fooResp)).
    Do(context.TODO())
mustNoError(t, err)

fmt.Printf("name=%q thing=%q method=%q", foo.Name, foo.Thing)
// Output:
// name="fooName" thing="fooThing" method="GET"

POST example with JSON encoder/decoder

// server echos body and adds HTTP method name

type foo struct {
	Name   string
	Thing  string
	Method string
}

client := httpc.New(doer)

expected := foo{Name: "name", Thing: "thing"}
var fooResp foo
err := client.
    POST(svr.URL + "/foo").
    Body(expected).
    Success(httpc.StatusOK()).
    Decode(httpc.JSONDecode(&fooResp)).
    Do(context.TODO())
mustNoError(t, err)

fmt.Printf("name=%q thing=%q method=%q", foo.Name, foo.Thing, foo.Method)
// Output:
// name="name" thing="thing" method="POST"

PUT example with GOB encoder/decoder

// server echos body and adds HTTP method name

type foo struct {
	Name   string
	Thing  string
	Method string
}

client := httpc.New(doer, httpc.Encode(httpc.GobEncode()))

expected := foo{Name: "name", Thing: "thing"}
var fooResp foo
err := client.
    PUT(svr.URL + "/foo").
    Body(expected).
    Success(httpc.StatusOK()).
    Decode(httpc.GobDecode(&fooResp)).
    Do(context.TODO())
mustNoError(t, err)

fmt.Printf("name=%q thing=%q method=%q", foo.Name, foo.Thing, foo.Method)
// Output:
// name="name" thing="thing" method="PUT"

Backoffs

Backoffs are applied via a policy established by the client (defaults for request) or the backoff provided via the Request's Backoff method.

The backoffs run as long as the response is retriable. The retry behavior is prescribe via the Retry method, matching on the response codes provided.

type fakeDoer struct {
	doCallCount int
	doFn        func(*http.Request) (*http.Response, error)
}

func (f *fakeDoer) Do(r *http.Request) (*http.Response, error) {
	f.doCallCount++
	return f.doFn(r)
}

func TestBackoff(t *testing.T) {
    t.Run("applies backoff on retry", func(t *testing.T) {
        doer := new(fakeDoer)
        doer.doFn = func(*http.Request) (*http.Response, error) {
            return stubResp(http.StatusNotFound), nil
        }

        boffer := httpc.NewConstantBackoff(time.Nanosecond, 3)
        client := httpc.New(doer, httpc.Backoff(boffer))

        err := client.
            DELETE("/foo").
            Success(httpc.StatusNoContent()).
            Retry(httpc.StatusNotFound()).
            Do(context.TODO())
        mustError(t, err)

        equals(t, true, retryErr(err))
        equals(t, 3, doer.doCallCount)
    })

    t.Run("does not backoff on non retry error", func(t *testing.T) {
        doer := new(fakeDoer)
        doer.doFn = func(*http.Request) (*http.Response, error) {
            return stubResp(http.StatusInternalServerError), nil
        }

        boffer := httpc.NewConstantBackoff(time.Nanosecond, 10)
        client := httpc.New(doer, httpc.Backoff(boffer))

        err := client.
            GET("/foo").
            Success(httpc.StatusOK()).
            Retry(httpc.StatusNotFound()).
            Do(context.TODO())
        mustError(t, err)

        equals(t, false, retryErr(err))
        equals(t, 1, doer.doCallCount)
    })
}

func retryErr(err error) bool {
	type retrier interface {
		Retry() bool
	}
	r, ok := err.(retrier)
	return ok && r.Retry()
}

func equals(t *testing.T, expected interface{}, actual interface{}) {
	t.Helper()
	if expected == actual {
		return
	}
	t.Errorf("expected: %v\tgot: %v", expected, actual)
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var ErrInvalidEncodeFn = errors.New("no encode fn provided for body")

ErrInvalidEncodeFn is an error that is returned when calling the Request Do and the encode function is not set.

Functions

func Attempt

func Attempt(ctx context.Context) (int, bool)

Attempt returns the backoff attempt that is currently in motion.

func NewClientErr

func NewClientErr(opts ...ErrOptFn) error

NewClientErr is a constructor for a client error. The provided options allow the caller to set an optional retry.

Types

type AuthFn

type AuthFn func(*http.Request) *http.Request

AuthFn adds authorization to an http request.

func BasicAuth

func BasicAuth(user, pass string) AuthFn

BasicAuth sets the basic authFn on the request.

func BearerTokenAuth

func BearerTokenAuth(token string) AuthFn

BearerTokenAuth sets the token authentication on the request.

type BackoffFunc

type BackoffFunc func(retry int) (time.Duration, bool)

BackoffFunc specifies the signature of a function that returns the time to wait before the next call to a resource. To stop retrying return false in the 2nd return value.

type BackoffOptFn

type BackoffOptFn func() Backoffer

BackoffOptFn is a constructor func for a backoff.

func NewConstantBackoff

func NewConstantBackoff(interval time.Duration, maxCalls int) BackoffOptFn

NewConstantBackoff returns a new ConstantBackoff.

func NewExponentialBackoff

func NewExponentialBackoff(initialTimeout, maxTimeout time.Duration, maxCalls int) BackoffOptFn

NewExponentialBackoff returns a ExponentialBackoff backoff policy. Use initialTimeout to set the first/minimal interval and maxTimeout to set the maximum wait interval.

func NewSimpleBackoff

func NewSimpleBackoff(maxCalls int, jitter bool, ticks ...int) BackoffOptFn

NewSimpleBackoff creates a SimpleBackoff algorithm with the specified list of fixed intervals in milliseconds.

func NewStopBackoff

func NewStopBackoff() BackoffOptFn

func NewZeroBackoff

func NewZeroBackoff(maxCalls int) BackoffOptFn

NewZeroBackoff creates a zero backoff with max set calls. When set to 0, will backoff without end.

type Backoffer

type Backoffer interface {
	// Next implements a BackoffFunc.
	Next(retry int) (time.Duration, bool)
}

Backoffer allows callers to implement their own Backoffer strategy.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the httpc client. The client sets the default backoff and encode func on the request that are created when making an http call. Those defaults can be overridden in the request builder.

func New

func New(doer Doer, opts ...ClientOptFn) *Client

New returns a new client.

func (*Client) Connect

func (c *Client) Connect(addr string) *Request

Connect makes a connect http request.

func (*Client) Delete

func (c *Client) Delete(addr string) *Request

Delete makes a delete http request.

func (*Client) Get

func (c *Client) Get(addr string) *Request

Get makes a get http request.

func (*Client) Head

func (c *Client) Head(addr string) *Request

HEAD makes a head http request.

func (*Client) Options

func (c *Client) Options(addr string) *Request

Options makes a options http request.

func (*Client) Patch

func (c *Client) Patch(addr string) *Request

Patch makes a patch http request.

func (*Client) Post

func (c *Client) Post(addr string) *Request

Post makes a post http request.

func (*Client) Put

func (c *Client) Put(addr string) *Request

Put makes a put http request.

func (*Client) Req

func (c *Client) Req(method, addr string) *Request

Req makes an http request.

type ClientOptFn

type ClientOptFn func(Client) Client

ClientOptFn sets keys on a client type.

func WithAuth

func WithAuth(authFn AuthFn) ClientOptFn

WithAuth sets the authorization func on the client type, and will be used as the default authFn for all requests from this client unless overwritten atn the request lvl.

func WithBackoff

func WithBackoff(b BackoffOptFn) ClientOptFn

WithBackoff sets teh backoff on the client.

func WithBaseURL

func WithBaseURL(baseURL string) ClientOptFn

WithBaseURL sets teh base url for all requests. Any path provided will be appended to this WithBaseURL.

func WithContentType

func WithContentType(cType string) ClientOptFn

WithContentType sets content type that will be applied to all requests.

func WithEncoder

func WithEncoder(fn EncodeFn) ClientOptFn

WithEncoder sets the encode func for the client.

func WithHeader

func WithHeader(key, value string) ClientOptFn

WithHeader sets headers that will be applied to all requests.

type ConstantBackoff

type ConstantBackoff struct {
	// contains filtered or unexported fields
}

ConstantBackoff is a backoff policy that always returns the same delay.

func (*ConstantBackoff) Next

func (b *ConstantBackoff) Next(retry int) (time.Duration, bool)

Next implements BackoffFunc for ConstantBackoff.

type DecodeFn

type DecodeFn func(r io.Reader) error

DecodeFn is a decoder func.

func GobDecode

func GobDecode(v interface{}) DecodeFn

GobDecode sets the client's decodeFn to a gob decoder.

func JSONDecode

func JSONDecode(v interface{}) DecodeFn

JSONDecode sets the client's decodeFn to a json decoder.

type Doer

type Doer interface {
	Do(*http.Request) (*http.Response, error)
}

Doer is an abstraction around a http client.

type EncodeFn

type EncodeFn func(interface{}) (io.Reader, error)

EncodeFn is an encoder func.

func GobEncode

func GobEncode() EncodeFn

GobEncode sets the client's encodeFn to a gob encoder.

func JSONEncode

func JSONEncode() EncodeFn

JSONEncode sets the client's encodeFn to a json encoder.

type ErrOptFn

type ErrOptFn func(o errOpt) errOpt

ErrOptFn is a optional parameter that allows one to extend a client error.

func Err

func Err(err error) ErrOptFn

func Exists

func Exists() ErrOptFn

Exists sets the client error to Exists, exists=true.

func NotFound

func NotFound() ErrOptFn

NotFound sets the client error to NotFound, notFound=true.

func Op

func Op(c string) ErrOptFn

func Resp

func Resp(resp *http.Response) ErrOptFn

func Retry

func Retry() ErrOptFn

Retry sets the option and subsequent client error to retriable, retry=true.

type ExponentialBackoff

type ExponentialBackoff struct {
	// contains filtered or unexported fields
}

ExponentialBackoff implements the simple exponential backoff described by Douglas Thain at http://dthain.blogspot.de/2009/02/exponential-backoff-in-distributed.html.

func (*ExponentialBackoff) Next

func (b *ExponentialBackoff) Next(retry int) (time.Duration, bool)

Next implements BackoffFunc for ExponentialBackoff.

type HTTPErr

type HTTPErr struct {
	// contains filtered or unexported fields
}

HTTPErr is an error type that provides useful error messages that include both request and response bodies, status code of response and valid request parameters.

func (*HTTPErr) BackoffMessage

func (e *HTTPErr) BackoffMessage() string

BackoffMessage provides a condensed error message that can be consumed during a backoff loop.

func (*HTTPErr) Error

func (e *HTTPErr) Error() string

Error returns the full client error message.

func (*HTTPErr) Exists

func (e *HTTPErr) Exists() bool

Exists provides the Exister behavior.

func (*HTTPErr) NotFound

func (e *HTTPErr) NotFound() bool

NotFound provides the NotFounder behavior.

func (*HTTPErr) Retry

func (e *HTTPErr) Retry() bool

Retry provides the retry behavior.

type Request

type Request struct {
	Method, Addr string
	// contains filtered or unexported fields
}

Request is built up to create an http request.

func (*Request) Auth

func (r *Request) Auth(authFn AuthFn) *Request

Auth sets the authorization for hte request, overriding the authFn set by the client.

func (*Request) Backoff

func (r *Request) Backoff(b BackoffOptFn) *Request

Backoff sets the backoff of the Request.

func (*Request) Body

func (r *Request) Body(v interface{}) *Request

Body sets the body of the Request.

func (*Request) ContentType

func (r *Request) ContentType(cType string) *Request

ContentType sets the content type for the outgoing request.

func (*Request) Decode

func (r *Request) Decode(fn DecodeFn) *Request

Decode sets the decoder func for the Request.

func (*Request) DecodeJSON

func (r *Request) DecodeJSON(v interface{}) *Request

DecodeJSON is a shorthand for decoding JSON response body.

func (*Request) Do

func (r *Request) Do(ctx context.Context) error

Do makes the http request and applies the backoff.

func (*Request) Exists

func (r *Request) Exists(fn StatusFn) *Request

Exists appends a exists func to the Request.

func (*Request) Header

func (r *Request) Header(key, value string) *Request

Header adds a header to the request.

func (*Request) Headers

func (r *Request) Headers(key, value string, pairs ...string) *Request

Headers allows a user to set multiple query params at one time. Following the same rules as Header. If a key is provided without a value, it will not be added to the request. If it is desired, pass in "" to add a header with no value.

func (*Request) NotFound

func (r *Request) NotFound(fn StatusFn) *Request

NotFound appends a not found func to the Request.

func (*Request) OnError

func (r *Request) OnError(fn DecodeFn) *Request

OnError provides a decode hook to decode a responses body. Applied when the response's status code does not match the expected.

func (*Request) QueryParam

func (r *Request) QueryParam(key, value string) *Request

QueryParam allows a user to set query params on their request. This can be called numerous times. Will add keys for each value that is passed in here. In the case of duplicate query param values, the last pair that is entered will be set and the former will not be available.

func (*Request) QueryParams

func (r *Request) QueryParams(key, value string, pairs ...string) *Request

QueryParams allows a user to set multiple query params at one time. Following the same rules as QueryParam. If a key is provided without a value, it will not be added to the request. If it is desired, pass in "" to add a query param with no value.

func (*Request) Retry

func (r *Request) Retry(fn RetryFn) *Request

Retry sets the retry policy(s) on the request.

func (*Request) Success

func (r *Request) Success(fn StatusFn) *Request

Success appends a success func to the Request.

type ResponseErrorFn

type ResponseErrorFn func(error) error

ResponseErrorFn is a response error function that can be used to provide behavior when a response fails to "Do".

type RetryFn

type RetryFn func(*Request) *Request

RetryFn will apply a retry policy to a request.

func RetryResponseError

func RetryResponseError(fn ResponseErrorFn) RetryFn

RetryResponseError applies a retry on all response errors. The errors typically associated with request timeouts or oauth token error. This option useful when the oauth auth made me invalid or a request timeout is an issue.

func RetryStatus

func RetryStatus(fn StatusFn) RetryFn

RetryStatus appends a retry func to the Request.

type SimpleBackoff

type SimpleBackoff struct {
	sync.Mutex
	// contains filtered or unexported fields
}

SimpleBackoff takes a list of fixed values for backoff intervals. Each call to Next returns the next value from that fixed list. After each value is returned, subsequent calls to Next will only return the last element. The values are optionally "jittered" (off by default).

func (*SimpleBackoff) Next

func (b *SimpleBackoff) Next(retry int) (time.Duration, bool)

Next implements BackoffFunc for SimpleBackoff.

type StatusFn

type StatusFn func(statusCode int) bool

StatusFn is func for comparing expected status code against an expected status code.

func StatusCreated

func StatusCreated() StatusFn

StatusCreated compares the response's status code to match Status Created.

func StatusIn

func StatusIn(status ...int) StatusFn

StatusIn checks whether the response's status code matches at least 1 of the input status codes provided.

func StatusInRange

func StatusInRange(low, high int) StatusFn

StatusInRange checks the response's status code is in in the range provided.

func StatusInternalServerError

func StatusInternalServerError() StatusFn

StatusInternalServerError compares the response's status code to match Status Internal Server Error.

func StatusNoContent

func StatusNoContent() StatusFn

StatusNoContent compares the response's status code to match Status No Content.

func StatusNot

func StatusNot(status int) StatusFn

StatusNot compares the response's status code to verify it does match the status code input.

func StatusNotFound

func StatusNotFound() StatusFn

StatusNotFound compares the response's status code to match Status Not Found.

func StatusNotIn

func StatusNotIn(status ...int) StatusFn

StatusNotIn checks whether the response's status code does match any of the input status codes provided.

func StatusOK

func StatusOK() StatusFn

StatusOK compares the response's status code to match Status OK.

func StatusUnprocessableEntity

func StatusUnprocessableEntity() StatusFn

StatusUnprocessableEntity compares the response's status code to match Status Unprocessable Entity.

type StopBackoff

type StopBackoff struct{}

StopBackoff is a fixed backoff policy that always returns false for Next(), meaning that the operation should never be retried.

func (StopBackoff) Next

func (b StopBackoff) Next(retry int) (time.Duration, bool)

Next implements BackoffFunc for StopBackoff.

type ZeroBackoff

type ZeroBackoff struct {
	// contains filtered or unexported fields
}

ZeroBackoff is a fixed backoff policy whose backoff time is always zero, meaning that the operation is retried immediately without waiting, indefinitely.

func (ZeroBackoff) Next

func (b ZeroBackoff) Next(retry int) (time.Duration, bool)

Next implements BackoffFunc for ZeroBackoff.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL