retryhttp

package
v6.0.4 Latest Latest
Warning

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

Go to latest
Published: Jun 10, 2024 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package httputil implements utility for httpClient with request retry and optional tracing

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrResponseFromUpstream = fmt.Errorf("HttpClient.Call: non 2xx status")

ErrResponseFromUpstream is an error returned when the upstream server returns a non-2xx status.

View Source
var ErrResponseUnmarshal = fmt.Errorf("error marshalling response body")

ErrResponseUnmarshal is an error returned when the response body cannot be unmarshalled.

Functions

func NewHTTPClient

func NewHTTPClient() *http.Client

NewHTTPClient creates and configures a new HTTP client with custom transport settings.

Types

type Backoff

type Backoff func(min, max time.Duration, attemptNum int, resp *http.Response) time.Duration

Backoff defines a function type for determining the backoff duration between retries.

type CheckRetry

type CheckRetry func(ctx context.Context, resp *http.Response, err error) (bool, error)

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

type HTTPClient struct {
	*http.Client
	// contains filtered or unexported fields
}

HTTPClient extends http.Client with following

  1. Default exponential backoff and retry mechanism
  2. Request body can be any object
  3. Optionally decode success response to the passed object
  4. Additional debug logging
  5. 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

func (h *HTTPClient) Do(req *http.Request) (*http.Response, error)

Do sends an HTTP request and performs retries with exponential backoff as needed, based on the retry and backoff configuration.

func (*HTTPClient) Encode

func (h *HTTPClient) Encode(ctx context.Context, data interface{}) (io.Reader, error)

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

func WithBackoff(backoff Backoff) Option

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

func WithHTTPClient(client *http.Client) Option

WithHTTPClient sets the HTTP client for the config.

func WithLog

func WithLog(log log.Log) Option

WithLog sets the log instance for the config.

func WithMaxRetryWait

func WithMaxRetryWait(maxRetryWait time.Duration) Option

WithMaxRetryWait sets the maximum retry wait duration for the config.

func WithMinRetryWait

func WithMinRetryWait(minRetryWait time.Duration) Option

WithMinRetryWait sets the minimum retry wait duration for the config.

func WithRetryMax

func WithRetryMax(retryMax uint) Option

WithRetryMax sets the maximum number of retries for the config.

func WithTracer

func WithTracer(tr Tracer) Option

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.

Jump to

Keyboard shortcuts

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