reqs

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Apr 30, 2026 License: Apache-2.0 Imports: 33 Imported by: 0

README

goreqs

HTTP requests in Go, the easy way.

test Go Reference Go Report Card License


What is reqs?

Making HTTP requests in Go means a lot of boilerplate — create a request, set headers, pick a client, read the body, close the body, unmarshal the JSON. For something that should be simple, it takes a lot of code.

reqs fixes that. Inspired by Python's requests library, it gives you a clean one-line API for HTTP calls while keeping everything built on top of Go's standard net/http package. No magic, no hidden types — just less typing.

resp, err := reqs.Get(ctx, "https://api.example.com/users")
if err != nil {
    return err
}

var users []User
if err := resp.JSON(&users); err != nil {
    return err
}

Compare that with the standard library approach:

req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil { return err }
req.Header.Set("Accept", "application/json")

resp, err := http.DefaultClient.Do(req)
if err != nil { return err }
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil { return err }

var users []User
if err := json.Unmarshal(body, &users); err != nil { return err }

Same result, half the code.

Install

go get github.com/aoncodev/goreqs

Requires Go 1.22+. Only two dependencies: golang.org/x/net and golang.org/x/text (both from the Go team).

Quick start

GET a JSON API
resp, err := reqs.Get(ctx, "https://api.example.com/users")
if err != nil {
    return err
}

var users []User
if err := resp.JSON(&users); err != nil {
    return err
}
fmt.Println(users)
POST with a JSON body
resp, err := reqs.Post(ctx, "https://api.example.com/users",
    reqs.Opts{JSON: map[string]string{"name": "alice", "role": "admin"}})
if err != nil {
    return err
}
fmt.Println(resp.StatusCode) // 201
Add headers, auth, and a timeout in one place
resp, err := reqs.Post(ctx, url, reqs.Opts{
    JSON:    payload,
    Header:  http.Header{"X-API-Key": {key}},
    Auth:    reqs.BasicAuth("user", "pass"),
    Timeout: 5 * time.Second,
})

Everything goes in a single Opts struct. No builders, no chains, no ceremony. If you don't need options, just don't pass any.

How it works

reqs wraps Go's net/http — it doesn't replace it. Under the hood, every call builds a standard *http.Request, sends it through an http.RoundTripper, and returns a *Response that embeds *http.Response. You're always one field access away from the stdlib types you already know.

The library handles the repetitive parts for you:

  • Encodes your struct as JSON and sets the Content-Type
  • Reads and caches the response body (so you can call .JSON(), .Text(), or .Bytes() without worrying about closing)
  • Follows redirects with proper method rewriting and auth stripping
  • Manages cookies, connection pooling, and timeouts
  • Maps transport errors to clear sentinels you can check with errors.Is

Examples

GET with query params
resp, err := reqs.Get(ctx, "https://api.github.com/search/users",
    reqs.Opts{Params: url.Values{"q": {"location:tokyo"}, "per_page": {"5"}}})
if err != nil {
    return err
}
// Request URL becomes: https://api.github.com/search/users?q=location%3Atokyo&per_page=5

var result struct {
    TotalCount int    `json:"total_count"`
    Items      []User `json:"items"`
}
if err := resp.JSON(&result); err != nil {
    return err
}
Submit a form
resp, err := reqs.Post(ctx, "https://example.com/login",
    reqs.Opts{Form: url.Values{"username": {"alice"}, "password": {"secret"}}})
if err != nil {
    return err
}
// Content-Type is automatically set to application/x-www-form-urlencoded
Upload a file (multipart)
f, err := os.Open("photo.png")
if err != nil {
    return err
}
defer f.Close()

resp, err := reqs.Post(ctx, "https://api.example.com/upload",
    reqs.Opts{
        Files: []reqs.File{{Name: "image", File: f}},
        Form:  url.Values{"caption": {"my cat"}},
    })
if err != nil {
    return err
}
Persistent Client with cookies and connection pooling
c := reqs.New()  // comes with a cookie jar and connection pool
defer c.Close()

c.Header = http.Header{"User-Agent": {"my-app/1.0"}}
c.Auth   = reqs.BasicAuth("user", "pass")

// First request — server sets a session cookie
resp, err := c.Post(ctx, "https://api.example.com/login",
    reqs.Opts{JSON: credentials})
if err != nil {
    return err
}

// Second request — cookie is sent automatically
resp, err = c.Get(ctx, "https://api.example.com/dashboard")
if err != nil {
    return err
}
Handle errors and status codes
resp, err := reqs.Get(ctx, "https://api.example.com/users/999")
if err != nil {
    if errors.Is(err, reqs.ErrTimeout) {
        // request timed out
    }
    if errors.Is(err, reqs.ErrCanceled) {
        // context was canceled
    }
    if errors.Is(err, reqs.ErrConnect) {
        // couldn't reach the server
    }
    return err
}

// Check HTTP status
fmt.Println(resp.OK())         // true for 2xx and 3xx
fmt.Println(resp.StatusCode)   // 404

// Or raise an error for 4xx/5xx
if err := resp.RaiseForStatus(); err != nil {
    var httpErr *reqs.HTTPError
    if errors.As(err, &httpErr) {
        fmt.Println(httpErr.Resp.StatusCode) // 404
    }
}
Timeout and context cancellation
// Per-request timeout
resp, err := reqs.Get(ctx, url, reqs.Opts{Timeout: 2 * time.Second})

// Or use a context with deadline
ctx, cancel := context.WithTimeout(parent, 5*time.Second)
defer cancel()
resp, err = reqs.Get(ctx, url)

// Client-level default timeout for all requests
c := reqs.New()
c.Timeout = 10 * time.Second
resp, err = c.Get(ctx, url)
Stream a large response
resp, err := reqs.Get(ctx, "https://example.com/big-file.tar.gz",
    reqs.Opts{Stream: true})
if err != nil {
    return err
}
defer resp.Body.Close()

out, err := os.Create("big-file.tar.gz")
if err != nil {
    return err
}
defer out.Close()

if _, err := io.Copy(out, resp.Body); err != nil {
    return err
}
Digest authentication
// Digest auth handles the 401 challenge automatically.
// First request goes out without credentials, server responds with
// a 401 + WWW-Authenticate header, reqs computes the digest and retries.
resp, err := reqs.Get(ctx, "https://api.example.com/protected",
    reqs.Opts{Auth: reqs.DigestAuth("user", "pass")})
if err != nil {
    return err
}
// Supports MD5, MD5-SESS, SHA-1, SHA-256, SHA-512
Drop down to *http.Request when you need full control
c := reqs.New()
defer c.Close()

req, err := http.NewRequest("PROPFIND", url, body)
if err != nil {
    return err
}
req.Header.Set("Depth", "1")

// Client.Do accepts a standard *http.Request — you still get the reqs
// Response with Bytes/Text/JSON helpers, plus cookies and auth from the Client.
resp, err := c.Do(ctx, req)
if err != nil {
    return err
}

Features

Requests & responses

  • All HTTP methods: GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS, and custom verbs
  • JSON, form-encoded, and raw io.Reader request bodies with automatic Content-Type
  • Response helpers: Bytes(), Text(), and JSON() with body caching and charset detection
  • Query parameter merging via Opts.Params
  • Header validation against control characters

Authentication

  • HTTP Basic and Proxy Basic auth
  • HTTP Digest auth (RFC 7616) with MD5, MD5-SESS, SHA-1, SHA-256, SHA-512
  • Automatic ~/.netrc credential lookup
  • URL userinfo extraction (credentials in the URL are moved to headers automatically)
  • Auth priority: Opts.Auth > Client.Auth > URL userinfo > netrc

Sessions & connection management

  • *Client persists cookies, headers, auth, and the connection pool across requests
  • HTTP/2 support with connection pooling (100 idle connections, 10 per host)
  • Client.Do for sending hand-built *http.Request through the session pipeline

Redirects

  • Automatic following of 301, 302, 303, 307, 308 redirects (up to 30 hops, configurable)
  • Method rewriting: 303/302 to GET, 301+POST to GET, 307/308 preserve method and body
  • Authorization header stripped on cross-host redirects
  • Full redirect history available via Response.History

TLS & proxies

  • Custom CA bundles (single PEM file or directory of PEM files)
  • mTLS client certificates
  • Skip-verify for local development
  • Per-scheme proxy configuration with NO_PROXY / HTTP_PROXY / HTTPS_PROXY / ALL_PROXY support

Error handling

  • Sentinel errors for errors.Is: ErrTimeout, ErrCanceled, ErrConnect, ErrTLS, ErrTooManyRedirects, ErrInvalidURL, ErrMissingScheme, ErrInvalidHeader, ErrInvalidProxy, ErrUnrewindable
  • Typed errors for errors.As: *HTTPError, *URLError, *RequestError
  • resp.RaiseForStatus() returns *HTTPError for 4xx/5xx responses

Safety

  • Response size limiting via Client.MaxResponseSize
  • Streaming mode via Opts.Stream for large responses
  • IDNA encoding for international domain names
  • context.Context cancellation and deadlines, end-to-end

Configuration reference

Opts — per-request options

Every field is optional. Pass zero or one Opts to any request method.

Field What it does
Header Merged on top of Client.Header defaults
Params Query parameters appended to the URL
Cookies Extra cookies for this request
Body Raw io.Reader body (no auto Content-Type)
JSON Marshaled to JSON, Content-Type set to application/json
Form URL-encoded form body (application/x-www-form-urlencoded)
Files Multipart file uploads (can be combined with Form for text fields)
Auth Overrides Client.Auth for this request
Timeout Overrides Client.Timeout for this request
AllowRedirects Override redirect following (default: enabled for all methods except HEAD)
Stream Skip body buffering — you read and close resp.Body yourself
Proxies Override Client.Proxies for this request
Verify TLS verification: true (default), false, or path to CA file/dir
Cert Client certificate: single PEM path or [2]string{certPath, keyPath}
Client — shared session

Created with reqs.New() (includes cookie jar) or as a zero-value (no jar).

Field What it does
Transport Custom http.RoundTripper (default: tuned *http.Transport with HTTP/2)
Jar Cookie jar for cross-request persistence
Header Default headers applied to every request
Auth Default auth (overridden by Opts.Auth)
Timeout Default per-request timeout
Proxies Proxy URL map, keyed by scheme or scheme://host
Verify Default TLS verification setting
Cert Default client certificate
MaxRedirects Redirect chain limit (default: 30)
MaxResponseSize Max bytes read by Bytes()/Text()/JSON() (default: 0 = no limit)
TrustEnv Read *_PROXY, NO_PROXY, and ~/.netrc from environment (default: true)

Good to know

  • Responses are buffered by default. The body is read into memory before the call returns, so you can call Bytes(), Text(), or JSON() multiple times without worrying about closing. For large downloads, use Opts.Stream = true or set Client.MaxResponseSize.

  • Reuse your Client. A *Client keeps connections alive and pools them. Package-level functions like reqs.Get() share a DefaultClient but don't persist cookies between calls — use reqs.New() when you need session behavior.

  • TrustEnv is on by default. This means reqs reads proxy settings and ~/.netrc credentials from the environment, just like curl does. Set c.TrustEnv = false if you don't want that.

  • Verify: false disables TLS verification. Useful for local dev with self-signed certs. Don't use it in production.

Versioning

reqs follows semver. The v0.x series may still evolve; v1.0 will lock the API.

Contributing

Contributions are welcome! Feel free to open issues or pull requests at https://github.com/aoncodev/goreqs.

To run the tests:

go test -race ./...

License

Apache 2.0.

Documentation

Overview

Package reqs is requests-style HTTP for Go.

reqs wraps net/http with a clean API inspired by Python's requests library: context.Context first, io.Reader bodies, http.Header for headers, *http.Cookie for cookies, errors via errors.Is and errors.As.

Quick start:

resp, err := reqs.Get(ctx, "https://api.example.com/users")
if err != nil {
    return err
}
var users []User
if err := resp.JSON(&users); err != nil {
    return err
}

All optional knobs go in one struct, Opts. Pass zero or one:

resp, err := reqs.Post(ctx, url, reqs.Opts{
    JSON:   payload,
    Header: http.Header{"X-Custom": {"value"}},
    Auth:   reqs.BasicAuth("user", "pass"),
})

Use *Client to share cookies, headers, auth, and the connection pool across many requests:

c := reqs.New()
c.Header.Set("X-API-Key", apiKey)
resp, err := c.Get(ctx, url)

Index

Examples

Constants

View Source
const (
	// Version is the library version.
	Version = "0.2.0"

	// UserAgent is the default User-Agent header sent with each request.
	UserAgent = "reqs/" + Version
)

Variables

View Source
var (
	// ErrTimeout is returned when a request exceeds its deadline (connect
	// or read) or the parent context expires with a deadline.
	ErrTimeout = errors.New("reqs: timeout")

	// ErrCanceled is returned when a request is canceled via its context
	// without a deadline.
	ErrCanceled = errors.New("reqs: canceled")

	// ErrConnect is returned for transport-level connection failures
	// (dial errors, refused connections, broken pipes).
	ErrConnect = errors.New("reqs: connection error")

	// ErrTLS is returned for TLS handshake or certificate verification
	// failures.
	ErrTLS = errors.New("reqs: tls error")

	// ErrTooManyRedirects is returned when a redirect chain exceeds
	// (*Client).MaxRedirects.
	ErrTooManyRedirects = errors.New("reqs: too many redirects")

	// ErrInvalidURL is returned when a URL fails parsing or validation.
	ErrInvalidURL = errors.New("reqs: invalid url")

	// ErrInvalidScheme is returned when a URL scheme is not http or https
	// (and the request was not handed off to a custom transport).
	ErrInvalidScheme = errors.New("reqs: invalid scheme")

	// ErrMissingScheme is returned when a URL has no scheme (e.g. "example.com").
	ErrMissingScheme = errors.New("reqs: missing scheme")

	// ErrInvalidHeader is returned when a header name or value contains
	// reserved or control characters.
	ErrInvalidHeader = errors.New("reqs: invalid header")

	// ErrInvalidProxy is returned when a configured proxy URL cannot be
	// parsed or has no host.
	ErrInvalidProxy = errors.New("reqs: invalid proxy url")

	// ErrUnrewindable is returned when a redirect requires resending the
	// request body but the body is not seekable.
	ErrUnrewindable = errors.New("reqs: unrewindable body")
)

Sentinel errors. Errors returned by this package wrap one of these (where applicable) and can be checked with errors.Is.

View Source
var DefaultClient = &Client{TrustEnv: true}

DefaultClient is used by the package-level convenience functions (Get, Post, ...). It has no Jar by default, so cookies are not persisted across top-level calls — matching Python requests' module functions which create a fresh Session per call.

Functions

func CookiesAsMap

func CookiesAsMap(cs []*http.Cookie) map[string]string

CookiesAsMap collapses a slice of cookies into a name→value map. When multiple cookies share a name (e.g., from different paths or domains) the last one wins. For domain/path-aware lookup, iterate the slice directly.

func CookiesFromMap

func CookiesFromMap(m map[string]string) []*http.Cookie

CookiesFromMap turns a name→value map into a slice of cookies. The resulting cookies have no Domain or Path set; the receiving jar will scope them to whatever URL is supplied at SetCookies time.

Types

type Auth

type Auth interface {
	// Apply sets headers (or other state) on req before it is sent. Auth
	// implementations must not consume or modify the request body.
	Apply(req *http.Request) error
}

Auth attaches credentials to outgoing requests.

func BasicAuth

func BasicAuth(user, pass string) Auth

BasicAuth attaches HTTP Basic authentication to each request.

Example

ExampleBasicAuth attaches HTTP Basic credentials.

s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	_, _ = w.Write([]byte(r.Header.Get("Authorization")))
}))
defer s.Close()

resp, _ := reqs.Get(context.Background(), s.URL,
	reqs.Opts{Auth: reqs.BasicAuth("alice", "wonder")})
body, _ := resp.Bytes()
fmt.Print(string(body))
Output:
Basic YWxpY2U6d29uZGVy

func DigestAuth

func DigestAuth(user, pass string) Auth

DigestAuth attaches HTTP Digest authentication. Supported algorithms: MD5 (default), MD5-SESS, SHA, SHA-256, SHA-512. The first request goes out unauthenticated; on a 401 challenge the credentials are computed and the request is retried once. Subsequent requests reuse the challenge state (preemptive auth) until the server issues a new nonce.

Safe for concurrent use. State is per-DigestAuth instance.

func ProxyAuth

func ProxyAuth(user, pass string) Auth

ProxyAuth attaches HTTP Basic credentials to the Proxy-Authorization header instead of Authorization.

type ChallengeAuth

type ChallengeAuth interface {
	Auth
	Challenge(req *http.Request, resp *http.Response) error
}

ChallengeAuth is implemented by auth schemes that need to inspect a 401 response (e.g. Digest). When the server returns 401, Client.Do calls Challenge once with the original request and 401 response, then retries the request a single time.

type Client

type Client struct {
	// Transport is the underlying http.RoundTripper. Nil means a tuned
	// *http.Transport is initialized lazily and reused.
	Transport http.RoundTripper

	// Jar persists cookies across requests. Nil means cookies are not
	// stored (per-request Cookies are still sent and Set-Cookie headers
	// on responses are visible via Response.Cookies()).
	Jar http.CookieJar

	// Header is merged into every outgoing request below Opts.Header.
	Header http.Header

	// Auth is applied to every outgoing request unless Opts.Auth
	// overrides it.
	Auth Auth

	// Timeout is the default per-request timeout. Zero means no timeout.
	// Opts.Timeout overrides per request.
	Timeout time.Duration

	// Proxies maps scheme/host keys to proxy URLs. See selectProxy in
	// proxy.go for the lookup rules.
	Proxies map[string]string

	// Verify controls TLS server certificate verification:
	//   - nil or bool true: use system CA bundle
	//   - bool false: skip verification (NOT for production)
	//   - string: path to a PEM file or directory of PEM files
	Verify any

	// Cert configures client-side certificates (mTLS):
	//   - string: path to a PEM file containing both cert and key
	//   - [2]string: {certPath, keyPath}
	Cert any

	// MaxRedirects is the redirect chain limit. Zero means 30 (the
	// Python requests default).
	MaxRedirects int

	// TrustEnv enables NETRC, NO_PROXY, and *_PROXY environment lookups.
	TrustEnv bool

	// MaxResponseSize limits the number of bytes read by Response.Bytes
	// (and therefore Text/JSON). Zero means no limit.
	MaxResponseSize int64
	// contains filtered or unexported fields
}

Client persists configuration and connection state across requests. It is the goreqs analogue of Python requests.Session.

The zero value is usable; New returns a Client with TrustEnv set and a fresh in-memory cookie jar (matching requests.Session behavior).

Use *Client to share cookies, default headers, auth, the connection pool, and TLS settings across many calls.

Example

ExampleClient shows persistent state across requests.

s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, r.Header.Get("X-API-Key"))
}))
defer s.Close()

c := reqs.New()
defer c.Close()
c.Header = http.Header{"X-API-Key": {"secret"}}

resp, _ := c.Get(context.Background(), s.URL)
body, _ := resp.Bytes()
fmt.Print(string(body))
Output:
secret

func New

func New() *Client

New returns a new Client with TrustEnv enabled and a fresh in-memory cookie jar.

func (*Client) Close

func (c *Client) Close() error

Close releases idle connections held by the Client's transport. Safe to call multiple times. Calling Close does not invalidate the Client; subsequent requests will reopen connections.

func (*Client) Delete

func (c *Client) Delete(ctx context.Context, url string, opts ...Opts) (*Response, error)

Delete sends a DELETE request.

func (*Client) Do

func (c *Client) Do(ctx context.Context, req *http.Request) (*Response, error)

Do sends a hand-built *http.Request through the Client's transport pipeline. This is the stdlib drop-down: minimal magic. Client.Header fills missing slots, Client.Auth applies if no Authorization is set, Client.Jar attaches and extracts cookies, Client.Timeout applies if the request's context has no deadline. URL and body are not touched.

func (*Client) Get

func (c *Client) Get(ctx context.Context, url string, opts ...Opts) (*Response, error)

Get sends a GET request.

func (*Client) Head

func (c *Client) Head(ctx context.Context, url string, opts ...Opts) (*Response, error)

Head sends a HEAD request. Redirects are NOT followed by default (matching Python). Override with Opts.AllowRedirects.

func (*Client) Options

func (c *Client) Options(ctx context.Context, url string, opts ...Opts) (*Response, error)

Options sends an OPTIONS request.

func (*Client) Patch

func (c *Client) Patch(ctx context.Context, url string, opts ...Opts) (*Response, error)

Patch sends a PATCH request.

func (*Client) Post

func (c *Client) Post(ctx context.Context, url string, opts ...Opts) (*Response, error)

Post sends a POST request. The body, if any, comes from Opts.Body, Opts.JSON, Opts.Form, or Opts.Files (see Opts for precedence).

func (*Client) Put

func (c *Client) Put(ctx context.Context, url string, opts ...Opts) (*Response, error)

Put sends a PUT request.

func (*Client) Request

func (c *Client) Request(ctx context.Context, method, url string, opts ...Opts) (*Response, error)

Request sends a request with the given method. Use this for custom verbs (mirrors Python requests.request).

type CookieJar

type CookieJar = http.CookieJar

CookieJar is an alias for http.CookieJar. goreqs does not wrap it.

func NewJar

func NewJar() (CookieJar, error)

NewJar returns a fresh in-memory cookie jar suitable for *Client.Jar.

type File

type File struct {
	// Name is the multipart form field name. Required.
	Name string

	// Filename is the filename reported to the server. If empty, it is
	// derived from the underlying *os.File (or falls back to Name).
	Filename string

	// File is the upload body. Required.
	File io.Reader

	// ContentType is the Content-Type for this part. If empty, the
	// content type is inferred from the filename extension and falls
	// back to application/octet-stream.
	ContentType string

	// Header optionally augments or overrides the default part headers
	// (Content-Disposition, Content-Type).
	Header textproto.MIMEHeader
}

File describes a single multipart upload field. It collapses Python requests' 2/3/4-tuple variants (filename / fileobj / content-type / custom-headers) into one struct.

type HTTPError

type HTTPError struct {
	Resp *Response
}

HTTPError is returned by (*Response).RaiseForStatus when StatusCode is in the 4xx or 5xx range.

func (*HTTPError) Error

func (e *HTTPError) Error() string
type Link struct {
	URL    string
	Rel    string
	Params map[string]string
}

Link is a single entry parsed from a Link response header (RFC 5988).

type Opts

type Opts struct {
	// Header is merged into the request headers above any Client.Header
	// defaults.
	Header http.Header

	// Params are appended to the URL's query string.
	Params url.Values

	// Cookies are added to the request via (*http.Request).AddCookie.
	Cookies []*http.Cookie

	// Body is a raw request body. Used as-is; no Content-Type is set
	// automatically.
	Body io.Reader

	// JSON is marshaled with encoding/json and sent with
	// Content-Type: application/json.
	JSON any

	// Form is sent application/x-www-form-urlencoded. When Files is
	// also set, Form is included as text parts of the multipart body.
	Form url.Values

	// Files trigger multipart/form-data encoding.
	Files []File

	// Auth overrides Client.Auth for this request.
	Auth Auth

	// Timeout is a per-request timeout. Zero means use Client.Timeout
	// (or no timeout). Implemented via context.WithTimeout in Client.do.
	Timeout time.Duration

	// AllowRedirects enables automatic redirect following. Nil uses the
	// per-method default: true for GET/POST/PUT/PATCH/DELETE/OPTIONS,
	// false for HEAD.
	AllowRedirects *bool

	// Stream, when true, leaves the response body unbuffered. The
	// caller must read and close r.Body. When false (the default), the
	// body is consumed into r.Bytes() before Client.do returns.
	Stream bool

	// Proxies overrides Client.Proxies for this request.
	Proxies map[string]string

	// Verify overrides Client.Verify for this request.
	Verify any

	// Cert overrides Client.Cert for this request.
	Cert any
}

Opts is the bag of optional knobs for a single request. All fields are optional. Pass zero or one Opts value to a convenience function:

goreqs.Get(ctx, url)                                  // no Opts
goreqs.Get(ctx, url, goreqs.Opts{Params: q})          // with Opts
goreqs.Post(ctx, url, goreqs.Opts{JSON: payload})

At most one of Body, JSON, Form, or Files may be set, with one exception: Files may coexist with Form (the Form fields are added as text parts in the multipart body). Conflicting bodies cause buildRequest to return an error.

type RequestError

type RequestError struct {
	Req  *http.Request
	Resp *Response
	Err  error
}

RequestError wraps a transport-level failure, retaining the originating request and any partial response that was received.

func (*RequestError) Error

func (e *RequestError) Error() string

func (*RequestError) Unwrap

func (e *RequestError) Unwrap() error

type Response

type Response struct {
	*http.Response

	// Elapsed is the wall-clock time from sending the request to
	// receiving the first byte of the response headers.
	Elapsed time.Duration

	// History is the redirect chain leading to this response, oldest
	// first. Empty if no redirects occurred.
	History []*Response
	// contains filtered or unexported fields
}

Response wraps *http.Response with the goreqs ergonomic surface (Bytes, Text, JSON, RaiseForStatus, Links). The embedded *http.Response is part of the public API: drop down to net/http types whenever the goreqs sugar is not enough.

Body handling: the first call to Bytes/Text/JSON reads the body to EOF, closes it, and caches the bytes. Subsequent calls return the cached bytes; Body is replaced with a re-readable io.NopCloser over the cache so consumer code that ranges over Body still works.

When Opts.Stream is true, Bytes/Text/JSON are NOT called automatically by Client.Do, so the caller is responsible for reading and closing Body. In that mode the embedded *http.Response.Body is the live network stream.

func Delete

func Delete(ctx context.Context, url string, opts ...Opts) (*Response, error)

Delete sends a DELETE request via DefaultClient.

func Get

func Get(ctx context.Context, url string, opts ...Opts) (*Response, error)

Get sends a GET request via DefaultClient.

Example

Get sends a GET request via the package-level DefaultClient.

s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintln(w, "hello")
}))
defer s.Close()

resp, err := reqs.Get(context.Background(), s.URL)
if err != nil {
	fmt.Println("error:", err)
	return
}
body, _ := resp.Bytes()
fmt.Print(string(body))
Output:
hello
Example (Params)

ExampleGet_params shows attaching query parameters via Opts.Params.

s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, r.URL.Query().Get("name"))
}))
defer s.Close()

resp, _ := reqs.Get(context.Background(), s.URL,
	reqs.Opts{Params: url.Values{"name": {"alice"}}})
body, _ := resp.Bytes()
fmt.Print(string(body))
Output:
alice
func Head(ctx context.Context, url string, opts ...Opts) (*Response, error)

Head sends a HEAD request via DefaultClient.

func Options

func Options(ctx context.Context, url string, opts ...Opts) (*Response, error)

Options sends an OPTIONS request via DefaultClient.

func Patch

func Patch(ctx context.Context, url string, opts ...Opts) (*Response, error)

Patch sends a PATCH request via DefaultClient.

func Post

func Post(ctx context.Context, url string, opts ...Opts) (*Response, error)

Post sends a POST request via DefaultClient.

Example (Json)

ExamplePost_json sends a JSON body.

s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", r.Header.Get("Content-Type"))
	_, _ = w.Write([]byte("ok"))
}))
defer s.Close()

resp, _ := reqs.Post(context.Background(), s.URL,
	reqs.Opts{JSON: map[string]int{"x": 1}})
fmt.Println(resp.Header.Get("Content-Type"))
Output:
application/json

func Put

func Put(ctx context.Context, url string, opts ...Opts) (*Response, error)

Put sends a PUT request via DefaultClient.

func Request

func Request(ctx context.Context, method, url string, opts ...Opts) (*Response, error)

Request sends a request with the given method via DefaultClient.

func (*Response) ApparentEncoding

func (r *Response) ApparentEncoding() string

ApparentEncoding sniffs the cached body for a charset (BOM, HTML meta, frequency heuristics). Returns "" when the body is empty or no encoding can be determined. Calling ApparentEncoding reads the body (via Bytes) if it has not been read yet.

func (*Response) Bytes

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

Bytes reads the response body into memory and returns it. The first call closes the underlying body and caches the bytes; subsequent calls return the cache. After the first call, Body is replaced with a re-readable io.NopCloser over the cache.

func (*Response) Encoding

func (r *Response) Encoding() string

Encoding returns the charset declared by the Content-Type header, or "" if none. This is the value Text uses unless ApparentEncoding falls back. Set r.encoding directly only via the setter SetEncoding (so we can clear caches if we add any later).

func (*Response) JSON

func (r *Response) JSON(v any) error

JSON decodes the response body as JSON into v. When the Content-Type has no charset, the encoding is guessed from BOM/null patterns per RFC 4627 §3 (matching Python requests' guess_json_utf).

func (r *Response) Links() map[string]Link

Links parses the Link response header into a map keyed by rel (or URL if rel is missing). Returns nil when the header is absent.

func (*Response) OK

func (r *Response) OK() bool

OK reports whether StatusCode is in the success range [200, 400).

func (*Response) RaiseForStatus

func (r *Response) RaiseForStatus() error

RaiseForStatus returns *HTTPError when StatusCode is in [400, 600), nil otherwise. Mirrors Python requests' raise_for_status.

Example

ExampleResponse_RaiseForStatus shows error handling for non-2xx responses.

s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	http.NotFound(w, r)
}))
defer s.Close()

resp, _ := reqs.Get(context.Background(), s.URL+"/missing")
if err := resp.RaiseForStatus(); err != nil {
	fmt.Println("status:", resp.StatusCode)
}
Output:
status: 404

func (*Response) SetEncoding

func (r *Response) SetEncoding(name string)

SetEncoding overrides the encoding used by Text. Useful when the server lies about charset in Content-Type.

func (*Response) Text

func (r *Response) Text() (string, error)

Text returns the response body decoded to a string. The encoding is determined in this order:

  1. The charset parameter of the Content-Type header.
  2. text/* default of ISO-8859-1; application/json default of utf-8.
  3. golang.org/x/net/html/charset.DetermineEncoding (BOM + HTML meta + heuristics).
  4. The raw bytes interpreted as UTF-8.

To override, set r.Encoding before calling Text.

type URLError

type URLError struct {
	URL string
	Err error
}

URLError wraps a URL-validation failure with the offending URL string. errors.Is matches the wrapped sentinel (ErrInvalidURL, ErrMissingScheme, ...).

func (*URLError) Error

func (e *URLError) Error() string

func (*URLError) Unwrap

func (e *URLError) Unwrap() error

Jump to

Keyboard shortcuts

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