httpapi

package
v3.16.7 Latest Latest
Warning

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

Go to latest
Published: Dec 7, 2022 License: GPL-3.0 Imports: 14 Imported by: 0

Documentation

Overview

Package httpapi contains code for calling HTTP APIs.

We model HTTP APIs as follows:

1. |Endpoint| is an API endpoint (e.g., https://api.ooni.io);

2. |Descriptor| describes the specific API you want to use (e.g., GET /api/v1/test-list/urls with JSON response body).

Generally, you use |Call| to call the API identified by a |Descriptor| on the specified |Endpoint|. However, there are cases where you need more complex calling patterns. For example, with |SequenceCaller| you can invoke the same API |Descriptor| with multiple equivalent API |Endpoint|s until one of them succeeds or all fail.

Index

Constants

View Source
const DefaultCallTimeout = 60 * time.Second

DefaultCallTimeout is the default timeout for an httpapi call.

View Source
const DefaultMaxBodySize = 1 << 22

DefaultMaxBodySize is the default value for the maximum body size you can fetch using the httpapi package.

Variables

View Source
var ErrAllEndpointsFailed = errors.New("httpapi: all endpoints failed")

ErrAllEndpointsFailed indicates that all endpoints failed.

Functions

func Call

func Call(ctx context.Context, desc *Descriptor, endpoint *Endpoint) ([]byte, error)

Call invokes the API described by |desc| on the given HTTP |endpoint| and returns the response body (as a slice of bytes) or an error.

Note: this function returns ErrHTTPRequestFailed if the HTTP status code is greater or equal than 400. You could use errors.As to obtain a copy of the error that was returned and see for yourself the actual status code.

func CallWithJSONResponse

func CallWithJSONResponse(ctx context.Context, desc *Descriptor, endpoint *Endpoint, response any) error

CallWithJSONResponse is like Call but also assumes that the response is a JSON body and attempts to parse it into the |response| field.

Note: this function returns ErrHTTPRequestFailed if the HTTP status code is greater or equal than 400. You could use errors.As to obtain a copy of the error that was returned and see for yourself the actual status code.

Types

type Descriptor

type Descriptor struct {
	// Accept contains the OPTIONAL accept header.
	Accept string

	// Authorization is the OPTIONAL authorization.
	Authorization string

	// ContentType is the OPTIONAL content-type header.
	ContentType string

	// LogBody OPTIONALLY enables logging bodies.
	LogBody bool

	// Logger is the MANDATORY logger to use.
	//
	// For example, model.DiscardLogger.
	Logger model.Logger

	// MaxBodySize is the OPTIONAL maximum response body size. If
	// not set, we use the |DefaultMaxBodySize| constant.
	MaxBodySize int64

	// Method is the MANDATORY request method.
	Method string

	// RequestBody is the OPTIONAL request body.
	RequestBody []byte

	// Timeout is the OPTIONAL timeout for this call. If no timeout
	// is specified we will use the |DefaultCallTimeout| const.
	Timeout time.Duration

	// URLPath is the MANDATORY URL path.
	URLPath string

	// URLQuery is the OPTIONAL query.
	URLQuery url.Values
}

Descriptor contains the parameters for calling a given HTTP API (e.g., GET /api/v1/test-list/urls).

The zero value of this struct is invalid. Please, fill all the fields marked as MANDATORY for correct initialization.

func MustNewPOSTJSONWithJSONResponseDescriptor

func MustNewPOSTJSONWithJSONResponseDescriptor(logger model.Logger, urlPath string, request any) *Descriptor

MustNewPOSTJSONWithJSONResponseDescriptor is like NewPOSTJSONWithJSONResponseDescriptor except that it panics in case it's not possible to JSON serialize the |request|.

func NewGETJSONDescriptor

func NewGETJSONDescriptor(logger model.Logger, urlPath string) *Descriptor

NewGETJSONDescriptor is a convenience factory for creating a new descriptor that uses the GET method and expects a JSON response.

func NewGETJSONWithQueryDescriptor

func NewGETJSONWithQueryDescriptor(logger model.Logger, urlPath string, query url.Values) *Descriptor

NewGETJSONWithQueryDescriptor is like NewGETJSONDescriptor but it also allows you to provide |query| arguments. Leaving |query| nil or empty is equivalent to calling NewGETJSONDescriptor directly.

func NewGETResourceDescriptor

func NewGETResourceDescriptor(logger model.Logger, urlPath string) *Descriptor

NewGETResourceDescriptor creates a generic descriptor for GETting a resource of unspecified type using the given |urlPath|.

func NewPOSTJSONWithJSONResponseDescriptor

func NewPOSTJSONWithJSONResponseDescriptor(logger model.Logger, urlPath string, request any) (*Descriptor, error)

NewPOSTJSONWithJSONResponseDescriptor creates a descriptor that POSTs a JSON document and expects to receive back a JSON document from the API.

This function ONLY fails if we cannot serialize the |request| to JSON. So, if you know that |request| is JSON-serializable, you can safely call MustNewPostJSONWithJSONResponseDescriptor instead.

func (*Descriptor) WithBodyLogging

func (desc *Descriptor) WithBodyLogging(value bool) *Descriptor

WithBodyLogging returns a SHALLOW COPY of |Descriptor| with LogBody set to |value|. You SHOULD only use this method when initializing the descriptor you want to use.

type Endpoint

type Endpoint struct {
	// BaseURL is the MANDATORY endpoint base URL. We will honour the
	// path of this URL and prepend it to the actual path specified inside
	// a |Descriptor.URLPath|. However, we will always discard any query
	// that may have been set inside the BaseURL. The only query string
	// will be composed from the |Descriptor.URLQuery| values.
	//
	// For example, https://api.ooni.io.
	BaseURL string

	// HTTPClient is the MANDATORY HTTP client to use.
	//
	// For example, http.DefaultClient. You can introduce circumvention
	// here by using an HTTPClient bound to a specific tunnel.
	HTTPClient model.HTTPClient

	// Host is the OPTIONAL host header to use.
	//
	// If this field is empty we use the BaseURL's hostname. A specific
	// host header may be needed when using cloudfronting.
	Host string

	// User-Agent is the OPTIONAL user-agent to use. If empty,
	// we'll use the stdlib's default user-agent string.
	UserAgent string
}

Endpoint models an HTTP endpoint on which you can call several HTTP APIs (e.g., https://api.ooni.io) using a given HTTP client potentially using a circumvention tunnel mechanism such as psiphon or torsf.

The zero value of this struct is invalid. Please, fill all the fields marked as MANDATORY for correct initialization.

func NewEndpointList

func NewEndpointList(httpClient model.HTTPClient,
	userAgent string, services ...model.OOAPIService) (out []*Endpoint)

NewEndpointList constructs a list of API endpoints from |services| returned by the OONI backend (or known in advance).

Arguments:

- httpClient is the HTTP client to use for accessing the endpoints;

- userAgent is the user agent you would like to use;

- service is the list of services gathered from the backend.

type ErrHTTPRequestFailed

type ErrHTTPRequestFailed struct {
	// StatusCode is the status code that failed.
	StatusCode int
}

ErrHTTPRequestFailed indicates that the server returned >= 400.

func (*ErrHTTPRequestFailed) Error

func (err *ErrHTTPRequestFailed) Error() string

Error implements error.

type SequenceCaller

type SequenceCaller struct {
	// Descriptor is the API |Descriptor|.
	Descriptor *Descriptor

	// Endpoints is the list of |Endpoint| to use.
	Endpoints []*Endpoint
}

SequenceCaller calls the API specified by |Descriptor| once for each of the available |Endpoints| until one of them succeeds.

CAVEAT: this code will ONLY retry API calls with subsequent endpoints when the error originates in the HTTP round trip or while reading the body.

func NewSequenceCaller

func NewSequenceCaller(desc *Descriptor, endpoints ...*Endpoint) *SequenceCaller

NewSequenceCaller is a factory for creating a |SequenceCaller|.

func (*SequenceCaller) Call

func (sc *SequenceCaller) Call(ctx context.Context) ([]byte, int, error)

Call calls |Call| for each |Endpoint| and |Descriptor| until one endpoint succeeds. The return value is the response body and the selected endpoint index or the error.

CAVEAT: this code will ONLY retry API calls with subsequent endpoints when the error originates in the HTTP round trip or while reading the body.

func (*SequenceCaller) CallWithJSONResponse

func (sc *SequenceCaller) CallWithJSONResponse(ctx context.Context, response any) (int, error)

CallWithJSONResponse is like |SequenceCaller.Call| except that it invokes the underlying |CallWithJSONResponse| rather than invoking |Call|.

CAVEAT: this code will ONLY retry API calls with subsequent endpoints when the error originates in the HTTP round trip or while reading the body.

Jump to

Keyboard shortcuts

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