Documentation ¶
Overview ¶
Package httputil implements utility for httpClient with request retry and optional tracing
Index ¶
- Variables
- func NewHTTPClient() *http.Client
- type Backoff
- type CheckRetry
- type Config
- type HTTPClient
- func (h *HTTPClient) Call(ctx context.Context, method, url string, reqBody, resBody interface{}, ...) (*http.Response, error)
- func (h *HTTPClient) Decode(ctx context.Context, body []byte, data interface{}) (io.ReadCloser, error)
- func (h *HTTPClient) Delete(ctx context.Context, url string, reqBody, resBody interface{}, ...) (*http.Response, error)
- func (h *HTTPClient) Do(req *http.Request) (*http.Response, error)
- func (h *HTTPClient) Encode(ctx context.Context, data interface{}) (io.Reader, error)
- func (h *HTTPClient) Get(ctx context.Context, url string, reqBody, resBody interface{}, ...) (*http.Response, error)
- func (h *HTTPClient) Patch(ctx context.Context, url string, reqBody, resBody interface{}, ...) (*http.Response, error)
- func (h *HTTPClient) Post(ctx context.Context, url string, reqBody, resBody interface{}, ...) (*http.Response, error)
- func (h *HTTPClient) Put(ctx context.Context, url string, reqBody, resBody interface{}, ...) (*http.Response, error)
- type Option
- func WithBackoff(backoff Backoff) Option
- func WithCheckRetry(checkRetry CheckRetry) Option
- func WithHTTPClient(client *http.Client) Option
- func WithLog(log log.Log) Option
- func WithMaxRetryWait(maxRetryWait time.Duration) Option
- func WithMinRetryWait(minRetryWait time.Duration) Option
- func WithRetryMax(retryMax uint) Option
- func WithTracer(tr Tracer) Option
- type Tracer
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrResponseFromUpstream = fmt.Errorf("HttpClient.Call: non 2xx status")
ErrResponseFromUpstream is an error returned when the upstream server returns a non-2xx status.
var ErrResponseUnmarshal = fmt.Errorf("error marshalling response body")
ErrResponseUnmarshal is an error returned when the response body cannot be unmarshalled.
Functions ¶
func NewHTTPClient ¶
NewHTTPClient creates and configures a new HTTP client with custom transport settings.
Types ¶
type Backoff ¶
Backoff defines a function type for determining the backoff duration between retries.
type CheckRetry ¶
CheckRetry defines a function type for determining if a request should be retried.
type Config ¶
type Config struct { Log log.Log // Log is the logger used for logging HTTP client activities. RetryMax uint // RetryMax is the maximum number of retry attempts for failed requests. MinRetryWait time.Duration // MinRetryWait is the minimum duration to wait before retrying a failed request. MaxRetryWait time.Duration // MaxRetryWait is the maximum duration to wait before retrying a failed request. CheckRetry CheckRetry // CheckRetry is the function to determine if a request should be retried. Backoff Backoff // Backoff is the function to determine the wait duration between retries. Tracer Tracer // Tracer is used for tracing HTTP requests (assuming it's defined elsewhere). Client *http.Client // Client is the underlying HTTP client used to make requests. }
Config contains the configuration settings for the HTTP client, including logging, retry policies, backoff strategies, and the HTTP client itself.
func GetDefaultConfig ¶
func GetDefaultConfig() Config
GetDefaultConfig returns a Config instance with default settings for the HTTP client.
type HTTPClient ¶
HTTPClient extends http.Client with following
- Default exponential backoff and retry mechanism
- Request body can be any object
- Optionally decode success response to the passed object
- Additional debug logging
- Custom tracing interface
Example ¶
package main import ( "context" "fmt" "github.com/sabariramc/goserverbase/v6/correlation" "github.com/sabariramc/goserverbase/v6/log" "github.com/sabariramc/goserverbase/v6/utils/retryhttp" ) var HTTPUtilTestLogger log.Log const ServiceName = "HTTPClientTest" func GetCorrelationContext() context.Context { ctx := context.WithValue(context.Background(), correlation.ContextKeyCorrelation, correlation.NewCorrelationParam(ServiceName)) return ctx } const URL = "http://localhost:64000/service/v1/echo/a/b" func main() { client := retryhttp.New(retryhttp.WithLog(HTTPUtilTestLogger)) data := make(map[string]any) res, err := client.Get(GetCorrelationContext(), URL, nil, &data, nil) fmt.Println(err) fmt.Println(res.StatusCode) }
Output: <nil> 200
Example (Errorresponsebody) ¶
package main import ( "context" "errors" "fmt" "github.com/sabariramc/goserverbase/v6/correlation" "github.com/sabariramc/goserverbase/v6/log" "github.com/sabariramc/goserverbase/v6/utils/retryhttp" ) var HTTPUtilTestLogger log.Log const MIMEJSON = "application/json" const ServiceName = "HTTPClientTest" func GetCorrelationContext() context.Context { ctx := context.WithValue(context.Background(), correlation.ContextKeyCorrelation, correlation.NewCorrelationParam(ServiceName)) return ctx } const URL = "http://localhost:64000/service/v1/echo/a/b" func main() { client := retryhttp.New(retryhttp.WithLog(HTTPUtilTestLogger)) response := 0 body := map[string]string{ "tag": "Test", } _, err := client.Post(GetCorrelationContext(), URL, &body, &response, map[string]string{ContentTypeHeader: MIMEJSON}) fmt.Println(errors.Is(err, retryhttp.ErrResponseUnmarshal)) }
Output: true
Example (Responsebody) ¶
package main import ( "context" "fmt" "github.com/sabariramc/goserverbase/v6/correlation" "github.com/sabariramc/goserverbase/v6/log" "github.com/sabariramc/goserverbase/v6/utils/retryhttp" ) var HTTPUtilTestLogger log.Log const MIMEJSON = "application/json" const ServiceName = "HTTPClientTest" const URL = "http://localhost:64000/service/v1/echo/a/b" func main() { client := retryhttp.New(retryhttp.WithLog(HTTPUtilTestLogger)) response := map[string]any{} // object to decode response body body := map[string]string{ "tag": "Test", } //URL is a echo endpoint that returns the whole request as response body ctx := context.WithValue(context.Background(), correlation.ContextKeyCorrelation, &correlation.CorrelationParam{ CorrelationID: ServiceName, }) res, err := client.Post(ctx, URL, &body, &response, map[string]string{ContentTypeHeader: MIMEJSON}) fmt.Println(err) fmt.Println(res.StatusCode) fmt.Printf("%+v", response) }
Output: <nil> 200 map[body:map[tag:Test] headers:map[accept-encoding:gzip connection:close content-length:15 content-type:application/json host:backend user-agent:Go-http-client/1.1 x-correlation-id:HTTPClientTest] method:POST pathParams:map[path_1:a path_2:b] url:http://backend/service/v1/echo/a/b]
Example (Retry) ¶
package main import ( "context" "fmt" "github.com/sabariramc/goserverbase/v6/correlation" "github.com/sabariramc/goserverbase/v6/log" "github.com/sabariramc/goserverbase/v6/utils/retryhttp" ) var HTTPUtilTestLogger log.Log const ServiceName = "HTTPClientTest" const RetryURL = "http://localhost:64000/service/v1/echo/error/b" func main() { client := retryhttp.New(retryhttp.WithLog(HTTPUtilTestLogger)) data := make(map[string]any) body := map[string]any{ "tag": "Test", } ctx := context.WithValue(context.Background(), correlation.ContextKeyCorrelation, &correlation.CorrelationParam{ CorrelationID: ServiceName, }) //RetryURL is a endpoint that returns the always returns 500 res, err := client.Call(ctx, "POST", RetryURL, &body, &data, nil) fmt.Println(err) fmt.Println(res.StatusCode) }
Output: [2024-05-21T14:33:04.793+05:30] [INFO] [HTTPClientTest] [default] [log] [Request] [map[string]interface {}] [{ "headers": { "X-Correlation-Id": [ "HTTPClientTest" ] }, "method": "POST", "url": "http://localhost:64000/service/v1/echo/error/b" }] [2024-05-21T14:33:04.801+05:30] [NOTICE] [HTTPClientTest] [default] [log] [request failed with status code 500 retry 1 of 4 in 10ms] [string] [{"url": "http://backend/service/v1/echo/error/b", "headers": {"host": "backend", "connection": "close", "content-length": "15", "user-agent": "Go-http-client/1.1", "x-correlation-id": "HTTPClientTest", "accept-encoding": "gzip"}, "method": "POST", "body": {"tag": "Test"}, "pathParams": {"path_1": "error", "path_2": "b"}}] [2024-05-21T14:33:04.829+05:30] [NOTICE] [HTTPClientTest] [default] [log] [request failed with status code 500 retry 2 of 4 in 20ms] [string] [{"url": "http://backend/service/v1/echo/error/b", "headers": {"host": "backend", "connection": "close", "content-length": "15", "user-agent": "Go-http-client/1.1", "x-correlation-id": "HTTPClientTest", "accept-encoding": "gzip"}, "method": "POST", "body": {"tag": "Test"}, "pathParams": {"path_1": "error", "path_2": "b"}}] [2024-05-21T14:33:04.868+05:30] [NOTICE] [HTTPClientTest] [default] [log] [request failed with status code 500 retry 3 of 4 in 40ms] [string] [{"url": "http://backend/service/v1/echo/error/b", "headers": {"host": "backend", "connection": "close", "content-length": "15", "user-agent": "Go-http-client/1.1", "x-correlation-id": "HTTPClientTest", "accept-encoding": "gzip"}, "method": "POST", "body": {"tag": "Test"}, "pathParams": {"path_1": "error", "path_2": "b"}}] [2024-05-21T14:33:04.930+05:30] [NOTICE] [HTTPClientTest] [default] [log] [request failed with status code 500 retry 4 of 4 in 80ms] [string] [{"url": "http://backend/service/v1/echo/error/b", "headers": {"host": "backend", "connection": "close", "content-length": "15", "user-agent": "Go-http-client/1.1", "x-correlation-id": "HTTPClientTest", "accept-encoding": "gzip"}, "method": "POST", "body": {"tag": "Test"}, "pathParams": {"path_1": "error", "path_2": "b"}}] [2024-05-21T14:33:05.031+05:30] [ERROR] [HTTPClientTest] [default] [log] [Response] [map[string]interface {}] [{ "headers": { "Connection": [ "keep-alive" ], "Content-Length": [ "322" ], "Date": [ "Tue, 21 May 2024 09:03:05 GMT" ], "Server": [ "nginx/1.26.0" ] }, "statusCode": 500 }] HttpClient.Call: non 2xx status 500
func New ¶
func New(options ...Option) *HTTPClient
New creates a new HTTPClient wrapper with the provided options.
func NewH2CClient ¶
func NewH2CClient(options ...Option) *HTTPClient
NewH2CClient creates a new HTTPClient configured to support HTTP/2 Cleartext (h2c) servers.
func (*HTTPClient) Call ¶
func (h *HTTPClient) Call(ctx context.Context, method, url string, reqBody, resBody interface{}, headers map[string]string) (*http.Response, error)
Call pre-processes the HTTP request and response, performing retries and backoff as needed. It sends the request and decodes the response body if provided.
func (*HTTPClient) Decode ¶
func (h *HTTPClient) Decode(ctx context.Context, body []byte, data interface{}) (io.ReadCloser, error)
Decode unmarshals the provided byte slice into the provided object and returns an io.ReadCloser for the response body.
func (*HTTPClient) Delete ¶
func (h *HTTPClient) Delete(ctx context.Context, url string, reqBody, resBody interface{}, headers map[string]string) (*http.Response, error)
Delete sends an HTTP DELETE request to the specified URL with the provided request and response bodies, and headers, returning the HTTP response.
func (*HTTPClient) Do ¶
Do sends an HTTP request and performs retries with exponential backoff as needed, based on the retry and backoff configuration.
func (*HTTPClient) Encode ¶
Encode marshals the provided data into an io.Reader object for the request body.
func (*HTTPClient) Get ¶
func (h *HTTPClient) Get(ctx context.Context, url string, reqBody, resBody interface{}, headers map[string]string) (*http.Response, error)
Get sends an HTTP GET request to the specified URL with the provided request and response bodies, and headers, returning the HTTP response.
func (*HTTPClient) Patch ¶
func (h *HTTPClient) Patch(ctx context.Context, url string, reqBody, resBody interface{}, headers map[string]string) (*http.Response, error)
Patch sends an HTTP PATCH request to the specified URL with the provided request and response bodies, and headers, returning the HTTP response.
func (*HTTPClient) Post ¶
func (h *HTTPClient) Post(ctx context.Context, url string, reqBody, resBody interface{}, headers map[string]string) (*http.Response, error)
Post sends an HTTP POST request to the specified URL with the provided request and response bodies, and headers, returning the HTTP response.
func (*HTTPClient) Put ¶
func (h *HTTPClient) Put(ctx context.Context, url string, reqBody, resBody interface{}, headers map[string]string) (*http.Response, error)
Put sends an HTTP PUT request to the specified URL with the provided request and response bodies, and headers, returning the HTTP response.
type Option ¶
type Option func(*Config)
Option represents an option function for configuring the config struct.
func WithBackoff ¶
WithBackoff sets the backoff strategy for the config.
func WithCheckRetry ¶
func WithCheckRetry(checkRetry CheckRetry) Option
WithCheckRetry sets the check retry function for the config.
func WithHTTPClient ¶
WithHTTPClient sets the HTTP client for the config.
func WithMaxRetryWait ¶
WithMaxRetryWait sets the maximum retry wait duration for the config.
func WithMinRetryWait ¶
WithMinRetryWait sets the minimum retry wait duration for the config.
func WithRetryMax ¶
WithRetryMax sets the maximum number of retries for the config.
func WithTracer ¶
WithTracer sets the tracer instance for the config.
type Tracer ¶
type Tracer interface { HTTPWrapTransport(http.RoundTripper) http.RoundTripper HTTPRequestTrace(context.Context) *httptrace.ClientTrace span.SpanOp }
Tracer defines an interface for custom tracing implementations.