Documentation
¶
Overview ¶
Package httpc provides HTTP client functionality. This file contains the DebugTransport implementation for detailed request/response logging.
Package httpc provides a modern, fluent HTTP client for Go with support for retry logic, interceptors, and convenient request building.
Features ¶
- Fluent API with chainable methods for building requests
- Automatic retry with configurable exponential backoff
- Interceptor pattern for request modification (auth, logging, rate limiting, etc.)
- Built-in JSON marshaling and unmarshaling
- Full context.Context support with cancellation, timeouts, and deadlines
- All HTTP methods (GET, POST, PUT, DELETE, PATCH) with context variants
- Thread-safe client that can be used concurrently
- HTTP/2 support with automatic fallback to HTTP/1.1
- Connection pooling and keep-alive
Quick Start ¶
Create a client and make a simple GET request:
client := httpc.NewClient()
resp, err := client.Get("https://api.example.com/users")
if err != nil {
log.Fatal(err)
}
body, _ := resp.String()
fmt.Println(body)
Client Configuration ¶
Configure a client with base URL, timeout, and headers:
retryConfig := httpc.DefaultRetryConfig()
retryConfig.MaxRetries = 3
retryConfig.Backoff = time.Second
client := httpc.NewClient(
httpc.WithBaseURL("https://api.example.com"),
httpc.WithTimeout(30*time.Second),
httpc.WithHeader("User-Agent", "MyApp/1.0"),
httpc.WithRetry(*retryConfig),
)
JSON Requests ¶
Send and receive JSON easily:
type User struct {
Name string `json:"name"`
Email string `json:"email"`
}
user := User{Name: "John", Email: "john@example.com"}
var result User
err := client.PostJSON("/users", user, &result)
Request Builder ¶
Use the fluent request builder for complex requests:
resp, err := client.NewRequest().
Method("POST").
URL("/api/users").
Header("Authorization", "Bearer token").
Query("include", "profile").
JSON(userData).
Timeout(10*time.Second).
Do()
Interceptors ¶
Add interceptors for cross-cutting concerns:
client := httpc.NewClient(
httpc.WithDebug(),
httpc.WithAuthorization("your-token"),
httpc.WithLogger(log.Default()),
)
Available built-in options:
- WithAuthorization(token): Bearer token authentication
- WithBaseAuth(user, pass): HTTP Basic authentication
- WithApiKey(header, key): API key authentication
- WithUserAgent(ua): Sets User-Agent header
- WithRequestId(header): Adds unique request IDs
- WithLogger(logger): Request/response logging
- WithDebug(): Debug mode with detailed logging
- WithBlockedList(domains): Blocks requests to specific domains
- WithHeaders(map): Adds custom headers
Custom interceptors wrap the underlying http.RoundTripper:
customInterceptor := func(rt http.RoundTripper) http.RoundTripper {
return &customTransport{transport: rt}
}
client := httpc.NewClient(httpc.WithInterceptor(customInterceptor))
Retry Logic ¶
Configure automatic retries with exponential backoff:
retryConfig := httpc.DefaultRetryConfig() retryConfig.MaxRetries = 3 retryConfig.Backoff = time.Second client := httpc.NewClient( httpc.WithRetry(*retryConfig), )
By default, requests are retried on:
- Network errors and timeouts
- 5xx server errors
- 429 (rate limit) responses
Error Handling ¶
Check for both network errors and HTTP errors:
resp, err := client.Get("/api/users")
if err != nil {
if httpc.IsTimeout(err) {
log.Println("Request timed out")
}
log.Fatal(err)
}
if resp.StatusCode >= 400 {
log.Printf("HTTP error: %d", resp.StatusCode)
}
Context Support ¶
All HTTP methods support context for cancellation, timeouts, and deadlines:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // All methods have WithContext variants resp, err := client.GetWithContext(ctx, "/api/users") resp, err := client.PostWithContext(ctx, "/api/users", userData) resp, err := client.PutWithContext(ctx, "/api/users/123", userData) resp, err := client.DeleteWithContext(ctx, "/api/users/123") resp, err := client.PatchWithContext(ctx, "/api/users/123", updates) // JSON methods also support context var users []User err := client.GetJSONWithContext(ctx, "/api/users", &users)
Or use the request builder:
resp, err := client.NewRequest().
Method("GET").
URL("/api/users").
Context(ctx).
Timeout(5*time.Second). // Timeout wraps the context
Do()
Or pass context as a RequestOption:
resp, err := client.Get("/api/users",
httpc.WithContext(ctx),
httpc.WithQuery("page", "1"),
)
Content Types ¶
The package provides constants for common content types:
httpc.ContentTypeJSON // application/json httpc.ContentTypeXML // application/xml httpc.ContentTypeForm // application/x-www-form-urlencoded httpc.ContentTypePlainText // text/plain httpc.ContentTypeHTML // text/html // ... and more
Thread Safety ¶
The Client is safe for concurrent use. You should create one client and reuse it for multiple requests to benefit from connection pooling:
// Create once, use many times from different goroutines
client := httpc.NewClient()
go func() {
resp, _ := client.Get("/api/endpoint1")
// ...
}()
go func() {
resp, _ := client.Get("/api/endpoint2")
// ...
}()
Best Practices ¶
- Reuse clients to benefit from connection pooling
- Always use contexts for production code
- Set appropriate timeouts to prevent hanging requests
- Enable retry logic for transient failures
- Use interceptors to centralize cross-cutting concerns
- Check both error return values and HTTP status codes
Package httpc provides HTTP client functionality. This file defines the Interceptor type for wrapping http.RoundTripper implementations.
Package httpc provides HTTP client functionality. This file contains various http.RoundTripper implementations for features like headers, authentication, retry logic, logging, and request blocking.
Example ¶
Example demonstrates basic usage of the HTTP client.
package main
import (
"fmt"
"log"
"time"
"github.com/mam-coder/httpc"
)
func main() {
client := httpc.NewClient(
httpc.WithBaseURL("https://api.example.com"),
httpc.WithTimeout(10*time.Second),
)
resp, err := client.Get("/users")
if err != nil {
log.Fatal(err)
}
fmt.Println("Status:", resp.StatusCode)
}
Index ¶
- Constants
- func IsTimeout(err error) bool
- type Client
- func (c *Client) Delete(url string, opts ...RequestOption) (*Response, error)
- func (c *Client) DeleteWithContext(ctx context.Context, url string, opts ...RequestOption) (*Response, error)
- func (c *Client) Get(url string, opts ...RequestOption) (*Response, error)
- func (c *Client) GetCSV(url string, result interface{}, opts ...RequestOption) error
- func (c *Client) GetCSVWithContext(ctx context.Context, url string, result interface{}, opts ...RequestOption) error
- func (c *Client) GetCSVWithSeparator(url string, separator rune, result interface{}, opts ...RequestOption) error
- func (c *Client) GetCSVWithSeparatorAndContext(ctx context.Context, url string, separator rune, result interface{}, ...) error
- func (c *Client) GetJSON(url string, result interface{}, opts ...RequestOption) error
- func (c *Client) GetJSONWithContext(ctx context.Context, url string, result interface{}, opts ...RequestOption) error
- func (c *Client) GetWithContext(ctx context.Context, url string, opts ...RequestOption) (*Response, error)
- func (c *Client) GetXML(url string, result interface{}, opts ...RequestOption) error
- func (c *Client) GetXMLWithContext(ctx context.Context, url string, result interface{}, opts ...RequestOption) error
- func (c *Client) NewRequest() *RequestBuilder
- func (c *Client) Patch(url string, body interface{}, opts ...RequestOption) (*Response, error)
- func (c *Client) PatchWithContext(ctx context.Context, url string, body interface{}, opts ...RequestOption) (*Response, error)
- func (c *Client) Post(url string, body interface{}, opts ...RequestOption) (*Response, error)
- func (c *Client) PostJSON(url string, body interface{}, result interface{}, opts ...RequestOption) error
- func (c *Client) PostJSONWithContext(ctx context.Context, url string, body interface{}, result interface{}, ...) error
- func (c *Client) PostWithContext(ctx context.Context, url string, body interface{}, opts ...RequestOption) (*Response, error)
- func (c *Client) PostXML(url string, body interface{}, result interface{}, opts ...RequestOption) error
- func (c *Client) PostXMLWithContext(ctx context.Context, url string, body interface{}, result interface{}, ...) error
- func (c *Client) Put(url string, body interface{}, opts ...RequestOption) (*Response, error)
- func (c *Client) PutWithContext(ctx context.Context, url string, body interface{}, opts ...RequestOption) (*Response, error)
- type DebugTransport
- type Error
- type Interceptor
- type Option
- func WithAccept(accept string) Option
- func WithApiKey(headerName, apiKey string) Option
- func WithAuthorization(token string) Option
- func WithBaseAuth(username, password string) Option
- func WithBaseURL(baseURL string) Option
- func WithBlockedList(blockedList []string) Option
- func WithContentType(contentType string) Option
- func WithDebug() Option
- func WithHeader(key, value string) Option
- func WithHeaders(headers map[string]string) Option
- func WithInterceptor(interceptor Interceptor) Option
- func WithLogger(logger *log.Logger) Option
- func WithRequestId(headerName string) Option
- func WithRetry(config RetryConfig) Option
- func WithTimeout(timeout time.Duration) Option
- func WithUserAgent(userAgent string) Option
- type RequestBuilder
- func (rb *RequestBuilder) Body(body io.Reader) *RequestBuilder
- func (rb *RequestBuilder) Context(ctx context.Context) *RequestBuilder
- func (rb *RequestBuilder) Do() (*Response, error)
- func (rb *RequestBuilder) Header(key, value string) *RequestBuilder
- func (rb *RequestBuilder) JSON(v interface{}) *RequestBuilder
- func (rb *RequestBuilder) Method(method string) *RequestBuilder
- func (rb *RequestBuilder) Query(key, value string) *RequestBuilder
- func (rb *RequestBuilder) QueryParams(params map[string]string) *RequestBuilder
- func (rb *RequestBuilder) Timeout(timeout time.Duration) *RequestBuilder
- func (rb *RequestBuilder) URL(url string) *RequestBuilder
- func (rb *RequestBuilder) XML(v interface{}) *RequestBuilder
- type RequestOption
- type Response
- type RetryConfig
Examples ¶
Constants ¶
const ( // ContentTypeJSON represents JSON content (application/json) ContentTypeJSON = "application/json" // ContentTypeXML represents XML content (application/xml) ContentTypeXML = "application/xml" // ContentTypeForm represents URL-encoded form data (application/x-www-form-urlencoded) ContentTypeForm = "application/x-www-form-urlencoded" // ContentTypeMultipart represents multipart form data (multipart/form-data) ContentTypeMultipart = "multipart/form-data" // ContentTypePlainText represents plain text content (text/plain) ContentTypePlainText = "text/plain" // ContentTypeHTML represents HTML content (text/html) ContentTypeHTML = "text/html" // ContentTypeCSV represents CSV content (text/csv) ContentTypeCSV = "text/csv" // ContentTypeApplicationCSV represents CSV content (application/csv) ContentTypeApplicationCSV = "application/csv" // ContentTypeJavaScript represents JavaScript content (application/javascript) ContentTypeJavaScript = "application/javascript" // ContentTypeCSS represents CSS content (text/css) ContentTypeCSS = "text/css" // ContentTypePDF represents PDF content (application/pdf) ContentTypePDF = "application/pdf" // ContentTypeZip represents ZIP archive content (application/zip) ContentTypeZip = "application/zip" // ContentTypeOctetStream represents arbitrary binary data (application/octet-stream) ContentTypeOctetStream = "application/octet-stream" )
Common Content-Type header values for HTTP requests and responses.
Variables ¶
This section is empty.
Functions ¶
func IsTimeout ¶
IsTimeout checks if the error is a timeout error. It returns true if the error is either a net.Error with Timeout() == true or a context.DeadlineExceeded error.
Example:
resp, err := client.Get("/api/slow-endpoint")
if httpc.IsTimeout(err) {
log.Println("Request timed out")
}
Example ¶
ExampleIsTimeout demonstrates checking for timeout errors.
package main
import (
"fmt"
"time"
"github.com/mam-coder/httpc"
)
func main() {
client := httpc.NewClient(
httpc.WithTimeout(1 * time.Millisecond),
)
_, err := client.Get("https://example.com/slow-endpoint")
if httpc.IsTimeout(err) {
fmt.Println("Request timed out")
}
}
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is the main HTTP client type that provides a fluent API for making HTTP requests. It supports configuration through options, automatic retries, request interceptors, and various convenience methods for common HTTP operations.
Client is safe for concurrent use by multiple goroutines.
func Default ¶
func Default() *Client
Default creates a client with sensible defaults. This is equivalent to calling NewClient() with no options. The returned client will have a 30-second timeout and standard transport settings.
Example:
client := httpc.Default()
resp, err := client.Get("https://api.example.com/users")
func NewClient ¶
NewClient creates a new HTTP client with the specified options. Options can configure the base URL, timeout, headers, retry logic, interceptors, and other client behavior.
Available options:
- WithBaseURL: Set base URL for all requests
- WithTimeout: Set request timeout duration
- WithHeader: Add a single header to all requests
- WithHeaders: Add multiple headers to all requests
- WithUserAgent: Set User-Agent header
- WithContentType: Set Content-Type header
- WithAccept: Set Accept header
- WithAuthorization: Add Bearer token authentication
- WithBaseAuth: Add HTTP Basic authentication
- WithApiKey: Add API key authentication
- WithRequestId: Add unique request ID header
- WithRetry: Configure automatic retry logic
- WithLogger: Add request/response logging
- WithDebug: Enable debug mode
- WithBlockedList: Block requests to specific domains
- WithInterceptor: Add custom request/response interceptor
Example:
client := httpc.NewClient(
httpc.WithBaseURL("https://api.example.com"),
httpc.WithTimeout(30*time.Second),
httpc.WithHeader("User-Agent", "MyApp/1.0"),
)
Example ¶
ExampleNewClient demonstrates creating a new HTTP client with options.
package main
import (
"fmt"
"log"
"time"
"github.com/mam-coder/httpc"
)
func main() {
client := httpc.NewClient(
httpc.WithBaseURL("https://api.example.com"),
httpc.WithTimeout(30*time.Second),
httpc.WithHeader("User-Agent", "MyApp/1.0"),
)
resp, err := client.Get("/users")
if err != nil {
log.Fatal(err)
}
fmt.Println("Response received:", resp.StatusCode >= 200 && resp.StatusCode < 300)
}
func (*Client) Delete ¶
func (c *Client) Delete(url string, opts ...RequestOption) (*Response, error)
Delete sends an HTTP DELETE request to the specified URL.
Example:
resp, err := client.Delete("/api/users/123")
func (*Client) DeleteWithContext ¶
func (c *Client) DeleteWithContext(ctx context.Context, url string, opts ...RequestOption) (*Response, error)
DeleteWithContext sends an HTTP DELETE request with a context. The context can be used for cancellation or setting deadlines.
Example:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() resp, err := client.DeleteWithContext(ctx, "/api/users/123")
func (*Client) Get ¶
func (c *Client) Get(url string, opts ...RequestOption) (*Response, error)
Get sends an HTTP GET request to the specified URL. Optional RequestOption functions can be provided to customize the request.
Example:
resp, err := client.Get("/api/users",
httpc.Header("Accept", httpc.ContentTypeJSON),
httpc.WithQuery("page", "1"),
)
Example ¶
ExampleClient_Get demonstrates making a GET request.
package main
import (
"fmt"
"log"
"github.com/mam-coder/httpc"
)
func main() {
client := httpc.NewClient(
httpc.WithBaseURL("https://api.example.com"),
)
resp, err := client.Get("/users",
httpc.WithQuery("page", "1"),
httpc.WithQuery("limit", "10"),
)
if err != nil {
log.Fatal(err)
}
body, _ := resp.String()
fmt.Println("Response:", len(body) > 0)
}
func (*Client) GetCSV ¶ added in v1.0.1
func (c *Client) GetCSV(url string, result interface{}, opts ...RequestOption) error
GetCSV is a convenience method that sends a GET request and automatically unmarshals the CSV response into the result parameter. The result must be a pointer to a slice of structs.
Example:
type User struct {
ID string `csv:"id"`
Name string `csv:"name"`
}
var users []User
err := client.GetCSV("/api/users.csv", &users,
httpc.WithQuery("status", "active"),
)
func (*Client) GetCSVWithContext ¶ added in v1.0.1
func (c *Client) GetCSVWithContext(ctx context.Context, url string, result interface{}, opts ...RequestOption) error
GetCSVWithContext is a convenience method that sends a GET request with a context and automatically unmarshals the CSV response into the result parameter. The result must be a pointer to a slice of structs.
Example:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
type User struct {
ID string `csv:"id"`
Name string `csv:"name"`
}
var users []User
err := client.GetCSVWithContext(ctx, "/api/users.csv", &users,
httpc.WithQuery("status", "active"),
)
func (*Client) GetCSVWithSeparator ¶ added in v1.0.1
func (c *Client) GetCSVWithSeparator(url string, separator rune, result interface{}, opts ...RequestOption) error
GetCSVWithSeparator is a convenience method that sends a GET request and automatically unmarshals the CSV response using a custom separator into the result parameter. This is useful for TSV files or other delimited formats. The result must be a pointer to a slice of structs.
Example:
type User struct {
ID string `csv:"id"`
Name string `csv:"name"`
}
var users []User
err := client.GetCSVWithSeparator("/api/users.tsv", '\t', &users,
httpc.WithQuery("status", "active"),
)
func (*Client) GetCSVWithSeparatorAndContext ¶ added in v1.0.1
func (c *Client) GetCSVWithSeparatorAndContext(ctx context.Context, url string, separator rune, result interface{}, opts ...RequestOption) error
GetCSVWithSeparatorAndContext is a convenience method that sends a GET request with a context and automatically unmarshals the CSV response using a custom separator into the result parameter. This is useful for TSV files or other delimited formats. The result must be a pointer to a slice of structs.
Example:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
type User struct {
ID string `csv:"id"`
Name string `csv:"name"`
}
var users []User
err := client.GetCSVWithSeparatorAndContext(ctx, "/api/users.tsv", '\t', &users,
httpc.WithQuery("status", "active"),
)
func (*Client) GetJSON ¶
func (c *Client) GetJSON(url string, result interface{}, opts ...RequestOption) error
GetJSON is a convenience method that sends a GET request and automatically unmarshals the JSON response into the result parameter.
Example:
var users []User
err := client.GetJSON("/api/users", &users,
httpc.WithQuery("status", "active"),
)
Example ¶
ExampleClient_GetJSON demonstrates getting and parsing JSON response.
package main
import (
"fmt"
"log"
"github.com/mam-coder/httpc"
)
func main() {
client := httpc.NewClient(
httpc.WithBaseURL("https://api.example.com"),
)
var users []map[string]interface{}
err := client.GetJSON("/users", &users)
if err != nil {
log.Fatal(err)
}
fmt.Println("Got users:", len(users) >= 0)
}
func (*Client) GetJSONWithContext ¶
func (c *Client) GetJSONWithContext(ctx context.Context, url string, result interface{}, opts ...RequestOption) error
GetJSONWithContext is a convenience method that sends a GET request with a context and automatically unmarshals the JSON response into the result parameter.
Example:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
var users []User
err := client.GetJSONWithContext(ctx, "/api/users", &users,
httpc.WithQuery("status", "active"),
)
func (*Client) GetWithContext ¶
func (c *Client) GetWithContext(ctx context.Context, url string, opts ...RequestOption) (*Response, error)
GetWithContext sends an HTTP GET request with a context. The context can be used for cancellation or setting deadlines.
Example:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() resp, err := client.GetWithContext(ctx, "/api/users")
func (*Client) GetXML ¶ added in v1.0.1
func (c *Client) GetXML(url string, result interface{}, opts ...RequestOption) error
GetXML is a convenience method that sends a GET request and automatically unmarshals the XML response into the result parameter.
Example:
var config Config
err := client.GetXML("/api/config", &config,
httpc.WithQuery("env", "production"),
)
func (*Client) GetXMLWithContext ¶ added in v1.0.1
func (c *Client) GetXMLWithContext(ctx context.Context, url string, result interface{}, opts ...RequestOption) error
GetXMLWithContext is a convenience method that sends a GET request with a context and automatically unmarshals the XML response into the result parameter.
Example:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
var config Config
err := client.GetXMLWithContext(ctx, "/api/config", &config,
httpc.WithQuery("env", "production"),
)
func (*Client) NewRequest ¶
func (c *Client) NewRequest() *RequestBuilder
NewRequest creates a new RequestBuilder for building and executing HTTP requests.
Example:
rb := client.NewRequest()
resp, err := rb.Method("GET").URL("/api/users").Do()
Example ¶
ExampleClient_NewRequest demonstrates using the request builder.
package main
import (
"fmt"
"log"
"time"
"github.com/mam-coder/httpc"
)
func main() {
client := httpc.NewClient()
resp, err := client.NewRequest().
Method("POST").
URL("https://api.example.com/users").
Header("Authorization", "Bearer token").
Query("include", "profile").
JSON(map[string]string{"name": "John"}).
Timeout(5 * time.Second).
Do()
if err != nil {
log.Fatal(err)
}
fmt.Println("Request completed:", resp != nil)
}
func (*Client) Patch ¶
func (c *Client) Patch(url string, body interface{}, opts ...RequestOption) (*Response, error)
Patch sends an HTTP PATCH request with a JSON body to the specified URL. The body is automatically marshaled to JSON and the Content-Type header is set. Pass nil for body if no request body is needed.
Example:
updates := map[string]string{"status": "active"}
resp, err := client.Patch("/api/users/123", updates)
func (*Client) PatchWithContext ¶
func (c *Client) PatchWithContext(ctx context.Context, url string, body interface{}, opts ...RequestOption) (*Response, error)
PatchWithContext sends an HTTP PATCH request with a context and JSON body. The context can be used for cancellation or setting deadlines.
Example:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() resp, err := client.PatchWithContext(ctx, "/api/users/123", updates)
func (*Client) Post ¶
func (c *Client) Post(url string, body interface{}, opts ...RequestOption) (*Response, error)
Post sends an HTTP POST request with a JSON body to the specified URL. The body is automatically marshaled to JSON and the Content-Type header is set. Pass nil for body if no request body is needed.
Example:
user := map[string]string{"name": "John", "email": "john@example.com"}
resp, err := client.Post("/api/users", user)
Example ¶
ExampleClient_Post demonstrates making a POST request with JSON.
package main
import (
"fmt"
"log"
"github.com/mam-coder/httpc"
)
func main() {
client := httpc.NewClient(
httpc.WithBaseURL("https://api.example.com"),
)
user := map[string]string{
"name": "John Doe",
"email": "john@example.com",
}
resp, err := client.Post("/users", user)
if err != nil {
log.Fatal(err)
}
fmt.Println("Created:", resp.StatusCode == 201)
}
func (*Client) PostJSON ¶
func (c *Client) PostJSON(url string, body interface{}, result interface{}, opts ...RequestOption) error
PostJSON is a convenience method that sends a POST request with a JSON body and automatically unmarshals the JSON response into the result parameter. Pass nil for result if you don't need to parse the response.
Example:
user := User{Name: "John", Email: "john@example.com"}
var created User
err := client.PostJSON("/api/users", user, &created)
func (*Client) PostJSONWithContext ¶
func (c *Client) PostJSONWithContext(ctx context.Context, url string, body interface{}, result interface{}, opts ...RequestOption) error
PostJSONWithContext is a convenience method that sends a POST request with a context, JSON body, and automatically unmarshals the JSON response into the result parameter. Pass nil for result if you don't need to parse the response.
Example:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
user := User{Name: "John", Email: "john@example.com"}
var created User
err := client.PostJSONWithContext(ctx, "/api/users", user, &created)
func (*Client) PostWithContext ¶
func (c *Client) PostWithContext(ctx context.Context, url string, body interface{}, opts ...RequestOption) (*Response, error)
PostWithContext sends an HTTP POST request with a context and JSON body. The context can be used for cancellation or setting deadlines.
Example:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() resp, err := client.PostWithContext(ctx, "/api/users", userData)
func (*Client) PostXML ¶ added in v1.0.1
func (c *Client) PostXML(url string, body interface{}, result interface{}, opts ...RequestOption) error
PostXML is a convenience method that sends a POST request with an XML body and automatically unmarshals the XML response into the result parameter. Pass nil for result if you don't need to parse the response.
Example:
config := Config{Environment: "production"}
var created Config
err := client.PostXML("/api/config", config, &created)
func (*Client) PostXMLWithContext ¶ added in v1.0.1
func (c *Client) PostXMLWithContext(ctx context.Context, url string, body interface{}, result interface{}, opts ...RequestOption) error
PostXMLWithContext is a convenience method that sends a POST request with a context, XML body, and automatically unmarshals the XML response into the result parameter. Pass nil for result if you don't need to parse the response.
Example:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
config := Config{Environment: "production"}
var created Config
err := client.PostXMLWithContext(ctx, "/api/config", config, &created)
func (*Client) Put ¶
func (c *Client) Put(url string, body interface{}, opts ...RequestOption) (*Response, error)
Put sends an HTTP PUT request with a JSON body to the specified URL. The body is automatically marshaled to JSON and the Content-Type header is set. Pass nil for body if no request body is needed.
Example:
user := map[string]string{"name": "John Doe"}
resp, err := client.Put("/api/users/123", user)
func (*Client) PutWithContext ¶
func (c *Client) PutWithContext(ctx context.Context, url string, body interface{}, opts ...RequestOption) (*Response, error)
PutWithContext sends an HTTP PUT request with a context and JSON body. The context can be used for cancellation or setting deadlines.
Example:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() resp, err := client.PutWithContext(ctx, "/api/users/123", userData)
type DebugTransport ¶
type DebugTransport struct {
Debug bool
Logger *log.Logger
LogBody bool
MaxBodySize int64
// contains filtered or unexported fields
}
DebugTransport is a wrapper around http.DefaultTransport that logs HTTP requests and responses.
func NewDebugTransport ¶
func NewDebugTransport(transport http.RoundTripper, debug bool) *DebugTransport
type Error ¶
type Error struct {
// StatusCode is the HTTP status code returned by the server
StatusCode int
// Message is a human-readable error message
Message string
// Body is the raw response body from the server
Body []byte
}
Error represents an HTTP error response from the client. It contains the HTTP status code, error message, and response body.
Example:
resp, err := client.Get("/api/users")
if err != nil {
var httpErr *httpc.Error
if errors.As(err, &httpErr) {
fmt.Printf("Status: %d, Message: %s\n", httpErr.StatusCode, httpErr.Message)
}
}
type Interceptor ¶
type Interceptor func(tripper http.RoundTripper) http.RoundTripper
Interceptor is a function that wraps an http.RoundTripper to add custom behavior. Interceptors can be chained together to create complex request/response processing pipelines.
Example:
customInterceptor := func(rt http.RoundTripper) http.RoundTripper {
return &myCustomTransport{transport: rt}
}
client := httpc.NewClient(httpc.WithInterceptor(customInterceptor))
type Option ¶
type Option func(*Client)
Option is a function that configures a Client. Options are passed to NewClient to customize client behavior such as setting timeouts, base URLs, headers, retry logic, and interceptors.
Example with multiple options:
client := httpc.NewClient(
httpc.WithBaseURL("https://api.example.com"),
httpc.WithTimeout(30*time.Second),
httpc.WithHeader("User-Agent", "MyApp/1.0"),
httpc.WithAuthorization("your-api-token"),
httpc.WithDebug(),
)
func WithAccept ¶
WithAccept sets the Accept header for all requests.
Example:
client := httpc.NewClient(httpc.WithAccept(httpc.ContentTypeJSON))
func WithApiKey ¶
WithApiKey sets an API key header for authentication. If headerName is empty, defaults to "X-Api-Key".
Example:
client := httpc.NewClient(httpc.WithApiKey("X-API-Key", "secret-key-123"))
// Or with default header name:
client := httpc.NewClient(httpc.WithApiKey("", "secret-key-123"))
func WithAuthorization ¶
WithAuthorization sets a Bearer token for authentication. The token is added to the Authorization header as "Bearer <token>".
Example:
client := httpc.NewClient(httpc.WithAuthorization("your-api-token"))
Example ¶
ExampleWithAuthorization demonstrates Bearer token authentication.
package main
import (
"fmt"
"log"
"github.com/mam-coder/httpc"
)
func main() {
client := httpc.NewClient(
httpc.WithBaseURL("https://api.example.com"),
httpc.WithAuthorization("your-api-token"),
)
resp, err := client.Get("/protected/resource")
if err != nil {
log.Fatal(err)
}
fmt.Println("Authenticated:", resp.StatusCode != 401)
}
func WithBaseAuth ¶
WithBaseAuth configures HTTP Basic Authentication for all requests. The username and password are automatically encoded and added to the Authorization header.
Example:
client := httpc.NewClient(httpc.WithBaseAuth("user", "password"))
Example ¶
ExampleWithBaseAuth demonstrates HTTP Basic authentication.
package main
import (
"fmt"
"log"
"github.com/mam-coder/httpc"
)
func main() {
client := httpc.NewClient(
httpc.WithBaseURL("https://api.example.com"),
httpc.WithBaseAuth("username", "password"),
)
resp, err := client.Get("/protected/resource")
if err != nil {
log.Fatal(err)
}
fmt.Println("Authenticated:", resp.StatusCode != 401)
}
func WithBaseURL ¶
WithBaseURL sets the base URL for all requests made by the client. Relative URLs in requests will be resolved against this base URL. If a request uses an absolute URL (starting with http:// or https://), the base URL is ignored for that request.
Example:
client := httpc.NewClient(httpc.WithBaseURL("https://api.example.com"))
resp, err := client.Get("/users") // Goes to https://api.example.com/users
func WithBlockedList ¶
WithBlockedList configures a list of domains that should be blocked. Requests to any domain in the blockedList will fail with an error. This is useful for preventing requests to known malicious or unwanted domains.
Example:
client := httpc.NewClient(httpc.WithBlockedList([]string{
"malicious-site.com",
"blocked-domain.com",
}))
// Attempting to access a blocked domain will return an error
resp, err := client.Get("https://malicious-site.com/api/data")
if err != nil {
fmt.Println(err)
// Output: request to blocked domain: malicious-site.com
}
func WithContentType ¶
WithContentType sets the Content-Type header for all requests.
Example:
client := httpc.NewClient(httpc.WithContentType(httpc.ContentTypeJSON))
func WithDebug ¶
func WithDebug() Option
WithDebug enables debug logging for the client. When enabled, the client will log detailed information about requests and responses.
Example:
client := httpc.NewClient(httpc.WithDebug())
func WithHeader ¶
WithHeader sets a single HTTP header that will be added to every request. Multiple calls to WithHeader will add multiple headers.
Example:
client := httpc.NewClient(
httpc.WithHeader("User-Agent", "MyApp/1.0"),
httpc.WithHeader("Accept", "application/json"),
)
func WithHeaders ¶
WithHeaders sets multiple HTTP headers that will be added to every request. Headers set here can be overridden on a per-request basis.
Example:
client := httpc.NewClient(httpc.WithHeaders(map[string]string{
"X-App-Version": "1.0",
"X-Environment": "production",
}))
func WithInterceptor ¶
func WithInterceptor(interceptor Interceptor) Option
WithInterceptor adds a request interceptor to the client. Interceptors are executed in the order they are added and can modify requests before they are sent or return errors to prevent execution. Multiple interceptors can be added by calling this option multiple times.
Example of adding custom headers to all requests:
customHeaderInterceptor := func(rt http.RoundTripper) http.RoundTripper {
return &customHeaderTransport{transport: rt}
}
type customHeaderTransport struct {
transport http.RoundTripper
}
func (t *customHeaderTransport) RoundTrip(req *http.Request) (*http.Response, error) {
req.Header.Set("X-Custom-Header", "MyValue")
req.Header.Set("X-Request-Time", time.Now().Format(time.RFC3339))
return t.transport.RoundTrip(req)
}
client := httpc.NewClient(httpc.WithInterceptor(customHeaderInterceptor))
Example of request/response timing interceptor:
timingInterceptor := func(rt http.RoundTripper) http.RoundTripper {
return roundTripFunc(func(req *http.Request) (*http.Response, error) {
start := time.Now()
resp, err := rt.RoundTrip(req)
duration := time.Since(start)
log.Printf("%s %s took %v", req.Method, req.URL, duration)
return resp, err
})
}
client := httpc.NewClient(httpc.WithInterceptor(timingInterceptor))
Example of conditional request modifier:
conditionalInterceptor := func(rt http.RoundTripper) http.RoundTripper {
return roundTripFunc(func(req *http.Request) (*http.Response, error) {
// Add API version header only for specific paths
if strings.HasPrefix(req.URL.Path, "/api/v2") {
req.Header.Set("X-API-Version", "2.0")
}
return rt.RoundTrip(req)
})
}
client := httpc.NewClient(httpc.WithInterceptor(conditionalInterceptor))
func WithLogger ¶
WithLogger configures request/response logging using the provided logger. All HTTP requests and responses will be logged with method, URL, status code, and timing.
Example:
logger := log.New(os.Stdout, "[HTTP] ", log.LstdFlags) client := httpc.NewClient(httpc.WithLogger(logger))
func WithRequestId ¶
WithRequestId generates and sets a unique request ID header for tracing. If headerName is empty, defaults to "X-Request-Id". A new request ID is generated for each client instance.
Example:
client := httpc.NewClient(httpc.WithRequestId("X-Request-ID"))
func WithRetry ¶
func WithRetry(config RetryConfig) Option
WithRetry configures automatic retry logic with exponential backoff. Use RetryConfig to specify max retries, backoff duration, and retry conditions.
Example with default retry condition:
config := httpc.RetryConfig{
MaxRetries: 3,
Backoff: time.Second,
RetryIf: httpc.DefaultRetryCondition,
}
client := httpc.NewClient(httpc.WithRetry(config))
Example with custom retry condition:
customRetryCondition := func(resp *http.Response, err error) bool {
// Retry on network errors
if err != nil {
return true
}
// Retry on specific status codes
if resp.StatusCode == 429 || resp.StatusCode == 503 {
return true
}
// Retry on 500 errors only if response contains specific message
if resp.StatusCode == 500 {
body, _ := io.ReadAll(resp.Body)
resp.Body = io.NopCloser(bytes.NewReader(body))
return strings.Contains(string(body), "temporary")
}
return false
}
config := httpc.RetryConfig{
MaxRetries: 5,
Backoff: 2 * time.Second,
RetryIf: customRetryCondition,
}
client := httpc.NewClient(httpc.WithRetry(config))
Example ¶
ExampleWithRetry demonstrates configuring retry logic.
package main
import (
"fmt"
"log"
"time"
"github.com/mam-coder/httpc"
)
func main() {
config := httpc.DefaultRetryConfig()
config.MaxRetries = 3
config.Backoff = time.Second
client := httpc.NewClient(
httpc.WithBaseURL("https://api.example.com"),
httpc.WithRetry(*config),
)
resp, err := client.Get("/users")
if err != nil {
log.Fatal(err)
}
fmt.Println("Success:", resp.StatusCode == 200)
}
func WithTimeout ¶
WithTimeout sets the request timeout for all requests made by the client. Individual requests can override this timeout using the RequestBuilder.Timeout method. The default timeout is 30 seconds.
Example:
client := httpc.NewClient(httpc.WithTimeout(10*time.Second))
func WithUserAgent ¶
WithUserAgent sets the User-Agent header for all requests.
Example:
client := httpc.NewClient(httpc.WithUserAgent("MyApp/1.0"))
type RequestBuilder ¶
type RequestBuilder struct {
// contains filtered or unexported fields
}
RequestBuilder provides a fluent interface for building HTTP requests. It allows chaining method calls to configure all aspects of an HTTP request before executing it with Do().
Example:
resp, err := client.NewRequest().
Method("POST").
URL("/api/users").
Header("Authorization", "Bearer token").
Query("include", "profile").
JSON(userData).
Timeout(10*time.Second).
Do()
func (*RequestBuilder) Body ¶
func (rb *RequestBuilder) Body(body io.Reader) *RequestBuilder
Body sets the request body from an io.Reader. For JSON bodies, use the JSON() method instead.
Example:
rb.Body(strings.NewReader("plain text body"))
func (*RequestBuilder) Context ¶
func (rb *RequestBuilder) Context(ctx context.Context) *RequestBuilder
Context sets the context for the request, allowing for cancellation and deadlines.
Example:
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() rb.Context(ctx)
func (*RequestBuilder) Do ¶
func (rb *RequestBuilder) Do() (*Response, error)
Do executes the HTTP request and returns the response. This should be called as the final method in the RequestBuilder chain. Any errors that occurred during request building will be returned here.
Example:
resp, err := client.NewRequest().
Method("GET").
URL("/api/users").
Do()
func (*RequestBuilder) Header ¶
func (rb *RequestBuilder) Header(key, value string) *RequestBuilder
Header adds a header to the request. Can be called multiple times to add multiple headers.
Example:
rb.Header("Authorization", "Bearer token").
Header("Accept", httpc.ContentTypeJSON)
func (*RequestBuilder) JSON ¶
func (rb *RequestBuilder) JSON(v interface{}) *RequestBuilder
JSON marshals the provided value to JSON and sets it as the request body. It automatically sets the Content-Type header to application/json.
Example:
client.NewRequest().
Method("POST").
URL("/api/users").
JSON(map[string]string{"name": "John"}).
Do()
func (*RequestBuilder) Method ¶
func (rb *RequestBuilder) Method(method string) *RequestBuilder
Method sets the HTTP method for the request (GET, POST, PUT, DELETE, PATCH, etc.).
Example:
rb.Method("POST")
func (*RequestBuilder) Query ¶
func (rb *RequestBuilder) Query(key, value string) *RequestBuilder
Query adds a query parameter to the request. Can be called multiple times. Values with the same key will be appended (e.g., ?tag=a&tag=b).
Example:
rb.Query("page", "1").Query("limit", "10")
func (*RequestBuilder) QueryParams ¶
func (rb *RequestBuilder) QueryParams(params map[string]string) *RequestBuilder
QueryParams adds multiple query parameters from a map. Each key-value pair will be set (replacing any existing values for those keys).
Example:
rb.QueryParams(map[string]string{
"page": "1",
"limit": "10",
"sort": "name",
})
func (*RequestBuilder) Timeout ¶
func (rb *RequestBuilder) Timeout(timeout time.Duration) *RequestBuilder
Timeout sets a request-specific timeout, overriding the client's default timeout. This creates a timeout context that will cancel the request if it exceeds the duration. The timeout applies to the entire request/response cycle, including connection time, redirects, and reading the response body.
Example:
rb.Timeout(5 * time.Second)
func (*RequestBuilder) URL ¶
func (rb *RequestBuilder) URL(url string) *RequestBuilder
URL sets the request URL. Can be absolute or relative to the client's base URL.
Example:
rb.URL("/api/users") // Relative to base URL
rb.URL("https://api.example.com/users") // Absolute URL
func (*RequestBuilder) XML ¶ added in v1.0.1
func (rb *RequestBuilder) XML(v interface{}) *RequestBuilder
XML marshals the provided value to XML and sets it as the request body. It automatically sets the Content-Type header to application/xml.
Example:
client.NewRequest().
Method("POST").
URL("/api/config").
XML(map[string]string{"key": "value"}).
Do()
type RequestOption ¶
type RequestOption func(*RequestBuilder)
RequestOption is a function that configures a RequestBuilder. It allows customizing individual requests with headers, query parameters, etc.
func Header ¶
func Header(key, value string) RequestOption
Header returns a RequestOption that adds a header to the request.
Example:
resp, err := client.Get("/api/users",
httpc.Header("Authorization", "Bearer token"),
httpc.Header("Accept", httpc.ContentTypeJSON),
)
func WithContext ¶
func WithContext(ctx context.Context) RequestOption
WithContext returns a RequestOption that sets the context for the request. This is useful when you want to pass a context along with other request options.
Example:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
resp, err := client.Get("/api/users",
httpc.WithContext(ctx),
httpc.WithQuery("page", "1"),
)
func WithQuery ¶
func WithQuery(key, value string) RequestOption
WithQuery returns a RequestOption that adds a query parameter to the request.
Example:
resp, err := client.Get("/api/users",
httpc.WithQuery("page", "1"),
httpc.WithQuery("limit", "10"),
)
type Response ¶
Response wraps http.Response and provides convenience methods for reading and parsing the response body. The body is cached after the first read, so multiple calls to Bytes(), String(), or JSON() will return the same data without re-reading.
func (*Response) Bytes ¶
Bytes returns the response body as a byte slice. The body is read and cached on the first call, subsequent calls return the cached data without re-reading from the network.
Example:
resp, err := client.Get("/api/users")
if err != nil {
log.Fatal(err)
}
body, err := resp.Bytes()
if err != nil {
log.Fatal(err)
}
func (*Response) CSV ¶ added in v1.0.1
CSV unmarshals the response body into the provided slice of structs. The first row is expected to contain header names that match struct field names or csv tags. The body is read and cached on the first call. By default, comma (,) is used as the separator. Use SetCSVSeparator to change it.
Example:
type User struct {
ID string `csv:"id"`
Name string `csv:"name"`
}
resp, err := client.Get("/api/users.csv")
if err != nil {
log.Fatal(err)
}
var users []User
if err := resp.CSV(&users); err != nil {
log.Fatal(err)
}
func (*Response) JSON ¶
JSON unmarshals the response body into the provided value. The body is read and cached on the first call.
Example:
type User struct {
ID int `json:"id"`
Name string `json:"name"`
}
resp, err := client.Get("/api/users/123")
if err != nil {
log.Fatal(err)
}
var user User
if err := resp.JSON(&user); err != nil {
log.Fatal(err)
}
func (*Response) SetCSVSeparator ¶ added in v1.0.1
SetCSVSeparator sets the separator (delimiter) for CSV parsing. The default separator is comma (,). Use this method to parse TSV files (tab-separated) or other delimited formats.
Example:
resp, err := client.Get("/api/users.tsv")
if err != nil {
log.Fatal(err)
}
resp.SetCSVSeparator('\t') // Set tab as separator
var users []User
if err := resp.CSV(&users); err != nil {
log.Fatal(err)
}
func (*Response) String ¶
String returns the response body as a string. The body is read and cached on the first call, subsequent calls return the cached data without re-reading from the network.
Example:
resp, err := client.Get("/api/status")
if err != nil {
log.Fatal(err)
}
body, err := resp.String()
if err != nil {
log.Fatal(err)
}
fmt.Println(body)
type RetryConfig ¶
type RetryConfig struct {
// MaxRetries is the maximum number of retry attempts
MaxRetries int
// Backoff is the base duration between retries (multiplied by attempt number for exponential backoff)
Backoff time.Duration
// RetryIf is a function that determines whether a request should be retried
// based on the response or error. If nil, defaultRetryCondition is used.
RetryIf func(*http.Response, error) bool
}
RetryConfig configures the retry behavior for failed HTTP requests. It defines the maximum number of retries, backoff duration, and a custom condition function to determine if a request should be retried.
func DefaultRetryConfig ¶
func DefaultRetryConfig() *RetryConfig
DefaultRetryConfig returns a RetryConfig with sensible defaults: - 3 maximum retries - 1 second base backoff - Retries on network errors, 5xx status codes, and 429 (rate limit)
Example:
config := httpc.DefaultRetryConfig() // Customize if needed config.MaxRetries = 5