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
- Variables
- func Call[RequestType, ResponseType any](ctx context.Context, desc *Descriptor[RequestType, ResponseType], ...) (ResponseType, error)
- type Descriptor
- type Endpoint
- type ErrHTTPRequestFailed
- type JSONResponseDescriptor
- type RawRequest
- type RawResponseDescriptor
- type RequestDescriptor
- type ResponseDescriptor
- type SequenceCaller
Constants ¶
const ApplicationJSON = "application/json"
ApplicationJSON is the content-type for JSON
const DefaultCallTimeout = 60 * time.Second
DefaultCallTimeout is the default timeout for an httpapi call.
const DefaultMaxBodySize = 1 << 24
DefaultMaxBodySize is the default value for the maximum body size you can fetch using the httpapi package.
Variables ¶
var ErrAllEndpointsFailed = errors.New("httpapi: all endpoints failed")
ErrAllEndpointsFailed indicates that all endpoints failed.
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 ¶ added in v3.17.0
type JSONResponseDescriptor[T any] struct{}
JSONResponseDescriptor is the type to use with Descriptor when the response's body is encoded using JSON.
type RawRequest ¶ added in v3.17.0
type RawRequest struct{}
RawRequest is the type to use with RequestDescriptor and Descriptor when the request body is just raw bytes.
type RawResponseDescriptor ¶ added in v3.17.0
type RawResponseDescriptor struct{}
RawResponseDescriptor is the type to use with Descriptor when the response's body is just raw bytes.
type RequestDescriptor ¶ added in v3.17.0
RequestDescriptor describes the request.
type ResponseDescriptor ¶ added in v3.17.0
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.