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 ¶
- Constants
- Variables
- func DefaultResponseTester(resp *http.Response, err error) (bool, error)
- func ExampleRetryAfterRequester()
- func HTTPClient() *http.Client
- func NewErrorHTTPResponse(resp *http.Response) error
- func NewErrorHTTPResponseLimitBody(resp *http.Response, limit int64) error
- func NewRetryableRequest(method string, url string, body interface{}) (*http.Request, error)
- func TryCount(req *http.Request) uint
- func WithTryCount(req *http.Request, try uint) *http.Request
- type AuthorizedRequester
- type Authorizer
- type Backoff
- type ErrorHTTPResponse
- type Limiter
- type LoggingRequester
- type Requester
- type ResponseTester
- type RetryAfterRequester
- type RetryBackoffRequester
- type ThrottledRequester
Examples ¶
Constants ¶
const (
// DefaultRetryAttempts is used when no attempt count is given
DefaultRetryAttempts = 10
)
const ( // ErrorHTTPResponseBodyBytes is the default maximum amount of bytes read from a response when constructing an HTTP Error ErrorHTTPResponseBodyBytes = int64(1000000) // 1MB )
Variables ¶
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 ¶
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 ¶
HTTPClient makes fresh `http.Client` that mimics `http.DefaultClient`
func NewErrorHTTPResponse ¶
NewErrorHTTPResponse converts a http response into an error. This will read the entire response body.
func NewErrorHTTPResponseLimitBody ¶
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 ¶
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.
Types ¶
type AuthorizedRequester ¶
type AuthorizedRequester struct { Requester Authorizer }
AuthorizedRequester adds authorization to outgoing requests
type Authorizer ¶
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 ¶
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 ¶
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:
type Requester ¶
Requester executes HTTP requests and returns a response An error is returned if the request could not be made
type ResponseTester ¶
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
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:
type ThrottledRequester ¶
ThrottledRequester waits before sending a request using the associated Waiter