Documentation
¶
Overview ¶
Package httpx provides comprehensive utilities for building and executing HTTP requests with advanced features including fluent request building, automatic retry logic, and type-safe generic clients.
Requirements: Go 1.22 or higher is required to use this package.
Zero Dependencies: This package is built entirely using the Go standard library, with no external dependencies. This ensures maximum reliability, security, and minimal maintenance overhead for your projects.
This package is designed to simplify HTTP client development in Go by providing:
- A fluent, chainable API for building HTTP requests with validation
- Type-safe HTTP clients using Go generics for automatic JSON marshaling
- Configurable retry logic with multiple backoff strategies
- Comprehensive error handling and validation
- Production-ready defaults with full customization support
- Zero external dependencies - built purely with Go standard library
Quick Start ¶
Build and execute a simple GET request:
req, err := httpx.NewRequestBuilder("https://api.example.com").
WithMethodGET().
Path("/users/123").
Header("Accept", "application/json").
Build()
Use type-safe generic client:
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
client := httpx.NewGenericClient[User](
httpx.// )
response, err := client.Get("/users/123")
fmt.Printf("User: %s\n", response.Data.Name)
Request Builder ¶
The RequestBuilder provides a fluent API for constructing HTTP requests with comprehensive input validation and error accumulation. All inputs are validated before the request is built, ensuring early error detection.
Basic usage:
req, err := httpx.NewRequestBuilder("https://api.example.com").
WithMethodPOST().
Path("/users").
QueryParam("notify", "true").
Header("Content-Type", "application/json").
BearerAuth("your-token-here").
JSONBody(user).
Build()
Request builder features:
- HTTP methods: GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, TRACE, CONNECT
- Convenience methods: WithMethodGET, WithMethodPOST, WithMethodPUT, WithMethodDELETE, WithMethodPATCH, WithMethodHEAD, WithMethodOPTIONS, WithMethodTRACE, WithMethodCONNECT
- Query parameters with automatic URL encoding and validation
- Custom headers with format validation
- Authentication: Basic Auth and Bearer Token with validation
- Multiple body formats: JSON (auto-marshal), string, bytes, io.Reader
- Context support for timeouts and cancellation
- Input validation with error accumulation
- Detailed error messages indicating what failed
- Reset and reuse builder
Validation features:
The builder validates all inputs and accumulates errors, allowing you to detect multiple issues at once:
builder := httpx.NewRequestBuilder("https://api.example.com")
builder.HTTPMethod("") // Error: empty method
builder.WithHeader("", "value") // Error: empty header key
builder.WithQueryParam("key=", "val") // Error: invalid character in key
// Check accumulated errors
if builder.HasErrors() {
for _, err := range builder.GetErrors() {
log.Printf("Validation error: %v", err)
}
}
// Or let Build() report all errors
req, err := builder.Build() // Returns all accumulated errors
Builder reuse:
builder := httpx.NewRequestBuilder("https://api.example.com")
req1, _ := builder.WithWithMethodGET().WithPath("/users").Build()
builder.Reset() // Clear state
req2, _ := builder.WithWithMethodPOST().WithPath("/posts").Build()
Generic HTTP Client ¶
The GenericClient provides type-safe HTTP requests using Go generics with automatic JSON marshaling and unmarshaling. This eliminates the need for manual type assertions and reduces boilerplate code.
Basic usage:
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
client := httpx.NewGenericClient[User](
httpx.WithTimeout[User](10*time.Second),
httpx.WithMaxRetries[User](3),
httpx.WithRetryStrategy[User](httpx.ExponentialBackoffStrategy),
)
// GET request - response.Data is strongly typed as User
response, err := client.Get("/users/1")
if err != nil {
log.Fatal(err)
}
fmt.Printf("User: %s (%s)\n", response.Data.Name, response.Data.Email)
Generic client features:
- Type-safe responses with automatic JSON unmarshaling
- Compile-time type checking for response data
- Convenience methods: Get, Post, Put, Delete, Patch
- Execute method for custom requests (works with RequestBuilder)
- ExecuteRaw for non-JSON responses (images, files, etc.)
- Flexible configuration via option pattern
- Built-in retry logic with configurable strategies
- Connection pooling and timeout configuration
- TLS handshake and idle connection timeout settings
- Structured error responses with ErrorResponse type
- Full integration with ClientBuilder for complex configurations
- Debug logging support (uses slog)
Configuration options:
- WithTimeout: Set request timeout
- WithMaxRetries: Set maximum retry attempts
- WithRetryStrategy: Configure retry strategy (fixed, jitter, exponential)
- WithRetryBaseDelay: Set base delay for retry strategies
- WithRetryMaxDelay: Set maximum delay for retry strategies
- WithMaxIdleConns: Set maximum idle connections
- WithIdleConnTimeout: Set idle connection timeout
- WithTLSHandshakeTimeout: Set TLS handshake timeout
- WithExpectContinueTimeout: Set expect continue timeout
- WithMaxIdleConnsPerHost: Set maximum idle connections per host
- WithDisableKeepAlive: Disable HTTP keep-alive
- WithProxy: Configure HTTP/HTTPS proxy server
- WithHTTPClient: Use a pre-configured HTTP client (takes precedence)
Integration with RequestBuilder:
req, err := httpx.NewRequestBuilder("https://api.example.com").
WithMethodPOST().
Path("/users").
ContentType("application/json").
Header("X-Request-ID", "unique-123").
JSONBody(newUser).
Build()
response, err := client.Execute(req) // Type-safe execution
Multiple typed clients:
userClient := httpx.NewGenericClient[User](...)
postClient := httpx.NewGenericClient[Post](...)
user, _ := userClient.Get("/users/1")
posts, _ := postClient.Get(fmt.Sprintf("/users/%d/posts", user.Data.ID))
Error handling:
response, err := client.Get("/users/999")
if err != nil {
if apiErr, ok := err.(*httpx.ErrorResponse); ok {
// Structured API error
fmt.Printf("API Error %d: %s\n", apiErr.StatusCode, apiErr.Message)
} else {
// Network error, parsing error, etc.
log.Printf("Request failed: %v\n", err)
}
}
Retry Logic ¶
The package provides transparent retry logic that automatically retries failed requests using configurable backoff strategies. Retry logic preserves all request properties including headers and authentication.
What gets retried:
- Network errors (connection failures, timeouts)
- HTTP 5xx server errors (500-599)
- HTTP 429 (Too Many Requests)
What does NOT get retried:
- HTTP 4xx client errors (except 429)
- HTTP 2xx/3xx successful responses
- Requests without GetBody (non-replayable)
Available retry strategies:
Exponential Backoff (recommended for most use cases):
strategy := httpx.ExponentialBackoff(500*time.Millisecond, 10*time.Second) // Wait times: 500ms → 1s → 2s → 4s → 8s (capped at maxDelay)
Fixed Delay (useful for predictable retry timing):
strategy := httpx.FixedDelay(1*time.Second) // Wait times: 1s → 1s → 1s
Jitter Backoff (prevents thundering herd problem):
strategy := httpx.JitterBackoff(500*time.Millisecond, 10*time.Second) // Wait times: random(0-500ms) → random(0-1s) → random(0-2s)
Direct usage (advanced):
client := httpx.NewHTTPRetryClient(
httpx.WithMaxRetriesRetry(3),
httpx.WithRetryStrategyRetry(httpx.ExponentialBackoff(500*time.Millisecond, 10*time.Second)),
httpx.WithBaseTransport(http.DefaultTransport),
)
Client Builder ¶
The ClientBuilder provides a fluent API for configuring HTTP clients with retry logic, timeouts, and connection pooling. All settings are validated and default to production-ready values if out of range.
Basic configuration:
client := httpx.NewClientBuilder().
WithTimeout(30 * time.Second).
WithMaxRetries(3).
WithRetryStrategy(httpx.ExponentialBackoffStrategy).
Build()
Advanced configuration:
client := httpx.NewClientBuilder().
// Timeouts
WithTimeout(30 * time.Second).
WithIdleConnTimeout(90 * time.Second).
WithTLSHandshakeTimeout(10 * time.Second).
WithExpectContinueTimeout(1 * time.Second).
// Connection pooling
WithMaxIdleConns(100).
WithMaxIdleConnsPerHost(10).
WithDisableKeepAlive(false).
// Retry configuration
WithMaxRetries(3).
WithRetryStrategy(httpx.ExponentialBackoffStrategy).
WithRetryBaseDelay(500 * time.Millisecond).
WithRetryMaxDelay(10 * time.Second).
// Proxy configuration
WithProxy("http://proxy.example.com:8080").
Build()
Combine with GenericClient:
retryClient := httpx.NewClientBuilder().
WithMaxRetries(3).
WithRetryStrategy(httpx.ExponentialBackoffStrategy).
Build()
client := httpx.NewGenericClient[User](
httpx.WithHTTPClient[User](retryClient),
httpx.// )
Default values (validated and adjusted if out of range):
- Timeout: 5 seconds (valid: 1s-30s)
- MaxRetries: 3 (valid: 1-10)
- RetryBaseDelay: 500ms (valid: 300ms-5s)
- RetryMaxDelay: 10s (valid: 300ms-120s)
- MaxIdleConns: 100 (valid: 1-200)
- IdleConnTimeout: 90s (valid: 1s-120s)
- TLSHandshakeTimeout: 10s (valid: 1s-15s)
Error Handling ¶
The package provides comprehensive error handling with specific error types:
ErrorResponse (from GenericClient): Structured API errors with status code and message
ClientError: Generic HTTP client operation errors
RequestBuilder validation errors: Accumulated during building, reported at Build() time
Error handling examples:
// Generic client errors
response, err := client.Get("/users/999")
if err != nil {
if apiErr, ok := err.(*httpx.ErrorResponse); ok {
switch apiErr.StatusCode {
case 404:
log.Printf("Not found: %s", apiErr.Message)
case 401:
log.Printf("Unauthorized: %s", apiErr.Message)
case 429:
log.Printf("Rate limited: %s", apiErr.Message)
default:
log.Printf("API error %d: %s", apiErr.StatusCode, apiErr.Message)
}
} else {
log.Printf("Network error: %v", err)
}
}
// Builder validation errors
builder := httpx.NewRequestBuilder("https://api.example.com")
builder.WithHeader("", "value") // Invalid
if builder.HasErrors() {
for _, err := range builder.GetErrors() {
log.Printf("Validation: %v", err)
}
}
Best Practices ¶
Use type-safe clients for JSON APIs:
client := httpx.NewGenericClient[User](...) response, err := client.Get("/users/1") // response.Data is User, not interface{}
Configure retry logic for production:
retryClient := httpx.NewClientBuilder(). WithMaxRetries(3). WithRetryStrategy(httpx.ExponentialBackoffStrategy). Build()
Reuse HTTP clients (they're safe for concurrent use):
client := httpx.NewGenericClient[User](...) // Use from multiple goroutines safely
Use contexts for timeouts and cancellation:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() req, _ := builder.WithContext(ctx).Build()
Validate before building:
if builder.HasErrors() { // Handle validation errors }
Handle API errors appropriately:
if apiErr, ok := err.(*httpx.ErrorResponse); ok { // Handle specific status codes }
Proxy Configuration ¶
The package provides comprehensive proxy support for all HTTP clients. Proxy configuration works transparently across all client types and supports both HTTP and HTTPS proxies with optional authentication.
Basic proxy configuration with ClientBuilder:
client := httpx.NewClientBuilder().
WithProxy("http://proxy.example.com:8080").
Build()
HTTPS proxy:
client := httpx.NewClientBuilder().
WithProxy("https://secure-proxy.example.com:3128").
Build()
Proxy with authentication:
client := httpx.NewClientBuilder().
WithProxy("http://username:[email protected]:8080").
Build()
Proxy with GenericClient:
client := httpx.NewGenericClient[User](
httpx.WithProxy[User]("http://proxy.example.com:8080"),
httpx.WithTimeout[User](10*time.Second),
httpx.WithMaxRetries[User](3),
)
Proxy with retry client:
client := httpx.NewHTTPRetryClient(
httpx.WithProxyRetry("http://proxy.example.com:8080"),
httpx.WithMaxRetriesRetry(5),
)
Disable proxy (override environment variables):
client := httpx.NewClientBuilder().
WithProxy(""). // Empty string disables proxy
Build()
Common proxy ports:
- HTTP proxy: 8080, 3128, 8888
- HTTPS proxy: 3128, 8443
- SOCKS proxy: 1080 (not directly supported, use custom transport)
The proxy configuration:
- Works transparently with all request types
- Preserves all headers and authentication
- Compatible with retry logic
- Supports connection pooling
- Respects timeout settings
- Validates proxy URL format
- Falls back gracefully on invalid URLs
Thread Safety ¶
All utilities in this package are safe for concurrent use across multiple goroutines:
- RequestBuilder instances should not be shared between goroutines
- GenericClient instances are safe for concurrent use
- HTTP clients built by ClientBuilder are safe for concurrent use
- Retry logic preserves request immutability
Example concurrent usage:
client := httpx.NewGenericClient[User](...)
var wg sync.WaitGroup
for i := 1; i <= 10; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
user, err := client.Get(fmt.Sprintf("/users/%d", id))
// Process user
}(i)
}
wg.Wait()
Debugging ¶
The package uses slog for debug logging. Enable debug logging to see:
- Request details (method, URL, headers, body)
- Response details (status, headers, body)
- Retry attempts and delays
Enable debug logging:
logger := slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
Level: slog.LevelDebug,
}))
slog.SetDefault(logger)
Documentation ¶
You can view the full documentation and examples locally by running:
go doc -http=:8080
Then navigate to http://localhost:8080/pkg/github.com/slashdevops/httpx/ in your browser to browse the complete documentation, examples, and source code.
See Also ¶
For complete examples and API reference, see the README.md file or visit: https://pkg.go.dev/github.com/slashdevops/httpx
Example ¶
Example demonstrates using exponential backoff.
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"sync/atomic"
"time"
"github.com/slashdevops/httpx"
)
func main() {
var requestCount int32
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
count := atomic.AddInt32(&requestCount, 1)
if count <= 3 { // Fail first 3 times
fmt.Printf("Server: Request %d -> 500 Internal Server Error\n", count)
w.WriteHeader(http.StatusInternalServerError)
} else {
fmt.Printf("Server: Request %d -> 200 OK\n", count)
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte("Success after backoff"))
}
}))
defer server.Close()
// Create a client with exponential backoff.
// Base delay 5ms, max delay 50ms, max 4 retries.
retryClient := httpx.NewHTTPRetryClient(
httpx.WithMaxRetriesRetry(4),
httpx.WithRetryStrategyRetry(httpx.ExponentialBackoff(5*time.Millisecond, 50*time.Millisecond)),
)
fmt.Println("Client: Making request with exponential backoff...")
resp, err := retryClient.Get(server.URL)
if err != nil {
fmt.Printf("Client: Request failed: %v\n", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Printf("Client: Received response: Status=%s, Body='%s'\n", resp.Status, string(body))
// Note: Duration will vary slightly, but should reflect increasing delays.
fmt.Printf("Client: Total time approx > %dms (due to backoff)\n", (5 + 10 + 20)) // 5ms + 10ms + 20ms delays
}
Output: Client: Making request with exponential backoff... Server: Request 1 -> 500 Internal Server Error Server: Request 2 -> 500 Internal Server Error Server: Request 3 -> 500 Internal Server Error Server: Request 4 -> 200 OK Client: Received response: Status=200 OK, Body='Success after backoff' Client: Total time approx > 35ms (due to backoff)
Example (WithLogger_disabled) ¶
Example_withLogger_disabled demonstrates the default behavior with no logging. This example shows that by default, logging is disabled for clean, silent operation.
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"sync/atomic"
"time"
"github.com/slashdevops/httpx"
)
func main() {
attempts := atomic.Int32{}
// Create a test server that fails twice, then succeeds
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
attempt := attempts.Add(1)
if attempt <= 2 {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, "Success")
}))
defer server.Close()
// Create client WITHOUT logging (default behavior)
// No logger means silent retries - clean operation without log noise
client := httpx.NewClientBuilder().
WithMaxRetries(3).
WithRetryBaseDelay(500 * time.Millisecond). // Use valid delay
WithRetryMaxDelay(10 * time.Second). // Use valid delay
Build() // No WithLogger call = no logging
fmt.Println("Making request with logging disabled (default)...")
resp, err := client.Get(server.URL)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Printf("Response: %s\n", body)
fmt.Println("No retry logs appear - silent operation")
}
Output: Making request with logging disabled (default)... Response: Success No retry logs appear - silent operation
Index ¶
- Constants
- Variables
- func NewHTTPRetryClient(options ...RetryClientOption) *http.Client
- type Client
- type ClientBuilder
- func (b *ClientBuilder) Build() *http.Client
- func (b *ClientBuilder) WithDisableKeepAlive(disableKeepAlive bool) *ClientBuilder
- func (b *ClientBuilder) WithExpectContinueTimeout(expectContinueTimeout time.Duration) *ClientBuilder
- func (b *ClientBuilder) WithIdleConnTimeout(idleConnTimeout time.Duration) *ClientBuilder
- func (b *ClientBuilder) WithLogger(logger *slog.Logger) *ClientBuilder
- func (b *ClientBuilder) WithMaxIdleConns(maxIdleConns int) *ClientBuilder
- func (b *ClientBuilder) WithMaxIdleConnsPerHost(maxIdleConnsPerHost int) *ClientBuilder
- func (b *ClientBuilder) WithMaxRetries(maxRetries int) *ClientBuilder
- func (b *ClientBuilder) WithProxy(proxyURL string) *ClientBuilder
- func (b *ClientBuilder) WithRetryBaseDelay(baseDelay time.Duration) *ClientBuilder
- func (b *ClientBuilder) WithRetryMaxDelay(maxDelay time.Duration) *ClientBuilder
- func (b *ClientBuilder) WithRetryStrategy(strategy Strategy) *ClientBuilder
- func (b *ClientBuilder) WithRetryStrategyAsString(strategy string) *ClientBuilder
- func (b *ClientBuilder) WithTLSHandshakeTimeout(tlsHandshakeTimeout time.Duration) *ClientBuilder
- func (b *ClientBuilder) WithTimeout(timeout time.Duration) *ClientBuilder
- type ClientError
- type ErrorResponse
- type GenericClient
- func (c *GenericClient[T]) Delete(url string) (*Response[T], error)
- func (c *GenericClient[T]) Do(req *http.Request) (*Response[T], error)
- func (c *GenericClient[T]) Execute(req *http.Request) (*Response[T], error)
- func (c *GenericClient[T]) ExecuteRaw(req *http.Request) (*http.Response, error)
- func (c *GenericClient[T]) Get(url string) (*Response[T], error)
- func (c *GenericClient[T]) Patch(url string, body io.Reader) (*Response[T], error)
- func (c *GenericClient[T]) Post(url string, body io.Reader) (*Response[T], error)
- func (c *GenericClient[T]) Put(url string, body io.Reader) (*Response[T], error)
- type GenericClientOption
- func WithDisableKeepAlive[T any](disableKeepAlive bool) GenericClientOption[T]
- func WithExpectContinueTimeout[T any](expectContinueTimeout time.Duration) GenericClientOption[T]
- func WithHTTPClient[T any](httpClient HTTPClient) GenericClientOption[T]
- func WithIdleConnTimeout[T any](idleConnTimeout time.Duration) GenericClientOption[T]
- func WithLogger[T any](logger *slog.Logger) GenericClientOption[T]
- func WithMaxIdleConns[T any](maxIdleConns int) GenericClientOption[T]
- func WithMaxIdleConnsPerHost[T any](maxIdleConnsPerHost int) GenericClientOption[T]
- func WithMaxRetries[T any](maxRetries int) GenericClientOption[T]
- func WithProxy[T any](proxyURL string) GenericClientOption[T]
- func WithRetryBaseDelay[T any](baseDelay time.Duration) GenericClientOption[T]
- func WithRetryMaxDelay[T any](maxDelay time.Duration) GenericClientOption[T]
- func WithRetryStrategy[T any](strategy Strategy) GenericClientOption[T]
- func WithRetryStrategyAsString[T any](strategy string) GenericClientOption[T]
- func WithTLSHandshakeTimeout[T any](tlsHandshakeTimeout time.Duration) GenericClientOption[T]
- func WithTimeout[T any](timeout time.Duration) GenericClientOption[T]
- type HTTPClient
- type RequestBuilder
- func (rb *RequestBuilder) Build() (*http.Request, error)
- func (rb *RequestBuilder) GetErrors() []error
- func (rb *RequestBuilder) HasErrors() bool
- func (rb *RequestBuilder) Reset() *RequestBuilder
- func (rb *RequestBuilder) WithAccept(accept string) *RequestBuilder
- func (rb *RequestBuilder) WithBasicAuth(username, password string) *RequestBuilder
- func (rb *RequestBuilder) WithBearerAuth(token string) *RequestBuilder
- func (rb *RequestBuilder) WithBytesBody(body []byte) *RequestBuilder
- func (rb *RequestBuilder) WithContentType(contentType string) *RequestBuilder
- func (rb *RequestBuilder) WithContext(ctx context.Context) *RequestBuilder
- func (rb *RequestBuilder) WithHeader(key, value string) *RequestBuilder
- func (rb *RequestBuilder) WithHeaders(headers map[string]string) *RequestBuilder
- func (rb *RequestBuilder) WithJSONBody(body any) *RequestBuilder
- func (rb *RequestBuilder) WithMethod(method string) *RequestBuilder
- func (rb *RequestBuilder) WithMethodCONNECT() *RequestBuilder
- func (rb *RequestBuilder) WithMethodDELETE() *RequestBuilder
- func (rb *RequestBuilder) WithMethodGET() *RequestBuilder
- func (rb *RequestBuilder) WithMethodHEAD() *RequestBuilder
- func (rb *RequestBuilder) WithMethodOPTIONS() *RequestBuilder
- func (rb *RequestBuilder) WithMethodPATCH() *RequestBuilder
- func (rb *RequestBuilder) WithMethodPOST() *RequestBuilder
- func (rb *RequestBuilder) WithMethodPUT() *RequestBuilder
- func (rb *RequestBuilder) WithMethodTRACE() *RequestBuilder
- func (rb *RequestBuilder) WithPath(path string) *RequestBuilder
- func (rb *RequestBuilder) WithQueryParam(key, value string) *RequestBuilder
- func (rb *RequestBuilder) WithQueryParams(params map[string]string) *RequestBuilder
- func (rb *RequestBuilder) WithRawBody(body io.Reader) *RequestBuilder
- func (rb *RequestBuilder) WithStringBody(body string) *RequestBuilder
- func (rb *RequestBuilder) WithUserAgent(userAgent string) *RequestBuilder
- type Response
- type RetryClientOption
- func WithBaseTransport(transport http.RoundTripper) RetryClientOption
- func WithLoggerRetry(logger *slog.Logger) RetryClientOption
- func WithMaxRetriesRetry(maxRetries int) RetryClientOption
- func WithProxyRetry(proxyURL string) RetryClientOption
- func WithRetryStrategyRetry(strategy RetryStrategy) RetryClientOption
- type RetryStrategy
- type Strategy
Examples ¶
- Package
- Package (WithLogger_disabled)
- ClientBuilder.WithProxy
- ClientBuilder.WithProxy (Authentication)
- ClientBuilder.WithProxy (Disabling)
- ClientBuilder.WithProxy (Https)
- GenericClient (ErrorHandling)
- GenericClient (MultipleClients)
- GenericClient (ProductionConfiguration)
- GenericClient (RetryStrategies)
- GenericClient (WithCustomHeaders)
- GenericClient (WithRetry)
- GenericClient.Execute
- GenericClient.ExecuteRaw
- GenericClient.Post
- NewClientBuilder
- NewClientBuilder (AllOptions)
- NewClientBuilder (ConnectionPooling)
- NewClientBuilder (DisableKeepAlive)
- NewClientBuilder (FixedDelayStrategy)
- NewClientBuilder (JitterBackoffStrategy)
- NewClientBuilder (MultipleClients)
- NewClientBuilder (ProductionConfig)
- NewClientBuilder (TlsConfiguration)
- NewClientBuilder (Transparent)
- NewClientBuilder (WithRetryStrategy)
- NewClientBuilder (WithTimeout)
- NewGenericClient
- NewGenericClient (AllOptions)
- NewGenericClient (WithPreConfiguredClient)
- NewGenericClient (WithProxy)
- NewGenericClient (WithProxy_combined)
- NewGenericClient (WithProxy_portVariations)
- NewHTTPRetryClient (WithCustomTransport)
- NewHTTPRetryClient (WithExistingAuth)
- NewHTTPRetryClient (WithProxy)
- RequestBuilder (ComplexRequest)
- RequestBuilder (FullExample)
- RequestBuilder (PostWithJSON)
- RequestBuilder (SimpleGET)
- RequestBuilder (WithAcceptHeader)
- RequestBuilder (WithBasicAuth)
- RequestBuilder (WithBearerAuth)
- RequestBuilder (WithContext)
- RequestBuilder (WithMethodCONNECT)
- RequestBuilder (WithMethodHEAD)
- RequestBuilder (WithMethodOPTIONS)
- RequestBuilder (WithMethodTRACE)
- RequestBuilder (WithMultipleAcceptTypes)
- RequestBuilder (WithQueryParams)
Constants ¶
const ( ValidMaxIdleConns = 200 ValidMinIdleConns = 1 ValidMaxIdleConnsPerHost = 200 ValidMinIdleConnsPerHost = 1 ValidMaxIdleConnTimeout = 120 * time.Second ValidMinIdleConnTimeout = 1 * time.Second ValidMaxTLSHandshakeTimeout = 15 * time.Second ValidMinTLSHandshakeTimeout = 1 * time.Second ValidMaxExpectContinueTimeout = 5 * time.Second ValidMinExpectContinueTimeout = 1 * time.Second ValidMaxTimeout = 30 * time.Second ValidMinTimeout = 1 * time.Second ValidMaxRetries = 10 ValidMinRetries = 1 ValidMaxBaseDelay = 5 * time.Second ValidMinBaseDelay = 300 * time.Millisecond ValidMaxMaxDelay = 120 * time.Second ValidMinMaxDelay = 300 * time.Millisecond // DefaultMaxRetries is the default number of retry attempts DefaultMaxRetries = 3 // DefaultBaseDelay is the default base delay for backoff strategies DefaultBaseDelay = 500 * time.Millisecond // DefaultMaxDelay is the default maximum delay for backoff strategies DefaultMaxDelay = 10 * time.Second // DefaultMaxIdleConns is the default maximum number of idle connections DefaultMaxIdleConns = 100 // DefaultIdleConnTimeout is the default idle connection timeout DefaultIdleConnTimeout = 90 * time.Second // DefaultTLSHandshakeTimeout is the default TLS handshake timeout DefaultTLSHandshakeTimeout = 10 * time.Second // DefaultExpectContinueTimeout is the default expect continue timeout DefaultExpectContinueTimeout = 1 * time.Second // DefaultDisableKeepAlive is the default disable keep-alive setting DefaultDisableKeepAlive = false // DefaultMaxIdleConnsPerHost is the default maximum number of idle connections per host DefaultMaxIdleConnsPerHost = 100 // DefaultTimeout is the default timeout for HTTP requests DefaultTimeout = 5 * time.Second )
Variables ¶
var ErrAllRetriesFailed = errors.New("all retry attempts failed")
Functions ¶
func NewHTTPRetryClient ¶
func NewHTTPRetryClient(options ...RetryClientOption) *http.Client
NewHTTPRetryClient creates a new http.Client configured with the retry transport. Use the provided options to customize the retry behavior. By default, it uses 3 retries with exponential backoff strategy and no logging.
Example (WithCustomTransport) ¶
ExampleNewHTTPRetryClient_withCustomTransport demonstrates using a custom base transport with specific transport settings while maintaining transparent retry behavior.
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"sync/atomic"
"time"
"github.com/slashdevops/httpx"
)
func main() {
var requestCount int32
// Create a test server that fails initially to show retry behavior with custom transport
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
count := atomic.AddInt32(&requestCount, 1)
fmt.Printf("Server: Request %d from custom transport\n", count)
if count <= 1 {
w.WriteHeader(http.StatusInternalServerError)
} else {
w.WriteHeader(http.StatusOK)
if _, err := w.Write([]byte("Custom transport with retries works!")); err != nil {
fmt.Printf("Failed to write response: %v\n", err)
}
}
}))
defer server.Close()
// Create a custom transport with specific settings
customTransport := &http.Transport{
MaxIdleConns: 50, // Custom connection pool size
IdleConnTimeout: 30 * time.Second, // Custom idle timeout
DisableKeepAlives: false, // Enable keep-alive
MaxIdleConnsPerHost: 10, // Custom per-host connection limit
TLSHandshakeTimeout: 5 * time.Second, // Custom TLS timeout
}
// Create retry client with custom transport
client := httpx.NewHTTPRetryClient(
httpx.WithMaxRetriesRetry(3),
httpx.WithRetryStrategyRetry(httpx.ExponentialBackoff(5*time.Millisecond, 50*time.Millisecond)),
httpx.WithBaseTransport(customTransport),
)
fmt.Println("Client: Making request with custom transport...")
resp, err := client.Get(server.URL)
if err != nil {
fmt.Printf("Client: Request failed: %v\n", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Printf("Client: Response: %s\n", string(body))
fmt.Printf("Client: Custom transport config preserved (MaxIdleConns: %d)\n",
customTransport.MaxIdleConns)
}
Output: Client: Making request with custom transport... Server: Request 1 from custom transport Server: Request 2 from custom transport Client: Response: Custom transport with retries works! Client: Custom transport config preserved (MaxIdleConns: 50)
Example (WithExistingAuth) ¶
ExampleNewHTTPRetryClient_withExistingAuth demonstrates how the default client transparently preserves existing authentication headers in requests.
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"sync/atomic"
"time"
"github.com/slashdevops/httpx"
)
func main() {
var requestCount int32
// Create a server that requires authentication
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
count := atomic.AddInt32(&requestCount, 1)
auth := r.Header.Get("Authorization")
if auth == "" {
fmt.Printf("Server: Request %d -> 401 Unauthorized (no auth)\n", count)
w.WriteHeader(http.StatusUnauthorized)
return
}
fmt.Printf("Server: Request %d with %s -> ", count, auth)
if count <= 2 {
fmt.Println("500 Internal Server Error")
w.WriteHeader(http.StatusInternalServerError)
} else {
fmt.Println("200 OK")
w.WriteHeader(http.StatusOK)
if _, err := w.Write([]byte("Authenticated and retried successfully")); err != nil {
fmt.Printf("Failed to write response: %v\n", err)
}
}
}))
defer server.Close()
// Create default client - works transparently with any existing auth
client := httpx.NewHTTPRetryClient(
httpx.WithMaxRetriesRetry(3),
httpx.WithRetryStrategyRetry(httpx.ExponentialBackoff(5*time.Millisecond, 50*time.Millisecond)),
)
// Create request with existing auth token (from your app's auth system)
req, _ := http.NewRequest("GET", server.URL, nil)
req.Header.Set("Authorization", "Bearer my-token-123")
fmt.Println("Client: Making authenticated request...")
resp, err := client.Do(req)
if err != nil {
fmt.Printf("Client: Request failed: %v\n", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Printf("Client: Success! Status=%s, Body='%s'\n", resp.Status, string(body))
fmt.Printf("Client: Auth header preserved through %d retries\n", atomic.LoadInt32(&requestCount))
}
Output: Client: Making authenticated request... Server: Request 1 with Bearer my-token-123 -> 500 Internal Server Error Server: Request 2 with Bearer my-token-123 -> 500 Internal Server Error Server: Request 3 with Bearer my-token-123 -> 200 OK Client: Success! Status=200 OK, Body='Authenticated and retried successfully' Client: Auth header preserved through 3 retries
Example (WithProxy) ¶
ExampleNewHTTPRetryClient_withProxy demonstrates using a proxy with the retry client.
package main
import (
"fmt"
"time"
"github.com/slashdevops/httpx"
)
func main() {
// Create a retry client with proxy configuration
client := httpx.NewHTTPRetryClient(
httpx.WithProxyRetry("http://proxy.example.com:8080"),
httpx.WithMaxRetriesRetry(5),
httpx.WithRetryStrategyRetry(
httpx.ExponentialBackoff(500*time.Millisecond, 30*time.Second),
),
)
fmt.Printf("Retry client configured with proxy\n")
_ = client
}
Output: Retry client configured with proxy
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a custom HTTP client with configurable settings and retry strategies. Works transparently with existing request headers. It preserves all headers without requiring explicit configuration.
type ClientBuilder ¶
type ClientBuilder struct {
// contains filtered or unexported fields
}
ClientBuilder is a builder for creating a custom HTTP client
func NewClientBuilder ¶
func NewClientBuilder() *ClientBuilder
NewClientBuilder creates a new ClientBuilder with default settings and retry strategy
Example ¶
ExampleNewClientBuilder demonstrates creating a basic HTTP client with default settings
package main
import (
"fmt"
"log"
"github.com/slashdevops/httpx"
)
func main() {
// Create client with default settings
client := httpx.NewClientBuilder().Build()
// Use the client for HTTP requests
resp, err := client.Get("https://api.example.com/health")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
fmt.Printf("Status: %s\n", resp.Status)
// Output would show: Status: 200 OK
}
Example (AllOptions) ¶
ExampleNewClientBuilder_allOptions demonstrates using all available configuration options
package main
import (
"fmt"
"log"
"time"
"github.com/slashdevops/httpx"
)
func main() {
// Comprehensive configuration showing all available options
client := httpx.NewClientBuilder().
// HTTP client timeout
WithTimeout(20 * time.Second).
// Connection pool settings
WithMaxIdleConns(150).
WithMaxIdleConnsPerHost(15).
WithIdleConnTimeout(60 * time.Second).
// TLS and protocol settings
WithTLSHandshakeTimeout(8 * time.Second).
WithExpectContinueTimeout(2 * time.Second).
WithDisableKeepAlive(false).
// Retry configuration
WithMaxRetries(4).
WithRetryStrategy(httpx.JitterBackoffStrategy).
WithRetryBaseDelay(300 * time.Millisecond).
WithRetryMaxDelay(20 * time.Second).
Build()
// Use the fully configured client
resp, err := client.Get("https://api.example.com/comprehensive")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
fmt.Printf("Request completed with all options configured\n")
}
Example (ConnectionPooling) ¶
ExampleNewClientBuilder_connectionPooling demonstrates configuring connection pooling
package main
import (
"fmt"
"log"
"time"
"github.com/slashdevops/httpx"
)
func main() {
// Configure connection pooling for high-throughput scenarios
client := httpx.NewClientBuilder().
WithMaxIdleConns(200).
WithMaxIdleConnsPerHost(20).
WithIdleConnTimeout(90 * time.Second).
Build()
// Reuse connections efficiently across multiple requests
for i := 0; i < 10; i++ {
resp, err := client.Get(fmt.Sprintf("https://api.example.com/items/%d", i))
if err != nil {
log.Printf("Request %d failed: %v", i, err)
continue
}
resp.Body.Close()
}
fmt.Println("Completed batch requests")
}
Example (DisableKeepAlive) ¶
ExampleNewClientBuilder_disableKeepAlive demonstrates disabling connection reuse
package main
import (
"fmt"
"log"
"github.com/slashdevops/httpx"
)
func main() {
// Disable keep-alive for scenarios requiring fresh connections
client := httpx.NewClientBuilder().
WithDisableKeepAlive(true).
Build()
// Each request will use a new connection
resp, err := client.Get("https://api.example.com/status")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
fmt.Printf("Response: %s\n", resp.Status)
}
Example (FixedDelayStrategy) ¶
ExampleNewClientBuilder_fixedDelayStrategy demonstrates using fixed delay retry strategy
package main
import (
"fmt"
"log"
"time"
"github.com/slashdevops/httpx"
)
func main() {
// Configure client with fixed delay between retries
client := httpx.NewClientBuilder().
WithMaxRetries(3).
WithRetryStrategyAsString("fixed").
WithRetryBaseDelay(1 * time.Second).
Build()
// Each retry will wait exactly 1 second
resp, err := client.Get("https://api.example.com/endpoint")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
fmt.Printf("Success: %v\n", resp.StatusCode == 200)
}
Example (JitterBackoffStrategy) ¶
ExampleNewClientBuilder_jitterBackoffStrategy demonstrates using jitter backoff for retry
package main
import (
"fmt"
"log"
"time"
"github.com/slashdevops/httpx"
)
func main() {
// Configure client with jitter backoff to prevent thundering herd
client := httpx.NewClientBuilder().
WithMaxRetries(4).
WithRetryStrategy(httpx.JitterBackoffStrategy).
WithRetryBaseDelay(500 * time.Millisecond).
WithRetryMaxDelay(10 * time.Second).
Build()
// Retries will have randomized delays
resp, err := client.Get("https://api.example.com/resource")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
fmt.Printf("Completed with status: %d\n", resp.StatusCode)
}
Example (MultipleClients) ¶
ExampleNewClientBuilder_multipleClients demonstrates creating clients with different configurations
package main
import (
"fmt"
"time"
"github.com/slashdevops/httpx"
)
func main() {
// Fast client for health checks
healthClient := httpx.NewClientBuilder().
WithTimeout(2 * time.Second).
WithMaxRetries(1).
Build()
// Standard client for API calls
apiClient := httpx.NewClientBuilder().
WithTimeout(15 * time.Second).
WithMaxRetries(3).
WithRetryStrategy(httpx.ExponentialBackoffStrategy).
Build()
// Heavy client for bulk operations
bulkClient := httpx.NewClientBuilder().
WithTimeout(60 * time.Second).
WithMaxRetries(5).
WithRetryStrategy(httpx.JitterBackoffStrategy).
WithMaxIdleConns(200).
WithMaxIdleConnsPerHost(20).
Build()
// Use different clients for different purposes
_, _ = healthClient.Get("https://api.example.com/health")
_, _ = apiClient.Get("https://api.example.com/users/123")
_, _ = bulkClient.Post("https://api.example.com/bulk-import", "application/json", nil)
fmt.Println("Multiple clients with different configurations")
}
Example (ProductionConfig) ¶
ExampleNewClientBuilder_productionConfig demonstrates a production-ready configuration
package main
import (
"fmt"
"io"
"log"
"net/http"
"time"
"github.com/slashdevops/httpx"
)
func main() {
// Production-ready client with optimal settings
client := httpx.NewClientBuilder().
// Timeouts
WithTimeout(30 * time.Second).
WithTLSHandshakeTimeout(10 * time.Second).
WithExpectContinueTimeout(1 * time.Second).
// Connection pooling
WithMaxIdleConns(100).
WithMaxIdleConnsPerHost(10).
WithIdleConnTimeout(90 * time.Second).
// Retry configuration
WithMaxRetries(5).
WithRetryStrategy(httpx.ExponentialBackoffStrategy).
WithRetryBaseDelay(500 * time.Millisecond).
WithRetryMaxDelay(30 * time.Second).
Build()
// Create request with proper headers
req, err := http.NewRequest("GET", "https://api.example.com/v1/users", nil)
if err != nil {
log.Fatal(err)
}
req.Header.Set("Authorization", "Bearer prod-token-xyz")
req.Header.Set("User-Agent", "MyApp/2.0")
// Execute with automatic retries and connection pooling
resp, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Printf("Retrieved %d bytes with status %d\n", len(body), resp.StatusCode)
}
Example (TlsConfiguration) ¶
ExampleNewClientBuilder_tlsConfiguration demonstrates TLS timeout settings
package main
import (
"fmt"
"log"
"time"
"github.com/slashdevops/httpx"
)
func main() {
// Configure TLS handshake timeout for secure connections
client := httpx.NewClientBuilder().
WithTLSHandshakeTimeout(10 * time.Second).
WithExpectContinueTimeout(2 * time.Second).
Build()
resp, err := client.Get("https://secure-api.example.com/data")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
fmt.Printf("Secure request completed: %s\n", resp.Status)
}
Example (Transparent) ¶
ExampleNewClientBuilder_transparent demonstrates using the ClientBuilder for advanced configuration while maintaining transparent behavior.
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"time"
"github.com/slashdevops/httpx"
)
func main() {
// Create a simple test server
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Echo back any custom headers that were sent
customValue := r.Header.Get("X-Custom-Header")
if customValue != "" {
fmt.Printf("Server: Received custom header: %s\n", customValue)
}
w.WriteHeader(http.StatusOK)
if _, err := w.Write([]byte("Custom headers preserved!")); err != nil {
fmt.Printf("Failed to write response: %v\n", err)
}
}))
defer server.Close()
// Build client with custom settings - still works transparently
client := httpx.NewClientBuilder().
WithMaxRetries(5).
WithRetryStrategy(httpx.JitterBackoffStrategy).
WithTimeout(10 * time.Second).
Build()
// Create request with custom headers
req, _ := http.NewRequest("GET", server.URL, nil)
req.Header.Set("X-Custom-Header", "my-custom-value")
req.Header.Set("Authorization", "Bearer token-from-somewhere")
fmt.Println("Client: Making request with custom headers...")
resp, err := client.Do(req)
if err != nil {
fmt.Printf("Client: Request failed: %v\n", err)
return
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Printf("Client: Response: %s\n", string(body))
}
Output: Client: Making request with custom headers... Server: Received custom header: my-custom-value Client: Response: Custom headers preserved!
Example (WithRetryStrategy) ¶
ExampleNewClientBuilder_withRetryStrategy demonstrates configuring retry behavior
package main
import (
"fmt"
"log"
"time"
"github.com/slashdevops/httpx"
)
func main() {
// Configure client with exponential backoff retry strategy
client := httpx.NewClientBuilder().
WithMaxRetries(5).
WithRetryStrategy(httpx.ExponentialBackoffStrategy).
WithRetryBaseDelay(500 * time.Millisecond).
WithRetryMaxDelay(30 * time.Second).
Build()
// The client will automatically retry on transient failures
resp, err := client.Get("https://api.example.com/data")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
fmt.Printf("Status: %d\n", resp.StatusCode)
}
Example (WithTimeout) ¶
ExampleNewClientBuilder_withTimeout demonstrates configuring a client with custom timeout
package main
import (
"fmt"
"io"
"log"
"time"
"github.com/slashdevops/httpx"
)
func main() {
// Create client with custom timeout
client := httpx.NewClientBuilder().
WithTimeout(10 * time.Second).
Build()
resp, err := client.Get("https://api.example.com/users")
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
body, _ := io.ReadAll(resp.Body)
fmt.Printf("Response length: %d bytes\n", len(body))
}
func (*ClientBuilder) Build ¶
func (b *ClientBuilder) Build() *http.Client
Build creates and returns a new HTTP client with the specified settings and retry strategy. The client works transparently, preserving any existing headers in requests without requiring explicit configuration.
func (*ClientBuilder) WithDisableKeepAlive ¶
func (b *ClientBuilder) WithDisableKeepAlive(disableKeepAlive bool) *ClientBuilder
WithDisableKeepAlive sets whether to disable keep-alive and returns the ClientBuilder for method chaining
func (*ClientBuilder) WithExpectContinueTimeout ¶
func (b *ClientBuilder) WithExpectContinueTimeout(expectContinueTimeout time.Duration) *ClientBuilder
WithExpectContinueTimeout sets the expect continue timeout and returns the ClientBuilder for method chaining
func (*ClientBuilder) WithIdleConnTimeout ¶
func (b *ClientBuilder) WithIdleConnTimeout(idleConnTimeout time.Duration) *ClientBuilder
WithIdleConnTimeout sets the idle connection timeout and returns the ClientBuilder for method chaining
func (*ClientBuilder) WithLogger ¶
func (b *ClientBuilder) WithLogger(logger *slog.Logger) *ClientBuilder
WithLogger sets the logger for logging HTTP operations (retries, errors, etc.). Pass nil to disable logging (default behavior). and returns the ClientBuilder for method chaining
func (*ClientBuilder) WithMaxIdleConns ¶
func (b *ClientBuilder) WithMaxIdleConns(maxIdleConns int) *ClientBuilder
WithMaxIdleConns sets the maximum number of idle connections and returns the ClientBuilder for method chaining
func (*ClientBuilder) WithMaxIdleConnsPerHost ¶
func (b *ClientBuilder) WithMaxIdleConnsPerHost(maxIdleConnsPerHost int) *ClientBuilder
WithMaxIdleConnsPerHost sets the maximum number of idle connections per host and returns the ClientBuilder for method chaining
func (*ClientBuilder) WithMaxRetries ¶
func (b *ClientBuilder) WithMaxRetries(maxRetries int) *ClientBuilder
WithMaxRetries sets the maximum number of retry attempts and returns the ClientBuilder for method chaining
func (*ClientBuilder) WithProxy ¶ added in v0.0.2
func (b *ClientBuilder) WithProxy(proxyURL string) *ClientBuilder
WithProxy sets the proxy URL for HTTP requests. The proxy URL should be in the format "http://proxy.example.com:8080" or "https://proxy.example.com:8080". Pass an empty string to disable proxy (default behavior). and returns the ClientBuilder for method chaining
Example ¶
ExampleClientBuilder_WithProxy demonstrates how to configure an HTTP client with a proxy.
package main
import (
"fmt"
"time"
"github.com/slashdevops/httpx"
)
func main() {
// Create an HTTP client with proxy configuration
client := httpx.NewClientBuilder().
WithProxy("http://proxy.example.com:8080").
WithTimeout(10 * time.Second).
WithMaxRetries(3).
WithRetryStrategy(httpx.ExponentialBackoffStrategy).
Build()
// Client is ready to use with proxy
fmt.Printf("Client configured with proxy\n")
_ = client
}
Output: Client configured with proxy
Example (Authentication) ¶
ExampleClientBuilder_WithProxy_authentication demonstrates using a proxy with authentication.
package main
import (
"fmt"
"time"
"github.com/slashdevops/httpx"
)
func main() {
// Create an HTTP client with authenticated proxy
// Proxy credentials are included in the URL
client := httpx.NewClientBuilder().
WithProxy("http://username:[email protected]:8080").
WithTimeout(10 * time.Second).
Build()
fmt.Printf("Client configured with authenticated proxy\n")
_ = client
}
Output: Client configured with authenticated proxy
Example (Disabling) ¶
ExampleClientBuilder_WithProxy_disabling demonstrates how to disable proxy.
package main
import (
"fmt"
"time"
"github.com/slashdevops/httpx"
)
func main() {
// Create an HTTP client without proxy (explicit disable)
client := httpx.NewClientBuilder().
WithProxy(""). // Empty string disables proxy
WithTimeout(10 * time.Second).
Build()
fmt.Printf("Client configured without proxy\n")
_ = client
}
Output: Client configured without proxy
Example (Https) ¶
ExampleClientBuilder_WithProxy_https demonstrates using an HTTPS proxy.
package main
import (
"fmt"
"time"
"github.com/slashdevops/httpx"
)
func main() {
// Create an HTTP client with HTTPS proxy
client := httpx.NewClientBuilder().
WithProxy("https://secure-proxy.example.com:3128").
WithTimeout(15 * time.Second).
Build()
fmt.Printf("Client configured with HTTPS proxy\n")
_ = client
}
Output: Client configured with HTTPS proxy
func (*ClientBuilder) WithRetryBaseDelay ¶
func (b *ClientBuilder) WithRetryBaseDelay(baseDelay time.Duration) *ClientBuilder
WithRetryBaseDelay sets the base delay for retry strategies and returns the ClientBuilder for method chaining
func (*ClientBuilder) WithRetryMaxDelay ¶
func (b *ClientBuilder) WithRetryMaxDelay(maxDelay time.Duration) *ClientBuilder
WithRetryMaxDelay sets the maximum delay for retry strategies and returns the ClientBuilder for method chaining
func (*ClientBuilder) WithRetryStrategy ¶
func (b *ClientBuilder) WithRetryStrategy(strategy Strategy) *ClientBuilder
WithRetryStrategy sets the retry strategy type and returns the ClientBuilder for method chaining
func (*ClientBuilder) WithRetryStrategyAsString ¶
func (b *ClientBuilder) WithRetryStrategyAsString(strategy string) *ClientBuilder
WithRetryStrategyAsString sets the retry strategy type from a string and returns the ClientBuilder for method chaining
func (*ClientBuilder) WithTLSHandshakeTimeout ¶
func (b *ClientBuilder) WithTLSHandshakeTimeout(tlsHandshakeTimeout time.Duration) *ClientBuilder
WithTLSHandshakeTimeout sets the TLS handshake timeout and returns the ClientBuilder for method chaining
func (*ClientBuilder) WithTimeout ¶
func (b *ClientBuilder) WithTimeout(timeout time.Duration) *ClientBuilder
WithTimeout sets the timeout for HTTP requests and returns the ClientBuilder for method chaining
type ClientError ¶
type ClientError struct {
Message string
}
ClientError represents an error that occurs during HTTP client operations
func (*ClientError) Error ¶
func (e *ClientError) Error() string
type ErrorResponse ¶
type ErrorResponse struct {
Message string `json:"message,omitempty"`
ErrorMsg string `json:"error,omitempty"`
Details string `json:"details,omitempty"`
StatusCode int `json:"statusCode,omitempty"`
}
ErrorResponse represents an error response from the API.
func (*ErrorResponse) Error ¶
func (e *ErrorResponse) Error() string
Error implements the error interface for ErrorResponse. It returns a human-readable error message that includes the HTTP status code and any available error details from the API response.
type GenericClient ¶
type GenericClient[T any] struct { // contains filtered or unexported fields }
GenericClient is a generic HTTP client that can handle requests and responses with type safety. It wraps an HTTPClient and provides methods for executing typed HTTP requests.
Example (ErrorHandling) ¶
ExampleGenericClient_errorHandling demonstrates error handling
package main
import (
"fmt"
"log"
"github.com/slashdevops/httpx"
)
// User represents a user in the API
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
func main() {
client := httpx.NewGenericClient[User]()
response, err := client.Get("https://api.example.com/users/999999")
if err != nil {
// Check if it's an ErrorResponse
if apiErr, ok := err.(*httpx.ErrorResponse); ok {
fmt.Printf("API Error: Status %d - %s\n", apiErr.StatusCode, apiErr.Message)
return
}
// Other errors (network, parsing, etc.)
log.Fatal(err)
}
fmt.Printf("User: %s\n", response.Data.Name)
}
Example (MultipleClients) ¶
ExampleGenericClient_multipleClients demonstrates using multiple typed clients
package main
import (
"fmt"
"log"
"time"
"github.com/slashdevops/httpx"
)
// User represents a user in the API
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
// Post represents a blog post
type Post struct {
ID int `json:"id"`
Title string `json:"title"`
Body string `json:"body"`
UserID int `json:"userId"`
}
func main() {
baseURL := "https://api.example.com"
// Create a client for User responses with specific configuration
userClient := httpx.NewGenericClient[User](
httpx.WithTimeout[User](10*time.Second),
httpx.WithMaxRetries[User](3),
)
// Create a client for Post responses with different configuration
postClient := httpx.NewGenericClient[Post](
httpx.WithTimeout[Post](15*time.Second),
httpx.WithMaxRetries[Post](5),
httpx.WithRetryStrategy[Post](httpx.JitterBackoffStrategy),
)
// Fetch user
userResp, err := userClient.Get(baseURL + "/users/1")
if err != nil {
log.Fatal(err)
}
// Fetch posts by that user
postResp, err := postClient.Get(fmt.Sprintf("%s/users/%d/posts", baseURL, userResp.Data.ID))
if err != nil {
log.Fatal(err)
}
fmt.Printf("User %s has %d posts\n", userResp.Data.Name, len(postResp.Data.Title))
}
Example (ProductionConfiguration) ¶
ExampleGenericClient_productionConfiguration demonstrates a production-ready configuration
package main
import (
"fmt"
"log"
"time"
"github.com/slashdevops/httpx"
)
// User represents a user in the API
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
func main() {
// Configure client with production-ready settings
client := httpx.NewGenericClient[User](
// Aggressive retry for resilience
httpx.WithMaxRetries[User](5),
httpx.WithRetryStrategy[User](httpx.ExponentialBackoffStrategy),
httpx.WithRetryBaseDelay[User](500*time.Millisecond),
httpx.WithRetryMaxDelay[User](30*time.Second),
// Reasonable timeout
httpx.WithTimeout[User](30*time.Second),
// Connection pooling for performance
httpx.WithMaxIdleConns[User](100),
httpx.WithMaxIdleConnsPerHost[User](10),
httpx.WithIdleConnTimeout[User](90*time.Second),
// TLS optimization
httpx.WithTLSHandshakeTimeout[User](10*time.Second),
)
// Build request with headers
req, err := httpx.NewRequestBuilder("https://api.example.com").
WithMethodGET().
WithPath("/users/1").
WithHeader("Authorization", "Bearer prod-token").
WithHeader("X-Request-ID", "req-123").
WithUserAgent("MyApp/1.0.0").
Build()
if err != nil {
log.Fatal(err)
}
// Execute with automatic retries and error handling
response, err := client.Execute(req)
if err != nil {
if apiErr, ok := err.(*httpx.ErrorResponse); ok {
log.Printf("API Error: %d - %s", apiErr.StatusCode, apiErr.Message)
return
}
log.Fatal(err)
}
fmt.Printf("User: %s (%s)\n", response.Data.Name, response.Data.Email)
}
Example (RetryStrategies) ¶
ExampleGenericClient_retryStrategies demonstrates different retry strategies
package main
import (
"time"
"github.com/slashdevops/httpx"
)
// User represents a user in the API
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
func main() {
// Fixed delay - predictable retry timing
fixedClient := httpx.NewGenericClient[User](
httpx.WithRetryStrategyAsString[User]("fixed"),
httpx.WithMaxRetries[User](3),
httpx.WithRetryBaseDelay[User](1*time.Second),
)
// Exponential backoff - doubles delay each time
exponentialClient := httpx.NewGenericClient[User](
httpx.WithRetryStrategy[User](httpx.ExponentialBackoffStrategy),
httpx.WithMaxRetries[User](5),
httpx.WithRetryBaseDelay[User](500*time.Millisecond),
httpx.WithRetryMaxDelay[User](10*time.Second),
)
// Jitter backoff - random delays to prevent thundering herd
jitterClient := httpx.NewGenericClient[User](
httpx.WithRetryStrategy[User](httpx.JitterBackoffStrategy),
httpx.WithMaxRetries[User](3),
httpx.WithRetryBaseDelay[User](500*time.Millisecond),
httpx.WithRetryMaxDelay[User](5*time.Second),
)
// Use different clients based on use case
_, _ = fixedClient.Get("https://api.example.com/users/1")
_, _ = exponentialClient.Get("https://api.example.com/users/2")
_, _ = jitterClient.Get("https://api.example.com/users/3")
}
Example (WithCustomHeaders) ¶
ExampleGenericClient_withCustomHeaders demonstrates adding custom headers per request
package main
import (
"fmt"
"log"
"github.com/slashdevops/httpx"
)
// User represents a user in the API
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
func main() {
client := httpx.NewGenericClient[User]()
// Build request with headers using RequestBuilder
req, err := httpx.NewRequestBuilder("https://api.example.com").
WithMethodGET().
WithPath("/users/1").
WithHeader("Authorization", "Bearer token").
WithHeader("X-Request-ID", "unique-id-123").
WithHeader("X-Trace-ID", "trace-456").
Build()
if err != nil {
log.Fatal(err)
}
// Execute the request
response, err := client.Execute(req)
if err != nil {
log.Fatal(err)
}
fmt.Printf("User: %s\n", response.Data.Name)
}
Example (WithRetry) ¶
ExampleGenericClient_withRetry demonstrates using generic client with retry logic
package main
import (
"fmt"
"log"
"time"
"github.com/slashdevops/httpx"
)
// User represents a user in the API
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
func main() {
// Option 1: Use ClientBuilder and pass to GenericClient
retryClient := httpx.NewClientBuilder().
WithMaxRetries(3).
WithRetryStrategy(httpx.ExponentialBackoffStrategy).
WithRetryBaseDelay(500 * time.Millisecond).
WithTimeout(30 * time.Second).
Build()
client := httpx.NewGenericClient[User](
httpx.WithHTTPClient[User](retryClient),
)
// Option 2: Configure retry directly in GenericClient
client2 := httpx.NewGenericClient[User](
httpx.WithMaxRetries[User](3),
httpx.WithRetryStrategy[User](httpx.ExponentialBackoffStrategy),
httpx.WithRetryBaseDelay[User](500*time.Millisecond),
httpx.WithTimeout[User](30*time.Second),
)
// Make requests - they will automatically retry on failures
response, err := client.Get("https://api.example.com/users/1")
if err != nil {
log.Fatal(err)
}
fmt.Printf("User: %s\n", response.Data.Name)
// Using second client
response2, err := client2.Get("https://api.example.com/users/2")
if err != nil {
log.Fatal(err)
}
fmt.Printf("User: %s\n", response2.Data.Name)
}
func NewGenericClient ¶
func NewGenericClient[T any](options ...GenericClientOption[T]) *GenericClient[T]
NewGenericClient creates a new generic HTTP client with the specified type. By default, it builds an HTTP client using ClientBuilder with default settings. Use the provided options to customize the client behavior. If WithHTTPClient is used, it takes precedence over all other configuration options.
Example ¶
ExampleNewGenericClient demonstrates basic usage of the generic client
package main
import (
"fmt"
"log"
"time"
"github.com/slashdevops/httpx"
)
// User represents a user in the API
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
func main() {
// Create a typed client for User responses with configuration
client := httpx.NewGenericClient[User](
httpx.WithTimeout[User](10*time.Second),
httpx.WithMaxRetries[User](3),
httpx.WithRetryStrategy[User](httpx.ExponentialBackoffStrategy),
)
// Make a GET request with full URL
response, err := client.Get("https://api.example.com/users/1")
if err != nil {
log.Fatal(err)
}
fmt.Printf("User: %s (%s)\n", response.Data.Name, response.Data.Email)
}
Example (AllOptions) ¶
ExampleNewGenericClient_allOptions demonstrates using all configuration options
package main
import (
"fmt"
"log"
"time"
"github.com/slashdevops/httpx"
)
// User represents a user in the API
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
func main() {
// Create a fully configured client
client := httpx.NewGenericClient[User](
// Timeout configuration
httpx.WithTimeout[User](15*time.Second),
// Retry configuration
httpx.WithMaxRetries[User](5),
httpx.WithRetryStrategy[User](httpx.JitterBackoffStrategy),
httpx.WithRetryBaseDelay[User](500*time.Millisecond),
httpx.WithRetryMaxDelay[User](10*time.Second),
// Connection pooling
httpx.WithMaxIdleConns[User](100),
httpx.WithMaxIdleConnsPerHost[User](10),
httpx.WithIdleConnTimeout[User](90*time.Second),
// TLS and handshake timeouts
httpx.WithTLSHandshakeTimeout[User](10*time.Second),
httpx.WithExpectContinueTimeout[User](1*time.Second),
// Keep-alive
httpx.WithDisableKeepAlive[User](false),
)
response, err := client.Get("https://api.example.com/users/1")
if err != nil {
log.Fatal(err)
}
fmt.Printf("User: %s\n", response.Data.Name)
}
Example (WithPreConfiguredClient) ¶
ExampleNewGenericClient_withPreConfiguredClient demonstrates using WithHTTPClient
package main
import (
"fmt"
"log"
"time"
"github.com/slashdevops/httpx"
)
// User represents a user in the API
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
func main() {
// Build a custom HTTP client using ClientBuilder
httpClient := httpx.NewClientBuilder().
WithMaxRetries(3).
WithRetryStrategy(httpx.ExponentialBackoffStrategy).
WithRetryBaseDelay(500 * time.Millisecond).
WithTimeout(30 * time.Second).
WithMaxIdleConns(50).
Build()
// Use it with GenericClient (takes precedence over other options)
client := httpx.NewGenericClient[User](
httpx.WithHTTPClient[User](httpClient),
)
response, err := client.Get("https://api.example.com/users/1")
if err != nil {
log.Fatal(err)
}
fmt.Printf("User: %s\n", response.Data.Name)
}
Example (WithProxy) ¶
ExampleNewGenericClient_withProxy demonstrates using a proxy with the generic client.
package main
import (
"fmt"
"time"
"github.com/slashdevops/httpx"
)
func main() {
type APIResponse struct {
Message string `json:"message"`
Status string `json:"status"`
}
// Create a generic client with proxy configuration
client := httpx.NewGenericClient[APIResponse](
httpx.WithProxy[APIResponse]("http://proxy.example.com:8080"),
httpx.WithTimeout[APIResponse](10*time.Second),
httpx.WithMaxRetries[APIResponse](3),
)
fmt.Printf("Generic client configured with proxy\n")
_ = client
}
Output: Generic client configured with proxy
Example (WithProxy_combined) ¶
ExampleNewGenericClient_withProxy_combined demonstrates combining proxy with other options.
package main
import (
"fmt"
"time"
"github.com/slashdevops/httpx"
)
func main() {
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
// Create a fully configured generic client with proxy
client := httpx.NewGenericClient[User](
httpx.WithProxy[User]("http://proxy.example.com:8080"),
httpx.WithTimeout[User](15*time.Second),
httpx.WithMaxRetries[User](5),
httpx.WithRetryStrategy[User](httpx.JitterBackoffStrategy),
httpx.WithRetryBaseDelay[User](500*time.Millisecond),
httpx.WithRetryMaxDelay[User](30*time.Second),
)
fmt.Printf("Generic client with proxy and retry configuration\n")
_ = client
}
Output: Generic client with proxy and retry configuration
Example (WithProxy_portVariations) ¶
ExampleNewGenericClient_withProxy_portVariations demonstrates different proxy port configurations.
package main
import (
"fmt"
"github.com/slashdevops/httpx"
)
func main() {
type Data struct {
Value string `json:"value"`
}
// Example 1: Standard HTTP proxy on port 8080
client1 := httpx.NewGenericClient[Data](
httpx.WithProxy[Data]("http://proxy.example.com:8080"),
)
// Example 2: HTTPS proxy on port 3128 (common Squid port)
client2 := httpx.NewGenericClient[Data](
httpx.WithProxy[Data]("https://proxy.example.com:3128"),
)
// Example 3: Custom port
client3 := httpx.NewGenericClient[Data](
httpx.WithProxy[Data]("http://proxy.example.com:9090"),
)
fmt.Printf("Configured clients with different proxy ports\n")
_, _, _ = client1, client2, client3
}
Output: Configured clients with different proxy ports
func (*GenericClient[T]) Delete ¶
func (c *GenericClient[T]) Delete(url string) (*Response[T], error)
Delete performs a DELETE request and returns a typed response.
func (*GenericClient[T]) Do ¶
func (c *GenericClient[T]) Do(req *http.Request) (*Response[T], error)
Do performs an HTTP request and returns a typed response. This method is designed to work seamlessly with the RequestBuilder. It's an alias for Execute but with a more familiar name for those used to http.Client.Do().
func (*GenericClient[T]) Execute ¶
func (c *GenericClient[T]) Execute(req *http.Request) (*Response[T], error)
Execute performs an HTTP request and returns a typed response. It executes the request, reads the response body, and unmarshals the JSON response into the generic type T. Returns an error if the HTTP status code is >= 400.
Example ¶
ExampleGenericClient_Execute demonstrates using Execute with RequestBuilder
package main
import (
"fmt"
"log"
"github.com/slashdevops/httpx"
)
// Post represents a blog post
type Post struct {
ID int `json:"id"`
Title string `json:"title"`
Body string `json:"body"`
UserID int `json:"userId"`
}
func main() {
// Create a typed client for Post responses
client := httpx.NewGenericClient[Post]()
// Build a request with RequestBuilder
req, err := httpx.NewRequestBuilder("https://jsonplaceholder.typicode.com").
WithMethodGET().
WithPath("/posts/1").
WithAccept("application/json").
Build()
if err != nil {
log.Fatal(err)
}
// Execute the request
response, err := client.Execute(req)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Post: %s\n", response.Data.Title)
}
func (*GenericClient[T]) ExecuteRaw ¶
ExecuteRaw performs an HTTP request and returns the raw response without unmarshaling. This is useful when you need direct access to the http.Response, such as for streaming or when the response is not JSON.
Example ¶
ExampleGenericClient_ExecuteRaw demonstrates using ExecuteRaw for non-JSON responses
package main
import (
"fmt"
"log"
"net/http"
"github.com/slashdevops/httpx"
)
// User represents a user in the API
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}
func main() {
client := httpx.NewGenericClient[User]()
req, err := http.NewRequest(http.MethodGet, "https://example.com/image.png", nil)
if err != nil {
log.Fatal(err)
}
// Get raw response for binary data
response, err := client.ExecuteRaw(req)
if err != nil {
log.Fatal(err)
}
defer response.Body.Close()
fmt.Printf("Content-Type: %s\n", response.Header.Get("Content-Type"))
fmt.Printf("Status: %d\n", response.StatusCode)
}
func (*GenericClient[T]) Get ¶
func (c *GenericClient[T]) Get(url string) (*Response[T], error)
Get performs a GET request to the specified URL and returns a typed response.
func (*GenericClient[T]) Patch ¶
Patch performs a PATCH request with the specified body and returns a typed response.
func (*GenericClient[T]) Post ¶
Post performs a POST request with the specified body and returns a typed response.
Example ¶
ExampleGenericClient_Post demonstrates making a POST request
package main
import (
"fmt"
"log"
"github.com/slashdevops/httpx"
)
// Post represents a blog post
type Post struct {
ID int `json:"id"`
Title string `json:"title"`
Body string `json:"body"`
UserID int `json:"userId"`
}
func main() {
client := httpx.NewGenericClient[Post]()
// Create a new post using RequestBuilder
newPost := Post{
Title: "My New Post",
Body: "This is the post content",
UserID: 1,
}
req, err := httpx.NewRequestBuilder("https://jsonplaceholder.typicode.com").
WithMethodPOST().
WithPath("/posts").
WithContentType("application/json").
WithJSONBody(newPost).
Build()
if err != nil {
log.Fatal(err)
}
response, err := client.Do(req)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Created post with ID: %d\n", response.Data.ID)
}
type GenericClientOption ¶
type GenericClientOption[T any] func(*GenericClient[T])
GenericClientOption is a function type for configuring the GenericClient.
func WithDisableKeepAlive ¶
func WithDisableKeepAlive[T any](disableKeepAlive bool) GenericClientOption[T]
WithDisableKeepAlive sets whether to disable keep-alive.
func WithExpectContinueTimeout ¶
func WithExpectContinueTimeout[T any](expectContinueTimeout time.Duration) GenericClientOption[T]
WithExpectContinueTimeout sets the expect continue timeout. Uses ClientBuilder validation and defaults if the value is out of range.
func WithHTTPClient ¶
func WithHTTPClient[T any](httpClient HTTPClient) GenericClientOption[T]
WithHTTPClient configures the generic client to use a custom HTTPClient implementation. If httpClient is nil, the option is ignored and the client retains its current HTTPClient. This option takes precedence over all other configuration options. This is useful for using a pre-configured retry client or custom transport.
func WithIdleConnTimeout ¶
func WithIdleConnTimeout[T any](idleConnTimeout time.Duration) GenericClientOption[T]
WithIdleConnTimeout sets the idle connection timeout. Uses ClientBuilder validation and defaults if the value is out of range.
func WithLogger ¶
func WithLogger[T any](logger *slog.Logger) GenericClientOption[T]
WithLogger sets the logger for logging HTTP operations (retries, errors, etc.). Pass nil to disable logging (default behavior).
func WithMaxIdleConns ¶
func WithMaxIdleConns[T any](maxIdleConns int) GenericClientOption[T]
WithMaxIdleConns sets the maximum number of idle connections. Uses ClientBuilder validation and defaults if the value is out of range.
func WithMaxIdleConnsPerHost ¶
func WithMaxIdleConnsPerHost[T any](maxIdleConnsPerHost int) GenericClientOption[T]
WithMaxIdleConnsPerHost sets the maximum number of idle connections per host. Uses ClientBuilder validation and defaults if the value is out of range.
func WithMaxRetries ¶
func WithMaxRetries[T any](maxRetries int) GenericClientOption[T]
WithMaxRetries sets the maximum number of retry attempts. Uses ClientBuilder validation and defaults if the value is out of range.
func WithProxy ¶ added in v0.0.2
func WithProxy[T any](proxyURL string) GenericClientOption[T]
WithProxy sets the proxy URL for HTTP requests. The proxy URL should be in the format "http://proxy.example.com:8080" or "https://proxy.example.com:8080". Pass an empty string to disable proxy (default behavior).
func WithRetryBaseDelay ¶
func WithRetryBaseDelay[T any](baseDelay time.Duration) GenericClientOption[T]
WithRetryBaseDelay sets the base delay for retry strategies. Uses ClientBuilder validation and defaults if the value is out of range.
func WithRetryMaxDelay ¶
func WithRetryMaxDelay[T any](maxDelay time.Duration) GenericClientOption[T]
WithRetryMaxDelay sets the maximum delay for retry strategies. Uses ClientBuilder validation and defaults if the value is out of range.
func WithRetryStrategy ¶
func WithRetryStrategy[T any](strategy Strategy) GenericClientOption[T]
WithRetryStrategy sets the retry strategy type (fixed, jitter, or exponential). Uses ClientBuilder validation and defaults if the value is invalid.
func WithRetryStrategyAsString ¶
func WithRetryStrategyAsString[T any](strategy string) GenericClientOption[T]
WithRetryStrategyAsString sets the retry strategy type from a string. Valid values: "fixed", "jitter", "exponential". Uses ClientBuilder validation and defaults if the value is invalid.
func WithTLSHandshakeTimeout ¶
func WithTLSHandshakeTimeout[T any](tlsHandshakeTimeout time.Duration) GenericClientOption[T]
WithTLSHandshakeTimeout sets the TLS handshake timeout. Uses ClientBuilder validation and defaults if the value is out of range.
func WithTimeout ¶
func WithTimeout[T any](timeout time.Duration) GenericClientOption[T]
WithTimeout sets the request timeout for the generic client. Uses ClientBuilder validation and defaults if the value is out of range.
type HTTPClient ¶
HTTPClient is an interface that defines the methods required for making HTTP requests. This allows for easier testing and mocking of HTTP requests in unit tests.
type RequestBuilder ¶
type RequestBuilder struct {
// contains filtered or unexported fields
}
RequestBuilder provides a fluent API for building HTTP requests with and without body.
Example (ComplexRequest) ¶
ExampleRequestBuilder_complexRequest demonstrates a complex request with multiple options.
package main
import (
"fmt"
"github.com/slashdevops/httpx"
)
func main() {
type RequestData struct {
Action string `json:"action"`
Count int `json:"count"`
}
data := RequestData{Action: "update", Count: 5}
req, err := httpx.NewRequestBuilder("https://api.example.com").
WithMethodPUT().
WithPath("/resources/123").
WithQueryParam("force", "true").
WithHeader("X-Custom-Header", "custom-value").
WithUserAgent("MyApp/1.0").
WithBearerAuth("token123").
WithJSONBody(data).
Build()
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(req.Method)
fmt.Println(req.URL.Path)
fmt.Println(req.Header.Get("User-Agent"))
fmt.Println(req.Header.Get("X-Custom-Header"))
fmt.Println(req.Header.Get("Content-Type"))
}
Output: PUT /resources/123 MyApp/1.0 custom-value application/json
Example (FullExample) ¶
ExampleRequestBuilder_fullExample demonstrates a complete end-to-end example with a test server.
package main
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"github.com/slashdevops/httpx"
)
func main() {
// Create a test server
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"message":"success"}`))
}))
defer server.Close()
req, err := httpx.NewRequestBuilder(server.URL).
WithMethodGET().
WithPath("/api/test").
WithHeader("Accept", "application/json").
Build()
if err != nil {
fmt.Println("Error:", err)
return
}
// Execute the request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error:", err)
return
}
defer resp.Body.Close()
// Read the response
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(resp.StatusCode)
fmt.Println(string(body))
}
Output: 200 {"message":"success"}
Example (PostWithJSON) ¶
ExampleRequestBuilder_postWithJSON demonstrates how to create a POST request with JSON body.
package main
import (
"fmt"
"github.com/slashdevops/httpx"
)
func main() {
type User struct {
Name string `json:"name"`
Email string `json:"email"`
}
user := User{Name: "John Doe", Email: "[email protected]"}
req, err := httpx.NewRequestBuilder("https://api.example.com").
WithMethodPOST().
WithPath("/users").
WithJSONBody(user).
Build()
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(req.Method)
fmt.Println(req.Header.Get("Content-Type"))
}
Output: POST application/json
Example (SimpleGET) ¶
ExampleRequestBuilder_simpleGET demonstrates how to create a simple GET request.
package main
import (
"fmt"
"github.com/slashdevops/httpx"
)
func main() {
req, err := httpx.NewRequestBuilder("https://api.example.com").
WithMethodGET().
WithPath("/users").
Build()
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(req.Method)
fmt.Println(req.URL.String())
}
Output: GET https://api.example.com/users
Example (WithAcceptHeader) ¶
ExampleRequestBuilder_withAcceptHeader demonstrates how to set the Accept header.
package main
import (
"fmt"
"github.com/slashdevops/httpx"
)
func main() {
// Using the WithAccept() convenience method
req, err := httpx.NewRequestBuilder("https://api.example.com").
WithMethodGET().
WithPath("/api/data").
WithAccept("application/json").
Build()
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(req.Header.Get("Accept"))
}
Output: application/json
Example (WithBasicAuth) ¶
ExampleRequestBuilder_withBasicAuth demonstrates how to use basic authentication.
package main
import (
"fmt"
"github.com/slashdevops/httpx"
)
func main() {
req, err := httpx.NewRequestBuilder("https://api.example.com").
WithMethodGET().
WithPath("/protected").
WithBasicAuth("username", "password").
Build()
if err != nil {
fmt.Println("Error:", err)
return
}
authHeader := req.Header.Get("Authorization")
fmt.Println(authHeader[:6]) // Just print "Basic " prefix
}
Output: Basic
Example (WithBearerAuth) ¶
ExampleRequestBuilder_withBearerAuth demonstrates how to use bearer token authentication.
package main
import (
"fmt"
"github.com/slashdevops/httpx"
)
func main() {
req, err := httpx.NewRequestBuilder("https://api.example.com").
WithMethodGET().
WithPath("/api/data").
WithBearerAuth("your-token-here").
Build()
if err != nil {
fmt.Println("Error:", err)
return
}
authHeader := req.Header.Get("Authorization")
fmt.Println(authHeader[:7]) // Just print "Bearer " prefix
}
Output: Bearer
Example (WithContext) ¶
ExampleRequestBuilder_withContext demonstrates how to use a context.
package main
import (
"context"
"fmt"
"github.com/slashdevops/httpx"
)
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
req, err := httpx.NewRequestBuilder("https://api.example.com").
WithMethodGET().
WithPath("/data").
WithContext(ctx).
Build()
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(req.Context() != nil)
}
Output: true
Example (WithMethodCONNECT) ¶
ExampleRequestBuilder_withMethodCONNECT demonstrates how to create a CONNECT request.
package main
import (
"fmt"
"github.com/slashdevops/httpx"
)
func main() {
req, err := httpx.NewRequestBuilder("https://proxy.example.com").
WithMethodCONNECT().
WithPath("/tunnel").
Build()
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(req.Method)
fmt.Println(req.URL.Path)
}
Output: CONNECT /tunnel
Example (WithMethodHEAD) ¶
ExampleRequestBuilder_withMethodHEAD demonstrates how to create a HEAD request.
package main
import (
"fmt"
"github.com/slashdevops/httpx"
)
func main() {
req, err := httpx.NewRequestBuilder("https://api.example.com").
WithMethodHEAD().
WithPath("/resource").
Build()
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(req.Method)
fmt.Println(req.URL.Path)
}
Output: HEAD /resource
Example (WithMethodOPTIONS) ¶
ExampleRequestBuilder_withMethodOPTIONS demonstrates how to create an OPTIONS request.
package main
import (
"fmt"
"github.com/slashdevops/httpx"
)
func main() {
req, err := httpx.NewRequestBuilder("https://api.example.com").
WithMethodOPTIONS().
WithPath("/api/users").
Build()
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(req.Method)
fmt.Println(req.URL.Path)
}
Output: OPTIONS /api/users
Example (WithMethodTRACE) ¶
ExampleRequestBuilder_withMethodTRACE demonstrates how to create a TRACE request.
package main
import (
"fmt"
"github.com/slashdevops/httpx"
)
func main() {
req, err := httpx.NewRequestBuilder("https://api.example.com").
WithMethodTRACE().
WithPath("/debug").
Build()
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(req.Method)
fmt.Println(req.URL.Path)
}
Output: TRACE /debug
Example (WithMultipleAcceptTypes) ¶
ExampleRequestBuilder_withMultipleAcceptTypes demonstrates setting multiple Accept types with quality values.
package main
import (
"fmt"
"github.com/slashdevops/httpx"
)
func main() {
req, err := httpx.NewRequestBuilder("https://api.example.com").
WithMethodGET().
WithPath("/content").
WithAccept("application/json, application/xml;q=0.9, */*;q=0.8").
Build()
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(req.Header.Get("Accept"))
}
Output: application/json, application/xml;q=0.9, */*;q=0.8
Example (WithQueryParams) ¶
ExampleRequestBuilder_withQueryParams demonstrates how to add query parameters.
package main
import (
"fmt"
"github.com/slashdevops/httpx"
)
func main() {
req, err := httpx.NewRequestBuilder("https://api.example.com").
WithMethodGET().
WithPath("/search").
WithQueryParam("q", "golang").
WithQueryParam("limit", "10").
WithQueryParam("offset", "0").
Build()
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(req.URL.String())
}
Output: https://api.example.com/search?limit=10&offset=0&q=golang
func NewRequestBuilder ¶
func NewRequestBuilder(baseURL string) *RequestBuilder
NewRequestBuilder creates a new RequestBuilder with the specified base URL.
func (*RequestBuilder) Build ¶
func (rb *RequestBuilder) Build() (*http.Request, error)
Build creates an *http.Request from the builder configuration. Returns an error if any validation fails.
func (*RequestBuilder) GetErrors ¶
func (rb *RequestBuilder) GetErrors() []error
GetErrors returns all accumulated errors during the building process.
func (*RequestBuilder) HasErrors ¶
func (rb *RequestBuilder) HasErrors() bool
HasErrors returns true if there are any accumulated errors.
func (*RequestBuilder) Reset ¶
func (rb *RequestBuilder) Reset() *RequestBuilder
Reset clears all errors and resets the builder to a clean state.
func (*RequestBuilder) WithAccept ¶
func (rb *RequestBuilder) WithAccept(accept string) *RequestBuilder
WithAccept sets the Accept header.
func (*RequestBuilder) WithBasicAuth ¶
func (rb *RequestBuilder) WithBasicAuth(username, password string) *RequestBuilder
WithBasicAuth sets the Authorization header for basic authentication.
func (*RequestBuilder) WithBearerAuth ¶
func (rb *RequestBuilder) WithBearerAuth(token string) *RequestBuilder
WithBearerAuth sets the Authorization header for bearer token authentication.
func (*RequestBuilder) WithBytesBody ¶
func (rb *RequestBuilder) WithBytesBody(body []byte) *RequestBuilder
WithBytesBody sets the request body from a byte slice.
func (*RequestBuilder) WithContentType ¶
func (rb *RequestBuilder) WithContentType(contentType string) *RequestBuilder
WithContentType sets the Content-Type header.
func (*RequestBuilder) WithContext ¶
func (rb *RequestBuilder) WithContext(ctx context.Context) *RequestBuilder
WithContext sets the context for the request.
func (*RequestBuilder) WithHeader ¶
func (rb *RequestBuilder) WithHeader(key, value string) *RequestBuilder
WithHeader sets a single header.
func (*RequestBuilder) WithHeaders ¶
func (rb *RequestBuilder) WithHeaders(headers map[string]string) *RequestBuilder
WithHeaders sets multiple headers from a map.
func (*RequestBuilder) WithJSONBody ¶
func (rb *RequestBuilder) WithJSONBody(body any) *RequestBuilder
WithJSONBody sets the request body as JSON and sets the appropriate Content-Type header.
func (*RequestBuilder) WithMethod ¶
func (rb *RequestBuilder) WithMethod(method string) *RequestBuilder
WithMethod sets the HTTP method to the specified method. The method is normalized to uppercase and validated against standard HTTP methods.
func (*RequestBuilder) WithMethodCONNECT ¶
func (rb *RequestBuilder) WithMethodCONNECT() *RequestBuilder
WithMethodCONNECT sets the HTTP method to CONNECT.
func (*RequestBuilder) WithMethodDELETE ¶
func (rb *RequestBuilder) WithMethodDELETE() *RequestBuilder
WithMethodDELETE sets the HTTP method to DELETE.
func (*RequestBuilder) WithMethodGET ¶
func (rb *RequestBuilder) WithMethodGET() *RequestBuilder
WithMethodGET sets the HTTP method to GET.
func (*RequestBuilder) WithMethodHEAD ¶
func (rb *RequestBuilder) WithMethodHEAD() *RequestBuilder
WithMethodHEAD sets the HTTP method to HEAD.
func (*RequestBuilder) WithMethodOPTIONS ¶
func (rb *RequestBuilder) WithMethodOPTIONS() *RequestBuilder
WithMethodOPTIONS sets the HTTP method to OPTIONS.
func (*RequestBuilder) WithMethodPATCH ¶
func (rb *RequestBuilder) WithMethodPATCH() *RequestBuilder
WithMethodPATCH sets the HTTP method to PATCH.
func (*RequestBuilder) WithMethodPOST ¶
func (rb *RequestBuilder) WithMethodPOST() *RequestBuilder
WithMethodPOST sets the HTTP method to POST.
func (*RequestBuilder) WithMethodPUT ¶
func (rb *RequestBuilder) WithMethodPUT() *RequestBuilder
WithMethodPUT sets the HTTP method to PUT.
func (*RequestBuilder) WithMethodTRACE ¶
func (rb *RequestBuilder) WithMethodTRACE() *RequestBuilder
WithMethodTRACE sets the HTTP method to TRACE.
func (*RequestBuilder) WithPath ¶
func (rb *RequestBuilder) WithPath(path string) *RequestBuilder
WithPath sets the path component of the URL.
func (*RequestBuilder) WithQueryParam ¶
func (rb *RequestBuilder) WithQueryParam(key, value string) *RequestBuilder
WithQueryParam adds a single query parameter.
func (*RequestBuilder) WithQueryParams ¶
func (rb *RequestBuilder) WithQueryParams(params map[string]string) *RequestBuilder
WithQueryParams adds multiple query parameters from a map.
func (*RequestBuilder) WithRawBody ¶
func (rb *RequestBuilder) WithRawBody(body io.Reader) *RequestBuilder
WithRawBody sets the request body from an io.Reader.
func (*RequestBuilder) WithStringBody ¶
func (rb *RequestBuilder) WithStringBody(body string) *RequestBuilder
WithStringBody sets the request body from a string.
func (*RequestBuilder) WithUserAgent ¶
func (rb *RequestBuilder) WithUserAgent(userAgent string) *RequestBuilder
WithUserAgent sets the User-Agent header. The user agent is trimmed and validated to ensure it: - is non-empty after trimming - does not exceed 500 characters - does not contain control characters (\r, \n, \t)
type RetryClientOption ¶
type RetryClientOption func(*retryClientConfig)
RetryClientOption is a function type for configuring the retry HTTP client.
func WithBaseTransport ¶
func WithBaseTransport(transport http.RoundTripper) RetryClientOption
WithBaseTransport sets the base HTTP transport for the retry client. If not provided, http.DefaultTransport will be used.
func WithLoggerRetry ¶
func WithLoggerRetry(logger *slog.Logger) RetryClientOption
WithLoggerRetry sets the logger for the retry client. Pass nil to disable logging (default behavior).
func WithMaxRetriesRetry ¶
func WithMaxRetriesRetry(maxRetries int) RetryClientOption
WithMaxRetriesRetry sets the maximum number of retry attempts for the retry client.
func WithProxyRetry ¶ added in v0.0.2
func WithProxyRetry(proxyURL string) RetryClientOption
WithProxyRetry sets the proxy URL for the retry client. The proxy URL should be in the format "http://proxy.example.com:8080" or "https://proxy.example.com:8080". Pass an empty string to disable proxy (default behavior).
func WithRetryStrategyRetry ¶
func WithRetryStrategyRetry(strategy RetryStrategy) RetryClientOption
WithRetryStrategyRetry sets the retry strategy for the retry client.
type RetryStrategy ¶
RetryStrategy defines the function signature for different retry strategies
func ExponentialBackoff ¶
func ExponentialBackoff(base, maxDelay time.Duration) RetryStrategy
ExponentialBackoff returns a RetryStrategy that calculates delays growing exponentially with each retry attempt, starting from base and capped at maxDelay.
func FixedDelay ¶
func FixedDelay(delay time.Duration) RetryStrategy
FixedDelay returns a RetryStrategy that provides a constant delay for each retry attempt.
func JitterBackoff ¶
func JitterBackoff(base, maxDelay time.Duration) RetryStrategy
JitterBackoff returns a RetryStrategy that adds a random jitter to the exponential backoff delay calculated using base and maxDelay.
type Strategy ¶
type Strategy string
Strategy defines the type for retry strategies It is a string type to allow for easy conversion from string literals to the defined types
const ( // FixedDelayStrategy represents a fixed delay retry strategy // This strategy waits for a constant amount of time between retries // regardless of the number of attempts made FixedDelayStrategy Strategy = "fixed" // JitterBackoffStrategy represents a jitter backoff retry strategy // This strategy adds randomness to the backoff delay to prevent // synchronized retries across multiple clients JitterBackoffStrategy Strategy = "jitter" // ExponentialBackoffStrategy represents an exponential backoff retry strategy // This strategy increases the delay exponentially with each retry attempt, // up to a maximum delay ExponentialBackoffStrategy Strategy = "exponential" )