rest

package module
v0.0.1 Latest Latest
Warning

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

Go to latest
Published: Apr 12, 2019 License: Apache-2.0 Imports: 13 Imported by: 1

Documentation

Overview

Package rest is a thin wrapper around the standard `net/http` http calls with composable types implementing `Requester` to make robust calling of REST APIs easier.

Index

Examples

Constants

View Source
const (
	// DefaultRetryAttempts is used when no attempt count is given
	DefaultRetryAttempts = 10
)
View Source
const (

	// ErrorHTTPResponseBodyBytes is the default maximum amount of bytes read from a response when constructing an HTTP Error
	ErrorHTTPResponseBodyBytes = int64(1000000) // 1MB
)

Variables

View Source
var ErrBodyNotReReadable = errors.New(errPrefix + "body is not re-readable")

ErrBodyNotReReadable is returned by NewRetryableRequest when the given body cannot be re-read

Functions

func DefaultResponseTester

func DefaultResponseTester(resp *http.Response, err error) (bool, error)

DefaultResponseTester returns true if the response status code is between 200 and 399 (inclusive)

func ExampleRetryAfterRequester

func ExampleRetryAfterRequester()

ExampleRetryAfterRequester uses a requester that will detect a Retry-After header and retry the request after the provided number of seconds.

func HTTPClient

func HTTPClient() *http.Client

HTTPClient makes fresh `http.Client` that mimics `http.DefaultClient`

func NewErrorHTTPResponse

func NewErrorHTTPResponse(resp *http.Response) error

NewErrorHTTPResponse converts a http response into an error. This will read the entire response body.

func NewErrorHTTPResponseLimitBody

func NewErrorHTTPResponseLimitBody(resp *http.Response, limit int64) error

NewErrorHTTPResponseLimitBody converts a http response into an error. It can limit the number of bytes that are read from the response body. If the limit is <= 0, then there is no limit, and the entire body will be read.

func NewRetryableRequest

func NewRetryableRequest(method string, url string, body interface{}) (*http.Request, error)

NewRetryableRequest helps create http requests that can be retried It is preferable to give a body which implements `Len() int` Known Content-Length: - `nil` - `*bytes.Buffer` - `*bytes.Reader` - `*strings.Reader` - `string` - `[]rune` - `[]byte` - `encoding.BinaryMarshaler` - `encoding.TextMarshaler`

Potentially unknown Content-Length:

  • `io.ReadSeeker`
  • `func() (io.ReadCloser, error)`
  • `io.Reader`: This is tested last. This will read the entire body into a buffer and use that buffer for repeating requests. this is only acceptable for small bodies.

func TryCount

func TryCount(req *http.Request) uint

TryCount extracts the retry count from the request context

try := TryCount(req)

func WithTryCount

func WithTryCount(req *http.Request, try uint) *http.Request

WithTryCount sets try count in the request context

req = WithTryCount(req, uint(1))

Types

type AuthorizedRequester

type AuthorizedRequester struct {
	Requester
	Authorizer
}

AuthorizedRequester adds authorization to outgoing requests

func (*AuthorizedRequester) Do

Do applies authorization to the request before executing it

type Authorizer

type Authorizer func(req *http.Request) (*http.Request, error)

Authorizer adds authorization information to a request. Authorize receives an unauthorized http request and returns an http request with authorization information added. the request may be the same object, or it might be a new request.

func BasicAuthorizer

func BasicAuthorizer(user string, pass string) Authorizer

BasicAuthorizer creates an Authorizer for HTTP Basic Auth https://en.wikipedia.org/wiki/Basic_access_authentication

func HeaderAuthorizer

func HeaderAuthorizer(key string, value string) Authorizer

HeaderAuthorizer creates an Authorizer for arbitrary header values This is used for apis that implement a static auth token Like `X-Auth-Token: 4200322b-2073-4e85-9186-959b2f4c49d1`

type Backoff

type Backoff func(try uint) time.Duration

Backoff policy implements this interface which returns the duration that should be waited between each try at a given 'try' number

type ErrorHTTPResponse

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

ErrorHTTPResponse contains an error response from an HTTP request

func (*ErrorHTTPResponse) Body

func (e *ErrorHTTPResponse) Body() []byte

Body returns the body of the response read (up to 1MB)

func (*ErrorHTTPResponse) Error

func (e *ErrorHTTPResponse) Error() string

Error returns a string representation of the http error response

func (*ErrorHTTPResponse) Status

func (e *ErrorHTTPResponse) Status() int

Status returns the http status code of the error response

type Limiter

type Limiter interface {
	Delay() time.Duration
}

Limiter calculates the delay between successive requets. Implementers of Limiter can be stateful, and should expect that Delay is called before the request is made.

type LoggingRequester

type LoggingRequester struct {
	// RequestLogger is given the request before it is executed
	RequestLogger func(*http.Request)
	// ResponseLogger may log the response
	ResponseLogger func(*http.Response, *http.Request, time.Duration)
	// Requester to call with the wrapped request. If nil, then Do will panic
	Requester Requester
}

LoggingRequester may log the request and response

Example
rr := RetryAfterRequester{
	// Log requests and responses at debug level
	Requester: LoggingRequester{
		// Log the message 'http request'
		RequestLogger: func(req *http.Request) {
			log.Printf("request made to %s", req.URL)
		},
		// Log the message 'http response' on each response
		ResponseLogger: func(resp *http.Response, req *http.Request, dur time.Duration) {
			log.Printf("response returned: %s after %v", resp.Status, dur)
		},
		Requester: HTTPClient(),
	},
}
req, _ := NewRetryableRequest("POST", "http://example.com", "hello")
resp, err := rr.Do(req)
if err != nil {
	log.Println("error executing request: ", err)
	return
}
defer resp.Body.Close()
rb, err := ioutil.ReadAll(resp.Body)
if err != nil {
	log.Println("error reading body: ", err)
	return
}
log.Println("body:", string(rb))
Output:

func (LoggingRequester) Do

Do executes the request and logs requests / responses

type Requester

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

Requester executes HTTP requests and returns a response An error is returned if the request could not be made

type ResponseTester

type ResponseTester func(*http.Response, error) (bool, error)

ResponseTester tests the results returned by Requester.Do If the response is successful, it will return true, otherwise it returns false. If an error is returned, the response is considered a fatal error and will not be retried

func ResponseTesterStatusCodes

func ResponseTesterStatusCodes(tester ResponseTester, sc map[int]bool) ResponseTester

ResponseTesterStatusCodes wraps a `ResponseTester` that will test status codes If the response exists, it return the corresponding boolean that matches the status code in the map provided If there is no match, it will delegate to the given `tester` If no `tester` was given:

  • Request errors are returned immediately without testing the response code (it is assumed that response is nil in this case)
  • Unmatched response codes will convert the entire response to `ErrorHTTPResponse`. Errors converted in this way will read up to `ErrorHTTPResponseBodyBytes` bytes from the response and close the response body.

type RetryAfterRequester

type RetryAfterRequester struct {
	// StatusCodes that this requester will retry on, given a Retry-After header is present.
	// If this is not set, it defaults to `[]int{http.StatusTooManyRequests}`
	StatusCodes []int
	// HeaderName from which the delay in seconds will be read.
	// If the header name is missing, the request will not be retried
	// If this is unset, it defaults to "Retry-After"
	HeaderName string
	// Requester makes the actual http request
	// This must be set
	Requester Requester
}

RetryAfterRequester will retry requests

func (RetryAfterRequester) Do

Do performs a request

type RetryBackoffRequester

type RetryBackoffRequester struct {
	// Attempts is the number of times a request will be re-tried in addition to the initial attempt
	Attempts uint
	// Backoff is the strategy used to delay between attempts
	Backoff Backoff
	// ResponseTester is a function that returns true if the request should be retried
	ResponseTester ResponseTester
	// Requester performs the actual request for each attempt
	Requester Requester
}

RetryBackoffRequester performs a request with retries, using the provided backoff strategy between each attempt

Example
rr := RetryBackoffRequester{
	Attempts: 10,
	Backoff: func(try uint) time.Duration {
		return 100 * time.Millisecond
	},
	ResponseTester: ResponseTesterStatusCodes(DefaultResponseTester, map[int]bool{
		// Fail but Retryable
		http.StatusServiceUnavailable:  false,
		http.StatusInternalServerError: false,
		http.StatusBadGateway:          false,
		http.StatusGatewayTimeout:      false,
		http.StatusTooManyRequests:     false,

		// Success
		http.StatusOK:        true,
		http.StatusNoContent: true,
	}),
	Requester: HTTPClient(),
}
req, _ := NewRetryableRequest("POST", "http://example.com", "hello")
resp, err := rr.Do(req)
if err != nil {
	log.Println("error executing request: ", err)
	return
}
defer resp.Body.Close()
rb, err := ioutil.ReadAll(resp.Body)
if err != nil {
	log.Println("error reading body: ", err)
	return
}
log.Println("body:", string(rb))
Output:

func (RetryBackoffRequester) Do

Do performs a request

type ThrottledRequester

type ThrottledRequester struct {
	Limiter   Limiter
	Requester Requester
}

ThrottledRequester waits before sending a request using the associated Waiter

func (ThrottledRequester) Do

func (r ThrottledRequester) Do(req *http.Request) (resp *http.Response, err error)

Do request with throttling

Jump to

Keyboard shortcuts

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