httpcli

package
v1.54.1 Latest Latest
Warning

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

Go to latest
Published: Aug 21, 2025 License: MIT Imports: 13 Imported by: 1

README

Package httpcli

Пакет httpcli предоставляет расширенный HTTP-клиент с поддержкой middleware, ретраев, различных типов запросов и обработки ошибок. Интегрируется с системами логирования, метриками и политиками повторов.

Types

Client

Основная структура для выполнения HTTP-запросов. Управляет настройками транспорта, middleware и глобальными параметрами.

Methods:

New(opts ...Option) *Client

Конструктор клиента с настройками по умолчанию. Принимает опции:

  • WithMiddlewares(mws ...Middleware) Option – добавляет цепочку middleware к запросам клиента.
NewWithClient(cli *http.Client, opts ...Option) *Client

Конструктор клиента на основе базового клиента из стандартной библиотеки net/http.

(c *Client) GlobalRequestConfig() *GlobalRequestConfig

Получить глобальную конфигурацию запросов (таймауты, базовый URL, заголовки и т.д.).

(c *Client) Post(url string) *RequestBuilder

Создать билдер для POST-запроса. Аналогичные методы: Get(), Put(), Delete(), Patch().

(c *Client) Execute(ctx context.Context, builder *RequestBuilder) (*Response, error)

Выполнить HTTP-запрос из переданного билдера.

RequestBuilder

Структура для построения HTTP-запросов с цепочкой методов.

Methods:

(b *RequestBuilder) Header(name string, value string) *RequestBuilder

Добавить заголовок к запросу.

(b *RequestBuilder) BaseUrl(baseUrl string) *RequestBuilder

Установить базовый url в запросе на переданный.

(b *RequestBuilder) Url(url string) *RequestBuilder

Установить url запроса на переданный.

(b *RequestBuilder) Method(method string) *RequestBuilder

Изменить HTTP-метод запроса на указанный.

Добавить cookie к запросу.

(b *RequestBuilder) RequestBody(body []byte) *RequestBuilder

Добавить тело запроса.

(b *RequestBuilder) JsonRequestBody(value any) *RequestBuilder

Добавить тело запроса в формате JSON. Использует пакет json, поэтому может маршалить без тэгов в camelCase.

(b *RequestBuilder) JsonResponseBody(responsePtr any) *RequestBuilder

Записать JSON-ответ на запрос по переданному указателю. Операция выполнится только при статус кодах от 200 до 299.

(b *RequestBuilder) FormDataRequestBody(data map[string][]string) *RequestBuilder

Добавить тело запроса в формате формы.

(b *RequestBuilder) BasicAuth(ba BasicAuth) *RequestBuilder

Добавить базую аутентификацию в запрос.

(b *RequestBuilder) QueryParams(queryParams map[string]any) *RequestBuilder

Добавить query-параметры в запрос.

(b *RequestBuilder) Retry(cond RetryCondition, retryer Retryer) *RequestBuilder

Задать политику ретраев по заданному условию. Пример условия: httpcli.IfErrorOr5XXStatus()

(b *RequestBuilder) MultipartRequestBody(data *MultipartData) *RequestBuilder

Устанавить multipart/form-data тело запроса (для передачи файлов). Не поддерживает Request.Body в middleware и игнорирует ретраи.

(b *RequestBuilder) StatusCodeToError() *RequestBuilder

Возвращает ErrorResponse как ошибку при Response.IsSuccess = true.

(b *RequestBuilder) Timeout(timeout time.Duration) *RequestBuilder

Установить тайм-аут для каждой попытки запроса, тайм-аут по умолчанию 15 секунд.

(b *RequestBuilder) Middlewares(middlewares ...Middleware) *RequestBuilder

Добавить middleware в цепочку запроса.

(b *RequestBuilder) Do(ctx context.Context) (*Response, error)

Выполнить запрос собранный билдером.

(b *RequestBuilder) DoWithoutResponse(ctx context.Context) error

То же самое, что и Do, но записывает ответ по переданному раннее в билдере указателю.

(b *RequestBuilder) DoWithoutResponse(ctx context.Context) error

То же самое, что и Do, но не возвращает объект Response. Записывает тело ответа по переданному в метод JsonResponseBody указателю.

(b *RequestBuilder) DoAndReadBody(ctx context.Context) ([]byte, int, error)

То же самое, что и Do, но считывает из ответа только тело и статус код, а затем возвращает их.

Usage

Default
package main

import (
	"context"
	"log"

	"github.com/txix-open/isp-kit/http/httpcli"
)

type user struct {
	Id   string
	Name string
}

func main() {
	cli := httpcli.New()
	u := new(user)

	resp, err := cli.Get("https://api.example.com/users/1").
		JsonResponseBody(u).
		Do(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Close()

	if !resp.IsSuccess() {
		log.Fatalf("invalid status code: %d", resp.StatusCode())
	}
	log.Println(u.Name) /* result decoded from JSON */
}

Without response
package main

import (
	"context"
	"log"

	"github.com/txix-open/isp-kit/http/httpcli"
)

type user struct {
	Id   string
	Name string
}

func main() {
	cli := httpcli.New()
	u := new(user)

	err := cli.Get("https://api.example.com/users/1").
		JsonResponseBody(u).
		StatusCodeToError().
		DoWithoutResponse(context.Background())
	if err != nil {
		log.Fatal(err)
	}
	log.Println(u.Name) /* result decoded from JSON */
}

Exponential retries
package main

import (
	"context"
	"log"
	"time"

	"github.com/txix-open/isp-kit/http/httpcli"
	"github.com/txix-open/isp-kit/retry"
)

type user struct {
	Id   string
	Name string
}

const (
	maxRetryElapsedTime = 5 * time.Second
)

func main() {
	cli := httpcli.New()
	u := user{
		Id:   "x.x",
		Name: "Mark",
	}

	err := cli.Post("https://api.example.com/users").
		JsonRequestBody(u).
		Retry(httpcli.IfErrorOr5XXStatus(), retry.NewExponentialBackoff(maxRetryElapsedTime)).
		StatusCodeToError().
		DoWithoutResponse(context.Background())
	if err != nil {
		log.Fatal(err)
	}
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ReadingResponseMetricHookKey = ReadingResponseMetricHook{}
)

nolint:gochecknoglobals

View Source
var (
	StdClient = &http.Client{
		Transport: &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			DialContext: defaultTransportDialContext(&net.Dialer{
				Timeout:   3 * time.Second,
				KeepAlive: 30 * time.Second,
			}),
			ForceAttemptHTTP2:     true,
			MaxIdleConns:          512,
			MaxIdleConnsPerHost:   32,
			IdleConnTimeout:       90 * time.Second,
			TLSHandshakeTimeout:   5 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
			ReadBufferSize:        8 * 1024,
			WriteBufferSize:       8 * 1024,
		},
	}
)

nolint:mnd,gochecknoglobals

Functions

func CreateFormFile added in v1.41.3

func CreateFormFile(
	w *multipart.Writer,
	header map[string]string,
	fieldname string,
	filename string,
) (io.Writer, error)

func NoRetries

func NoRetries() (RetryCondition, Retryer)

nolint:ireturn

Types

type BasicAuth

type BasicAuth struct {
	Username string
	Password string
}

type Client

type Client struct {
	// contains filtered or unexported fields
}

func New

func New(opts ...Option) *Client

func NewWithClient

func NewWithClient(cli *http.Client, opts ...Option) *Client

func (*Client) Delete

func (c *Client) Delete(url string) *RequestBuilder

func (*Client) Execute added in v1.41.2

func (c *Client) Execute(ctx context.Context, builder *RequestBuilder) (*Response, error)

nolint:cyclop

func (*Client) Get

func (c *Client) Get(url string) *RequestBuilder

func (*Client) GlobalRequestConfig

func (c *Client) GlobalRequestConfig() *GlobalRequestConfig

func (*Client) Patch

func (c *Client) Patch(url string) *RequestBuilder

func (*Client) Post

func (c *Client) Post(url string) *RequestBuilder

func (*Client) Put

func (c *Client) Put(url string) *RequestBuilder

type ErrorResponse

type ErrorResponse struct {
	Url        *url.URL
	StatusCode int
	Body       []byte
}

nolint:errname

func (ErrorResponse) Error

func (e ErrorResponse) Error() string

type GlobalRequestConfig

type GlobalRequestConfig struct {
	Timeout   time.Duration
	BaseUrl   string
	BasicAuth *BasicAuth
	Cookies   []*http.Cookie
	Headers   map[string]string
}

func NewGlobalRequestConfig

func NewGlobalRequestConfig() *GlobalRequestConfig

type Middleware

type Middleware func(next RoundTripper) RoundTripper

func SetContentLength added in v1.39.0

func SetContentLength() Middleware

type MultipartData

type MultipartData struct {
	Files  map[string]MultipartFieldFile
	Values map[string]string
}

type MultipartFieldFile

type MultipartFieldFile struct {
	Headers  map[string]string
	Filename string
	Reader   io.ReadCloser
}

type Option

type Option func(c *Client)

func WithMiddlewares

func WithMiddlewares(mws ...Middleware) Option

type ReadingResponseMetricHook added in v1.38.0

type ReadingResponseMetricHook struct{}

type Request

type Request struct {
	Raw *http.Request
	// contains filtered or unexported fields
}

func (*Request) Body

func (r *Request) Body() []byte

Body Returns request body in bytes Always returns empty slice if you use RequestBuilder.MultipartRequestBody

type RequestBodyWriter

type RequestBodyWriter interface {
	Write(req *http.Request, w io.Writer) error
}

type RequestBuilder

type RequestBuilder struct {
	// contains filtered or unexported fields
}

func NewRequestBuilder

func NewRequestBuilder(
	method string,
	url string,
	cfg *GlobalRequestConfig,
	execute func(ctx context.Context, req *RequestBuilder) (*Response, error),
) *RequestBuilder

func (*RequestBuilder) BaseUrl added in v1.41.2

func (b *RequestBuilder) BaseUrl(baseUrl string) *RequestBuilder

func (*RequestBuilder) BasicAuth

func (b *RequestBuilder) BasicAuth(ba BasicAuth) *RequestBuilder

func (*RequestBuilder) Cookie

func (b *RequestBuilder) Cookie(cookie *http.Cookie) *RequestBuilder

func (*RequestBuilder) Do

func (b *RequestBuilder) Do(ctx context.Context) (*Response, error)

func (*RequestBuilder) DoAndReadBody

func (b *RequestBuilder) DoAndReadBody(ctx context.Context) ([]byte, int, error)

func (*RequestBuilder) DoWithoutResponse

func (b *RequestBuilder) DoWithoutResponse(ctx context.Context) error

func (*RequestBuilder) FormDataRequestBody

func (b *RequestBuilder) FormDataRequestBody(data map[string][]string) *RequestBuilder

func (*RequestBuilder) Header

func (b *RequestBuilder) Header(name string, value string) *RequestBuilder

func (*RequestBuilder) JsonRequestBody

func (b *RequestBuilder) JsonRequestBody(value any) *RequestBuilder

func (*RequestBuilder) JsonResponseBody

func (b *RequestBuilder) JsonResponseBody(responsePtr any) *RequestBuilder

JsonResponseBody If response status code between 200 and 299, unmarshal response body to responsePtr

func (*RequestBuilder) Method added in v1.41.2

func (b *RequestBuilder) Method(method string) *RequestBuilder

func (*RequestBuilder) Middlewares added in v1.39.0

func (b *RequestBuilder) Middlewares(middlewares ...Middleware) *RequestBuilder

func (*RequestBuilder) MultipartRequestBody

func (b *RequestBuilder) MultipartRequestBody(data *MultipartData) *RequestBuilder

MultipartRequestBody Useful function to transfer "big" files because it does not load files into memory Does not support Request.Body in middlewares and ignores Retry

func (*RequestBuilder) QueryParams

func (b *RequestBuilder) QueryParams(queryParams map[string]any) *RequestBuilder

func (*RequestBuilder) RequestBody

func (b *RequestBuilder) RequestBody(body []byte) *RequestBuilder

func (*RequestBuilder) Retry

func (b *RequestBuilder) Retry(cond RetryCondition, retryer Retryer) *RequestBuilder

func (*RequestBuilder) StatusCodeToError

func (b *RequestBuilder) StatusCodeToError() *RequestBuilder

StatusCodeToError If set and Response.IsSuccess is false, Do return ErrorResponse as error

func (*RequestBuilder) Timeout

func (b *RequestBuilder) Timeout(timeout time.Duration) *RequestBuilder

Timeout Set per request attempt timeout, default timeout 15 seconds

func (*RequestBuilder) Url added in v1.41.2

func (b *RequestBuilder) Url(url string) *RequestBuilder

type Response

type Response struct {
	Raw *http.Response
	// contains filtered or unexported fields
}

func (*Response) BodyCopy

func (r *Response) BodyCopy() ([]byte, error)

BodyCopy Return copy of response body Slice is available after calling Close

func (*Response) Close

func (r *Response) Close()

Close Release all resources associated with Response (buffer, tcp connection, context) After call, bytes slice returned by Body can not be used

func (*Response) IsSuccess

func (r *Response) IsSuccess() bool

func (*Response) StatusCode

func (r *Response) StatusCode() int

func (*Response) UnsafeBody added in v1.51.0

func (r *Response) UnsafeBody() ([]byte, error)

UnsafeBody Read and return full response body Be careful, after calling Close returned data is no longer available Do not call close or copy slice if you want to use data outside the calling function

type ResponseBodyReader

type ResponseBodyReader interface {
	Read(r io.Reader) error
}

type RetryCondition

type RetryCondition func(err error, response *Response) error

func IfErrorOr5XXStatus

func IfErrorOr5XXStatus() RetryCondition

type Retryer

type Retryer interface {
	Do(ctx context.Context, f func() error) error
}

type RoundTripper

type RoundTripper interface {
	RoundTrip(ctx context.Context, request *Request) (*Response, error)
}

type RoundTripperFunc

type RoundTripperFunc func(ctx context.Context, request *Request) (*Response, error)

func (RoundTripperFunc) RoundTrip

func (f RoundTripperFunc) RoundTrip(ctx context.Context, request *Request) (*Response, error)

Jump to

Keyboard shortcuts

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