request

package
v1.1.4 Latest Latest
Warning

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

Go to latest
Published: Aug 20, 2021 License: MIT Imports: 12 Imported by: 6

Documentation

Overview

Package request contains the core types Plan (describes an HTTP request plan) and Execution (describes a Plan execution). These two types are fundamental to making reliable HTTP requests.

The first core type is Plan, which represents a HTTP request plan.

A Plan describes how to make a logical HTTP request, potentially involving repeated HTTP request attempts if retry is necessary after a failure. For those familiar with the Go standard HTTP library, net/http, a Plan looks like a stripped-down http.Request structure with all server-side fields removed, and the body fields replaced with a simple []byte, because Plan requires a pre-buffered request body. Plan fields are named and typed consistently with http.Request wherever possible.

Create a plan to make a reliable HTTP request:

p, err := request.NewPlan("GET", "https://example.com", nil)
...
e, err := client.Do(p)
...

A plan may be assigned a context to allow timeouts to be set on the entire plan execution, and to allow the plan execution to be cancelled:

p, err := request.NewPlanWithContext(ctx, "POST", "https://example.com/upload", body)
...

If a deadline is set on the plan context, it is separate from the deadlines set on individual request attempts within the plan execution, which are dictated by the client's timeout.Policy. The effect is that an individual request attempt may fail due either to an attempt timeout or a plan timeout. The former is potentially retryable, the latter is not.

The second core type is Execution, which represents the state of the execution of an HTTP request plan. Execution is both the output type for httpx.Client's plan executing methods, and the input type for callbacks invoked during plan execution: timeout policies, retry policies, and event handlers. You will typically not allocate Execution instances yourself, but will instead work with the ones handed out by the client's request plan execution logic.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BodyBytes

func BodyBytes(body interface{}) ([]byte, error)

BodyBytes converts a generic body parameter to a byte slice for use as a request plan body.

The body parameter may be nil, or it may be a string, []byte, io.Reader, or io.ReadCloser. The conversion logic is:

• If body is nil, a nil byte slice and no error is returned.

• If body is a []byte, body itself and no error is returned.

• If body is a string, the built-in conversion from string to byte slice, and no error, is returned.

• If body is an io.Reader or io.ReadCloser, the result of reading the whole contents of the reader (and closing it if it implements Closer) is returned. If reading from the reader (and closing it if applicable) causes an error, the return value is a nil byte slice and the error. Otherwise, the result is the entire contents read from the reader and no error.

• If body is any other type than those listed above, a nil byte slice and an error is returned.

Types

type Execution

type Execution struct {
	// Plan specifies the HTTP request plan being executed. It is never
	// nil.
	Plan *Plan

	// Start is the start time of the HTTP request plan execution. It
	// is assigned a non-zero value when the plan execution starts, and
	// this value remains constant thereafter.
	Start time.Time

	// End is the end time of the HTTP request plan execution. It
	// contains the zero value until the plan execution is ends, when
	// it is set to the current time.
	End time.Time

	// Attempt is the zero-based number of the current HTTP request
	// attempt during the plan execution. It is set to zero on the
	// initial attempt, one on the first retry, and so on.
	//
	// When the execution is ended, Attempt contains the zero-based
	// number of the last attempt made during the execution. So for
	// example an execution that ends after an initial attempt plus two
	// retries will have an attempt number of 2.
	Attempt int

	// AttemptTimeouts is the count of how many HTTP request attempts
	// timed out during the execution.
	//
	// Plan timeouts (when the plan's own context deadline is exceeded)
	// do not contribute to the attempt timeout counter, but if an
	// attempt timeout and a plan timeout coincide, the attempt timeout
	// counter will be incremented by one due to the attempt timeout.
	AttemptTimeouts int

	// Request specifies the HTTP request to be made in the current
	// attempt, or already made in the last attempt.
	Request *http.Request

	// Response specifies the HTTP response received in the most recent
	// request attempt. It will be nil if the most recent attempt ended
	// in an error, or if a current attempt is underway, or before the
	// execution starts.
	Response *http.Response

	// Err indicates the error received while making the most recent
	// request attempt. It will be nil if the most recent attempt ended
	// without an error, or if a current attempt is underway, or before
	// the execution starts.
	//
	// Whenever Err is non-nil, it has the type *url.Error.
	//
	// While an execution is in-flight, Err may fluctuate between nil
	// and various non-nil error values. Once the execution has Ended,
	// Err will not change and has the same value as the error value
	// returned by the robust client's executing method.
	Err error

	// Body is the complete response body read from the response after
	// the most recent request attempt. It will be nil if the most
	// recent attempt ended in an error, or if a current attempt is
	// underway.
	//
	// Note that it is possible that both Body and Err are non-nil, if
	// a read of the body was partially successful. The Body field
	// of a completed execution should be treated as invalid unless Err
	// is nil.
	Body []byte

	// Wave is zero-based number of the current wave of request
	// attempts.
	//
	// If a racing policy is not installed on the robust HTTP client,
	// then each wave only contains a single attempt, and Wave is always
	// equal to Attempt. If racing is enabled, however, then a new wave
	// is started only when all parallel attempts racing in the previous
	// wave have finished or been cancelled, so Wave is less than or
	// equal to Attempt.
	Wave int

	// Racing is the count of request attempts racing in the current
	// wave. The value is zero before and after a wave, for example
	// during the BeforeExecutionStart and AfterExecutionEnd events,
	// and always at least one during the wave. The count is increased
	// by one whenever a new request attempt is started and reduced by
	// one whenever a request attempt ends.
	//
	// Unless a racing policy has been installed on the robust HTTP
	// client, the wave size is always one, so the value will be one
	// during events relating to request attempts and zero otherwise.
	Racing int

	// AttemptEnds is the count of how many HTTP request attempts have
	// ended within the execution.
	AttemptEnds int
	// contains filtered or unexported fields
}

An Execution represents the state of a single Plan execution.

When an HTTP request plan execution is requested, an Execution is created for it. The Execution is updated as the plan execution progresses (for example when the HTTP response becomes available, or when a retry is needed) and is ultimately returned as return value of the plan execution.

Timeout and retry policies and event handlers may set values on an Execution using its SetValue method and read them back using the Value method. However, they should treat the structure's exported field values as immutable and leave them unmodified, as the execution state is vital to the correct functioning of the plan execution logic. Limited exceptions to this rule include making reasonable changes to the http.Request before it is sent (for example, to support an OAuth or AWS signing use case), or to unzip or a request body after it is successfully read.

func (*Execution) Duration

func (e *Execution) Duration() time.Duration

Duration returns the duration of the execution.

If the execution has not yet started, the duration is zero. If the execution has Ended, the duration returned is equal to End minus Start. Otherwise, it is equal to the current time minus Start. The return value is thus monotonically increasing over the life of the execution, and becomes static when the execution has ended.

func (*Execution) Ended

func (e *Execution) Ended() bool

Ended indicates whether the execution has ended.

If the return value is false, the execution is still in-flight. If the return value is true, then the execution is over, End is a non-zero time, and there will be no further changes to the execution.

func (*Execution) Header

func (e *Execution) Header() http.Header

Header returns the HTTP response headers from the most recent request attempt in the execution. If there is no HTTP response, the nil header is returned.

A nil header due to no HTTP response will be returned if the most recent attempt ended in error, or if a current attempt is underway, or before the execution starts.

Note that a nil return value is always safe for read-only operations, since http.Header is a map type. There should never be a reason to write to the returned value, since it represents the response headers.

func (*Execution) SetValue

func (e *Execution) SetValue(key, value interface{})

SetValue allows event handlers to store arbitrary data in the request plan execution.

The key must follow the same rules as the key parameter in context.WithValue, namely it:

• it may not be nil;

• it must be comparable;

• it should not be of type string or any other built-in type to avoid collisions between different event handlers putting data into the same request execution.

func (*Execution) Started

func (e *Execution) Started() bool

Started indicates whether the execution has started.

If the return value is false, the execution has not started yet. If the return value is true, then the execution has started, and Start is a non-zero time, indicating the execution start time.

func (*Execution) StatusCode

func (e *Execution) StatusCode() int

StatusCode returns the status code of the HTTP response from the most recent request attempt in the execution. If there is no HTTP response, 0 is returned.

A zero value due to no HTTP response will be returned if the most recent attempt ended in error, or if a current attempt is underway, or before the execution starts.

func (*Execution) Timeout

func (e *Execution) Timeout() bool

Timeout indicates whether Err currently contains a non-nil value which indicates a timeout. The timeout may have been caused by an HTTP request attempt timeout, or by a plan timeout detected after the most recent request attempt.

Note that Timeout may return false even if AttemptTimeouts > 0 (if the most recent attempt did not end in a timeout, or a current attempt is underway); and it may return true even if AttemptTimeouts is zero (if a plan timeout is detected after the end of the most recent request attempt).

func (*Execution) Value

func (e *Execution) Value(key interface{}) interface{}

Value returns the data value associated with this execution for key, or nil if there is no value associated with key.

type Plan

type Plan struct {
	// Method specifies the HTTP method (GET, POST, PUT, etc.).
	// An empty string means GET.
	Method string

	// URL specifies the URL to access.
	//
	// The URL's Host specifies the server to connect to, while
	// the Request's Host field optionally specifies the Host
	// header value to send in the HTTP request.
	URL *urlpkg.URL

	// Header contains the request header fields to be sent by the
	// client.
	//
	// For further details, see the documentation of Request.Header in
	// the net/http package.
	Header http.Header

	// Body is the pre-buffered request body to be sent. A nil or
	// empty body indicates no request body should be sent, for example
	// on a GET or DELETE request.
	Body []byte

	// TransferEncoding lists the transfer encodings from outermost to
	// innermost. An empty list denotes the "identity" encoding.
	// TransferEncoding can usually be ignored if using the Go standard
	// http.Client (net/http) for as the lower-level HTTPDoer; http.Client
	// automatically adds and removes chunked encoding as necessary when
	// sending requests.
	TransferEncoding []string

	// Close stipulates whether to close the connection after sending
	// each lower-level (net/http) Request and reading the response.
	// Setting this field prevents re-use of TCP connections between
	// request attempts to the same host (including two request attempts
	// coming from the same plan) as if Transport.DisableKeepAlives were
	// set.
	Close bool

	// Host optionally overrides the Host header to send. If empty, the
	// value of URL.Host will be sent. Host may contain an international
	// domain name.
	Host string
	// contains filtered or unexported fields
}

A Plan contains a logical HTTP request plan for execution by a client.

The logical request described by a Plan will typically result in a lower-level http.Request (net/http) attempts being made, but may result in multiple request attempts, for example if the a failed attempt needs to be retried.

The field structure of plan mirrors the structure of the lower-level http.Request with the following differences. Server-only fields are removed (for example Proto). Some fields are either simplified (Body) or removed (Trailer) because their definition in the net/http Request is highly general to support stream-oriented features which are deliberately not supported by this transaction-oriented library.

Like the http.Request structure, a Plan has a context which controls the overall plan execution and can be used to cancel the inflight execution of a Plan at any time.

func NewPlan

func NewPlan(method, url string, body interface{}) (*Plan, error)

NewPlan wraps NewPlanWithContext using the background context.

Parameter body may be nil (empty body), or it may be a string, []byte, io.Reader, or io.ReadCloser. If body is an io.Reader, it is read to the end and buffered into a []byte. If body is an io.ReadCloser, it is closed after buffering.

func NewPlanWithContext

func NewPlanWithContext(ctx context.Context, method, url string, body interface{}) (*Plan, error)

NewPlanWithContext returns a new Plan given a method, URL, and optional body.

Parameter body may be nil (empty body), or it may be a string, []byte, io.Reader, or io.ReadCloser. If body is an io.Reader, it is read to the end and buffered into a []byte. If body is an io.ReadCloser, it is closed after buffering.

func (*Plan) AddCookie

func (p *Plan) AddCookie(c *http.Cookie)

AddCookie adds a cookie to the request. Per RFC 6265 section 5.4, AddCookie does not attach more than one Cookie header field. That means all cookies, if any, are written into the same line, separated by semicolons.

AddCookie only sanitizes c's name and value, and does not sanitize a Cookie header already present in the request.

func (*Plan) Context

func (p *Plan) Context() context.Context

Context returns the request plan's context. The context controls cancellation of the overall request plan. To change the context, use WithContext.

The returned context is always non-nil; it defaults to the background context.

func (*Plan) SetBasicAuth

func (p *Plan) SetBasicAuth(username, password string)

SetBasicAuth sets the request plan's Authorization header to use HTTP Basic Authentication with the provided username and password.

With HTTP Basic Authentication the provided username and password are not encrypted.

Some protocols may impose additional requirements on pre-escaping the username and password. For instance, when used with OAuth2, both arguments must be URL encoded first with url.QueryEscape.

func (*Plan) ToRequest

func (p *Plan) ToRequest(ctx context.Context) *http.Request

ToRequest creates an HTTP request corresponding to the given request plan. The context of the new request is set to ctx, which may not be nil.

func (*Plan) WithContext

func (p *Plan) WithContext(ctx context.Context) *Plan

WithContext returns a shallow copy of p with its context changed to ctx, which must be non-nil.

The context controls the entire lifetime of a logical request plan and its execution, including: making individual request attempts (obtaining a connection, sending the request, reading the response headers and body), running event handlers, and waiting for a retry wait period to expire.

To create a new request plan with a context, use NewPlanWithContext.

Jump to

Keyboard shortcuts

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