request

package module
v0.0.2 Latest Latest
Warning

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

Go to latest
Published: Oct 31, 2024 License: MIT Imports: 15 Imported by: 0

README

go-http-client

Installation

$ go get -u github.com/yeldisbayev/request

Example usage

package main

import (
	"context"
	"io"
	"log"
	"request"
)

type Todo struct {
	ID        int64  `json:"id"`
	UserID    int64  `json:"userID"`
	Title     string `json:"title"`
	Completed bool   `json:"completed"`
}

func main() {
	client := request.NewClient()

	res, err := client.Request().Get(context.Background(), "https://jsonplaceholder.typicode.com/todos/1")
	if err != nil {
		log.Fatalf("Request error: %v", err)
	}

	defer func(body io.ReadCloser) {
		_ = body.Close()
	}(res.Body)

	var todo Todo

	if res.IsSuccess() {
		if err := res.Decoder().Decode(&todo); err != nil {
			log.Fatalf("Decode error: %v", err)
		}
	}

	log.Printf(
		"todo = (id: %d, userId: %d, title: %s, completed: %t)",
		todo.ID,
		todo.UserID,
		todo.Title,
		todo.Completed,
	)

}

Client

Options

There is Options to set up Client

WithTimeout

Sets timeout for all client requests. Timeout implemented without using http.Client's Timeout property, but with context. Client timeout has lesser priority than Request timeout property. If not provided, DefaultTimeout is used.

timeout := time.Second * 5

client := request.NewClient(request.WithTimeout(timeout))
WithIdleConnectionTimeout

Controls amount of time an idle (keep-alive) connection will remain idle before closing itself. If not provided, DefaultIdleConnectionTimeout is used.

idleConnectionTimeout := time.Second * 30

client := request.NewClient(request.WithIdleConnectionTimeout(idleConnectionTimeout))
WithMaxIdleConnections

Controls the maximum number of idle (keep-alive) connections across all hosts. If not provided, DefaultMaxIdleConnections is used.

maxIdleConnections := 20

client := request.NewClient(request.WithMaxIdleConnections(maxIdleConnections))
WithMaxConnectionsPerHost

Optionally limits the total number of connections per host, including connections in the dialing, active, and idle states. On limit violation, dials will block. If not provided, DefaultMaxConnectionsPerHost is used.

maxConnectionsPerHost := 30

client := request.NewClient(request.WithMaxConnectionsPerHost(maxConnectionsPerHost))
WithMaxIdleConnectionsPerHost

Controls the maximum idle (keep-alive) connections to keep per-host. If not provided, DefaultMaxIdleConnectionsPerHost is used.

maxIdleConnectionsPerHost := 30

client := request.NewClient(request.WithMaxIdleConnectionsPerHost(maxIdleConnectionsPerHost))
WithForceAttemptHTTP2

Controls whether HTTP/2 is enabled when a non-zero Dial, DialTLS, or DialContext func or TLSClientConfig is provided. By default, use of any those fields conservatively disables HTTP/2. To use a custom dialer or TLS config and still attempt HTTP/2 upgrades, set this to true. If not provided, DefaultForceAttemptHTTP2 is used.

forceAttemptHTTP2 := true

client := request.NewClient(request.WithForceAttemptHTTP2(forceAttemptHTTP2))
WithInterceptors

Wraps Client with given interceptors

retry := request.Retry()

client := request.NewClient(request.WithInterceptors(retry))

Interceptor

Interceptor wraps http Transport and calls before or after due to client usage. In order to create custom one Interceptor function implementation is needed. There is also built in Retry interceptor and its should be at the end in interceptors chain.

package main

import (
	"context"
	"io"
	"log"
	"net/http"
	"request"
	"time"
)

func FirstInterceptor(rt http.RoundTripper) http.RoundTripper {
	return req.RoundTripper( // Wrap custom interceptor with req.RoundTripper
		func(req *http.Request) (res *http.Response, err error) {
			// Before request started
			log.Println("FirstInterceptor started")

			res, err = rt.RoundTrip(req)
            
			// After request completed
			log.Println("FirstInterceptor finished")

			return res, err

		},
	)
}

func SecondInterceptor(rt http.RoundTripper) http.RoundTripper {
	return req.RoundTripper( // Wrap custom interceptor with req.RoundTripper
		func(req *http.Request) (res *http.Response, err error) {
			// Before request started
			log.Println("SecondInterceptor started")
			startTime := time.Now()

			res, err = rt.RoundTrip(req)

			// After request completed
			log.Println("SecondInterceptor", "Request Duration", time.Since(startTime))
			log.Println("SecondInterceptor finished")

			return res, err

		},
	)
}

type Todo struct {
	ID        int64  `json:"id"`
	UserID    int64  `json:"userID"`
	Title     string `json:"title"`
	Completed bool   `json:"completed"`
}

func main() {
	client := request.NewClient(
		request.WithInterceptors(FirstInterceptor, SecondInterceptor), 
	)

	res, err := client.Request().Get(context.Background(), "https://jsonplaceholder.typicode.com/todos/1")
	if err != nil {
		log.Fatalf("Request error: %v", err)
	}

	defer func(body io.ReadCloser) {
		_ = body.Close()
	}(res.Body)

	var todo Todo

	if res.IsSuccess() {
		if err := res.Decoder().Decode(&todo); err != nil {
			log.Fatalf("Decode error: %v", err)
		}
	}

	log.Println(todo)

}
 FirstInterceptor started
 
 SecondInterceptor started
 
 SecondInterceptor Request Duration 697.908708ms
 
 SecondInterceptor finished
 
 FirstInterceptor finished
 
 {1 1 delectus aut autem false}

License

MIT License

Copyright (c) 2024 Duisen Yeldisbayev

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Documentation

Index

Constants

View Source
const (
	DefaultTimeout                   = 30 * time.Second
	DefaultIdleConnectionTimeout     = 90 * time.Second
	DefaultMaxIdleConnections        = 100
	DefaultMaxConnectionsPerHost     = 15
	DefaultMaxIdleConnectionsPerHost = 10
	DefaultForceAttemptHTTP2         = true
)
View Source
const (
	ContentType               = "Content-Type"
	ApplicationJSON           = "application/json"
	ApplicationXML            = "application/xml"
	ApplicationFormUrlencoded = "application/x-www-form-urlencoded"
	MultipartFormData         = "multipart/form-data"

	Authorization = "Authorization"
	Basic         = "Basic"
	Bearer        = "Bearer"
	JWT           = "JWT"
)

Variables

View Source
var (
	ErrNoBody = errors.New("no body")
)

Functions

func Queries

func Queries[T Query](values ...T) []string

func WithForceAttemptHTTP2

func WithForceAttemptHTTP2(forceAttemptHTTP2 bool) func(*client)

WithForceAttemptHTTP2 controls whether HTTP/2 is enabled when a non-zero Dial, DialTLS, or DialContext func or TLSClientConfig is provided. By default, use of any those fields conservatively disables HTTP/2. To use a custom dialer or TLS config and still attempt HTTP/2 upgrades, set this to true. If not provided, DefaultForceAttemptHTTP2 is used.

func WithIdleConnectionTimeout

func WithIdleConnectionTimeout(idleConnectionTimeout time.Duration) func(*client)

WithIdleConnectionTimeout controls amount of time an idle (keep-alive) connection will remain idle before closing itself. If not provided, DefaultIdleConnectionTimeout is used.

func WithInterceptors

func WithInterceptors(interceptors ...Interceptor) func(*client)

WithInterceptors wraps Client with given interceptors

func WithMaxConnectionsPerHost

func WithMaxConnectionsPerHost(maxConnectionsPerHost int) func(*client)

WithMaxConnectionsPerHost optionally limits the total number of connections per host, including connections in the dialing, active, and idle states. On limit violation, dials will block. If not provided, DefaultMaxConnectionsPerHost is used.

func WithMaxIdleConnections

func WithMaxIdleConnections(maxIdleConnections int) func(*client)

WithMaxIdleConnections controls the maximum number of idle (keep-alive) connections across all hosts. If not provided, DefaultMaxIdleConnections is used.

func WithMaxIdleConnectionsPerHost

func WithMaxIdleConnectionsPerHost(maxIdleConnectionsPerHost int) func(*client)

WithMaxIdleConnectionsPerHost controls the maximum idle (keep-alive) connections to keep per-host. If not provided, DefaultMaxIdleConnectionsPerHost is used.

func WithTimeout

func WithTimeout(timeout time.Duration) func(*client)

WithTimeout sets timeout for all client requests. Timeout implemented without using http.Client's Timeout property, but with context. Client timeout has lesser priority than Request timeout property. If not provided, DefaultTimeout is used.

Types

type Client

type Client interface {
	Request() Request
}

func NewClient

func NewClient(
	options ...func(*client),
) Client

type Decoder

type Decoder interface {
	Decode(interface{}) error
}

type Interceptor

type Interceptor func(http.RoundTripper) http.RoundTripper

func Retry

func Retry(statusCodes ...int) Interceptor

Retry interceptor retry request on request failure or on defined Response status codes. By default Retry uses defaultStatusCodes

type Query

type Query interface {
	bool | string |
		int8 | uint8 | int16 | uint16 | int32 | uint32 |
		int | uint | int64 | uint64 | float32 | float64 | big.Int | big.Float
}

type Request

type Request interface {
	Get(
		ctx context.Context,
		url string,
	) (resp *Response, err error)

	Head(
		ctx context.Context,
		url string,
	) (resp *Response, err error)

	Post(
		ctx context.Context,
		url string,
		body io.Reader,
	) (resp *Response, err error)

	Put(
		ctx context.Context,
		url string,
		body io.Reader,
	) (resp *Response, err error)

	Delete(
		ctx context.Context,
		url string,
	) (resp *Response, err error)

	Connect(
		ctx context.Context,
		url string,
	) (resp *Response, err error)

	Options(
		ctx context.Context,
		url string,
	) (resp *Response, err error)

	Trace(
		ctx context.Context,
		url string,
	) (resp *Response, err error)

	Patch(
		ctx context.Context,
		url string,
	) (resp *Response, err error)

	URL() *url.URL

	Header() http.Header

	Body() (io.Reader, error)

	WithHeader(
		key string,
		values ...string,
	) Request

	WithHeaders(
		values map[string][]string,
	) Request

	WithContentType(
		value string,
	) Request

	WithJSONContentType() Request

	WithXMLContentType() Request

	WithMultipartFormContentType() Request

	WithFormContentType() Request

	WithAuth(
		values ...string,
	) Request

	WithBasicAuth(
		username,
		password string,
	) Request

	WithBearerAuth(
		value string,
	) Request

	WithJWTAuth(
		value string,
	) Request

	WithQuery(
		name string,
		values ...string,
	) Request

	WithQueries(
		values map[string][]string,
	) Request

	WithTimeout(
		timeout time.Duration,
	) Request
}

type Response

type Response struct {
	*http.Response
}

func (*Response) Decoder

func (res *Response) Decoder() Decoder

Decoder returns JSON or XML decoder depending on content type.

func (*Response) IsSuccess

func (res *Response) IsSuccess() bool

IsSuccess checks response status code for success.

func (*Response) JSONDecoder

func (res *Response) JSONDecoder() Decoder

JSONDecoder returns JSON decoder.

func (*Response) XMLDecoder

func (res *Response) XMLDecoder() Decoder

XMLDecoder returns XML decoder.

type RoundTripper

type RoundTripper func(*http.Request) (*http.Response, error)

func (RoundTripper) RoundTrip

func (rt RoundTripper) RoundTrip(req *http.Request) (*http.Response, error)

Jump to

Keyboard shortcuts

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