httpc

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Jan 11, 2026 License: MIT Imports: 17 Imported by: 0

README

httpc

Go Reference Go Report Card

A fluent, batteries-included HTTP client wrapper for Go that makes common patterns trivial while staying close to Go's standard library philosophy.

Features

  • 🔗 Fluent & Chainable - Easy to read and write API
  • Sensible Defaults - Works great out of the box
  • 🎯 Gradual Complexity - Simple things simple, complex things possible
  • 🛡️ Production Ready - Retries, timeouts, circuit breakers included
  • 🧪 Testable - Easy mocking and request recording
  • 📝 Well Documented - Comprehensive examples and documentation

Installation

go get github.com/jnaneshd/httpc

Quick Start

Simple GET Request
resp, err := httpc.New().
    Get("https://api.example.com/users").
    Do()
if err != nil {
    log.Fatal(err)
}
defer resp.Close()

fmt.Println(resp.Status())
POST with JSON
user := User{Name: "John", Email: "john@example.com"}
resp, err := httpc.New().
    Post("https://api.example.com/users").
    JSON(user).
    Do()
Get Raw Response (String or Bytes)
// Get response as string - perfect for printing JSON
jsonStr, err := httpc.New().
    Get("https://api.example.com/users").
    String()

fmt.Println(jsonStr)  // {"id": 1, "name": "John", ...}

// Or as bytes
data, err := httpc.New().
    Get("https://api.example.com/users").
    Bytes()
Decode JSON Response
var users []User
err := httpc.New().
    Get("https://api.example.com/users").
    DecodeJSON(&users)
With Headers and Query Parameters
resp, err := httpc.New().
    Get("https://api.example.com/users").
    Header("Authorization", "Bearer token").
    Query("page", "1").
    Query("limit", "10").
    Do()

Configuration

Base URL and Default Headers
client := httpc.New().
    WithBaseURL("https://api.example.com").
    WithHeader("User-Agent", "MyApp/1.0").
    WithHeader("Accept", "application/json")

// Now all requests use the base URL
resp, err := client.Get("/users").Do()  // GET https://api.example.com/users
Timeout
client := httpc.New().
    WithTimeout(30 * time.Second)

// Or per-request timeout
resp, err := client.Get("/slow-endpoint").
    Timeout(60 * time.Second).
    Do()

Resilience Features

Automatic Retries
client := httpc.New().
    WithRetry(3, time.Second)  // 3 attempts, 1s initial delay

// Or with custom configuration
client := httpc.New().
    WithRetryConfig(&httpc.RetryConfig{
        MaxAttempts:  5,
        InitialDelay: 500 * time.Millisecond,
        MaxDelay:     30 * time.Second,
        Multiplier:   2.0,  // Exponential backoff
        Jitter:       true, // Randomize delays
    })
Circuit Breaker

Prevent cascading failures by stopping requests to failing services:

client := httpc.New().
    WithCircuitBreaker(5, time.Minute)  // Open after 5 failures, try again after 1 min

// Or with custom configuration
client := httpc.New().
    WithCircuitBreakerConfig(&httpc.CircuitBreakerConfig{
        Name:        "my-service",
        MaxFailures: 5,
        Timeout:     60 * time.Second,
        OnStateChange: func(name string, from, to gobreaker.State) {
            log.Printf("Circuit breaker %s: %s -> %s", name, from, to)
        },
    })
Rate Limiting
client := httpc.New().
    WithRateLimiter(10)  // 10 requests per second

// Or with burst capacity
client := httpc.New().
    WithRateLimiterConfig(10, 20)  // 10 req/s with burst of 20

Middleware

Add cross-cutting concerns like authentication, logging, or metrics:

import "net/http"

// Authentication middleware
authMiddleware := func(req *http.Request, next httpc.HTTPHandler) (*httpc.Response, error) {
    req.Header.Set("Authorization", "Bearer " + getToken())
    return next(req)
}

// Logging middleware
loggingMiddleware := func(req *http.Request, next httpc.HTTPHandler) (*httpc.Response, error) {
    start := time.Now()
    resp, err := next(req)
    log.Printf("%s %s took %v", req.Method, req.URL, time.Since(start))
    return resp, err
}

client := httpc.New().
    WithMiddleware(authMiddleware).
    WithMiddleware(loggingMiddleware)
Built-in Middlewares
// Bearer token
client.WithMiddleware(httpc.BearerTokenMiddleware("your-token"))

// Dynamic token (refreshed on each request)
client.WithMiddleware(httpc.DynamicBearerTokenMiddleware(func() (string, error) {
    return refreshToken()
}))

// User-Agent
client.WithMiddleware(httpc.UserAgentMiddleware("MyApp/1.0"))

// Default headers
client.WithMiddleware(httpc.HeadersMiddleware(map[string]string{
    "X-API-Key": "secret",
}))

Request Building

Body Types
// JSON
client.Post("/users").JSON(user)

// Form data
client.Post("/login").Form(map[string]string{
    "username": "john",
    "password": "secret",
})

// Raw bytes
client.Post("/upload").BodyBytes(data)

// String
client.Post("/text").BodyString("Hello, World!")

// Reader
client.Post("/stream").Body(reader)
Authentication
// Bearer token
client.Get("/protected").Bearer("your-token")

// Basic auth
client.Get("/protected").BasicAuth("username", "password")
Context
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

resp, err := client.Get("/users").Context(ctx).Do()
Expected Status
// Returns error if status doesn't match
resp, err := client.Post("/users").
    JSON(user).
    ExpectStatus(201).
    Do()

Response Handling

Quick Methods (One-liners)

These methods execute the request and return the result directly:

// Get raw JSON string - no decoding needed
jsonStr, err := client.Get("/users").String()
fmt.Println(jsonStr)  // prints raw JSON

// Get raw bytes
data, err := client.Get("/file").Bytes()

// Decode JSON directly
var users []User
err := client.Get("/users").DecodeJSON(&users)
Full Response Access

For more control, use .Do() to get the full response:

resp, err := client.Get("/users").Do()
if err != nil {
    log.Fatal(err)
}
defer resp.Close()

// Status
fmt.Println(resp.StatusCode())    // 200
fmt.Println(resp.Status())        // "200 OK"
fmt.Println(resp.IsSuccess())     // true (2xx)
fmt.Println(resp.IsError())       // false (4xx or 5xx)
fmt.Println(resp.IsClientError()) // false (4xx)
fmt.Println(resp.IsServerError()) // false (5xx)

// Headers
fmt.Println(resp.Header("Content-Type"))
fmt.Println(resp.ContentType())
fmt.Println(resp.ContentLength())

// Body (multiple ways)
body, _ := resp.String()    // As string (for printing/logging)
bytes, _ := resp.Bytes()    // As []byte (for binary data)
var data MyStruct
resp.JSON(&data)            // Decode JSON into struct

// Cookies
cookies := resp.Cookies()

Testing

Mock Server
import "github.com/jnaneshd/httpc/mock"

func TestAPI(t *testing.T) {
    server := mock.NewServer()
    defer server.Close()

    // Set up mock response
    server.OnGet("/users/1").
        Status(200).
        JSON(User{ID: 1, Name: "John"}).
        Reply()

    // Use mock server URL
    client := httpc.New().WithBaseURL(server.URL())

    var user User
    err := client.Get("/users/1").DecodeJSON(&user)
    if err != nil {
        t.Fatal(err)
    }

    // Verify request was made
    if server.RequestCount() != 1 {
        t.Errorf("expected 1 request, got %d", server.RequestCount())
    }
}
Custom Handler
server.OnPost("/users").
    Handler(func(w http.ResponseWriter, r *http.Request) {
        // Verify request
        if r.Header.Get("Authorization") == "" {
            w.WriteHeader(401)
            return
        }
        w.WriteHeader(201)
        w.Write([]byte(`{"id": 1}`))
    }).
    Reply()
Request Recording
// Access recorded requests
requests := server.Requests()
lastReq := server.LastRequest()

fmt.Println(lastReq.Method)  // "POST"
fmt.Println(lastReq.Path)    // "/users"
fmt.Println(lastReq.Headers) // http.Header

Full Example

package main

import (
    "fmt"
    "log"
    "time"

    "github.com/jnaneshd/httpc"
)

type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

func main() {
    // Create a production-ready client
    client := httpc.New().
        WithBaseURL("https://api.example.com").
        WithHeader("User-Agent", "MyApp/1.0").
        WithTimeout(30 * time.Second).
        WithRetry(3, time.Second).
        WithCircuitBreaker(5, time.Minute).
        WithRateLimiter(100).
        WithMiddleware(httpc.BearerTokenMiddleware(getToken()))

    // Create a user
    newUser := User{Name: "John", Email: "john@example.com"}
    var createdUser User

    err := client.
        Post("/users").
        JSON(newUser).
        ExpectStatus(201).
        DecodeJSON(&createdUser)

    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Created user: %+v\n", createdUser)

    // Get all users
    var users []User
    err = client.
        Get("/users").
        Query("limit", "10").
        DecodeJSON(&users)

    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Found %d users\n", len(users))
}

func getToken() string {
    return "your-api-token"
}

Error Handling

resp, err := client.Get("/users").Do()
if err != nil {
    var httpErr *httpc.Error
    if errors.As(err, &httpErr) {
        fmt.Println("Operation:", httpErr.Op)
        fmt.Println("URL:", httpErr.URL)
        fmt.Println("Status:", httpErr.StatusCode)
        fmt.Println("Retryable:", httpErr.IsRetryable())
    }
}
Sentinel Errors
if errors.Is(err, httpc.ErrCircuitOpen) {
    // Circuit breaker is open
}
if errors.Is(err, httpc.ErrRateLimited) {
    // Rate limit exceeded
}
if errors.Is(err, httpc.ErrRetryExhausted) {
    // All retry attempts failed
}

Comparison

Feature httpc net/http resty
Fluent API
JSON helpers
Retries
Circuit breaker
Rate limiting
Mock server
Middleware
Lightweight

Requirements

  • Go 1.21 or later

Dependencies

License

MIT License - see LICENSE for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Author

jnaneshd

Documentation

Overview

Package httpc provides a fluent, batteries-included HTTP client wrapper that makes common patterns trivial while staying close to Go's standard library philosophy.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrServerError indicates a 5xx server error response.
	ErrServerError = errors.New("server error")

	// ErrClientError indicates a 4xx client error response.
	ErrClientError = errors.New("client error")

	// ErrTimeout indicates a request timeout.
	ErrTimeout = errors.New("request timeout")

	// ErrCircuitOpen indicates the circuit breaker is open.
	ErrCircuitOpen = errors.New("circuit breaker is open")

	// ErrRateLimited indicates rate limit exceeded.
	ErrRateLimited = errors.New("rate limit exceeded")

	// ErrRetryExhausted indicates all retry attempts failed.
	ErrRetryExhausted = errors.New("retry attempts exhausted")
)

Common errors that can be checked with errors.Is().

Functions

This section is empty.

Types

type AdaptiveRateLimiter

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

AdaptiveRateLimiter adjusts the rate limit based on server responses.

func NewAdaptiveRateLimiter

func NewAdaptiveRateLimiter(minRate, maxRate float64) *AdaptiveRateLimiter

NewAdaptiveRateLimiter creates a new adaptive rate limiter.

func (*AdaptiveRateLimiter) CurrentRate

func (a *AdaptiveRateLimiter) CurrentRate() float64

CurrentRate returns the current rate limit.

func (*AdaptiveRateLimiter) RecordFailure

func (a *AdaptiveRateLimiter) RecordFailure()

RecordFailure records a failed request (e.g., 429) and reduces the rate.

func (*AdaptiveRateLimiter) RecordSuccess

func (a *AdaptiveRateLimiter) RecordSuccess()

RecordSuccess records a successful request and potentially increases the rate.

func (*AdaptiveRateLimiter) Wait

Wait blocks until the rate limiter allows an event.

type BearerTokenFunc

type BearerTokenFunc func() (string, error)

BearerTokenFunc is a function that returns a bearer token. This is useful for tokens that expire and need to be refreshed.

type CircuitBreakerConfig

type CircuitBreakerConfig struct {
	// Name is the name of the circuit breaker (for logging/metrics).
	Name string

	// MaxFailures is the number of consecutive failures before opening the circuit.
	// Default: 5
	MaxFailures int

	// MaxRequests is the maximum number of requests allowed in half-open state.
	// Default: 1
	MaxRequests int

	// Interval is the cyclical period of the closed state.
	// If 0, the circuit breaker doesn't clear counts in closed state.
	Interval time.Duration

	// Timeout is the duration to wait before transitioning from open to half-open.
	// Default: 60 seconds
	Timeout time.Duration

	// OnStateChange is called when the circuit breaker state changes.
	OnStateChange func(name string, from, to gobreaker.State)
}

CircuitBreakerConfig configures the circuit breaker behavior.

func DefaultCircuitBreakerConfig

func DefaultCircuitBreakerConfig() *CircuitBreakerConfig

DefaultCircuitBreakerConfig returns the default circuit breaker configuration.

func NewCircuitBreakerConfig

func NewCircuitBreakerConfig(opts ...CircuitBreakerOption) *CircuitBreakerConfig

NewCircuitBreakerConfig creates a new CircuitBreakerConfig with the given options.

type CircuitBreakerOption

type CircuitBreakerOption func(*CircuitBreakerConfig)

CircuitBreakerOption is a function that modifies CircuitBreakerConfig.

func WithCircuitBreakerInterval

func WithCircuitBreakerInterval(d time.Duration) CircuitBreakerOption

WithCircuitBreakerInterval sets the cyclical period of the closed state.

func WithCircuitBreakerMaxRequests

func WithCircuitBreakerMaxRequests(n int) CircuitBreakerOption

WithMaxRequests sets the maximum number of requests in half-open state.

func WithCircuitBreakerName

func WithCircuitBreakerName(name string) CircuitBreakerOption

WithCircuitBreakerName sets the name of the circuit breaker.

func WithCircuitBreakerStateChange

func WithCircuitBreakerStateChange(fn func(name string, from, to gobreaker.State)) CircuitBreakerOption

WithCircuitBreakerStateChange sets a callback for state changes.

func WithCircuitBreakerTimeout

func WithCircuitBreakerTimeout(d time.Duration) CircuitBreakerOption

WithCircuitBreakerTimeout sets the duration before transitioning from open to half-open.

func WithMaxFailures

func WithMaxFailures(n int) CircuitBreakerOption

WithMaxFailures sets the maximum number of failures before opening the circuit.

type CircuitState

type CircuitState int

CircuitState represents the state of the circuit breaker.

const (
	// CircuitClosed allows requests to pass through.
	CircuitClosed CircuitState = iota
	// CircuitHalfOpen allows a limited number of requests to test the service.
	CircuitHalfOpen
	// CircuitOpen blocks all requests.
	CircuitOpen
)

func (CircuitState) String

func (s CircuitState) String() string

String returns the string representation of the circuit state.

type Client

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

Client is the main HTTP client with fluent configuration. Use New() to create a new client with sensible defaults.

func New

func New() *Client

New creates a new HTTP client with sensible defaults.

Example:

client := httpc.New()
resp, err := client.Get("https://api.example.com/users").Do()

func (*Client) Delete

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

Delete creates a new DELETE request.

func (*Client) Do

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

Do executes a pre-built http.Request with all client configurations. This is useful when you need to work with existing http.Request objects.

func (*Client) Get

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

Get creates a new GET request.

Example:

resp, err := client.Get("https://api.example.com/users").Do()

func (*Client) Head

func (c *Client) Head(url string) *Request

Head creates a new HEAD request.

func (*Client) Options

func (c *Client) Options(url string) *Request

Options creates a new OPTIONS request.

func (*Client) Patch

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

Patch creates a new PATCH request.

func (*Client) Post

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

Post creates a new POST request.

Example:

resp, err := client.Post("https://api.example.com/users").JSON(user).Do()

func (*Client) Put

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

Put creates a new PUT request.

func (*Client) Request

func (c *Client) Request(method, url string) *Request

Request creates a new request with a custom HTTP method.

func (*Client) WithBaseURL

func (c *Client) WithBaseURL(url string) *Client

WithBaseURL sets the base URL for all requests. The path in request methods will be appended to this URL.

Example:

client := httpc.New().WithBaseURL("https://api.example.com")
resp, err := client.Get("/users").Do() // GET https://api.example.com/users

func (*Client) WithCircuitBreaker

func (c *Client) WithCircuitBreaker(maxFailures int, timeout time.Duration) *Client

WithCircuitBreaker configures a circuit breaker pattern.

Parameters:

  • maxFailures: Number of failures before opening the circuit
  • timeout: Duration to wait before attempting to close the circuit

Example:

client := httpc.New().WithCircuitBreaker(5, time.Minute)

func (*Client) WithCircuitBreakerConfig

func (c *Client) WithCircuitBreakerConfig(config *CircuitBreakerConfig) *Client

WithCircuitBreakerConfig configures a circuit breaker with custom settings.

func (*Client) WithHTTPClient

func (c *Client) WithHTTPClient(client *http.Client) *Client

WithHTTPClient sets a custom http.Client. Use this if you need custom transport settings like TLS configuration.

func (*Client) WithHeader

func (c *Client) WithHeader(key, value string) *Client

WithHeader sets a default header that will be included in all requests.

Example:

client := httpc.New().WithHeader("User-Agent", "MyApp/1.0")

func (*Client) WithHeaders

func (c *Client) WithHeaders(headers map[string]string) *Client

WithHeaders sets multiple default headers at once.

func (*Client) WithLogger

func (c *Client) WithLogger(logger Logger) *Client

WithLogger sets a custom logger for the client.

func (*Client) WithMiddleware

func (c *Client) WithMiddleware(mw Middleware) *Client

WithMiddleware adds a middleware function to the client. Middlewares are executed in the order they are added.

func (*Client) WithRateLimiter

func (c *Client) WithRateLimiter(requestsPerSecond float64) *Client

WithRateLimiter configures rate limiting.

Parameters:

  • requestsPerSecond: Maximum number of requests per second

Example:

client := httpc.New().WithRateLimiter(10) // 10 requests per second

func (*Client) WithRateLimiterConfig

func (c *Client) WithRateLimiterConfig(requestsPerSecond float64, burst int) *Client

WithRateLimiterConfig configures rate limiting with burst capacity.

Parameters:

  • requestsPerSecond: Maximum sustained rate of requests per second
  • burst: Maximum burst size (tokens available at any moment)

func (*Client) WithRetry

func (c *Client) WithRetry(maxAttempts int, initialDelay time.Duration) *Client

WithRetry configures automatic retry with exponential backoff.

Parameters:

  • maxAttempts: Maximum number of attempts (including the initial request)
  • initialDelay: Initial delay between retries (will be exponentially increased)

Example:

client := httpc.New().WithRetry(3, time.Second)

func (*Client) WithRetryConfig

func (c *Client) WithRetryConfig(config *RetryConfig) *Client

WithRetryConfig configures automatic retry with custom settings.

func (*Client) WithTimeout

func (c *Client) WithTimeout(timeout time.Duration) *Client

WithTimeout sets the default timeout for all requests. This can be overridden per-request using Request.Timeout().

type DefaultLogger

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

DefaultLogger implements Logger using slog.

func NewDefaultLogger

func NewDefaultLogger(level slog.Level) *DefaultLogger

NewDefaultLogger creates a new DefaultLogger with the specified level.

func NewDefaultLoggerWithHandler

func NewDefaultLoggerWithHandler(handler slog.Handler) *DefaultLogger

NewDefaultLoggerWithHandler creates a new DefaultLogger with a custom slog handler.

func (*DefaultLogger) LogError

func (l *DefaultLogger) LogError(req *http.Request, err error, duration time.Duration)

LogError logs a request error.

func (*DefaultLogger) LogRequest

func (l *DefaultLogger) LogRequest(req *http.Request)

LogRequest logs an outgoing request.

func (*DefaultLogger) LogResponse

func (l *DefaultLogger) LogResponse(req *http.Request, resp *http.Response, duration time.Duration)

LogResponse logs a received response.

func (*DefaultLogger) LogRetry

func (l *DefaultLogger) LogRetry(req *http.Request, attempt int, delay time.Duration)

LogRetry logs a retry attempt.

type Error

type Error struct {
	// Op is the operation that failed (e.g., "do", "retry", "circuit_breaker")
	Op string

	// URL is the URL of the request that failed
	URL string

	// StatusCode is the HTTP status code (if available)
	StatusCode int

	// Err is the underlying error
	Err error
}

Error represents an HTTP client error with additional context.

func (*Error) Error

func (e *Error) Error() string

Error returns the error message.

func (*Error) IsClientError

func (e *Error) IsClientError() bool

IsClientError returns true if this is a 4xx client error.

func (*Error) IsRetryable

func (e *Error) IsRetryable() bool

IsRetryable returns true if the error is potentially retryable.

func (*Error) IsServerError

func (e *Error) IsServerError() bool

IsServerError returns true if this is a 5xx server error.

func (*Error) IsTimeout

func (e *Error) IsTimeout() bool

IsTimeout returns true if this is a timeout error.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap returns the underlying error.

type ErrorHandler

type ErrorHandler func(resp *Response) error

ErrorHandler is a function that inspects a response and returns an error if appropriate.

type HTTPHandler

type HTTPHandler func(req *http.Request) (*Response, error)

HTTPHandler is a function that processes an http.Request and returns a Response.

type Handler

type Handler = HTTPHandler

Handler is an alias for HTTPHandler for backward compatibility. Deprecated: Use HTTPHandler instead.

type Logger

type Logger interface {
	// LogRequest logs an outgoing request.
	LogRequest(req *http.Request)

	// LogResponse logs a received response.
	LogResponse(req *http.Request, resp *http.Response, duration time.Duration)

	// LogError logs a request error.
	LogError(req *http.Request, err error, duration time.Duration)

	// LogRetry logs a retry attempt.
	LogRetry(req *http.Request, attempt int, delay time.Duration)
}

Logger defines the interface for httpc logging.

type Middleware

type Middleware func(req *http.Request, next HTTPHandler) (*Response, error)

Middleware is a function that wraps an HTTPHandler. Middlewares can inspect or modify requests and responses.

Example:

authMiddleware := func(req *http.Request, next httpc.HTTPHandler) (*httpc.Response, error) {
    req.Header.Set("Authorization", "Bearer " + getToken())
    return next(req)
}

client := httpc.New().WithMiddleware(authMiddleware)

func AuthMiddleware

func AuthMiddleware(scheme, credentials string) Middleware

AuthMiddleware creates a middleware that adds an Authorization header.

func BearerTokenMiddleware

func BearerTokenMiddleware(token string) Middleware

BearerTokenMiddleware creates a middleware that adds a Bearer token.

func Chain

func Chain(middlewares ...Middleware) Middleware

Chain combines multiple middlewares into a single middleware. Middlewares are executed in the order provided.

func DynamicBearerTokenMiddleware

func DynamicBearerTokenMiddleware(tokenFunc BearerTokenFunc) Middleware

DynamicBearerTokenMiddleware creates a middleware that fetches a Bearer token dynamically. This is useful for tokens that expire and need to be refreshed.

func ErrorHandlerMiddleware

func ErrorHandlerMiddleware(handler ErrorHandler) Middleware

ErrorHandlerMiddleware creates a middleware that converts specific status codes to errors.

func HeadersMiddleware

func HeadersMiddleware(headers map[string]string) Middleware

HeadersMiddleware creates a middleware that adds default headers.

func LoggingMiddleware

func LoggingMiddleware(logger Logger) Middleware

LoggingMiddleware creates a middleware that logs requests and responses. This can be used as an alternative to the client-level logger.

func RateLimiterMiddleware

func RateLimiterMiddleware(limiter *rate.Limiter) Middleware

RateLimiterMiddleware creates a middleware that applies rate limiting. This can be used as an alternative to the client-level rate limiter.

func RequestIDMiddleware

func RequestIDMiddleware(headerName string, idFunc func() string) Middleware

RequestIDMiddleware creates a middleware that adds a request ID header. The idFunc generates a new ID for each request.

func ResponseHookMiddleware

func ResponseHookMiddleware(hook ResponseHook) Middleware

ResponseHookMiddleware creates a middleware that calls a hook for every response.

func UserAgentMiddleware

func UserAgentMiddleware(userAgent string) Middleware

UserAgentMiddleware creates a middleware that sets the User-Agent header.

type NopLogger

type NopLogger struct{}

NopLogger is a logger that does nothing.

func (NopLogger) LogError

func (l NopLogger) LogError(req *http.Request, err error, duration time.Duration)

LogError does nothing.

func (NopLogger) LogRequest

func (l NopLogger) LogRequest(req *http.Request)

LogRequest does nothing.

func (NopLogger) LogResponse

func (l NopLogger) LogResponse(req *http.Request, resp *http.Response, duration time.Duration)

LogResponse does nothing.

func (NopLogger) LogRetry

func (l NopLogger) LogRetry(req *http.Request, attempt int, delay time.Duration)

LogRetry does nothing.

type RateLimiterConfig

type RateLimiterConfig struct {
	// RequestsPerSecond is the maximum sustained rate of requests per second.
	RequestsPerSecond float64

	// Burst is the maximum burst size (tokens available at any moment).
	// If 0, defaults to RequestsPerSecond.
	Burst int
}

RateLimiterConfig configures the rate limiter behavior.

func DefaultRateLimiterConfig

func DefaultRateLimiterConfig() *RateLimiterConfig

DefaultRateLimiterConfig returns the default rate limiter configuration.

func NewRateLimiterConfig

func NewRateLimiterConfig(opts ...RateLimiterOption) *RateLimiterConfig

NewRateLimiterConfig creates a new RateLimiterConfig with the given options.

func (*RateLimiterConfig) NewRateLimiter

func (c *RateLimiterConfig) NewRateLimiter() *rate.Limiter

NewRateLimiter creates a new rate.Limiter from the configuration.

type RateLimiterOption

type RateLimiterOption func(*RateLimiterConfig)

RateLimiterOption is a function that modifies RateLimiterConfig.

func WithBurst

func WithBurst(burst int) RateLimiterOption

WithBurst sets the maximum burst size.

func WithRequestsPerSecond

func WithRequestsPerSecond(rps float64) RateLimiterOption

WithRequestsPerSecond sets the maximum requests per second.

type Request

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

Request represents an HTTP request being built. Use the fluent methods to configure the request, then call Do() or DecodeJSON() to execute.

func (*Request) BasicAuth

func (r *Request) BasicAuth(username, password string) *Request

BasicAuth sets the Authorization header for Basic Authentication.

func (*Request) Bearer

func (r *Request) Bearer(token string) *Request

Bearer sets the Authorization header with a Bearer token.

Example:

resp, err := client.Get("/users").Bearer("your-token").Do()

func (*Request) Body

func (r *Request) Body(body io.Reader) *Request

Body sets the request body from an io.Reader.

func (*Request) BodyBytes

func (r *Request) BodyBytes(body []byte) *Request

BodyBytes sets the request body from a byte slice.

func (*Request) BodyString

func (r *Request) BodyString(body string) *Request

BodyString sets the request body from a string.

func (*Request) Bytes

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

Bytes executes the request and returns the response body as bytes.

func (*Request) Context

func (r *Request) Context(ctx context.Context) *Request

Context sets the context for this request.

func (*Request) DecodeJSON

func (r *Request) DecodeJSON(v interface{}) error

DecodeJSON executes the request and decodes the JSON response into v. This is a convenience method that combines Do() and Response.JSON().

Example:

var users []User
err := client.Get("/users").DecodeJSON(&users)

func (*Request) DecodeJSONResponse

func (r *Request) DecodeJSONResponse(v interface{}) (*Response, error)

DecodeJSONResponse executes the request and decodes the JSON response into v, also returning the Response for further inspection.

func (*Request) Do

func (r *Request) Do() (*Response, error)

Do executes the request and returns the response.

Example:

resp, err := client.Get("/users").Do()
if err != nil {
    log.Fatal(err)
}
defer resp.Close()

func (*Request) ExpectStatus

func (r *Request) ExpectStatus(codes ...int) *Request

ExpectStatus specifies expected status codes. If the response status doesn't match any of these, Do() will return an error.

Example:

resp, err := client.Post("/users").JSON(user).ExpectStatus(201).Do()

func (*Request) Form

func (r *Request) Form(data map[string]string) *Request

Form sets the request body as form-urlencoded data.

Example:

resp, err := client.Post("/login").Form(map[string]string{"user": "john", "pass": "secret"}).Do()

func (*Request) FormValues

func (r *Request) FormValues(data url.Values) *Request

FormValues sets the request body from url.Values.

func (*Request) Header

func (r *Request) Header(key, value string) *Request

Header sets a header for this request.

Example:

resp, err := client.Get("/users").Header("Authorization", "Bearer token").Do()

func (*Request) Headers

func (r *Request) Headers(headers map[string]string) *Request

Headers sets multiple headers at once.

func (*Request) JSON

func (r *Request) JSON(v interface{}) *Request

JSON sets the request body as JSON and sets the Content-Type header.

Example:

user := User{Name: "John", Email: "john@example.com"}
resp, err := client.Post("/users").JSON(user).Do()

func (*Request) Query

func (r *Request) Query(key, value string) *Request

Query adds a query parameter to the request URL.

Example:

resp, err := client.Get("/users").Query("page", "1").Query("limit", "10").Do()

func (*Request) QueryParams

func (r *Request) QueryParams(params map[string]string) *Request

QueryParams adds multiple query parameters at once.

func (*Request) QueryValues

func (r *Request) QueryValues(params url.Values) *Request

QueryValues adds url.Values as query parameters.

func (*Request) String

func (r *Request) String() (string, error)

String executes the request and returns the response body as a string.

func (*Request) Timeout

func (r *Request) Timeout(timeout time.Duration) *Request

Timeout sets the timeout for this specific request. This overrides the client's default timeout.

type Response

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

Response wraps an http.Response with convenient helper methods.

func (*Response) Body

func (r *Response) Body() io.ReadCloser

Body returns the response body as an io.ReadCloser. Remember to close it when done.

func (*Response) Bytes

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

Bytes reads and returns the entire response body as bytes. The body is cached, so subsequent calls return the same data.

func (*Response) Close

func (r *Response) Close() error

Close closes the response body. It's safe to call Close multiple times.

func (*Response) ContentLength

func (r *Response) ContentLength() int64

ContentLength returns the Content-Length header value.

func (*Response) ContentType

func (r *Response) ContentType() string

ContentType returns the Content-Type header value.

func (*Response) Cookies

func (r *Response) Cookies() []*http.Cookie

Cookies returns the cookies set in the response.

func (*Response) Header

func (r *Response) Header(key string) string

Header returns the value of the specified response header.

func (*Response) Headers

func (r *Response) Headers() http.Header

Headers returns all response headers.

func (*Response) IsClientError

func (r *Response) IsClientError() bool

IsClientError returns true if the status code is in the 4xx range.

func (*Response) IsError

func (r *Response) IsError() bool

IsError returns true if the status code is 4xx or 5xx.

func (*Response) IsServerError

func (r *Response) IsServerError() bool

IsServerError returns true if the status code is in the 5xx range.

func (*Response) IsSuccess

func (r *Response) IsSuccess() bool

IsSuccess returns true if the status code is in the 2xx range.

func (*Response) JSON

func (r *Response) JSON(v interface{}) error

JSON decodes the response body as JSON into v.

Example:

var user User
if err := resp.JSON(&user); err != nil {
    log.Fatal(err)
}

func (*Response) Location

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

Location returns the Location header value, typically used in redirects.

func (*Response) Raw

func (r *Response) Raw() *http.Response

Raw returns the underlying *http.Response. Use this when you need access to the full standard library response.

func (*Response) Status

func (r *Response) Status() string

Status returns the HTTP status string (e.g., "200 OK").

func (*Response) StatusCode

func (r *Response) StatusCode() int

StatusCode returns the HTTP status code.

func (*Response) String

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

String reads and returns the response body as a string.

type ResponseHook

type ResponseHook func(req *http.Request, resp *Response)

ResponseHook is a function called for every response.

type RetryConfig

type RetryConfig struct {
	// MaxAttempts is the maximum number of attempts (including the initial request).
	// Default: 3
	MaxAttempts int

	// InitialDelay is the initial delay between retries.
	// Default: 1 second
	InitialDelay time.Duration

	// MaxDelay is the maximum delay between retries.
	// Default: 30 seconds
	MaxDelay time.Duration

	// Multiplier is the factor by which the delay increases after each retry.
	// Default: 2.0 (exponential backoff)
	Multiplier float64

	// Jitter adds randomness to the delay to prevent thundering herd.
	// Default: true
	Jitter bool

	// RetryIf is a custom function to determine if a request should be retried.
	// If nil, the default retry logic is used.
	RetryIf func(statusCode int, err error) bool
}

RetryConfig configures the retry behavior.

func DefaultRetryConfig

func DefaultRetryConfig() *RetryConfig

DefaultRetryConfig returns the default retry configuration.

func NewRetryConfig

func NewRetryConfig(opts ...RetryOption) *RetryConfig

NewRetryConfig creates a new RetryConfig with the given options.

func (*RetryConfig) GetDelay

func (c *RetryConfig) GetDelay(attempt int) time.Duration

GetDelay calculates the delay for the given attempt number (0-indexed).

func (*RetryConfig) ShouldRetry

func (c *RetryConfig) ShouldRetry(statusCode int, err error) bool

ShouldRetry determines if the request should be retried based on status code and error.

type RetryOption

type RetryOption func(*RetryConfig)

RetryOption is a function that modifies RetryConfig.

func WithInitialDelay

func WithInitialDelay(d time.Duration) RetryOption

WithInitialDelay sets the initial delay between retries.

func WithJitter

func WithJitter(enabled bool) RetryOption

WithJitter enables or disables jitter in retry delays.

func WithMaxAttempts

func WithMaxAttempts(n int) RetryOption

WithMaxAttempts sets the maximum number of retry attempts.

func WithMaxDelay

func WithMaxDelay(d time.Duration) RetryOption

WithMaxDelay sets the maximum delay between retries.

func WithMultiplier

func WithMultiplier(m float64) RetryOption

WithMultiplier sets the delay multiplier for exponential backoff.

func WithRetryIf

func WithRetryIf(fn func(statusCode int, err error) bool) RetryOption

WithRetryIf sets a custom retry condition function.

Directories

Path Synopsis
examples
advanced command
Package main demonstrates advanced usage of the httpc library.
Package main demonstrates advanced usage of the httpc library.
basic command
Package main demonstrates basic usage of the httpc library.
Package main demonstrates basic usage of the httpc library.
Package mock provides utilities for mocking HTTP responses in tests.
Package mock provides utilities for mocking HTTP responses in tests.

Jump to

Keyboard shortcuts

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