querygo

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Jul 21, 2026 License: BSD-3-Clause Imports: 14 Imported by: 0

README

querygo

Go Reference Go Report Card CI

A small, dependency-free Go SDK for the HTTP QUERY method (RFC 10008).

QUERY is a safe and idempotent request method that carries a request body describing a query to run against the target resource. It combines the best of GET (safe, cacheable, retryable) and POST (arbitrary request body): the query travels in the body instead of the URI.

go get github.com/thatwasyahya/query-go-sdk
import querygo "github.com/thatwasyahya/query-go-sdk"

Features

Area API
Send a query Client.Query, Client.QueryString, Client.QueryBytes, Client.QueryForm, Client.QueryJSON
Build request bodies NewBodyString, NewBodyBytes, NewBodyForm, NewBodyJSON
Decode responses ReadBody, DecodeText, DecodeJSON, Client.QueryJSONInto
Error handling CheckStatus, StatusError, AsStatusError
Result / equivalent resource Client.FetchResult, Client.FetchEquivalent, ResponseInfo.ResultURI, ResponseInfo.EquivalentResourceURI
Discovery Client.Options, Discovery, SupportsQuery
Retries Client.DoWithRetry, DefaultRetryPolicy, RetryOnTransient, ExponentialBackoff
Client options WithBaseURL, WithUserAgent, WithDefaultHeader, WithHeader
Serve QUERY Handler, NewHandler, SetResultLocation, AdvertiseQuery
Caching CacheKey, CacheKeyForBody, Body.Bytes

Quick start

client := querygo.NewClient(nil,
    querygo.WithBaseURL("https://example.org"),
    querygo.WithUserAgent("my-app/1.0"),
)

var result struct {
    Hits int `json:"hits"`
}
err := client.QueryJSONInto(ctx, "/search",
    map[string]any{"q": "golang"}, &result, nil)

Sending a query

Pick the helper that matches your body's content type:

// Raw reader
resp, err := client.Query(ctx, url, body, "application/sql")

// String / bytes / form / JSON
resp, err := client.QueryString(ctx, url, "application/sql", "SELECT 1", "application/json", nil)
resp, err := client.QueryForm(ctx, url, url.Values{"q": {"golang"}}, "application/json", nil)
resp, err := client.QueryJSON(ctx, url, payload, "application/json", nil)

All helpers return the raw *http.Response; you must close the body.

Handling responses

resp, err := client.QueryJSON(ctx, url, payload, querygo.MediaTypeJSON, nil)
if err != nil {
    return err
}

var out SearchResult
if err := querygo.DecodeJSON(resp, &out); err != nil {
    // Non-2xx responses become a *StatusError.
    if se, ok := querygo.AsStatusError(err); ok {
        log.Printf("server returned %d: %s", se.StatusCode, se.Body)
    }
    return err
}

ReadBody, DecodeText and DecodeJSON validate the status and always close the body.

Result vs. equivalent resource

A QUERY response can advertise two distinct GET-retrievable URIs (RFC 10008 §2.3–2.4):

  • Content-Location — the stored result of the query just run. FetchResult follows it.
  • Location — the equivalent resource, whose GET re-runs the query (without resending the body) for a current result. FetchEquivalent follows it.
resp, _ := client.QueryJSON(ctx, url, payload, querygo.MediaTypeJSON, nil)

// The result computed for this exact query:
result, err := client.FetchResult(ctx, resp, nil)
if errors.Is(err, querygo.ErrNoQueryResult) {
    // No Content-Location: the result was returned inline.
}

// Re-run the same query later via a plain GET:
fresh, err := client.FetchEquivalent(ctx, resp, nil)

Discovering QUERY support

Accept-Query is an RFC 9651 Structured Fields List; the SDK parses and serializes it correctly (quoted strings, parameters, */* and type/* wildcards).

discovery, err := client.Options(ctx, url, nil)
if discovery.SupportsQuery {
    fmt.Println("accepted query formats:", discovery.AcceptQuery)
}
if discovery.AcceptsMediaType(querygo.MediaTypeJSON) {
    // ...
}

Retries

Because QUERY is safe and idempotent, transient failures can be retried transparently. The request body is replayed via Request.GetBody, which the Body constructors populate automatically.

req := querygo.RequestFromBody(url,
    querygo.NewBodyString("application/sql", "SELECT 1"),
    "application/json", nil)

resp, err := client.DoWithRetry(ctx, req, querygo.DefaultRetryPolicy())

DefaultRetryPolicy makes up to 3 attempts with exponential backoff, retrying transport errors and the 429, 502, 503, 504 status codes.

Redirects

QUERY redirects are handled per RFC 10008 §2.5, which differs from the standard library: 301, 302, 307 and 308 re-issue the QUERY (replaying the body), while only 303 switches to GET. The Go client alone would downgrade 301/302 to GET and drop the body, which the RFC forbids for QUERY.

client := querygo.NewClient(nil, querygo.WithMaxRedirects(5))

The limit defaults to querygo.DefaultMaxRedirects; exceeding it returns ErrTooManyRedirects. A 301/302/307/308 whose body cannot be replayed (a raw Body with no GetBody) returns ErrRedirectBodyNotReplayable.

Serving QUERY

Handler implements http.Handler. It dispatches QUERY requests, answers OPTIONS with the advertised support, and returns 405 (with an Allow header) for other methods. It mounts on the standard http.ServeMux.

mux := http.NewServeMux()
mux.Handle("/search", querygo.Handler{
    AcceptQuery:    []string{querygo.MediaTypeJSON},
    AllowedMethods: []string{http.MethodGet},
    Query: func(w http.ResponseWriter, r *http.Request) {
        // r.Body holds the query.
        querygo.SetResultLocation(w.Header(), "/results/42")
        w.Header().Set("Content-Type", "application/json")
        w.Write([]byte(`{"hits":2}`))
    },
})

A QUERY request without a Content-Type is rejected with 400 Bad Request (RFC 10008 §2). When AcceptQuery is set, requests with an unlisted Content-Type are rejected with 415 Unsupported Media Type and the server's Accept-Query. Use AdvertiseQuery from any handler (e.g. a GET) to surface QUERY support inline.

Caching

Unlike GET, a QUERY cache entry must be keyed on the request body. CacheKey computes a stable key over the method, URL, content type and body:

key := querygo.CacheKey(url, querygo.MediaTypeJSON, []byte(`{"q":"go"}`))
// or, from a Body:
key, _ := querygo.CacheKeyForBody(url, body)

Testing

go test ./... -race -cover

Contributing

Contributions are welcome — see CONTRIBUTING.md. Please run make check (gofmt, vet, build, race tests) before opening a pull request.

License

BSD-3-Clause.

Documentation

Overview

Package querygo is a small, dependency-free SDK for the HTTP QUERY method (RFC 10008).

QUERY is a safe, idempotent request method that carries a request body describing a query to be evaluated against the target resource. Unlike GET, the query parameters travel in the body rather than the URI, and unlike POST, the request is safe and may be retried.

Sending a query

client := querygo.NewClient(nil)
resp, err := client.QueryJSON(ctx, "https://example.org/search",
	map[string]any{"q": "golang"}, querygo.MediaTypeJSON, nil)
if err != nil {
	return err
}
defer resp.Body.Close()

The typed helpers QueryString, QueryBytes, QueryForm and QueryJSON build the request body for common content types. QueryJSONInto additionally decodes a JSON response:

var result SearchResult
err := client.QueryJSONInto(ctx, url, request, &result, nil)

Response handling

CheckStatus turns a non-2xx response into a *StatusError. ReadBody, DecodeText and DecodeJSON validate the status and consume the body.

Result location

A QUERY response may advertise two distinct URIs (RFC 10008 Sections 2.3 and 2.4): Content-Location identifies the stored result of the query just run (FetchResult follows it), while Location identifies the equivalent resource whose GET re-runs the query for a current result (FetchEquivalent follows it).

resp, _ := client.QueryJSON(ctx, url, request, querygo.MediaTypeJSON, nil)
result, err := client.FetchResult(ctx, resp, nil)

Discovery

Options issues an OPTIONS request and reports QUERY support based on the Allow and Accept-Query header fields.

Retries

Because QUERY is safe and idempotent, DoWithRetry can transparently retry transient failures, replaying the request body via Request.GetBody (which the Body constructors populate).

Example

Example shows a full round trip: a Handler serving QUERY, and a Client sending a JSON query and decoding the JSON response.

server := httptest.NewServer(Handler{
	AcceptQuery: []string{MediaTypeJSON},
	Query: func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set(HeaderContentType, MediaTypeJSON)
		_, _ = w.Write([]byte(`{"hits":2}`))
	},
})
defer server.Close()

client := NewClient(server.Client())

var result struct {
	Hits int `json:"hits"`
}
if err := client.QueryJSONInto(context.Background(), server.URL, map[string]string{"q": "golang"}, &result, nil); err != nil {
	log.Fatal(err)
}

fmt.Println(result.Hits)
Output:
2

Index

Examples

Constants

View Source
const (
	HeaderAcceptQuery     = "Accept-Query"
	HeaderAllow           = "Allow"
	HeaderContentType     = "Content-Type"
	HeaderContentLocation = "Content-Location"
	HeaderLocation        = "Location"
	HeaderAccept          = "Accept"
)

Header field names used by the HTTP QUERY method.

View Source
const (
	MediaTypeJSON = "application/json"
	MediaTypeForm = "application/x-www-form-urlencoded"
)

Common media types used for QUERY request bodies.

View Source
const DefaultMaxRedirects = 10

DefaultMaxRedirects is the redirect limit used when Client.MaxRedirects is zero or negative.

View Source
const MaxErrorBodySnippet = 4 << 10 // 4 KiB

MaxErrorBodySnippet bounds how many bytes of a response body are captured in a StatusError message.

View Source
const MethodQuery = "QUERY"

MethodQuery is the HTTP QUERY method token defined by RFC 10008. QUERY is a safe, idempotent request method that carries a request body describing a query to be evaluated against the target resource.

Variables

View Source
var ErrBodyNotReplayable = errors.New("querygo: body cannot be read without a GetReader")

ErrBodyNotReplayable is returned when a Body without a GetReader is asked to produce its bytes. The Body constructors (NewBodyString, NewBodyBytes, NewBodyForm, NewBodyJSON) always set GetReader.

View Source
var ErrNoEquivalentResource = errors.New("querygo: response has no equivalent resource location")

ErrNoEquivalentResource is returned when a QUERY response advertises no equivalent resource (no Location header field).

View Source
var ErrNoQueryResult = errors.New("querygo: response has no query result location")

ErrNoQueryResult is returned when a QUERY response advertises no result resource (no Content-Location header field).

View Source
var ErrRedirectBodyNotReplayable = errors.New("querygo: cannot follow redirect without a replayable body")

ErrRedirectBodyNotReplayable is returned when following a redirect requires resending the query body but the request has no GetBody to replay it. The Body constructors populate GetBody automatically.

View Source
var ErrTooManyRedirects = errors.New("querygo: too many redirects")

ErrTooManyRedirects is returned when a QUERY request exceeds the redirect limit (see Client.MaxRedirects).

Functions

func AdvertiseQuery

func AdvertiseQuery(header http.Header, mediaTypes ...string)

AdvertiseQuery sets the Accept-Query header field on a response, advertising the supported query media types as a Structured Fields List (RFC 10008 Section 3). It can be called from any handler (for example a GET handler) so clients discover QUERY support inline.

func CacheKey

func CacheKey(targetURL, contentType string, body []byte) string

CacheKey computes a stable cache key for a QUERY request. Unlike GET, a QUERY cache entry must be keyed on the request content in addition to the method and URI, because the query travels in the body. The returned value is the hex-encoded SHA-256 of those inputs.

Example

ExampleCacheKey shows that the cache key depends on the request body, so two different queries to the same URL produce different keys.

a := CacheKey("https://example.org/search", MediaTypeJSON, []byte(`{"q":"go"}`))
b := CacheKey("https://example.org/search", MediaTypeJSON, []byte(`{"q":"rust"}`))

fmt.Println(a == b)
Output:
false

func CacheKeyForBody

func CacheKeyForBody(targetURL string, body Body) (string, error)

CacheKeyForBody is CacheKey using a Body, materializing its content.

func CheckStatus

func CheckStatus(resp *http.Response) error

CheckStatus returns a *StatusError when resp carries a non-2xx status code, otherwise nil. On error it reads a bounded snippet of the body for the error message but does not close the body; the caller remains responsible for closing resp.Body.

func DecodeJSON

func DecodeJSON(resp *http.Response, v any) error

DecodeJSON validates the response status and decodes the JSON body into v. The body is always closed. A nil v drains and validates only.

func DecodeText

func DecodeText(resp *http.Response) (string, error)

DecodeText validates the response status and returns the body as a string. The body is always closed.

func ExponentialBackoff

func ExponentialBackoff(base, max time.Duration) func(retry int) time.Duration

ExponentialBackoff returns a backoff function that doubles the base delay on each retry, capped at max.

func NewRequest

func NewRequest(ctx context.Context, req Request) (*http.Request, error)

NewRequest builds an *http.Request that uses the QUERY method for the given Request. When GetBody is set it is wired onto the request so the standard library can replay the body across redirects.

func ReadBody

func ReadBody(resp *http.Response) ([]byte, error)

ReadBody validates the response status and returns the full response body. The body is always closed.

func RetryOnTransient

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

RetryOnTransient retries transport errors (except context cancellation) and the 429, 502, 503 and 504 status codes.

func SetResultLocation

func SetResultLocation(header http.Header, uri string)

SetResultLocation sets the Content-Location header field on a response, identifying the GET-retrievable resource that holds the query result. This is the server-side counterpart to Client.FetchResult.

func SupportsQuery

func SupportsQuery(resp *http.Response) bool

SupportsQuery reports whether a response advertises support for the QUERY method, either by listing QUERY in Allow or by carrying an Accept-Query header field.

Types

type Body

type Body struct {
	Reader      io.Reader
	GetReader   func() (io.Reader, error)
	ContentType string
}

Body pairs a request content reader with its media type. When GetReader is set the body can be produced again, enabling redirect and retry replay.

func NewBodyBytes

func NewBodyBytes(contentType string, value []byte) Body

NewBodyBytes builds a Body from a byte slice with the given content type.

func NewBodyForm

func NewBodyForm(values url.Values) Body

NewBodyForm builds an application/x-www-form-urlencoded Body from form values.

func NewBodyJSON

func NewBodyJSON(value any) (Body, error)

NewBodyJSON marshals value to JSON and builds an application/json Body.

func NewBodyString

func NewBodyString(contentType, value string) Body

NewBodyString builds a Body from a string with the given content type.

func (Body) Bytes

func (b Body) Bytes() ([]byte, error)

Bytes materializes the body content. It requires GetReader to be set; otherwise it returns ErrBodyNotReplayable.

type Client

type Client struct {
	// HTTPClient performs the underlying HTTP round trips. When nil,
	// http.DefaultClient is used.
	HTTPClient *http.Client

	// BaseURL, when set, is used to resolve relative request URLs. Absolute
	// request URLs are used as-is.
	BaseURL string

	// DefaultHeader holds header fields applied to every request unless the
	// request already carries a value for that field.
	DefaultHeader http.Header

	// UserAgent, when set, is applied as the User-Agent header field unless a
	// request already provides one.
	UserAgent string

	// MaxRedirects caps the number of redirects followed for a QUERY request.
	// When zero or negative, DefaultMaxRedirects is used. Redirects are handled
	// per RFC 10008 Section 2.5: 301, 302, 307 and 308 re-issue QUERY (replaying
	// the body), while 303 switches to GET.
	MaxRedirects int
}

Client sends HTTP QUERY requests. The zero value is usable and falls back to http.DefaultClient, but NewClient is the preferred constructor.

func NewClient

func NewClient(httpClient *http.Client, opts ...Option) *Client

NewClient returns a Client using the provided HTTP client (or http.DefaultClient when nil) configured with the given options.

func (*Client) Do

func (c *Client) Do(ctx context.Context, req Request) (*http.Response, error)

Do sends a QUERY request and returns the raw HTTP response. The caller is responsible for closing the response body.

func (*Client) DoWithRetry

func (c *Client) DoWithRetry(ctx context.Context, req Request, policy RetryPolicy) (*http.Response, error)

DoWithRetry sends a QUERY request, retrying according to policy. Between retries the previous response body is drained and closed and the request body is replayed via Request.GetBody. If the request carries a non-replayable body (Body set without GetBody), no retry is attempted.

The caller is responsible for closing the returned response body.

func (*Client) FetchEquivalent

func (c *Client) FetchEquivalent(ctx context.Context, resp *http.Response, header http.Header) (*http.Response, error)

FetchEquivalent performs a GET on the equivalent resource for the query, as advertised by the Location response field (RFC 10008 Section 2.4). Unlike FetchResult, this re-runs the query without resending its content and yields the current result. The URI is resolved relative to the original request URL. It returns ErrNoEquivalentResource when the response carries no Location.

The caller is responsible for closing the returned response body.

func (*Client) FetchResult

func (c *Client) FetchResult(ctx context.Context, resp *http.Response, header http.Header) (*http.Response, error)

FetchResult performs a GET on the resource holding the result of the query that was just performed, as advertised by the Content-Location response field (RFC 10008 Section 2.3). The URI is resolved relative to the original request URL. It returns ErrNoQueryResult when the response carries no Content-Location.

The caller is responsible for closing the returned response body.

func (*Client) Options

func (c *Client) Options(ctx context.Context, targetURL string, header http.Header) (Discovery, error)

Options issues an HTTP OPTIONS request to targetURL and interprets the response header fields to determine QUERY support. The response body is drained and closed.

Example

ExampleClient_Options shows discovering QUERY support via an OPTIONS request.

server := httptest.NewServer(Handler{AcceptQuery: []string{MediaTypeJSON}})
defer server.Close()

client := NewClient(server.Client())

discovery, err := client.Options(context.Background(), server.URL, nil)
if err != nil {
	log.Fatal(err)
}

fmt.Println(discovery.SupportsQuery, discovery.AcceptQuery[0])
Output:
true application/json

func (*Client) Query

func (c *Client) Query(ctx context.Context, targetURL string, body io.Reader, contentType string) (*http.Response, error)

Query is a convenience wrapper that sends a QUERY request with a body and Content-Type. Use Do or the typed Query* helpers for more control.

func (*Client) QueryBytes

func (c *Client) QueryBytes(ctx context.Context, targetURL, contentType string, value []byte, accept string, header http.Header) (*http.Response, error)

QueryBytes sends a QUERY request with a byte-slice body.

func (*Client) QueryForm

func (c *Client) QueryForm(ctx context.Context, targetURL string, values url.Values, accept string, header http.Header) (*http.Response, error)

QueryForm sends a QUERY request with a form-encoded body.

func (*Client) QueryJSON

func (c *Client) QueryJSON(ctx context.Context, targetURL string, value any, accept string, header http.Header) (*http.Response, error)

QueryJSON sends a QUERY request whose body is value marshaled to JSON.

func (*Client) QueryJSONInto

func (c *Client) QueryJSONInto(ctx context.Context, targetURL string, body any, result any, header http.Header) error

QueryJSONInto sends a QUERY request with a JSON body and decodes the JSON response into result. It is a convenience over QueryJSON + DecodeJSON.

func (*Client) QueryRequest

func (c *Client) QueryRequest(ctx context.Context, targetURL string, body Body, accept string, header http.Header) (*http.Response, error)

QueryRequest sends a QUERY request built from a Body.

func (*Client) QueryString

func (c *Client) QueryString(ctx context.Context, targetURL, contentType, value, accept string, header http.Header) (*http.Response, error)

QueryString sends a QUERY request with a string body.

type Discovery

type Discovery struct {
	// SupportsQuery is true when the response advertises the QUERY method via
	// the Allow header field or carries an Accept-Query header field.
	SupportsQuery bool

	// Allow lists the methods advertised in the Allow header field.
	Allow []string

	// AcceptQuery lists the query media types advertised in the Accept-Query
	// header field.
	AcceptQuery []string

	// Header is the raw response header.
	Header http.Header
}

Discovery describes a server's advertised support for the QUERY method, as reported by an OPTIONS response (or any response carrying the relevant header fields).

func DiscoveryFromResponse

func DiscoveryFromResponse(resp *http.Response) Discovery

DiscoveryFromResponse interprets a response's header fields as QUERY support metadata.

func (Discovery) AcceptsMediaType

func (d Discovery) AcceptsMediaType(mediaType string) bool

AcceptsMediaType reports whether mediaType is covered by the advertised Accept-Query media ranges, honoring the "*/*" and "type/*" wildcards.

type Handler

type Handler struct {
	// Query handles QUERY requests. Required. The request body carries the
	// query to evaluate.
	Query http.HandlerFunc

	// AcceptQuery lists the query media types advertised via the Accept-Query
	// header field (on OPTIONS and 405 responses). When non-empty, QUERY
	// requests whose Content-Type is not in the list are rejected with 415
	// Unsupported Media Type.
	AcceptQuery []string

	// AllowedMethods lists additional methods (besides QUERY and OPTIONS) that
	// the resource supports, advertised via the Allow header field.
	AllowedMethods []string

	// OnOptions, when set, handles OPTIONS requests instead of the built-in
	// response.
	OnOptions http.HandlerFunc
}

Handler serves the HTTP QUERY method for a resource. It dispatches QUERY requests to Query, answers OPTIONS with the advertised QUERY support, and rejects other methods with 405 Method Not Allowed. The zero value is not useful; set Query (see NewHandler).

Handler implements http.Handler and can be mounted on any router, including the standard http.ServeMux (works on Go 1.21+, which lacks method-based route patterns).

func NewHandler

func NewHandler(query http.HandlerFunc, acceptQuery ...string) Handler

NewHandler returns a Handler for the given QUERY handler function, advertising the provided query media types via Accept-Query.

func (Handler) ServeHTTP

func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP implements http.Handler.

type Option

type Option func(*Client)

Option customizes a Client during construction.

func WithBaseURL

func WithBaseURL(baseURL string) Option

WithBaseURL sets the base URL used to resolve relative request URLs.

func WithDefaultHeader

func WithDefaultHeader(header http.Header) Option

WithDefaultHeader sets header fields applied to every request unless already present. The provided header is cloned.

func WithHeader

func WithHeader(key, value string) Option

WithHeader adds a single default header field, allocating the header map if needed.

func WithMaxRedirects added in v0.2.0

func WithMaxRedirects(n int) Option

WithMaxRedirects sets the maximum number of redirects followed for a QUERY request. A value of zero or less selects DefaultMaxRedirects.

func WithUserAgent

func WithUserAgent(userAgent string) Option

WithUserAgent sets the default User-Agent header field.

type Request

type Request struct {
	// URL is the target URL. It may be relative when the client has a BaseURL.
	URL string

	// Body is the request content (the query). It may be nil for an empty
	// query.
	Body io.Reader

	// GetBody, when set, returns a fresh Body reader. It enables the request
	// to be replayed for redirects and retries. Body constructors such as
	// NewBodyString populate it automatically via RequestFromBody.
	GetBody func() (io.Reader, error)

	// ContentType sets the Content-Type header field describing Body.
	ContentType string

	// Accept sets the Accept header field advertising acceptable response
	// media types.
	Accept string

	// Header holds additional header fields to add to the request.
	Header http.Header
}

Request describes a single HTTP QUERY request.

func RequestFromBody

func RequestFromBody(targetURL string, body Body, accept string, header http.Header) Request

RequestFromBody converts a Body into a Request, propagating GetReader so the request can be replayed.

type ResponseInfo

type ResponseInfo struct {
	StatusCode      int
	ContentLocation string
	Location        string
	AcceptQuery     []string
	Allow           []string
	Header          http.Header
}

ResponseInfo captures the QUERY-relevant metadata of a response.

func ParseResponseInfo

func ParseResponseInfo(resp *http.Response) ResponseInfo

ParseResponseInfo extracts QUERY-relevant metadata from a response.

func (ResponseInfo) EquivalentResourceURI

func (info ResponseInfo) EquivalentResourceURI() string

EquivalentResourceURI returns the URI of the equivalent resource for the query, as advertised by the Location response field (RFC 10008 Section 2.4). A GET to this URI re-runs the same query without resending the content, yielding the current result. It is empty when the response carries no such field.

func (ResponseInfo) ResultURI

func (info ResponseInfo) ResultURI() string

ResultURI returns the URI of the resource holding the result of the query that was just performed, as advertised by the Content-Location response field (RFC 10008 Section 2.3). A GET to this URI retrieves that stored result. It is empty when the response carries no such field.

type RetryPolicy

type RetryPolicy struct {
	// MaxAttempts is the total number of attempts, including the first. Values
	// below 1 are treated as 1.
	MaxAttempts int

	// Backoff returns the delay to wait before a retry. Its argument is the
	// 1-indexed retry number (1 before the first retry). When nil, retries
	// happen without delay.
	Backoff func(retry int) time.Duration

	// RetryOn decides whether the outcome of an attempt should be retried.
	// When nil, no attempt is retried.
	RetryOn func(resp *http.Response, err error) bool
}

RetryPolicy configures automatic retries for QUERY requests. Because QUERY is a safe and idempotent method, transient failures may be retried without changing server state, provided the request body can be replayed (Request carries a GetBody, which the Body constructors populate).

func DefaultRetryPolicy

func DefaultRetryPolicy() RetryPolicy

DefaultRetryPolicy returns a policy that makes up to 3 attempts with exponential backoff, retrying transient transport errors and the 429/502/ 503/504 status codes.

type StatusError

type StatusError struct {
	StatusCode int
	Status     string
	Method     string
	URL        string
	Body       []byte
	Header     http.Header
}

StatusError is returned when a QUERY (or result) response carries a status code outside the 2xx success range.

func AsStatusError

func AsStatusError(err error) (*StatusError, bool)

AsStatusError reports whether err wraps a *StatusError and returns it.

func (*StatusError) Error

func (e *StatusError) Error() string

Error implements the error interface.

Jump to

Keyboard shortcuts

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