The package provides a very thin wrapper (no external dependencies) for http.Client
, allowing to use of layers (middlewares) on http.RoundTripper
level. The goal is to keep the way users leverage stdlib http client but add a few useful extras on top of the standard http.Client
.
Pls note: this is not a replacement for http.Client but rather a companion library.
rq := requester.New( // make the requester
http.Client{Timeout: 5*time.Second}, // set http client
requester.MaxConcurrent(8), // maximum number of concurrent requests
requester.JSON, // set json headers
requester.Header("X-AUTH", "123456789"),// set some auth header
requester.Logger(requester.StdLogger), // enable logging to stdout
)
req := http.NewRequest("GET", "http://example.com/api", nil) // create the usual http.Request
req.Header.Set("foo", "bar") // do the usual things with request, for example set some custome headers
resp, err := rq.Do(req) // instead of client.Do call requester.Do
go get -u github.com/go-pkgz/requester
Header
- appends user-defined headers to all requests.JSON
- sets headers"Content-Type": "application/json"
and"Accept": "application/json"
BasicAuth(user, passwd string)
- adds HTTP Basic AuthenticationMaxConcurrent
- sets maximum concurrencyRepeater
- sets repeater to retry failed requests. Doesn't provide repeater implementation but wraps it. Compatible with any repeater (for example go-pkgz/repeater) implementing a single method interfaceDo(ctx context.Context, fun func() error, errors ...error) (err error)
interface.Cache
- sets anyLoadingCache
implementation to be used for request/response caching. Doesn't provide cache, but wraps it. Compatible with any cache (for example a family of caches from go-pkgz/lcw) implementing a single-method interfaceGet(key string, fn func() (interface{}, error)) (val interface{}, err error)
Logger
- sets logger, compatible with any implementation of a single-method interfaceLogf(format string, args ...interface{})
, for example go-pkgz/lgrCircuitBreaker
- sets circuit breaker, interface compatible with sony/gobreaker
Users can add any custom middleware. All it needs is a handler RoundTripperHandler func(http.RoundTripper) http.RoundTripper
.
Convenient functional adapter middleware.RoundTripperFunc
provided.
See examples of the usage in _example
Logger should implement Logger
interface with a single method Logf(format string, args ...interface{})
.
For convenience, func type LoggerFunc
is provided as an adapter to allow the use of ordinary functions as Logger
.
Two basic implementation included:
NoOpLogger
do-nothing logger (default)StdLogger
wrapper for stdlib logger.
logging options:
Prefix(prefix string)
sets prefix for each logged lineWithBody
- allows request's body loggingWithHeaders
- allows request's headers logging
Note: if logging is allowed, it will log URL, method, and may log headers and the request body. It may affect application security. For example, if a request passes some sensitive info as a part of the body or header. In this case, consider turning logging off or provide your own logger suppressing all you need to hide.
MaxConcurrent middleware can be used to limit the concurrency of a given requester and limit overall concurrency for multiple
requesters. For the first case, MaxConcurrent(N)
should be created in the requester chain of middlewares, for example, rq := requester.New(http.Client{Timeout: 3 * time.Second}, middleware.MaxConcurrent(8))
. To make it global, MaxConcurrent
should be created once, outside the chain and passed into each requester, i.e.
mc := middleware.MaxConcurrent(16)
rq1 := requester.New(http.Client{Timeout: 3 * time.Second}, mc)
rq2 := requester.New(http.Client{Timeout: 1 * time.Second}, middleware.JSON, mc)
If request was limited, it will wait till the limit is released.
Cache expects LoadingCache
interface implementing a single method:
Get(key string, fn func() (interface{}, error)) (val interface{}, err error)
. LCW can
be used directly, and in order to adopt other caches see provided LoadingCacheFunc
.
By default, only GET
calls are cached. This can be changed with Methods(methods ...string)
option.
The default key composed of the full URL.
Several options are defining what part of the request will be used for the key:
KeyWithHeaders
- adds all headers to a keyKeyWithHeadersIncluded(headers ...string)
- adds only requested headersKeyWithHeadersExcluded(headers ...string)
- adds all headers excludedKeyWithBody
- adds request's body, limited to the first 16k of the bodyKeyFunc
- any custom logic provided by the caller
example: cache.New(lruCache, cache.Methods("GET", "POST"), cache.KeyFunc() {func(r *http.Request) string {return r.Host})
Cache
is not compatible with http streaming mode. Practically, this is rare and exotic, but allowing Cache
will effectively transform the streaming response to a "get all" typical response. It is due to cache
has to read response body fully to save it, so technically streaming will be working, but the client will get
all the data at once.
Repeater
expects a single method interface Do(fn func() error, failOnCodes ...error) (err error)
. repeater can be used directly.
By default, the repeater will retry on any error and any status code >=400.
However, user can pass failOnCodes
to explicitly define what status code should be treated as errors and retry only on those.
For example: Repeater(repeaterSvc, 500, 400)
repeats requests on 500 and 400 statuses only.
For a special case if user want to retry only on the underlying transport errors (network, timeouts, etc) and not on any status codes,
Repeater(repeaterSvc, 0)
can be used.
Users can add any additional handlers (middleware) to the chain. Each middleware provides middleware.RoundTripperHandler
and
can alter the request or implement any other custom functionality.
Example of a handler resetting particular header:
maskHeader := func(http.RoundTripper) http.RoundTripper {
fn := func(req *http.Request) (*http.Response, error) {
req.Header.Del("deleteme")
return next(req)
}
return middleware.RoundTripperFunc(fn)
}
rq := requester.New(http.Client{}, maskHeader)
There are 3 ways to add middleware(s):
- Pass it to
New
constructor, i.e.requester.New(http.Client{}, middleware.MaxConcurrent(8), middleware.Header("foo", "bar"))
- Add after construction with
Use
method - Create new, inherited requester by using
With
:
rq := requester.New(http.Client{}, middleware.Header("foo", "bar")) // make requester enforcing header foo:bar
resp, err := rq.Do(some_http_req) // send a request
rqLimited := rq.With(middleware.MaxConcurrent(8)) // make requester from rq (foo:bar enforced) and add 8 max concurrency
resp, err := rqLimited.Do(some_http_req)
For convenience requester.Client()
returns *http.Client
with all middlewares injected in. From this point user can call Do
of this client, and it will invoke the request with all the middlewares.
CircuitBreakerFunc func(req func() (interface{}, error)) (interface{}, error)
- adapter to allow the use of an ordinary functions as CircuitBreakerSvc.logger.Func func(format string, args ...interface{})
- functional adapter forlogger.Service
.cache.ServiceFunc func(key string, fn func() (interface{}, error)) (interface{}, error)
- functional adapter forcache.Service
.RoundTripperFunc func(*http.Request) (*http.Response, error)
- functional adapter for RoundTripperHandler