httpapi

package
v0.27.0 Latest Latest
Warning

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

Go to latest
Published: Dec 13, 2023 License: GPL-3.0 Imports: 15 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 ApplicationJSON = "application/json"

ApplicationJSON is the content-type for JSON

View Source
const DefaultCallTimeout = 60 * time.Second

DefaultCallTimeout is the default timeout for an httpapi call.

View Source
const DefaultMaxBodySize = 1 << 24

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.

View Source
var ErrTruncated = errors.New("httpapi: truncated response body")

ErrTruncated indicates we truncated the response body.

Functions

func Call

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

Call invokes the API described by desc on the given HTTP endpoint and returns the response body (as a ResponseType instance) 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.

Types

type Descriptor

type Descriptor[RequestType, ResponseType any] struct {
	// Accept contains the OPTIONAL accept header.
	Accept string

	// Authorization is the OPTIONAL authorization.
	Authorization string

	// AcceptEncodingGzip OPTIONALLY accepts gzip-encoding bodies.
	AcceptEncodingGzip bool

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

	// LogBody OPTIONALLY enables logging bodies.
	LogBody bool

	// 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

	// Request is the OPTIONAL request descriptor.
	Request *RequestDescriptor[RequestType]

	// Response is the MANDATORY response descriptor.
	Response ResponseDescriptor[ResponseType]

	// 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.

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

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

	// 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,
	logger model.Logger,
	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;

- logger is the logger to use;

- 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 JSONResponseDescriptor

type JSONResponseDescriptor[T any] struct{}

JSONResponseDescriptor is the type to use with Descriptor when the response's body is encoded using JSON.

func (*JSONResponseDescriptor[T]) Unmarshal

func (r *JSONResponseDescriptor[T]) Unmarshal(resp *http.Response, data []byte) (*T, error)

Unmarshal implements ResponseDescriptor

type RawRequest

type RawRequest struct{}

RawRequest is the type to use with RequestDescriptor and Descriptor when the request body is just raw bytes.

type RawResponseDescriptor

type RawResponseDescriptor struct{}

RawResponseDescriptor is the type to use with Descriptor when the response's body is just raw bytes.

func (*RawResponseDescriptor) Unmarshal

func (r *RawResponseDescriptor) Unmarshal(resp *http.Response, data []byte) ([]byte, error)

Unmarshal implements ResponseDescriptor

type RequestDescriptor

type RequestDescriptor[T any] struct {
	// Body is the raw request body.
	Body []byte
}

RequestDescriptor describes the request.

type ResponseDescriptor

type ResponseDescriptor[T any] interface {
	// Unmarshal unmarshals the raw response into a T.
	Unmarshal(resp *http.Response, data []byte) (T, error)
}

ResponseDescriptor describes the response.

type SequenceCaller

type SequenceCaller[RequestType, ResponseType any] struct {
	// Descriptor is the API [Descriptor].
	Descriptor *Descriptor[RequestType, ResponseType]

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

SequenceCaller calls the API specified by Descriptor once for each of the available [Endpoint]s 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[RequestType, ResponseType any](
	desc *Descriptor[RequestType, ResponseType],
	endpoints ...*Endpoint,
) *SequenceCaller[RequestType, ResponseType]

NewSequenceCaller is a factory for creating a SequenceCaller.

func (*SequenceCaller[RequestType, ResponseType]) Call

func (sc *SequenceCaller[RequestType, ResponseType]) Call(ctx context.Context) (ResponseType, 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.

Jump to

Keyboard shortcuts

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