requester

package module
v1.3.1 Latest Latest
Warning

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

Go to latest
Published: Sep 1, 2023 License: MIT Imports: 24 Imported by: 2

README

Requester

GoDoc Go Report Card Build

A.K.A "Yet Another Golang Requests Package"

Requester makes it a bit simpler to use Go's http package as a client. As an example, take a simple request, with the http package:

bodyBytes, err := json.Marshal(reqBodyStruct)
if err != nil { return err }

bodyBytes, err := json.Marshal(requestBody)
if err != nil {
   panic(err)
}

req, err := http.NewRequest("POST", "http://api.com/resources/", bytes.NewReader(bodyBytes))
if err != nil {
   panic(err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil {
   panic(err)
}

if resp.StatusCode != 201 {
   panic(errors.New("expected code 201"))
}

respBody, _ := ioutil.ReadAll(resp.Body)
var r Resource
if err := json.Unmarshal(respBody, &r); err != nil {
   panic(err)
}

fmt.Printf("%d %s %v", resp.StatusCode, string(respBody), r)

requester uses functional options to configure the request, and folds request building, execution, and response handling into a single call:

var r Resource

resp, body, err := requester.ReceiveContext(ctx, &r,
   requester.JSON(false),
   requester.Body(requestBody),
   requester.Post("http://api.com/resources/"),
   requester.ExpectCode(201),
)
if err != nil {
   panic(err)
}

fmt.Printf("%d %s %v", resp.StatusCode, string(body), r)

Installation

go get github.com/gemalto/requester

Note: this repo was moved to github.com/ThalesGroup, but the name of the module is still github.com/gemalto: that is the name you must use to install with go modules.

Features

  • Functional option pattern supports an ergonomic API
  • Options for configuring http.Client options
  • Tools for writing unit tests, like Inspector{}, MockDoer(), MockHandler(), and the httptestutil package
  • Embeddable
  • Client-side middleware (see Middleware and Doer)
  • context.Context support

Core API

The core functions are available on the package, or on instances of Requester{}.

// just build a request
Request(...Option) (*http.Request, error)
RequestContext(context.Context, ...Option) (*http.Request, error)

// build a request and execute it
Send(...Option) (*http.Response, error)
SendContext(context.Context, ...Option) (*http.Response, error)

// build and send a request, and handle the response
Receive(interface{}, ...Option) (*http.Response, []byte, error)
ReceiveContext(context.Context, interface{}, ...Option) (*http.Response, []byte, error)

Receive/ReceiveContext reads and closes the response body, returns it as a byte slice, and also attempts to unmarshal it into a target value, if one is provided. 

Each of these accept variadic functional options, which alter the request, the http.Client, or the control how to process the response. Option is defined as:

type Option interface {
   Apply(*Requester) error
}

FAQ

  • Why, when there are like, 50 other packages that do the exact same thing?

Yeah, good question. This library started as a few tweaks to https://github.com/dghubble/sling. Then it became more of a fork, then a complete rewrite, inspired by a bunch of other similar libraries.

A few things bugged me about other libraries:

  1. Some didn't offer enough control over the base http primitives, like the underlying http.Client, and all the esoteric attributes of http.Request.

  2. I wanted more control over marshaling and unmarshaling bodies, without sacrificing access to the raw body.

  3. Some libraries which offer lots more control or options also seemed to be much more complicated, or less idiomatic.

  4. Most libraries don't handle context.Contexts at all.

  5. The main thing: most other libraries use a "fluent" API, where you call methods on a builder instance to configure the request, and these methods each return the builder, making it simple to call the next method, something like this:

     req.Get("http://api.com").Header("Content-Type", "application/json").Body(reqBody)
    

    I used to like fluent APIs in other languages, but they don't feel right in Go. You typically end up deferring errors until later, so the error doesn't surface near the code that caused the error. Its difficult to mix fluent APIs with interfaces, because the concrete types tend to have lots of methods, and they all have to return the same concrete type. For the same reason, it's awkward to embed types with fluent APIs. Fluent APIs also make it hard to extend the library with additional, external options.

Requester swaps a fluent API for the functional option pattern. This hopefully keeps a fluent-like coding style, while being more idiomatic Go. Since Options are just a simple interface, it's easy to bring your own options, or contribute new options back to this library.

Also, making the options into objects improved ergonomics in a few places, like mirroring the main functions (Request(), Send(), Receive()) on the struct and the package. Options can be passed around as arguments or accumulated in slices.

Contributing

To build, be sure to have a recent go SDK, and make. Run make tools to install other dependencies. Run make to build.

Merge requests are welcome! Before submitting, please run make and make sure all tests pass and there are no linter findings.

Documentation

Overview

Package requester builds and executes HTTP requests. It's a thin wrapper around the http package with conveniences for configuring requests and processing responses.

The central, package-level functions are:

Request(...Option) (*http.Request, error)
Send(...Option) (*http.Response, error)
Receive(interface{}, ...Option) (*http.Response, []byte, error)

Context-aware variants are also available.

requester.Requester{} has the same methods. A Requester instance can be used to repeat a request, as a template for similar requests across a REST API surface, or embedded in another type, as the core of a language binding to a REST API. The exported attributes of requester.Requester{} control how it constructs requests, what client it uses to execute them, and how the responses are handled.

Most methods and functions in the package accept Options, which are functions that configure the attributes of Requesters. The package provides many options for configuring most attributes Requester.

Receive

Receive() builds a request, executes it, and reads the response body. If a target value is provided, Receive will attempt to unmarshal the body into the target value.

type Resource struct {
	Color string `json:"color"`
}

var res Resource

resp, body, err := requester.Receive(&res,
	requester.Get("http://api.com/resources/1"),
)

fmt.Println(body)      // {"color":"red"}
fmt.Println(res.Color) // red

The body of the response, if present, is always returned as a []byte, even when unmarshaling or returning an error.

By default, Receive uses the response's Content-Type header to determine how to unmarshal the response body into a struct. This can be customized by setting Requester.Unmarshaler:

reqs.Unmarshaler = &requester.XMLMarshaler(Indent:true)

Query Params

Requester.QueryParams will be merged into any query parameters encoded into the URL. For example:

reqs, _ := requester.New(
	requester.URL("http://test.com?color=red"),
)

reqs.Params().Set("flavor","vanilla")

r, _ := reqs.Request()
r.URL.String()
// Output: http://test.com?color=red&flavor=vanilla

The QueryParams() option can take a map[string]string, a map[string]interface{}, a url.Values, or a struct. Structs are marshaled into url.Values using "github.com/google/go-querystring":

type Params struct {
	Color string `url:"color"`
}

reqs, _ := requester.New(
	requester.URL("http://test.com"),
	requester.QueryParams(
		Params{Color:"blue"},
		map[string][]string{"flavor":[]string{"vanilla"}},
		map[string]string{"temp":"hot"},
		url.Values{"speed":[]string{"fast"}},
	),
	requester.QueryParam("volume","load"),
)

r, _ := reqs.Request()
r.URL.String()
// Output: http://test.com?color=blue,flavor=vanilla,temp=hot,speed=fast,volume=loud

Body

If Requester.Body is set to a string, []byte, or io.Reader, the value will be used directly as the request body:

req, _ := requester.Request(
	requester.Post("http://api.com"),
	requester.ContentType(requester.MediaTypeJSON),
	requester.Body(`{"color":"red"}`),
)

httputil.DumpRequest(req, true)

// POST / HTTP/1.1
// Host: api.com
// Content-Type: application/json
//
// {"color":"red"}

If Body is any other value, it will be marshaled into the body, using the Requester.Marshaler:

type Resource struct {
	Color string `json:"color"`
}

req, _ := requester.Request(
	requester.Post("http://api.com"),
	requester.Body(Resource{Color:"red"}),
)

httputil.DumpRequest(req, true)

// POST / HTTP/1.1
// Host: api.com
// Content-Type: application/json
//
// {"color":"red"}

Note the default Marshaler is JSON, and sets the request's Content-Type header.

HTTP Client Options

The HTTP client used to execute requests can also be customized with Options:

import "github.com/gemalto/requester/httpclient"

requester.Send(
	requester.Get("https://api.com"),
	requester.Client(httpclient.SkipVerify()),
)

"github.com/gemalto/requester/httpclient" is a standalone package for constructing and configuring http.Clients. The requester.Client(...httpclient.Option) option constructs a new HTTP client and installs it into Requester.Doer.

Doer and Middleware

Requester uses a Doer to execute requests, which is an interface. By default, http.DefaultClient is used, but this can be replaced by a customized client, or a mock Doer:

reqs.Doer = requester.DoerFunc(func(req *http.Request) (*http.Response, error) {
	return &http.Response{}
})

Requester itself is a Doer, so it can be nested in another Requester or composed with other packages that support Doers.

You can also install middleware into Requester, which can intercept the request and response:

mw := func(next requester.Doer) requester.Doer {
	return requester.DoerFunc(func(req *http.Request) (*http.Response, error) {
		fmt.Println(httputil.DumpRequest(req, true))
		resp, err := next(req)
		if err == nil {
			fmt.Println(httputil.DumpResponse(resp, true))
		}
		return resp, err
	})
}
reqs.Middleware = append(reqs.Middleware, mw)
Example
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(200)
	w.Write([]byte(`{"color":"red"}`))
}))
defer s.Close()

resp, body, _ := Receive(
	Get(s.URL),
)

fmt.Println(resp.StatusCode)
fmt.Println(string(body))
Output:

200
{"color":"red"}
Example (Everything)
type Resource struct {
	ID    string `json:"id"`
	Color string `json:"color"`
}

s := httptest.NewServer(MockHandler(201,
	JSON(true),
	Body(&Resource{Color: "red", ID: "123"}),
))
defer s.Close()

r := httptestutil.Requester(s,
	Post("/resources?size=big"),
	BearerAuth("atoken"),
	JSON(true),
	Body(&Resource{Color: "red"}),
	ExpectCode(201),
	Header("X-Request-Id", "5"),
	QueryParam("flavor", "vanilla"),
	QueryParams(&struct {
		Type string `url:"type"`
	}{Type: "upload"}),
	Client(
		httpclient.SkipVerify(true),
		httpclient.Timeout(5*time.Second),
		httpclient.MaxRedirects(3),
	),
)

r.MustApply(DumpToStderr())
httptestutil.Dump(s, os.Stderr)

serverInspector := httptestutil.Inspect(s)
clientInspector := Inspect(r)

var resource Resource

resp, body, err := r.Receive(&resource)
if err != nil {
	panic(err)
}

fmt.Println("client-side request url path:", clientInspector.Request.URL.Path)
fmt.Println("client-side request query:", clientInspector.Request.URL.RawQuery)
fmt.Println("client-side request body:", clientInspector.RequestBody.String())

ex := serverInspector.LastExchange()
fmt.Println("server-side request authorization header:", ex.Request.Header.Get("Authorization"))
fmt.Println("server-side request request body:", ex.RequestBody.String())
fmt.Println("server-side request response body:", ex.ResponseBody.String())

fmt.Println("client-side response body:", clientInspector.ResponseBody.String())

fmt.Println("response status code:", resp.StatusCode)
fmt.Println("raw response body:", string(body))
fmt.Println("unmarshaled response body:", resource)
Output:

client-side request url path: /resources
client-side request query: flavor=vanilla&size=big&type=upload
client-side request body: {
  "id": "",
  "color": "red"
}
server-side request authorization header: Bearer atoken
server-side request request body: {
  "id": "",
  "color": "red"
}
server-side request response body: {
  "id": "123",
  "color": "red"
}
client-side response body: {
  "id": "123",
  "color": "red"
}
response status code: 201
raw response body: {
  "id": "123",
  "color": "red"
}
unmarshaled response body: {123 red}
Example (Receive)
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(200)
	w.Write([]byte(`{"color":"red"}`))
}))
defer s.Close()

r := struct {
	Color string `json:"color"`
}{}

Receive(&r, Get(s.URL))

fmt.Println(r.Color)
Output:

red

Index

Examples

Constants

View Source
const (
	HeaderAccept        = "Accept"
	HeaderContentType   = "Content-Type"
	HeaderAuthorization = "Authorization"
	HeaderRange         = "Range"

	MediaTypeJSON          = "application/json"
	MediaTypeXML           = "application/xml"
	MediaTypeForm          = "application/x-www-form-urlencoded"
	MediaTypeOctetStream   = "application/octet-stream"
	MediaTypeTextPlain     = "text/plain"
	MediaTypeMultipart     = "multipart/mixed"
	MediaTypeMultipartForm = "multipart/form-data"
)

HTTP constants.

Variables

View Source
var DefaultBackoff = ExponentialBackoff{
	BaseDelay:  1.0 * time.Second,
	Multiplier: 1.6,
	Jitter:     0.2,
	MaxDelay:   120 * time.Second,
}

DefaultBackoff is a backoff configuration with the default values. nolint:gochecknoglobals

View Source
var DefaultRequester = Requester{}

DefaultRequester is the singleton used by the package-level Request/Send/Receive functions. nolint:gochecknoglobals

View Source
var DefaultRetryConfig = RetryConfig{}

DefaultRetryConfig is the default retry configuration used if nil is passed to Retry(). nolint:gochecknoglobals

Functions

func ChannelHandler added in v0.2.0

func ChannelHandler() (chan<- *http.Response, http.Handler)

ChannelHandler returns an http.Handler and an input channel. The Handler returns the http.Responses sent to the channel.

Example
in, h := ChannelHandler()

ts := httptest.NewServer(h)
defer ts.Close()

in <- &http.Response{
	StatusCode: 201,
	Body:       ioutil.NopCloser(strings.NewReader("pong")),
}

resp, body, _ := Receive(URL(ts.URL))

fmt.Println(resp.StatusCode)
fmt.Println(string(body))
Output:

201
pong

func DefaultShouldRetry added in v1.1.0

func DefaultShouldRetry(_ int, _ *http.Request, resp *http.Response, err error) bool

DefaultShouldRetry is the default ShouldRetryer. It retries the request if the error is a timeout, temporary, or EOF error, or if the status code is 429, >=500, except for 501 (Not Implemented).

func MockHandler added in v0.2.0

func MockHandler(statusCode int, options ...Option) http.Handler

MockHandler returns an http.Handler which returns responses built from the args. The Option arguments are used to build an http.Request, then the header and body of the request are copied into an http.Response object.

Example
h := MockHandler(201,
	JSON(false),
	Body(map[string]interface{}{"color": "blue"}),
)

ts := httptest.NewServer(h)
defer ts.Close()

resp, body, _ := Receive(URL(ts.URL))

fmt.Println(resp.StatusCode)
fmt.Println(resp.Header.Get(HeaderContentType))
fmt.Println(string(body))
Output:

201
application/json
{"color":"blue"}

func MockResponse added in v0.2.0

func MockResponse(statusCode int, options ...Option) *http.Response

MockResponse creates an *http.Response from the Options. Requests and Responses share most of the same fields, so we use the options to build a Request, then copy the values as appropriate into a Response. Useful for created mocked responses for tests.

func OnlyIdempotentShouldRetry added in v1.2.0

func OnlyIdempotentShouldRetry(_ int, req *http.Request, _ *http.Response, _ error) bool

OnlyIdempotentShouldRetry returns true if the request is using one of the HTTP methods which are intended to be idempotent: GET, HEAD, OPTIONS, and TRACE. Should be combined with other criteria using AllRetryers(), for example:

c.ShouldRetry = AllRetryers(ShouldRetryerFunc(DefaultShouldRetry), ShouldRetryerFunc(OnlyIdempotentShouldRetry))

func Receive

func Receive(into interface{}, opts ...Option) (*http.Response, []byte, error)

Receive uses the DefaultRequester to create a request, execute it, and read the response. The response body will be fully read and closed.

See Requester.Receive() for more details.

The first argument may be nil, an Option, or a value to unmarshal the response body into.

Example
resp, body, err := Receive(Get("http://api.com/resource"))

fmt.Println(resp.StatusCode, string(body), err)
Output:

Example (Unmarshal)
type Resource struct {
	Color string `json:"color"`
}

var r Resource

resp, body, err := Receive(&r, Get("http://api.com/resource"))

fmt.Println(resp.StatusCode, string(body), err)
Output:

func ReceiveContext

func ReceiveContext(ctx context.Context, into interface{}, opts ...Option) (*http.Response, []byte, error)

ReceiveContext does the same as Receive(), but attaches a Context to the request.

The second argument may be nil, an Option, or a value to unmarshal the response body into.

func Request

func Request(opts ...Option) (*http.Request, error)

Request uses the DefaultRequester to create a request.

See Requester.Request() for more details.

Example
req, err := Request(Get("http://api.com/resource"))

fmt.Println(req.URL.String(), err)
Output:

http://api.com/resource <nil>

func RequestContext

func RequestContext(ctx context.Context, opts ...Option) (*http.Request, error)

RequestContext does the same as Request(), but attaches a Context to the request.

func Send

func Send(opts ...Option) (*http.Response, error)

Send uses the DefaultRequester to create a request and execute it. The body will not be read or closed.

See Requester.Send() for more details.

Example
resp, err := Send(Get("http://api.com/resource"))

fmt.Println(resp.StatusCode, err)
Output:

func SendContext

func SendContext(ctx context.Context, opts ...Option) (*http.Response, error)

SendContext does the same as Send(), but attaches a Context to the request.

Types

type Backoffer added in v1.1.0

type Backoffer interface {
	Backoff(attempt int) time.Duration
}

Backoffer calculates how long to wait between attempts. The attempt argument is the attempt which just completed, and starts at 1. So attempt=1 should return the time to wait between attempt 1 and 2.

type BackofferFunc added in v1.1.0

type BackofferFunc func(int) time.Duration

BackofferFunc adapts a function to the Backoffer interface.

func (BackofferFunc) Backoff added in v1.1.0

func (b BackofferFunc) Backoff(attempt int) time.Duration

Backoff implements Backoffer

type ContentTypeUnmarshaler added in v1.0.3

type ContentTypeUnmarshaler struct {
	Unmarshalers map[string]Unmarshaler
}

ContentTypeUnmarshaler selects an unmarshaler based on the content type, which should be a valid media/mime type, in the form:

type "/" [tree "."] subtype ["+" suffix] *[";" parameter]

Unmarshalers are registered to handle a given media type. Parameters are ignored:

ct := NewContentTypeUnmarshaler()
ct.Unmarshalers["application/json"] = &JSONMarshaler{}

If the full media type has no match, but there is a suffix, it will look for an Unmarshaler registered for <type>/<suffix>. For example, if there was no match for `application/vnd.api+json`, it will look for `application/json`.

func NewContentTypeUnmarshaler added in v1.0.3

func NewContentTypeUnmarshaler() *ContentTypeUnmarshaler

NewContentTypeUnmarshaler returns a new ContentTypeUnmarshaler preconfigured to handle application/json and application/xml.

func (*ContentTypeUnmarshaler) Apply added in v1.0.3

func (c *ContentTypeUnmarshaler) Apply(r *Requester) error

Apply implements Option

func (*ContentTypeUnmarshaler) Unmarshal added in v1.0.3

func (c *ContentTypeUnmarshaler) Unmarshal(data []byte, contentType string, v interface{}) error

Unmarshal implements Unmarshaler.

If media type parsing fails, or no Unmarshaler is found, an error is returned.

type Doer

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

Doer executes http requests. It is implemented by *http.Client. You can wrap *http.Client with layers of Doers to form a stack of client-side middleware.

func Wrap

func Wrap(d Doer, m ...Middleware) Doer

Wrap applies a set of middleware to a Doer. The returned Doer will invoke the middleware in the order of the arguments.

type DoerFunc

type DoerFunc func(req *http.Request) (*http.Response, error)

DoerFunc adapts a function to implement Doer

func ChannelDoer added in v0.2.0

func ChannelDoer() (chan<- *http.Response, DoerFunc)

ChannelDoer returns a DoerFunc and a channel. The DoerFunc will return the responses send on the channel.

Example
in, d := ChannelDoer()

in <- &http.Response{
	StatusCode: 201,
	Body:       ioutil.NopCloser(strings.NewReader("pong")),
}

resp, body, _ := Receive(d)

fmt.Println(resp.StatusCode)
fmt.Println(string(body))
Output:

201
pong

func MockDoer added in v0.2.0

func MockDoer(statusCode int, options ...Option) DoerFunc

MockDoer creates a Doer which returns a mocked response, for writing tests. By default, the mocked response will contain the status code, and typical default values for some standard response fields, like the ProtoXXX fields.

Options can be passed in, which are used to construct a template http.Request. The fields of the template request are copied into the mocked responses (http.Request and http.Response share most fields, so we're leveraging the rich set of requester.Options to build the response).

Example
d := MockDoer(201,
	JSON(false),
	Body(map[string]interface{}{"color": "blue"}),
)

// Since DoerFunc is an Option, it can be passed directly to functions
// which accept Options.
resp, body, _ := Receive(d)

fmt.Println(resp.StatusCode)
fmt.Println(resp.Header.Get(HeaderContentType))
fmt.Println(string(body))
Output:

201
application/json
{"color":"blue"}

func (DoerFunc) Apply added in v0.2.0

func (f DoerFunc) Apply(r *Requester) error

Apply implements the Option interface. DoerFuncs can be used as requester options. They install themselves as the requester's Doer.

func (DoerFunc) Do

func (f DoerFunc) Do(req *http.Request) (*http.Response, error)

Do implements the Doer interface

type ExponentialBackoff added in v1.1.0

type ExponentialBackoff struct {
	// BaseDelay is the amount of time to backoff after the first failure.
	BaseDelay time.Duration
	// Multiplier is the factor with which to multiply backoffs after a
	// failed retry. Should ideally be greater than 1.  0 means no multiplier: delay
	// will be fixed (plus jitter).  This is equivalent to a Multiplier of 1.
	Multiplier float64
	// Jitter is the factor with which backoffs are randomized.  Should ideally be
	// less than 1.  If added jitter would make the delay greater than MaxDelay, the jitter
	// will be redistributed below the MaxDelay.  0 means no jitter.
	Jitter float64
	// MaxDelay is the upper bound of backoff delay.  0 means no max.
	MaxDelay time.Duration
}

ExponentialBackoff defines the configuration options for an exponential backoff strategy. The implementation is based on the one from grpc.

The zero value of this struct implements a zero backoff, i.e. no delay between retries.

Examples:

// exponential backoff.  First delay is one second, each subsequent
// delay is 1.6x higher, plus or minus %20 jitter, up to a max
// of 120 seconds.
&ExponentialBackoff{
  BaseDelay:  1.0 * time.Second,
  Multiplier: 1.6,
  Jitter:     0.2,
  MaxDelay:   120 * time.Second,
}

// no backoff
&ExponentialBackoff{}

// fixed backoff
&ExponentialBackoff{
  BaseDelay: 1 * time.Second,
}

// fixed backoff with some jitter
&ExponentialBackoff{
  BaseDelay: 1 * time.Second,
  Jitter: 0.2,
}

func ConstantBackoff added in v1.3.0

func ConstantBackoff(delay time.Duration) *ExponentialBackoff

ConstantBackoff returns a Backoffer with a fixed, constant delay between retries and no jitter.

func ConstantBackoffWithJitter added in v1.3.0

func ConstantBackoffWithJitter(delay time.Duration) *ExponentialBackoff

ConstantBackoffWithJitter returns a Backoffer with a fixed, constant delay between retries with 20% jitter.

func NoBackoff added in v1.3.0

func NoBackoff() *ExponentialBackoff

NoBackoff returns a Backoffer with zero backoff, and zero delay between retries.

func (*ExponentialBackoff) Backoff added in v1.1.0

func (c *ExponentialBackoff) Backoff(attempt int) time.Duration

type FormMarshaler

type FormMarshaler struct{}

FormMarshaler implements Marshaler. It marshals values into URL-Encoded form data.

The value can be either a map[string][]string, map[string]string, url.Values, or a struct with `url` tags.

Example
// *FormMarshaler implements Option, so it can be passed directly to functions
// which accept Options.
req, _ := Request(&FormMarshaler{}, Body(url.Values{"color": []string{"red"}}))

b, _ := ioutil.ReadAll(req.Body)

fmt.Println(string(b))
fmt.Println(req.Header.Get(HeaderContentType))
Output:

color=red
application/x-www-form-urlencoded; charset=UTF-8

func (*FormMarshaler) Apply added in v0.3.0

func (m *FormMarshaler) Apply(r *Requester) error

Apply implements Option.

func (*FormMarshaler) Marshal

func (*FormMarshaler) Marshal(v interface{}) (data []byte, contentType string, err error)

Marshal implements Marshaler.

type Inspector added in v0.2.0

type Inspector struct {

	// The last request sent by the client.
	Request *http.Request

	// The last response received by the client.
	Response *http.Response

	// The last client request body
	RequestBody *bytes.Buffer

	// The last client response body
	ResponseBody *bytes.Buffer
}

Inspector is a Requester Option which captures requests and responses. It's useful for inspecting the contents of exchanges in tests.

It not an efficient way to capture bodies, and keeps requests and responses around longer than their intended lifespan, so it should not be used in production code or benchmarks.

func Inspect added in v0.2.0

func Inspect(r *Requester) *Inspector

Inspect installs and returns an Inspector. The Inspector captures the last request, request body, response and response body. Useful in tests for inspecting traffic.

Example

Inspect returns an Inspector, which captures the traffic to and from a Requester. It's a tool for writing tests.

r := MustNew(
	MockDoer(201, Body("pong")),
	Header(HeaderAccept, MediaTypeTextPlain),
	Body("ping"),
)

i := Inspect(r)

r.Receive(nil)

fmt.Println(i.Request.Header.Get(HeaderAccept))
fmt.Println(i.RequestBody.String())
fmt.Println(i.Response.StatusCode)
fmt.Println(i.ResponseBody.String())
Output:

text/plain
ping
201
pong

func (*Inspector) Apply added in v0.2.0

func (i *Inspector) Apply(r *Requester) error

Apply implements Option

func (*Inspector) Clear added in v0.2.0

func (i *Inspector) Clear()

Clear clears the inspector's fields.

func (*Inspector) Wrap added in v0.2.0

func (i *Inspector) Wrap(next Doer) Doer

Wrap implements Middleware

type JSONMarshaler

type JSONMarshaler struct {
	Indent bool
}

JSONMarshaler implement Marshaler and Unmarshaler. It marshals values to and from JSON. If Indent is true, marshaled JSON will be indented.

r := requester.Requester{
    Body: &JSONMarshaler{},
}
Example
// *JSONMarshaler implements Option, so it can be passed directly to functions
// which accept Options.
req, _ := Request(&JSONMarshaler{Indent: false}, Body(map[string]interface{}{"color": "red"}))

b, _ := ioutil.ReadAll(req.Body)

fmt.Println(string(b))
fmt.Println(req.Header.Get(HeaderContentType))
Output:

{"color":"red"}
application/json; charset=UTF-8

func (*JSONMarshaler) Apply added in v0.3.0

func (m *JSONMarshaler) Apply(r *Requester) error

Apply implements Option.

func (*JSONMarshaler) Marshal

func (m *JSONMarshaler) Marshal(v interface{}) (data []byte, contentType string, err error)

Marshal implements Marshaler.

func (*JSONMarshaler) Unmarshal

func (m *JSONMarshaler) Unmarshal(data []byte, _ string, v interface{}) error

Unmarshal implements Unmarshaler.

type MarshalFunc

type MarshalFunc func(v interface{}) ([]byte, string, error)

MarshalFunc adapts a function to the Marshaler interface.

func (MarshalFunc) Apply added in v0.2.0

func (f MarshalFunc) Apply(r *Requester) error

Apply implements Option. MarshalFunc can be applied as a requester option, which install itself as the Marshaler.

func (MarshalFunc) Marshal

func (f MarshalFunc) Marshal(v interface{}) ([]byte, string, error)

Marshal implements the Marshaler interface.

type Marshaler

type Marshaler interface {
	Marshal(v interface{}) (data []byte, contentType string, err error)
}

Marshaler marshals values into a []byte.

If the content type returned is not empty, it will be used in the request's Content-Type header.

var DefaultMarshaler Marshaler = &JSONMarshaler{}

DefaultMarshaler is used by Requester if Requester.Marshaler is nil. nolint:gochecknoglobals

type Middleware

type Middleware func(Doer) Doer

Middleware can be used to wrap Doers with additional functionality.

Example
var m Middleware = func(next Doer) Doer {
	return DoerFunc(func(req *http.Request) (*http.Response, error) {
		d, _ := httputil.DumpRequest(req, true)
		fmt.Println(string(d))
		return next.Do(req)
	})
}

// Middleware implements Option, so it can be passed directly to functions which accept
// options
Send(m)

// ...or applied with the Use option
Send(Use(m))

// ...or applied directly
_ = Requester{
	Middleware: []Middleware{m},
}
Output:

func Dump

func Dump(w io.Writer) Middleware

Dump dumps requests and responses to a writer. Just intended for debugging.

func DumpToLog

func DumpToLog(logf func(a ...interface{})) Middleware

DumpToLog dumps the request and response to a logging function. logf is compatible with fmt.Print(), testing.T.Log, or log.XXX() functions.

logf will be invoked once for the request, and once for the response. Each invocation will only have a single argument (the entire request or response is logged as a single string value).

Example
Send(DumpToLog(func(a ...interface{}) {
	fmt.Println(a...)
}))

// compatible with the log package's functions
Send(DumpToLog(sdklog.Println))

// compatible with the testing package's function
var t *testing.T
Send(DumpToLog(t.Log))
Output:

func DumpToStderr added in v0.3.0

func DumpToStderr() Middleware

DumpToStderr dumps requests and responses to os.Stderr

func DumpToStout added in v0.2.0

func DumpToStout() Middleware

DumpToStout dumps requests and responses to os.Stdout

func ExpectCode added in v0.2.0

func ExpectCode(code int) Middleware

ExpectCode generates an error if the response's status code does not match the expected code.

The response body will still be read and returned.

Example
_, _, err := Receive(
	MockDoer(400),
	ExpectCode(201),
)

fmt.Println(err.Error())
Output:

server returned unexpected status code.  expected: 201, received: 400

func ExpectSuccessCode added in v0.2.0

func ExpectSuccessCode() Middleware

ExpectSuccessCode is middleware which generates an error if the response's status code is not between 200 and 299.

The response body will still be read and returned.

Example
_, _, err := Receive(
	MockDoer(400),
	ExpectSuccessCode(),
)

fmt.Println(err.Error())
Output:

server returned an unsuccessful status code: 400

func Retry added in v1.1.0

func Retry(config *RetryConfig) Middleware

Retry retries the http request under certain conditions. The number of retries, retry conditions, and the time to sleep between retries can be configured. If config is nil, the DefaultRetryConfig will be used.

Requests with bodies can only be retried if the request's GetBody function is set. It will be used to rewind the request body for the next attempt. This is set automatically for most body types, like strings, byte slices, string readers, or byte readers.

func (Middleware) Apply

func (m Middleware) Apply(r *Requester) error

Apply implements Option

type MultiUnmarshaler

type MultiUnmarshaler = ContentTypeUnmarshaler

MultiUnmarshaler is a legacy alias for ContentTypeUnmarshaler.

type Option

type Option interface {
	// Apply modifies the Requester argument.  The Requester pointer will never be nil.
	// Returning an error will stop applying the request of the Options, and the error
	// will float up to the original caller.
	Apply(*Requester) error
}

Option applies some setting to a Requester object. Options can be passed as arguments to most of Requester's methods.

func Accept

func Accept(accept string) Option

Accept sets the Accept header.

Example
r := MustNew(Accept(MediaTypeJSON))

fmt.Println(r.Headers().Get(HeaderAccept))
Output:

application/json

func AddHeader

func AddHeader(key, value string) Option

AddHeader adds a header value, using Header.Add()

Example
r := MustNew(
	AddHeader("color", "red"),
	AddHeader("color", "blue"),
)

fmt.Println(r.Headers()["Color"])
Output:

[red blue]

func AppendPath added in v1.0.0

func AppendPath(elements ...string) Option

AppendPath appends path elements to the end of the URL.Path. It ensures it won't create duplicate slashes between elements, and will trim out empty elements.

Unlike RelativeURL(), it doesn't matter whether elements have trailing slashes or not, all elements will still be retained in final path. RelativeURL() follows the rules of resolving relative URLs against absolute URLs, but the results are often unexpected or inconvenient. This option is better suited when simple joining a set path elements together.

For example:

baseURL = http://test.com/users/bob
+ RelativeURL(frank) = http://test.com/users/frank
+ AppendPath(frank)  = http://test.com/users/bob/frank

See ExampleAppendPath for more examples.

Example
r := MustNew(URL("http://test.com/users/bob"))

fmt.Println("RelativeURL: " + r.MustWith(RelativeURL("frank")).URL.String())
fmt.Println("AppendPath:  " + r.MustWith(AppendPath("frank")).URL.String())

fmt.Println("RelativeURL: " + r.MustWith(RelativeURL("/frank")).URL.String())
fmt.Println("AppendPath:  " + r.MustWith(AppendPath("/frank")).URL.String())

fmt.Println("RelativeURL: " + r.MustWith(RelativeURL("frank", "nicknames")).URL.String())
fmt.Println("AppendPath:  " + r.MustWith(AppendPath("frank", "nicknames")).URL.String())
Output:

RelativeURL: http://test.com/users/frank
AppendPath:  http://test.com/users/bob/frank
RelativeURL: http://test.com/frank
AppendPath:  http://test.com/users/bob/frank
RelativeURL: http://test.com/users/nicknames
AppendPath:  http://test.com/users/bob/frank/nicknames

func BasicAuth

func BasicAuth(username, password string) Option

BasicAuth sets the Authorization header to "Basic <encoded username and password>". If username and password are empty, it deletes the Authorization header.

Example
r := MustNew(BasicAuth("user", "password"))

fmt.Println(r.Header.Get(HeaderAuthorization))
Output:

Basic dXNlcjpwYXNzd29yZA==

func BearerAuth

func BearerAuth(token string) Option

BearerAuth sets the Authorization header to "Bearer <token>". If the token is empty, it deletes the Authorization header.

Example
r := MustNew(BearerAuth("1234"))

fmt.Println(r.Header.Get(HeaderAuthorization))
Output:

Bearer 1234

func Body

func Body(body interface{}) Option

Body sets the body of the request.

If the body value is a string, []byte, io.Reader, the value will be used directly as the body of the request.

If the value is nil, the request body will be empty.

If the value is anything else, Requester will use the Marshaler to marshal the value into the request body.

Example
v := struct {
	Color string `json:"color"`
}{
	Color: "red",
}

req, _ := Request(Body(v))

b, _ := ioutil.ReadAll(req.Body)

fmt.Println(string(b))
Output:

{"color":"red"}
Example (Map)

The body value doesn't need to be a struct. So long as the Marshaler can marshal it.

req, _ := Request(Body(map[string]interface{}{"color": "red"}))

b, _ := ioutil.ReadAll(req.Body)

fmt.Println(string(b))
Output:

{"color":"red"}
Example (Raw)
req, _ := Request(
	// all these are equivalent
	Body("red"),
	Body([]byte("red")),
	Body(strings.NewReader("red")),
)

b, _ := ioutil.ReadAll(req.Body)

fmt.Println(string(b))
Output:

red

func Client

func Client(opts ...httpclient.Option) Option

Client replaces Requester.Doer with an *http.Client. The client will be created and configured using the httpclient package.

Example
Send(
	URL("https://localhost:6060"),
	Client(httpclient.SkipVerify(true)),
)
Output:

func ContentType

func ContentType(contentType string) Option

ContentType sets the Content-Type header.

Example
r := MustNew(ContentType(MediaTypeTextPlain))

fmt.Println(r.Headers().Get(HeaderContentType))
Output:

text/plain

func Delete

func Delete(paths ...string) Option

Delete sets the HTTP method to "DELETE". Optional path arguments will be applied via the RelativeURL option.

Example
r := MustNew(Delete("/resources/", "1"))

fmt.Println(r.Method, r.URL.String())
Output:

DELETE /resources/1

func DeleteHeader

func DeleteHeader(key string) Option

DeleteHeader deletes a header key, using Header.Del()

Example
r := Requester{
	Header: http.Header{
		"Color":  []string{"red"},
		"Flavor": []string{"vanilla"},
	},
}

r.MustApply(DeleteHeader("color"))

fmt.Println(r.Header)
Output:

map[Flavor:[vanilla]]

func Form

func Form() Option

Form sets Requester.Marshaler to the FormMarshaler, which marshals the body into form-urlencoded. The FormMarshaler will set the Content-Type header to "application/x-www-form-urlencoded" unless explicitly overwritten.

func Get

func Get(paths ...string) Option

Get sets the HTTP method to "GET". Optional path arguments will be applied via the RelativeURL option.

Example
r := MustNew(Get("/resources/", "1"))

fmt.Println(r.Method, r.URL.String())
Output:

GET /resources/1
func Head(paths ...string) Option

Head sets the HTTP method to "HEAD". Optional path arguments will be applied via the RelativeURL option.

Example
r := MustNew(Head("/resources/", "1"))

fmt.Println(r.Method, r.URL.String())
Output:

HEAD /resources/1
func Header(key, value string) Option

Header sets a header value, using Header.Set()

Example
r := MustNew(Header("color", "red"))

fmt.Println(r.Header)
Output:

map[Color:[red]]

func Host

func Host(host string) Option

Host sets Requester.Host

Example
r, _ := Request(Host("api.com"))

fmt.Println(r.Host)
Output:

api.com

func JSON

func JSON(indent bool) Option

JSON sets Requester.Marshaler to the JSONMarshaler. If the arg is true, the generated JSON will be indented. The JSONMarshaler will set the Content-Type header to "application/json" unless explicitly overwritten.

func Method

func Method(m string, paths ...string) Option

Method sets the HTTP method (e.g. GET/DELETE/etc). If path arguments are passed, they will be applied via the RelativeURL option.

Example
r := MustNew(Method("CONNECT", "/resources/", "1"))

fmt.Println(r.Method, r.URL.String())
Output:

CONNECT /resources/1

func Patch

func Patch(paths ...string) Option

Patch sets the HTTP method to "PATCH". Optional path arguments will be applied via the RelativeURL option.

Example
r := MustNew(Patch("/resources/", "1"))

fmt.Println(r.Method, r.URL.String())
Output:

PATCH /resources/1

func Post

func Post(paths ...string) Option

Post sets the HTTP method to "POST". Optional path arguments will be applied via the RelativeURL option.

Example
r := MustNew(Post("/resources/", "1"))

fmt.Println(r.Method, r.URL.String())
Output:

POST /resources/1

func Put

func Put(paths ...string) Option

Put sets the HTTP method to "PUT". Optional path arguments will be applied via the RelativeURL option.

Example
r := MustNew(Put("/resources/", "1"))

fmt.Println(r.Method, r.URL.String())
Output:

PUT /resources/1

func QueryParam

func QueryParam(k, v string) Option

QueryParam adds a query parameter.

Example
r := MustNew(QueryParam("color", "red"))

fmt.Println(r.Params().Encode())
Output:

color=red

func QueryParams

func QueryParams(queryStructs ...interface{}) Option

QueryParams adds params to the Requester.QueryParams member. The arguments may be either map[string][]string, map[string]string, url.Values, or a struct. The argument values are merged into Requester.QueryParams, overriding existing values.

If the arg is a struct, the struct is marshaled into a url.Values object using the github.com/google/go-querystring/query package. Structs should tag their members with the "url" tag, e.g.:

type ReqParams struct {
    Color string `url:"color"`
}

An error will be returned if marshaling the struct fails.

Example
type Params struct {
	Color string `url:"color"`
}

// QueryParams option accepts several types
r := MustNew(QueryParams(
	Params{Color: "red"},                   // struct with url tags
	map[string]string{"flavor": "vanilla"}, // map[string]string
	map[string][]string{"size": {"big"}},   // map[string][]string
	url.Values{"volume": []string{"loud"}}, // url.Values
))

// params already encoded in the URL are retained
req, _ := r.Request(RelativeURL("?weight=heavy"))

fmt.Println(req.URL.RawQuery)
Output:

color=red&flavor=vanilla&size=big&volume=loud&weight=heavy

func Range added in v1.0.0

func Range(byteRange string) Option

Range sets the Range header.

func RelativeURL

func RelativeURL(paths ...string) Option

RelativeURL resolves the arg as a relative URL references against the current URL, using the standard lib's url.URL.ResolveReference() method.

Example
r := MustNew(
	Get("http://test.com/green/"),
	// See the docs for url.URL#ResolveReference for details
	RelativeURL("red/", "blue"),
)

fmt.Println(r.URL.String())
Output:

http://test.com/green/red/blue

func URL

func URL(rawurl string) Option

URL sets the request URL. Returns an error if arg is not a valid URL.

func Use

func Use(m ...Middleware) Option

Use appends middlware to Requester.Middleware. Middleware is invoked in the order added.

func WithDoer

func WithDoer(d Doer) Option

WithDoer replaces Requester.Doer. If nil, Requester will revert to using the http.DefaultClient.

func WithMarshaler added in v0.2.0

func WithMarshaler(m Marshaler) Option

WithMarshaler sets Requester.WithMarshaler

func WithUnmarshaler added in v0.2.0

func WithUnmarshaler(m Unmarshaler) Option

WithUnmarshaler sets Requester.WithUnmarshaler

func XML

func XML(indent bool) Option

XML sets Requester.Marshaler to the XMLMarshaler. If the arg is true, the generated XML will be indented. The XMLMarshaler will set the Content-Type header to "application/xml" unless explicitly overwritten.

type OptionFunc

type OptionFunc func(*Requester) error

OptionFunc adapts a function to the Option interface.

func (OptionFunc) Apply

func (f OptionFunc) Apply(r *Requester) error

Apply implements Option.

type Requester

type Requester struct {

	// Method defaults to "GET".
	Method string
	URL    *url.URL

	// Header supplies the request headers.  If the Content-Type header
	// is explicitly set here, it will override the Content-Type header
	// supplied by the Marshaler.
	Header http.Header

	// advanced options, not typically used.  If not sure, leave them
	// blank.
	// Most of these settings are set automatically by the http package.
	// Setting them here will override the automatic values.
	GetBody          func() (io.ReadCloser, error)
	ContentLength    int64
	TransferEncoding []string
	Close            bool
	Host             string
	Trailer          http.Header

	// QueryParams are added to the request, in addition to any
	// query params already encoded in the URL
	QueryParams url.Values

	// Body can be set to a string, []byte, io.Reader, or a struct.
	// If set to a string, []byte, or io.Reader,
	// the value will be used as the body of the request.
	// If set to a struct, the Marshaler
	// will be used to marshal the value into the request body.
	Body interface{}

	// Marshaler will be used to marshal the Body value into the body
	// of requester.  It is only used if Body is a struct value.
	// Defaults to the DefaultMarshaler, which marshals to JSON.
	//
	// If no Content-Type header has been explicitly set in Requester.Header, the
	// Marshaler will supply an appropriate one.
	Marshaler Marshaler

	// Doer holds the HTTP client for used to execute requester.
	// Defaults to http.DefaultClient.
	Doer Doer

	// Middleware wraps the Doer.  The middleware is ordered from outermost
	// to innermost.
	Middleware []Middleware

	// Unmarshaler will be used by the Receive methods to unmarshal
	// the response body.  Defaults to DefaultUnmarshaler, which unmarshals
	// multiple content types based on the Content-Type response header.
	Unmarshaler Unmarshaler
}

Requester is an HTTP request builder and HTTP client.

Requester can be used to construct requests, send requests via a configurable HTTP client, and unmarshal the response. A Requester is configured by setting its members, which in most cases mirror the members of *http.Request. A Requester can also be configured by applying functional Options, which simply modify Requester's members.

A Requester can be constructed as a literal:

r := requester.Requester{
         URL:    u,
         Method: "POST",
         Body:   b,
     }

...or via the New() and MustNew() constructors, which take Options:

reqs, err := requester.New(requester.Post("http://test.com/red"), requester.Body(b))

Additional options can be applied with Apply() and MustApply():

err := reqs.Apply(requester.Accept("application/json"))

Requesters can be cloned. The clone can then be further configured without affecting the parent:

reqs2 := reqs.Clone()
err := reqs2.Apply(Header("X-Frame","1"))

With()/MustWith() is equivalent to Clone() and Apply()/MustApply():

reqs2, err := reqs.With(requester.Header("X-Frame","1"))

The remaining methods of Requester are for creating HTTP requests, sending them, and handling the responses: Request(), Send(), and Receive().

req, err := reqs.Request()          // create a requests
resp, err := reqs.Send()            // create and send a request

var m Resource
resp, body, err := reqs.Receive(&m) // create and send request, read and unmarshal response

Request(), Send(), and Receive() all accept a varargs of Options, which will be applied only to a single request, not to the Requester.

req, err := reqs.Request(
                     requester.Put("users/bob"),
                     requester.Body(bob),
                   )

RequestContext(), SendContext(), and ReceiveContext() variants accept a context, which is attached to the constructed request:

req, err        := reqs.RequestContext(ctx)

func MustNew

func MustNew(options ...Option) *Requester

MustNew creates a new Requester, applying all options. If an error occurs applying options, this will panic.

func New

func New(options ...Option) (*Requester, error)

New returns a new Requester, applying all options.

func (*Requester) Apply

func (r *Requester) Apply(opts ...Option) error

Apply applies the options to the receiver.

func (*Requester) Clone

func (r *Requester) Clone() *Requester

Clone returns a deep copy of a Requester.

Example
base, _ := New(Get("https://api.io/"))

foo := base.Clone()
foo.Apply(Get("foo/"))

bar := base.Clone()
bar.Apply(Get("bar/"))
Output:

func (*Requester) Do

func (r *Requester) Do(req *http.Request) (*http.Response, error)

Do implements Doer. Executes the request using the configured Doer and Middleware.

func (*Requester) Headers

func (r *Requester) Headers() http.Header

Headers returns the Header, initializing it if necessary. Never returns nil.

func (*Requester) MustApply

func (r *Requester) MustApply(opts ...Option)

MustApply applies the options to the receiver. Panics on errors.

func (*Requester) MustWith added in v0.3.0

func (r *Requester) MustWith(opts ...Option) *Requester

MustWith clones the Requester, then applies the options to the clone. Panics if applying options returns an error

Equivalent to:

r2 := r.Clone()
r2.MustApply(...)

func (*Requester) Params

func (r *Requester) Params() url.Values

Params returns the QueryParams, initializing them if necessary. Never returns nil.

func (*Requester) Receive

func (r *Requester) Receive(into interface{}, opts ...Option) (resp *http.Response, body []byte, err error)

Receive creates a new HTTP request and returns the response. Any error creating the request, sending it, or decoding a 2XX response is returned.

The second argument may be nil, an Option, or a value to unmarshal the response body into.

If option arguments are passed, they are applied to this single request only.

Example
r := MustNew(MockDoer(200,
	Body("red"),
))

resp, body, _ := r.Receive(Get("http://api.com/resource"))

fmt.Println(resp.StatusCode, string(body))
Output:

200 red
Example (Unmarshal)
type Resource struct {
	Color string `json:"color"`
}

r := MustNew(MockDoer(200,
	JSON(true),
	Body(Resource{Color: "red"}),
))

var resource Resource

resp, body, _ := r.Receive(&resource, Get("http://api.com/resource"))

fmt.Println(resp.StatusCode)
fmt.Println(string(body))
fmt.Println(resource.Color)
Output:

200
{
  "color": "red"
}
red

func (*Requester) ReceiveContext

func (r *Requester) ReceiveContext(ctx context.Context, into interface{}, opts ...Option) (resp *http.Response, body []byte, err error)

ReceiveContext does the same as Receive, but requires a context.

The second argument may be nil, an Option, or a value to unmarshal the response body into.

func (*Requester) Request

func (r *Requester) Request(opts ...Option) (*http.Request, error)

Request returns a new http.Request.

If Options are passed, they will only by applied to this single request.

If r.Body is a struct, it will be marshaled into the request body using r.Marshaler. The Marshaler will also set the Content-Type header, unless this header is already explicitly set in r.Header.

If r.Body is an io.Reader, string, or []byte, it is set as the request body directly, and no default Content-Type is set.

Example
r := MustNew(
	Get("http://api.com/resource"),
	Header("X-Color", "red"),
	QueryParam("flavor", "vanilla"),
)

req, _ := r.Request(
	JSON(true),
	Body(map[string]interface{}{"size": "big"}),
)

fmt.Printf("%s %s %s\n", req.Method, req.URL.String(), req.Proto)
fmt.Println(HeaderContentType+":", req.Header.Get(HeaderContentType))
fmt.Println(HeaderAccept+":", req.Header.Get(HeaderAccept))
fmt.Println("X-Color:", req.Header.Get("X-Color"))
io.Copy(os.Stdout, req.Body)
Output:

GET http://api.com/resource?flavor=vanilla HTTP/1.1
Content-Type: application/json
Accept: application/json
X-Color: red
{
  "size": "big"
}

func (*Requester) RequestContext

func (r *Requester) RequestContext(ctx context.Context, opts ...Option) (*http.Request, error)

RequestContext does the same as Request, but requires a context. Use this to set a request timeout:

req, err := r.RequestContext(context.WithTimeout(context.Background(), 10 * time.Seconds))

func (*Requester) Send

func (r *Requester) Send(opts ...Option) (*http.Response, error)

Send executes a request with the Doer. The response body is not closed: it is the caller's responsibility to close the response body. If the caller prefers the body as a byte slice, or prefers the body unmarshaled into a struct, see the Receive methods below.

Additional options arguments can be passed. They will be applied to this request only.

Example
r := MustNew(MockDoer(204))

resp, _ := r.Send(Get("resources/1"))

fmt.Println(resp.StatusCode)
Output:

204

func (*Requester) SendContext

func (r *Requester) SendContext(ctx context.Context, opts ...Option) (*http.Response, error)

SendContext does the same as Request, but requires a context.

func (*Requester) Trailers

func (r *Requester) Trailers() http.Header

Trailers returns the Trailer, initializing it if necessary. Never returns nil.

func (*Requester) With

func (r *Requester) With(opts ...Option) (*Requester, error)

With clones the Requester, then applies the options to the clone.

Equivalent to:

r2 := r.Clone()
r2.Apply(...)

type RetryConfig added in v1.1.0

type RetryConfig struct {
	// MaxAttempts is the number of times to attempt the request.
	// Defaults to 3.
	MaxAttempts int
	// ShouldRetry tests whether a response should be retried.  Defaults to
	// DefaultShouldRetry, which retries for temporary network errors, network
	// timeout errors, or response codes >= 500, except for 501.
	ShouldRetry ShouldRetryer
	// Backoff returns how long to wait between retries.  Defaults to
	// an exponential backoff with some jitter.
	Backoff Backoffer
	// ReadResponse will ensure the entire response is read before
	// consider the request a success
	ReadResponse bool
}

RetryConfig defines settings for the Retry middleware.

type ShouldRetryer added in v1.1.0

type ShouldRetryer interface {
	ShouldRetry(attempt int, req *http.Request, resp *http.Response, err error) bool
}

ShouldRetryer evaluates whether an HTTP request should be retried. resp may be nil. Attempt is the number of the attempt which was just completed, and starts at 1. For example, if attempt=1, ShouldRetry should return true if attempt 2 should be tried.

func AllRetryers added in v1.2.0

func AllRetryers(s ...ShouldRetryer) ShouldRetryer

AllRetryers returns a ShouldRetryer which returns true only if all the supplied retryers return true.

type ShouldRetryerFunc added in v1.1.0

type ShouldRetryerFunc func(attempt int, req *http.Request, resp *http.Response, err error) bool

ShouldRetryerFunc adapts a function to the ShouldRetryer interface

func (ShouldRetryerFunc) ShouldRetry added in v1.1.0

func (s ShouldRetryerFunc) ShouldRetry(attempt int, req *http.Request, resp *http.Response, err error) bool

ShouldRetry implements ShouldRetryer

type UnmarshalFunc

type UnmarshalFunc func(data []byte, contentType string, v interface{}) error

UnmarshalFunc adapts a function to the Unmarshaler interface.

func (UnmarshalFunc) Apply added in v0.2.0

func (f UnmarshalFunc) Apply(r *Requester) error

Apply implements Option. UnmarshalFunc can be applied as a requester option, which install itself as the Unmarshaler.

func (UnmarshalFunc) Unmarshal

func (f UnmarshalFunc) Unmarshal(data []byte, contentType string, v interface{}) error

Unmarshal implements the Unmarshaler interface.

type Unmarshaler

type Unmarshaler interface {
	Unmarshal(data []byte, contentType string, v interface{}) error
}

Unmarshaler unmarshals a []byte response body into a value. It is provided the value of the Content-Type header from the response.

var DefaultUnmarshaler Unmarshaler = NewContentTypeUnmarshaler()

DefaultUnmarshaler is used by Requester if Requester.Unmarshaler is nil. nolint:gochecknoglobals

type XMLMarshaler

type XMLMarshaler struct {
	Indent bool
}

XMLMarshaler implements Marshaler and Unmarshaler. It marshals values to and from XML. If Indent is true, marshaled XML will be indented.

r := requester.Requester{
    Marshaler: &XMLMarshaler{},
}
Example
type Resource struct {
	Color string
}

// *XMLMarshaler implements Option, so it can be passed directly to functions
// which accept Options.
req, _ := Request(&XMLMarshaler{Indent: false}, Body(Resource{Color: "red"}))

b, _ := ioutil.ReadAll(req.Body)

fmt.Println(string(b))
fmt.Println(req.Header.Get(HeaderContentType))
Output:

<Resource><Color>red</Color></Resource>
application/xml; charset=UTF-8

func (*XMLMarshaler) Apply added in v0.3.0

func (m *XMLMarshaler) Apply(r *Requester) error

Apply implements Option.

func (*XMLMarshaler) Marshal

func (m *XMLMarshaler) Marshal(v interface{}) (data []byte, contentType string, err error)

Marshal implements Marshaler.

func (*XMLMarshaler) Unmarshal

func (*XMLMarshaler) Unmarshal(data []byte, _ string, v interface{}) error

Unmarshal implements Unmarshaler.

Directories

Path Synopsis
Package httpclient is a set of utilities for creating and configuring instances of http.Client.
Package httpclient is a set of utilities for creating and configuring instances of http.Client.
Package httptestutil contains utilities for use in HTTP tests, particular when using httptest.Server.
Package httptestutil contains utilities for use in HTTP tests, particular when using httptest.Server.

Jump to

Keyboard shortcuts

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