request

package module
v0.9.3 Latest Latest
Warning

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

Go to latest
Published: Mar 11, 2024 License: MIT Imports: 25 Imported by: 6

README

go-request

GoVersion GoDoc License Report

master Test codecov

dev Test codecov

A Package to send requests to HTTP/REST services.

Usage

The main func allows to send HTTP request to REST servers and takes care of payloads, JSON, result collection.

Examples:

res, err := request.Send(&request.Options{
    URL: myURL,
}, nil)
if err != nil {
    return err
}
data := struct{Data string}{}
err := res.UnmarshalContentJSON(&data)

Here we send an HTTP GET request and unmarshal the response.

It is also possible to let request.Send do the unmarshal for us:

data := struct{Data string}{}
_, err := request.Send(&request.Options{
    URL: myURL,
}, &data)

In that case, the returned Content's data is an empty byte array. Its other properties are valid (like the size, mime type, etc)

You can also download data directly to an io.Writer:

writer, err := os.Create(filepath.Join("tmp", "data"))
defer writer.Close()
res, err := request.Send(&request.Options{
  URL: serverURL,
}, writer)
log.Infof("Downloaded %d bytes", res.Length)

In that case, the returned Content's data is an empty byte array. Its other properties are valid (like the size, mime type, etc)

Authorization can be stored in the Options.Authorization:

payload := struct{Key string}{}
res, err := request.Send(&request.Options{
    URL:           myURL,
    Authorization: request.BasicAuthorization("user", "password"),
}, nil)

or, with a Bearer Token:

payload := struct{Key string}{}
res, err := request.Send(&request.Options{
    URL:           myURL,
    Authorization: request.BearerAuthorization("myTokenABCD"),
}, nil)

Objects can be sent as payloads:

payload := struct{Key string}{}
res, err := request.Send(&request.Options{
    URL:     myURL,
    Payload: payload,
}, nil)

A payload will induce an HTTP POST unless mentioned.

So, to send an HTTP PUT, simply write:

payload := struct{Key string}{}
res, err := request.Send(&request.Options{
    Method:  http.MethodPut,
    URL:     myURL,
    Payload: payload,
}, nil)

To send an x-www-form, use a map in the payload:

res, err := request.Send(&request.Options{
    Method:  http.MethodPut,
    URL:     myURL,
    Payload: map[string]string{
        "ID":   "1234",
        "Kind": "stuff,"
    },
}, nil)

To send a multipart form with an attachment, use a map, an attachment, and one of the key must start with >:

attachment := io.Open("/path/to/file")
res, err := request.Send(&request.Options{
    Method:  http.MethodPut,
    URL:     myURL,
    Payload: map[string]string{
        "ID":    "1234",
        "Kind":  "stuff,"
        ">file": "image.png",
    },
    Attachment: attachment,
}, nil)

The file name and its key will be written in the multipart/form-data's Content-Disposition header as: form-data; name="file"; filename="image.png".

To send the request again when receiving a Service Unavailable (Attempts and Timeout are optional):

res, err := request.Send(&request.Options{
    URL:                  myURL,
    RetryableStatusCodes: []int{http.StatusServiceUnavailable},
    Attempts:             10,
    Timeout:              2 * time.Second,
}, nil)

By default, upon receiving a retryable status code, Send will use am exponential backoff algorithm to retry the request. By default, it will wait for 3 seconds before retrying for 5 minutes, then 9 seconds between 5 and 10 minutes, then 27 seconds between 10 and 15 minutes, etc.

You can change the delay and the backoff factor like this:

res, err := request.Send(&request.Options{
    URL:                         myURL,
    InterAttemptDelay:           5 * time.Second,
    InterAttemptBackoffInterval: 2 * time.Minute,
}, nil)

You can also not use the backoff algorithm and use the Retry-After header instead:

res, err := request.Send(&request.Options{
    URL:                       myURL,
    // ...
    InterAttemptUseRetryAfter: true,
}, nil)

When sending requests to upload data streams, you can provide an io.Writer to write the progress to:

import "github.com/schollz/progressbar/v3"

reader, err := os.Open(pathToFile)
defer reader.Close()
stat, err := reader.Stat()
bar := progressbar.DefaultBytes(stat.Size(), "Uploading")
res, err := request.Send(&request.Options{
  Method:         http.MethodPost,
  URL:            serverURL,
  Payload:        reader,
  ProgressWriter: bar,
}, reader)

If the progress io.Writer is also an io.Closer, it will be closed at the end of the request.Send().

When sending requests to download data streams, you can provide an io.Writer to write the progress to:

import "github.com/schollz/progressbar/v3"

writer, err := os.Create(pathToFile)
defer writer.Close()
bar := progressbar.DefaultBytes(-1, "Downloading") // will use a spinner
res, err := request.Send(&request.Options{
  URL:            serverURL,
  ProgressWriter: bar,
}, writer)

Again, if the progress io.Writer is also an io.Closer, it will be closed at the end of the request.Send().

if you provide a request.Options.ProgressSetMax func or if the io.Writer is a request.ProgressBarMaxSetter or a request.ProgressBarMaxChanger, request.Send will call it to set the maximum value of the progress bar from the response Content-Length:

import "github.com/schollz/progressbar/v3"

writer, err := os.Create(pathToFile)
defer writer.Close()
bar := progressbar.DefaultBytes(1, "Downloading") // use a temporary max value
res, err := request.Send(&request.Options{
  URL:            serverURL,
  ProgressWriter: bar,
}, writer)
import "github.com/cheggaaa/pb/v3"

writer, err := os.Create(pathToFile)
defer writer.Close()
bar := pb.StartNew(1) // use a temporary max value
res, err := request.Send(&request.Options{
  URL:                serverURL,
  ProgressWriter:     bar,
  ProgressSetMaxFunc: func(max int64) { bar.SetTotal64(max) },
}, writer)

Notes:

  • if the PayloadType is not mentioned, it is calculated when processing the Payload.
  • if the payload is a ContentReader or a Content, it is used directly.
  • if the payload is a map[string]xxx where xxx is not string, the fmt.Stringer is used whenever possible to get the string version of the values.
  • if the payload is a struct or a pointer to struct, the body is sent as application/json and marshaled.
  • if the payload is an array or a slice, the body is sent as application/json and marshaled.
  • The option Logger can be used to let the request library log to a gildas/go-logger. By default, it logs to a NilStream (see github.com/gildas/go-logger).
  • When using a logger, you can control how much of the Request/Response Body is logged with the options RequestBodyLogSize/ResponseBodyLogSize. By default they are set to 2048 bytes. If you do not want to log them, set the options to -1.
  • Send() makes 5 attempts by default to reach the given URL. If option RetryableStatusCodes is given, it will attempt the request again when it receives an HTTP Status Code in the given list. If it is not given, the default list is []int{http.StatusServiceUnavailable, http.StatusGatewayTimeout, http.StatusBadGateway, http.StatusRequestTimeout, http.StatusTooManyRequests}.
  • The default timeout for Send() is 1 second.

TODO:

  • Support other kinds of map in the payload, like map[string]int, etc.
  • Maybe have an interface for the Payload to allow users to provide the logic of building the payload themselves. (type PayloadBuilder interface { BuildPayload() *ContentReader}?!?)

Documentation

Index

Constants

View Source
const DefaultAttempts = 5

DefaultAttempts defines the number of attempts for requests by default

View Source
const DefaultInterAttemptBackoffInterval = 5 * time.Minute

DefaultInterAttemptBackoffInterval defines the interval between 2 inter attempt delay increases

View Source
const DefaultInterAttemptDelay = 3 * time.Second

DefaultInterAttemptDelay defines the sleep delay between 2 attempts during the first backoff interval

View Source
const DefaultRequestBodyLogSize = 2048

DefaultRequestBodyLogSize defines the maximum size of the request body that should be logged

View Source
const DefaultResponseBodyLogSize = 2048

DefaultResponseBodyLogSize defines the maximum size of the response body that should be logged

View Source
const DefaultTimeout = 2 * time.Second

DefaultTimeout defines the timeout for a request

Variables

View Source
var VERSION = "0.9.3" + commit

VERSION is the version of this library

Functions

func BasicAuthorization added in v0.2.0

func BasicAuthorization(user, password string) string

BasicAuthorization builds a basic authorization string

func BearerAuthorization added in v0.2.0

func BearerAuthorization(token string) string

BearerAuthorization builds a Token authorization string

Types

type Content

type Content struct {
	Type    string         `json:"Type"`
	Name    string         `json:"Name,omitempty"`
	URL     *url.URL       `json:"-"`
	Length  uint64         `json:"Length"`
	Data    []byte         `json:"Data"`
	Headers http.Header    `json:"headers,omitempty"`
	Cookies []*http.Cookie `json:"-"`
}

Content defines some content

func ContentFromReader

func ContentFromReader(reader io.Reader, options ...interface{}) (*Content, error)

ContentFromReader instantiates a Content from an I/O reader

func ContentWithData

func ContentWithData(data []byte, options ...interface{}) *Content

ContentWithData instantiates a Content from a simple byte array

func Send

func Send(options *Options, results interface{}) (*Content, error)

Send sends an HTTP request

func (Content) Decrypt added in v0.7.1

func (content Content) Decrypt(algorithm CryptoAlgorithm, key []byte) (*Content, error)

func (Content) DecryptWithAESCTR added in v0.7.1

func (content Content) DecryptWithAESCTR(key []byte) (*Content, error)

func (Content) Encrypt added in v0.7.1

func (content Content) Encrypt(algorithm CryptoAlgorithm, key []byte) (*Content, error)

func (Content) EncryptWithAESCTR added in v0.7.1

func (content Content) EncryptWithAESCTR(key []byte) (*Content, error)

func (Content) LogString added in v0.3.7

func (content Content) LogString(maxSize uint64) string

LogString generates a string suitable for logging

func (Content) MarshalJSON added in v0.6.1

func (content Content) MarshalJSON() ([]byte, error)

MarshalJSON marshals the Content into JSON

implements json.Marshaler

func (*Content) ReadCloser added in v0.5.0

func (content *Content) ReadCloser() io.ReadCloser

ReadCloser gets an io.ReadCloser from this Content

func (*Content) Reader

func (content *Content) Reader() io.Reader

Reader gets an io.Reader from this Content

func (Content) UnmarshalContentJSON added in v0.5.0

func (content Content) UnmarshalContentJSON(v interface{}) (err error)

UnmarshalContentJSON unmarshals its Data into JSON

func (*Content) UnmarshalJSON added in v0.6.1

func (content *Content) UnmarshalJSON(payload []byte) error

UnmarshalJSON unmarshals the Content from JSON

implements json.Unmarshaler

type CryptoAlgorithm added in v0.7.1

type CryptoAlgorithm uint
const (
	NONE CryptoAlgorithm = iota
	AESCTR
)

func CryptoAlgorithmFromString added in v0.7.2

func CryptoAlgorithmFromString(algorithm string) (CryptoAlgorithm, error)

func (CryptoAlgorithm) MarshalJSON added in v0.7.2

func (algorithm CryptoAlgorithm) MarshalJSON() ([]byte, error)

func (CryptoAlgorithm) String added in v0.7.1

func (algorithm CryptoAlgorithm) String() string

func (*CryptoAlgorithm) UnmarshalJSON added in v0.7.2

func (algorithm *CryptoAlgorithm) UnmarshalJSON(data []byte) (err error)

type Options

type Options struct {
	Context                     context.Context
	Method                      string
	URL                         *url.URL
	Proxy                       *url.URL
	Headers                     map[string]string
	Cookies                     []*http.Cookie
	Parameters                  map[string]string
	Accept                      string
	PayloadType                 string      // if not provided, it is computed. See https://gihub.com/gildas/go-request#payload
	Payload                     interface{} // See https://gihub.com/gildas/go-request#payload
	AttachmentType              string      // MIME type of the attachment
	Attachment                  io.Reader   // binary data that should be attached to the paylod (e.g.: multipart forms)
	Authorization               string
	RequestID                   string
	UserAgent                   string
	Transport                   *http.Transport
	ProgressWriter              io.Writer // if not nil, the progress of the request will be written to this writer
	ProgressSetMaxFunc          func(int64)
	RetryableStatusCodes        []int         // Status codes that should be retried, by default: 429, 502, 503, 504
	Attempts                    uint          // number of attempts, by default: 5
	InterAttemptDelay           time.Duration // how long to wait between 2 attempts during the first backoff interval, by default: 3s
	InterAttemptBackoffInterval time.Duration // how often the inter attempt delay should be increased, by default: 5 minutes
	InterAttemptUseRetryAfter   bool          // if true, the Retry-After header will be used to wait between 2 attempts, otherwise an exponential backoff will be used, by default: false
	Timeout                     time.Duration
	RequestBodyLogSize          int // how many characters of the request body should be logged, if possible (<0 => nothing logged)
	ResponseBodyLogSize         int // how many characters of the response body should be logged (<0 => nothing logged)
	Logger                      *logger.Logger
}

Options defines options of an HTTP request

type ProgressBarMaxChanger added in v0.9.0

type ProgressBarMaxChanger interface {
	ChangeMax64(int64)
}

ProgressBarMaxChanger is an interface that allows setting the maximum value of a progress bar

This interface allows packages such as "/github.com/schollz/progressbar/v3" to be used as progress bars

type ProgressBarMaxSetter added in v0.9.0

type ProgressBarMaxSetter interface {
	SetMax64(int64)
}

ProgressBarMaxSetter is an interface that allows setting the maximum value of a progress bar

Jump to

Keyboard shortcuts

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