client

package
v0.0.0-...-f155a4b Latest Latest
Warning

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

Go to latest
Published: Aug 29, 2025 License: MIT Imports: 14 Imported by: 0

README

The Client package provides a flexible HTTP client that simplifies making HTTP requests (GET, POST, PUT, DELETE) with automatic body parsing. It supports passing request bodies as strings, structs (which are marshaled to JSON), or any type that implements io.Reader.

📌 Overview

🔹 Main features:

  • 🚀 Global functions for fast HTTP requests using a standard client.
  • ⚙️ Custom client creation with options for context, headers and HTTP transport configuration.
  • 🔄 Flexible body parsing for POST and PUT requests, accepting different input types.
  • 🔁 Requests with Retry and Failover for greater resilience.

📦 Go package documentation

To access the documentation for each Quick Framework package, click on the links below:

Pacote Descrição Go.dev
quick/http/client HTTP client optimized for requests and failover GoDoc

✅ Method Reference
Method Signature Description
func Get(url string) (*ClientResponse, error) Global GET request using the default client
func Post(url string, body any) (*ClientResponse, error) Global POST request with flexible body input
func Put(url string, body any) (*ClientResponse, error) Global PUT request with flexible body input
func Delete(url string) (*ClientResponse, error) Global DELETE request using the default client
func (c *Client) Get(url string) (*ClientResponse, error) GET request using a custom client instance
func (c *Client) Post(url string, body any) (*ClientResponse, error) POST request using a custom client instance
func (c *Client) Put(url string, body any) (*ClientResponse, error) PUT request using a custom client instance
func (c *Client) Delete(url string) (*ClientResponse, error) DELETE request using a custom client instance
func New(opts ...Option) *Client Creates a new Client with optional custom configurations
func WithContext(ctx context.Context) Option Option to set a custom context for the client
func WithHeaders(headers map[string]string) Option Option to set custom headers
func WithHTTPClientConfig(cfg *HTTPClientConfig) Option Option to set a custom HTTP transport configuration

📌 Example Usage with ReqRes API

🔹 GET Request Example

Retrieves a list of users from ReqRes API.

package main

import (
	"fmt"
	"log"

	"github.com/jeffotoni/quick/http/client"
)

func main() {
	// Use the default client
	resp, err := client.Get("https://reqres.in/api/users")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("GET response:", string(resp.Body))
}

🔹 POST Request Example (Sending JSON)

This sends a POST request to create a new user.

POST Request Example (Using a Struct)
package main

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/jeffotoni/quick/http/client"
)

func main() {
	// Define a struct to send as JSON
	data := struct {
		user string `json:"user"`
	}{
		user: "Emma",
	}

	// POST request to ReqRes API
	resp, err := client.Post("https://reqres.in/api/users", data)
	if err != nil {
		log.Fatal(err)
	}

	// Unmarshal the JSON response (if applicable)
	var result map[string]string
	if err := json.Unmarshal(resp.Body, &result); err != nil {
		log.Fatal(err)
	}
	fmt.Println("POST response:", result)
}

🔹 PUT Request Example (Using a String)

Updates an existing user.

package main

import (
	"fmt"
	"log"

	"github.com/jeffotoni/quick/http/client"
)

func main() {
	// Define a struct with user data
	data := struct {
		user string `json:"name"`
	}{
		user: "Jeff",
	}

	// Convert struct to JSON
	jsonData, err := json.Marshal(data)
	if err != nil {
		log.Fatal("Error encoding JSON:", err)
	}

	// PUT request to ReqRes API
	resp, err := client.Put("https://reqres.in/api/users/2", string(jsonData))
	if err != nil {
		log.Fatal("Error making request:", err)
	}

	// Print the HTTP status and response body
	fmt.Println("HTTP Status Code:", resp.StatusCode)
	fmt.Println("Raw Response Body:", string(resp.Body))
}
🔹 DELETE Request Example

Deletes a user and checks if the response is 204 No Content.

package main

import (
	"fmt"
	"log"

	"github.com/jeffotoni/quick/http/client"
)

func main() {

	// DELETE request to ReqRes API
	resp, err := client.Delete("https://reqres.in/api/users/2")
	if err != nil {
		log.Fatal("Error making request:", err)
	}

	// Print the HTTP status to confirm deletion
	fmt.Println("HTTP Status Code:", resp.StatusCode)

	// Since DELETE usually returns no content, we check if it's empty
	if len(resp.Body) > 0 {
		fmt.Println("Raw Response Body:", string(resp.Body))
	} else {
		fmt.Println("Response Body is empty (expected for 204 No Content)")
	}
}

🔹 Retry with Dynamic Failover

This example configures the HTTP client to retry automatically in case of failure, using automatic retry, exponential backoff and dynamic failover for alternative URLs.

package main

import (
	"fmt"
	"log"
	"time"

	"github.com/jeffotoni/quick/http/client"
)

func main() {
	// Retry configuration: maximum 3 attempts, 
	// with delay of 2s, exponential backoff
	retryConfig := client.RetryConfig{
	// Sets the maximum number of retry attempts
	MaxRetries: 3, 
	// Sets the base time between attempts before trying again
	Delay: 2 * time. Second,   
	// Enables exponential backoff to increase the time between failed attempts
	UseBackoff: true,        
	// List of HTTP status codes that trigger an automatic retry
	Status: []int{500, 502, 503, 504},   
	// Alternative URLs for failover in case of failure on the original request
	FailoverURLs: []string{"http://hosterror", "https://httpbin.org/get"}, 
	// Enable logs to log retry attempts
	EnableLog: true 
}


	// Create an HTTP client with retry configured
	httpClient := client.New(
		client.WithRetry(retryConfig),
	)

	// Making a GET request
	resp, err := httpClient.Get("https://httpbin_error.org/get")
	if err != nil {
		log.Fatal("Error in request:", err)
	}

	fmt.Println("HTTP code:", resp.StatusCode)
	fmt.Println("Answer:", string(resp.Body))
}


🔹 WithHeaders

This code demonstrates the creation of a highly configurable HTTP client.

package main

import (
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"log"
	"time"

	"github.com/jeffotoni/quick/http/client"
)

// Example of creating an HTTP client using a fluent and modular approach.
// This allows fine-grained control over HTTP settings without requiring a full config struct.
//
// - WithContext: Injects a context for the client (context.TODO() used as placeholder).
// - WithHeaders: Adds custom headers (e.g., Content-Type: application/xml).
// - WithRetry: Enables automatic retries for specific HTTP status codes (500, 502, 503, 504)
// - WithHTTPClientConfig: Defines advanced transport settings like connection pooling.
func main() {
	cfg := &client.HTTPClientConfig{
		Timeout:             20 * time.Second,
		DisableKeepAlives:   false,
		MaxIdleConns:        20,
		MaxConnsPerHost:     20,
		MaxIdleConnsPerHost: 20,
		TLSClientConfig: &tls.Config{
			InsecureSkipVerify: false,
			MinVersion:         tls.VersionTLS12,
		},
	}

	// Creating an HTTP client with the pre-defined configuration.
	//
	// - WithContext: Sets a custom context for handling request cancellation and deadlines.
	// - WithHeaders: Adds a map of default headers (e.g., "Content-Type: application/xml").
	// - WithHTTPClientConfig: Applies the entire configuration object (cfg) to the client.
	cClient := client.New(
		client.WithContext(context.TODO()),
		client.WithHeaders(map[string]string{"Content-Type": "application/xml"}),
		client.WithHTTPClientConfig(cfg),
		client.WithRetry(
			client.RetryConfig{
				MaxRetries: 2,
				Delay:      1 * time.Second,
				UseBackoff: true,
				Statuses:   []int{500},
				FailoverURLs: []string{
					"http://backup1",
					"https://httpbin_error.org/post",
					"https://httpbin.org/post"},
				EnableLog: true,
			}),
	)

	// Define a struct to send as JSON
	data := struct {
		Message string `json:"message"`
	}{
		Message: "Hello, POST!",
	}

	resp, err := cClient.Post("https://httpbin_error.org/post ", data)
	if err != nil {
		log.Fatal(err)
	}

	// Unmarshal the JSON response (if applicable)
	var result map[string]string
	if err := json.Unmarshal(resp.Body, &result); err != nil {
		log.Fatal(err)
	}
	fmt.Println("POST response:", result["message"])
}


🔹 FullMethodsRetry

This example shows a full methods retry

package main

import (
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"log"
	"time"

	"github.com/jeffotoni/quick/http/client"
)

// Example of creating an HTTP client using a fluent and modular approach.
// This allows fine-grained control over HTTP settings without requiring a full config struct.
//
//   - WithTimeout: Sets the HTTP client timeout to 30 seconds.
//   - WithDisableKeepAlives: Enables or disables HTTP keep-alives (false = keep-alives enabled).
//   - WithMaxIdleConns: Defines the maximum number of idle connections (20).
//   - WithMaxConnsPerHost: Sets the maximum connections allowed per host (20).
//   - WithMaxIdleConnsPerHost: Sets the maximum number of idle connections per host (20).
//   - WithContext: Injects a context for the client (context.TODO() used as placeholder).
//   - WithHeaders: Adds custom headers (e.g., Content-Type: application/json).
//   - WithTLSConfig: Configures TLS settings, including InsecureSkipVerify and TLS version.
//   - WithRetry: Enables automatic retries for specific HTTP status codes (500, 502, 503, 504)
//     with exponential backoff (2s-bex) and a maximum of 3 attempts.
func main() {

	// Create a new Quick HTTP client with custom settings
	cClient := client.New(
		client.WithTimeout(5*time.Second),   // Sets the request timeout to 5 seconds
		client.WithDisableKeepAlives(false), // Enables persistent connections (Keep-Alive)
		client.WithMaxIdleConns(20),         // Defines a maximum of 20 idle connections
		client.WithMaxConnsPerHost(20),      // Limits simultaneous connections per host to 20
		client.WithMaxIdleConnsPerHost(20),  // Limits idle connections per host to 20
		client.WithContext(context.TODO()),  // Injects a context (can be used for cancellation)
		client.WithHeaders(
			map[string]string{
				"Content-Type":  "application/json", // Specifies the request content type
				"Authorization": "Bearer Token",     // Adds an authorization token for authentication
			},
		),
		client.WithTLSConfig(&tls.Config{
			InsecureSkipVerify: true,             // ⚠ Disables SSL certificate verification (use with caution)
			MinVersion:         tls.VersionTLS12, // Enforces a minimum TLS version for security
		}),
		client.WithRetry(
			client.RetryConfig{
				MaxRetries: 2,                         // Allows up to 2 retry attempts for failed requests
				Delay:      1 * time.Second,           // Delay of 1 second between retries
				UseBackoff: true,                      // Enables exponential backoff for retries
				Statuses:   []int{502, 503, 504, 403}, // Retries only on specific HTTP status codes
				FailoverURLs: []string{ // Backup URLs in case the primary request fails
					"http://backup1",
					"https://reqres.in/api/users",
					"https://httpbin_error.org/post",
				},
				EnableLog: true, // Enables logging for debugging retry behavior
			}),
	)
	// Send a POST request to the primary URL
	resp, err := cClient.Post("https://httpbin_error.org/post",
		map[string]string{"message": "Hello, POST in Quick!"})
	if err != nil {
		log.Fatal(err) // Logs an error and exits if the request fails
	}

	// Unmarshal the JSON response (if applicable)
	var result map[string]string
	if err := json.Unmarshal(resp.Body, &result); err != nil {
		log.Fatal(err) // Logs an error if the response cannot be parsed
	}

	// Print the response
	fmt.Println("POST response:", result)
}

📌 What I included in this README

  • ✅ Overview: Explanation of the HTTP client in Quick.
  • ✅ Method Reference: Quick lookup for available functions.
  • ✅ GET, POST, PUT, DELETE Examples: How to use each method with ReqRes API.
  • ✅ Retry with Failover: Demonstration of automatic retries, exponential backoff, and dynamic failover URLs.
  • ✅ Testing with cURL: Alternative manual testing.
  • ✅ Response Handling Improvements: Ensuring valid JSON parsing and response verification.

Now you can complete with your specific examples where I left the spaces go ...

🚀 If you need adjustments or improvements, just let me know! 😃🔥

Documentation

Overview

Package client provides an advanced HTTP client for making HTTP requests.

It supports: - Customizable HTTP clients with configurable timeouts, connection pooling, and TLS settings. - Built-in retry logic with exponential backoff and failover support. - Thread-safe header management to prevent data races in concurrent requests. - Structured logging using `slog.Logger` for debugging and observability.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func NewHTTPClientFromConfig

func NewHTTPClientFromConfig(cfg *HTTPClientConfig) httpGoClient

NewHTTPClientFromConfig creates and configures an HTTP client based on the provided settings.

If no configuration is provided (cfg is nil), default settings are applied with sensible values.

/Parameters:

  • cfg: Pointer to `HTTPClientConfig` containing custom configurations. If nil, defaults are used.

/Return:

  • httpGoClient: Configured HTTP client with timeout, connection settings, and TLS options.

Types

type Client

type Client struct {
	Ctx          context.Context   // Context for request cancellation
	ClientHTTP   httpGoClient      // Underlying HTTP client implementation
	Headers      map[string]string // Default headers for requests
	EnableLogger bool              // Flag to enable/disable logging
	Logger       *slog.Logger      // Logger instance
	// contains filtered or unexported fields
}

Client represents a configurable HTTP client with advanced features

func GetDefaultClient

func GetDefaultClient() *Client

GetDefaultClient returns a singleton instance of the default HTTP client.

This ensures that the same client instance is reused across the application, reducing the overhead of creating multiple clients.

/Return:

  • *Client: Pointer to the default Client instance.

func New

func New(opts ...Option) *Client

New creates a new Client instance with optional configurations.

This function initializes a client with default values, but allows additional configurations to be passed as options. These options are applied sequentially to modify the client settings.

/Parameters:

  • opts: Variadic slice of `Option` functions to customize the client.

/Return:

  • *Client: Pointer to the configured HTTP client.
Example (WithRetry)

ExampleNew_withRetry demonstrates how to use the Client with retry logic.

// Create a test HTTP server that fails twice before succeeding.
attempts := 0
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	attempts++
	if attempts < 3 {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("Retry succeeded"))
}))
defer ts.Close()

// Initialize a client with retry logic enabled.
client := New(
	WithRetry(RetryConfig{
		MaxRetries:   3,
		Delay:        500 * time.Millisecond,
		UseBackoff:   true,
		Statuses:     []int{http.StatusInternalServerError},
		FailoverURLs: []string{ts.URL},
		EnableLog:    false,
	}))

// Perform a GET request to the test server.
resp, err := client.Get(ts.URL + "/invalid")
if err != nil {
	fmt.Println("Error:", err)
	return
}
fmt.Println(string(resp.Body))
Output:

Retry succeeded

func (*Client) Delete

func (c *Client) Delete(url string) (*ClientResponse, error)

Delete performs an HTTP DELETE request.

Sends a DELETE request to remove a resource.

/Parameters:

  • url (string): The target URL.

/Return:

  • *ClientResponse: Response containing status and body.
  • error: Error if the request fails.

Example:

client.Delete("/users/2")
Example

ExampleClient_Delete demonstrates how to use the Client's Delete method.

// Create a test HTTP server that responds with "DELETE OK" to DELETE requests.
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("DELETE OK"))
}))
defer ts.Close()

// Initialize a new client.
client := New()

// Perform a DELETE request to the test server.
resp, err := client.Delete(ts.URL)
if err != nil {
	fmt.Println("Error:", err)
	return
}
fmt.Println(string(resp.Body))
Output:

DELETE OK

func (*Client) Get

func (c *Client) Get(url string) (*ClientResponse, error)

Get performs an HTTP GET request.

Sends a GET request to the specified URL.

/Parameters:

  • url (string): The target URL.

/Return:

  • *ClientResponse: Response containing status and body.
  • error: Error if the request fails.

Example:

client.Get("/users/2")
Example

ExampleClient_Get demonstrates how to use the Client's Get method.

// Create a test HTTP server that responds with "GET OK" to GET requests.
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("GET OK"))
}))
defer ts.Close()

// Initialize a new client.
client := New()

// Perform a GET request to the test server.
resp, err := client.Get(ts.URL)
if err != nil {
	fmt.Println("Error:", err)
	return
}
fmt.Println(string(resp.Body))
Output:

GET OK

func (*Client) Log

func (c *Client) Log(msg string, args ...interface{})

log writes log messages if logging is enabled for the client.

This function is used internally to log messages at the INFO level, helping with debugging and monitoring request activity.

/Parameters:

  • msg: The log message format string.
  • args: Additional arguments to format within the log message.

/Return:

  • None (void function).

func (*Client) Post

func (c *Client) Post(url string, body any) (*ClientResponse, error)

Post performs an HTTP POST request.

Sends data to the specified URL using a POST request.

/Parameters:

  • url (string): The target URL.
  • body (any): Request body, auto-serialized if needed.

/Return:

  • *ClientResponse: Response containing status and body.
  • error: Error if the request fails.

Example:

client.Post("/users", userData)
Example

ExampleClient_Post demonstrates how to use the Client's Post method with different types of request bodies.

// Create a test HTTP server that echoes the request body.
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	body, _ := io.ReadAll(r.Body)
	w.WriteHeader(http.StatusCreated)
	w.Write(body)
}))
defer ts.Close()

// Initialize a new client.
client := New()

// Example 1: Sending a string as the POST body.
resp, err := client.Post(ts.URL, "Hello, POST!")
if err != nil {
	fmt.Println("Error:", err)
	return
}
fmt.Println("String body:", string(resp.Body))

// Example 2: Sending a struct as the POST body (automatically marshaled to JSON).
data := struct {
	Message string `json:"message"`
}{
	Message: "Hello, JSON POST!",
}
resp, err = client.Post(ts.URL, data)
if err != nil {
	fmt.Println("Error:", err)
	return
}
var result map[string]string
if err := json.Unmarshal(resp.Body, &result); err != nil {
	fmt.Println("Error unmarshalling JSON:", err)
	return
}
fmt.Println("Struct body:", result["message"])

// Example 3: Sending an io.Reader as the POST body.
reader := strings.NewReader("Reader POST")
resp, err = client.Post(ts.URL, reader)
if err != nil {
	fmt.Println("Error:", err)
	return
}
fmt.Println("io.Reader body:", string(resp.Body))
Output:

String body: Hello, POST!
Struct body: Hello, JSON POST!
io.Reader body: Reader POST

func (*Client) PostForm

func (c *Client) PostForm(url string, formData url.Values) (*ClientResponse, error)

PostForm performs an HTTP POST request with form data.

Sends form-encoded data (`application/x-www-form-urlencoded`).

/Parameters:

  • url (string): The target URL.
  • formData (url.Values): Form fields as key-value pairs.

/Return:

  • *ClientResponse: Response containing status and body.
  • error: Error if the request fails.

Example:

client.PostForm("/login", formData)
Example

ExampleClient_PostForm demonstrates how to use the Client's PostForm method to send URL-encoded form data. This function is named ExampleClient_PostForm() it with the Examples type.

// Creating a test server that returns the form data.
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	r.ParseForm() // Analyzes the form data
	w.WriteHeader(http.StatusOK)
	w.Write([]byte(r.Form.Encode())) // Returns the form data already formatted
}))
defer ts.Close()

// Initializing the client.
client := New()

// Creating the form data.
formData := url.Values{
	"key":   {"value"},
	"hello": {"world"},
}

// Enviando a requisição POST com o formulário.
resp, err := client.PostForm(ts.URL, formData)
if err != nil {
	fmt.Println("Error:", err)
	return
}

// Displaying the answer.
fmt.Println("Form POST response:", string(resp.Body))
Output:

Form POST response: hello=world&key=value

func (*Client) Put

func (c *Client) Put(url string, body any) (*ClientResponse, error)

Put performs an HTTP PUT request.

Sends a PUT request to update a resource at the specified URL.

/Parameters:

  • url (string): The target URL.
  • body (any): Request body, auto-serialized.

/Return:

  • *ClientResponse: Response containing status and body.
  • error: Error if the request fails.

Example:

client.Put("/users/2", userData)
Example

ExampleClient_Put demonstrates how to use the Client's Put method with different types of request bodies.

// Create a test HTTP server that echoes the request body.
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	body, _ := io.ReadAll(r.Body)
	w.WriteHeader(http.StatusOK)
	w.Write(body)
}))
defer ts.Close()

// Initialize a new client.
client := New()

// Example 1: Sending a string as the PUT body.
resp, err := client.Put(ts.URL, "Hello, PUT!")
if err != nil {
	fmt.Println("Error:", err)
	return
}
fmt.Println("String body:", string(resp.Body))

// Example 2: Sending a struct as the PUT body (automatically marshaled to JSON).
data := struct {
	Value int `json:"value"`
}{Value: 42}

resp, err = client.Put(ts.URL, data)
if err != nil {
	fmt.Println("Error:", err)
	return
}
var result map[string]int
if err := json.Unmarshal(resp.Body, &result); err != nil {
	fmt.Println("Error unmarshalling JSON:", err)
	return
}
fmt.Println("Struct body:", result["value"])
Output:

String body: Hello, PUT!
Struct body: 42

type ClientResponse

type ClientResponse struct {
	Body       []byte // Response body
	StatusCode int    // HTTP status code
}

ClientResponse represents the response from an HTTP request

func Delete

func Delete(url string) (*ClientResponse, error)

Delete performs a DELETE request using the default client.

/Parameters:

  • url: The target URL for the request.

/Return:

  • *ClientResponse: Pointer to the response containing the status code and body.
  • error: Error object if the request fails, otherwise nil.

func Get

func Get(url string) (*ClientResponse, error)

Get performs a GET request using the default client.

/Parameters:

  • url: The URL to send the GET request to.

/Return:

  • *ClientResponse: Pointer to the response containing the status code and body.
  • error: Error object if the request fails, otherwise nil.

func Post

func Post(url string, body any) (*ClientResponse, error)

Post performs a POST request using the default client.

/Parameters:

  • url: The target URL for the request.
  • body: The request payload, which can be a string, struct, or JSON object.

/Return:

  • *ClientResponse: Pointer to the response containing the status code and body.
  • error: Error object if the request fails, otherwise nil.

func PostForm

func PostForm(url string, formData url.Values) (*ClientResponse, error)

PostForm performs a form-encoded POST request.

Sends a `application/x-www-form-urlencoded` POST request with form data.

/Parameters:

  • url (string): The target URL.
  • formData (url.Values): Form fields as key-value pairs.

/Return:

  • *ClientResponse: Response with status code and body.
  • error: Error if the request fails.

Example:

PostForm("/login", formData)

func Put

func Put(url string, body any) (*ClientResponse, error)

Put performs a PUT request using the default client.

/Parameters:

  • url: The target URL for the request.
  • body: The request payload, which can be a string, struct, or JSON object.

/Return:

  • *ClientResponse: Pointer to the response containing the status code and body.
  • error: Error object if the request fails, otherwise nil.

type HTTPClientConfig

type HTTPClientConfig struct {
	Timeout             time.Duration // Request timeout
	DisableKeepAlives   bool          // Disable HTTP keep-alive
	MaxIdleConns        int           // Maximum idle connections
	MaxConnsPerHost     int           // Maximum connections per host
	MaxIdleConnsPerHost int           // Maximum idle connections per host
	TLSClientConfig     *tls.Config   // TLS configuration
}

HTTPClientConfig defines configuration for the underlying HTTP client

type Option

type Option func(*Client)

Option defines a functional option for configuring the Client

func WithContext

func WithContext(ctx context.Context) Option

WithContext sets a custom context for the client.

This function assigns a custom `context.Context` to the client, enabling request cancellation and deadline management.

/Parameters:

  • ctx (context.Context): The custom context to use.

/Return:

  • Option: A functional option that sets the client's context.

Example Usage:

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

client := New(WithContext(ctx))
Example

ExampleWithContext demonstrates how to use the Client with a custom context.

// Create a test HTTP server that delays its response.
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	time.Sleep(200 * time.Millisecond)
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("Delayed response"))
}))
defer ts.Close()

// Create a context with a 100ms deadline.
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
defer cancel()

// Initialize a client with the custom context.
client := New(WithContext(ctx))

// Perform a GET request (expected to timeout).
_, err := client.Get(ts.URL)
if err != nil {
	fmt.Println("Request canceled due to timeout")
} else {
	fmt.Println("Request succeeded")
}
Output:

Request canceled due to timeout

func WithCustomHTTPClient

func WithCustomHTTPClient(client *http.Client) Option

WithCustomHTTPClient replaces the default HTTP client with a fully custom one.

This function allows users to replace the internal HTTP client with a custom instance, providing complete control over transport, timeouts, and other configurations.

/Parameters:

  • client (*http.Client): A fully configured HTTP client.

/Return:

  • Option: A functional option that sets the custom HTTP client.

Example Usage:

client := New(WithCustomHTTPClient(&http.Client{Timeout: 10 * time.Second}))
Example

ExampleWithCustomHTTPClient demonstrates how to use the Client with a fully custom *http.Client.

// Create a test HTTP server that responds with "Custom Client OK".
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("Custom Client OK"))
}))
defer ts.Close()

// Create a custom HTTP client with specific settings.
customHTTPClient := &http.Client{
	Timeout: 5 * time.Second, // Set a custom timeout.
	Transport: &http.Transport{
		MaxIdleConns:        50, // Limit the maximum idle connections.
		MaxConnsPerHost:     20, // Limit concurrent connections per host.
		MaxIdleConnsPerHost: 10, // Limit idle connections per host.
	},
}

// Initialize a Client with the custom HTTP client.
client := New(WithCustomHTTPClient(customHTTPClient))

// Perform a GET request using the custom client.
resp, err := client.Get(ts.URL)
if err != nil {
	fmt.Println("Error:", err)
	return
}
fmt.Println(string(resp.Body))
Output:

Custom Client OK

func WithDisableKeepAlives

func WithDisableKeepAlives(disable bool) Option

WithDisableKeepAlives enables or disables HTTP keep-alive connections.

This function modifies the client's transport settings to control keep-alive behavior. Disabling keep-alives can be useful in cases where short-lived connections are preferred.

/Parameters:

  • disable (bool): If true, keep-alives are disabled; if false, they remain enabled.

/Return:

  • Option: A functional option that configures the client's transport.

Example Usage:

client := New(WithDisableKeepAlives(true))
Example

ExampleWithDisableKeepAlives demonstrates how to disable HTTP keep-alive connections. This function is named ExampleWithMaxConnsPerHost() it with the Examples type.

// Create a test HTTP server.
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("OK"))
}))
defer ts.Close()

// Initialize a client with keep-alives disabled.
client := New(WithDisableKeepAlives(true))

// Perform a GET request.
_, err := client.Get(ts.URL)
if err != nil {
	fmt.Println("Error:", err)
	return
}

// Confirm that keep-alives are disabled (for example purposes, normally you might not print this).
fmt.Println("Keep-alives are disabled")
Output:

Keep-alives are disabled

func WithHTTPClientConfig

func WithHTTPClientConfig(cfg *HTTPClientConfig) Option

WithHTTPClientConfig applies a custom configuration to the HTTP client.

This function allows customization of the HTTP client's settings, such as timeout duration, connection pooling, keep-alive behavior, and TLS configuration.

/Parameters:

  • cfg (*HTTPClientConfig): Pointer to a struct containing the desired configuration settings.

/Return:

  • Option: A function that applies the custom HTTP configuration to the client.

Example Usage:

client := New(WithHTTPClientConfig(&HTTPClientConfig{
    Timeout: 10 * time.Second,
    MaxIdleConns: 20,
}))

func WithHeaders

func WithHeaders(headers map[string]string) Option

WithHeaders sets custom headers for the client.

This function defines default headers that will be included in every request made by the client. Existing headers with the same key will be overwritten.

/Parameters:

  • headers (map[string]string): A map containing key-value pairs of headers.

/Return:

  • Option: A functional option that sets the default headers.

Example Usage:

client := New(WithHeaders(map[string]string{"Authorization": "Bearer token"}))
Example

ExampleWithHeaders demonstrates how to use the Client with custom headers.

// Create a test HTTP server that checks for a custom header.
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	if r.Header.Get("X-Custom-Header") == "GoLang" {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("Header received"))
		return
	}
	w.WriteHeader(http.StatusBadRequest)
}))
defer ts.Close()

// Initialize a client with a custom header.
client := New(WithHeaders(map[string]string{"X-Custom-Header": "GoLang"}))

// Perform a GET request.
resp, err := client.Get(ts.URL)
if err != nil {
	fmt.Println("Error:", err)
	return
}
fmt.Println(string(resp.Body))
Output:

Header received

func WithInsecureTLS

func WithInsecureTLS(insecure bool) Option

WithInsecureTLS enables or disables TLS certificate verification.

This function modifies the TLS configuration of the HTTP client, allowing insecure connections by skipping certificate verification. This is useful for testing environments but should not be used in production due to security risks.

/Parameters:

  • insecure (bool): If true, certificate verification is disabled.

/Return:

  • Option: A functional option that configures the client's TLS settings.

Example Usage:

client := New(WithInsecureTLS(true))
Example

ExampleWithInsecureTLS demonstrates how to use the Client with insecure TLS enabled.

// Create a test TLS server that returns a response for HTTPS requests.
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("Insecure TLS OK"))
}))
defer ts.Close()

// Initialize a client with insecure TLS verification enabled.
client := New(WithInsecureTLS(true))

// Perform a GET request to the TLS test server.
resp, err := client.Get(ts.URL)
if err != nil {
	fmt.Println("Error:", err)
	return
}
fmt.Println(string(resp.Body))
Output:

Insecure TLS OK

func WithLogger

func WithLogger(enable bool) Option

WithLogger enables or disables logging for the HTTP client.

When enabled, the client will log request details, response statuses, and errors using the structured logging system provided by `slog.Logger`.

/Parameters:

  • enable (bool): If true, enables logging. If false, disables it.

/Return:

  • Option: A function that enables or disables logging in the client.

Example Usage:

client := New(WithLogger(true))
Example

ExampleWithLogger demonstrates how to use the Client with logging enabled.

// Create a log buffer to capture logs.
var logBuffer bytes.Buffer
fixedTime := time.Date(2025, 3, 28, 15, 0, 0, 0, time.FixedZone("-03", -3*60*60))

//Handler with ReplaceAttr to overwrite timestamp
handler := slog.NewTextHandler(&logBuffer, &slog.HandlerOptions{
	Level: slog.LevelInfo,
	ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
		if a.Key == slog.TimeKey {
			return slog.String("time", fixedTime.Format(time.RFC3339))
		}
		return a
	},
})

logger := slog.New(handler)

// Initialize a client with logging enabled.
client := New(WithLogger(true))
client.Logger = logger

// Perform a log entry.
client.Log("Testing logging")

// Print captured log.
fmt.Println(logBuffer.String())
Output:

time=2025-03-28T15:00:00-03:00 level=INFO msg="Testing logging"

func WithMaxConnsPerHost

func WithMaxConnsPerHost(max int) Option

WithMaxConnsPerHost sets the maximum concurrent connections per host.

This function limits the number of concurrent TCP connections to a single host. It helps control resource allocation when making multiple requests.

/Parameters:

  • max (int): The maximum number of concurrent connections per host.

/Return:

  • Option: A functional option that configures the client's transport.

Example Usage:

client := New(WithMaxConnsPerHost(10))
Example

ExampleWithMaxConnsPerHost demonstrates how to set the maximum number of concurrent connections per host for the HTTP client. This function is named ExampleWithMaxConnsPerHost() it with the Examples type.

// Create a test HTTP server.
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("OK"))
}))
defer ts.Close()

// Initialize a client with a custom maximum number of connections per host.
client := New(WithMaxConnsPerHost(10))

// Perform a GET request.
_, err := client.Get(ts.URL)
if err != nil {
	fmt.Println("Error:", err)
	return
}

// Confirm the setting (for example purposes, normally you might not print this).
fmt.Println("MaxConnsPerHost set to 10")
Output:

MaxConnsPerHost set to 10

func WithMaxIdleConns

func WithMaxIdleConns(max int) Option

WithMaxIdleConns sets the maximum number of idle connections.

This function configures the maximum number of idle (persistent) connections the HTTP client can maintain. Increasing this value can improve performance for frequent requests to the same server.

/Parameters:

  • max (int): The maximum number of idle connections.

/Return:

  • Option: A functional option that configures the client's transport.

Example Usage:

client := New(WithMaxIdleConns(50))
Example

ExampleWithMaxIdleConns demonstrates how to configure the maximum number of idle connections in the HTTP client. This function is named ExampleWithMaxIdleConns() it with the Examples type.

// Create a test HTTP server.
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("OK"))
}))
defer ts.Close()

// Initialize a client with a custom maximum number of idle connections.
client := New(WithMaxIdleConns(20))

// Perform a GET request.
_, err := client.Get(ts.URL)
if err != nil {
	fmt.Println("Error:", err)
	return
}

// You can print something related to the configuration to avoid the no output issue, or simply avoid the output comment
fmt.Println("MaxIdleConns set to 20")
Output:

MaxIdleConns set to 20

func WithMaxIdleConnsPerHost

func WithMaxIdleConnsPerHost(max int) Option

WithMaxIdleConnsPerHost sets the maximum idle connections per host.

This function defines the maximum number of idle (reusable) connections that the client can maintain per host.

/Parameters:

  • max (int): The maximum number of idle connections per host.

/Return:

  • Option: A functional option that configures the client's transport.

Example Usage:

client := New(WithMaxIdleConnsPerHost(5))

func WithRetry

func WithRetry(cfg RetryConfig) Option

WithRetry configures automatic request retries.

Adds retry behavior to the HTTP client, allowing it to attempt a request multiple times if it fails due to certain conditions.

/Parameters:

  • cfg (RetryConfig): Configuration specifying retry conditions.

/Return:

  • Option: A functional option that can be applied to a Client.
Example

ExampleWithRetry demonstrates how to use the Client with retry logic.

// Create a test HTTP server that fails twice before succeeding.
attempts := 0
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	attempts++
	if attempts < 3 {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("Retry succeeded"))
}))
defer ts.Close()

// Initialize a client with retry logic enabled.
client := New(WithRetry(RetryConfig{
	MaxRetries: 3,
	Delay:      500 * time.Millisecond,
	UseBackoff: true,
	Statuses:   []int{http.StatusInternalServerError},
	EnableLog:  false,
}))

// Perform a GET request to the test server.
resp, err := client.Get(ts.URL)
if err != nil {
	fmt.Println("Error:", err)
	return
}
fmt.Println(string(resp.Body))
Output:

Retry succeeded

func WithTLSConfig

func WithTLSConfig(tlsConfig *tls.Config) Option

WithTLSConfig applies a custom TLS configuration.

/Parameters:

  • tlsConfig (*tls.Config): Custom TLS settings.

/Return:

  • Option: A functional option for setting the TLS configuration.
Example

ExampleWithTLSConfig demonstrates how to use the Client with a custom TLS configuration.

// Create a test TLS server.
ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("Secure TLS OK"))
}))
defer ts.Close()

// Custom TLS configuration that allows insecure certificates (for testing purposes).
tlsConfig := &tls.Config{InsecureSkipVerify: true}

// Initialize a client with the custom TLS configuration.
client := New(WithTLSConfig(tlsConfig))

// Perform a GET request to the TLS test server.
resp, err := client.Get(ts.URL)
if err != nil {
	fmt.Println("Error:", err)
	return
}
fmt.Println(string(resp.Body))
Output:

Secure TLS OK

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout sets the timeout for HTTP requests.

/Parameters:

  • d (time.Duration): The maximum duration before a request times out.

/Return:

  • Option: A functional option for setting the timeout.
Example

ExampleWithTimeout demonstrates how to use the Client with a custom timeout.

// Create a test HTTP server that delays its response.
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	time.Sleep(100 * time.Millisecond)
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("Delayed response"))
}))
defer ts.Close()

// Initialize a client with a 50ms timeout.
client := New(WithTimeout(50 * time.Millisecond))

// Perform a GET request (expected to timeout).
_, err := client.Get(ts.URL)
if err != nil {
	fmt.Println("Request timed out")
} else {
	fmt.Println("Request succeeded")
}
Output:

Request timed out

func WithTransport

func WithTransport(transport http.RoundTripper) Option

WithTransport sets a custom HTTP transport.

This function allows users to set a custom `http.RoundTripper` transport for the client, which is useful for scenarios requiring advanced transport configurations, such as proxies or custom connection handling.

/Parameters:

  • transport (http.RoundTripper): The custom transport to use.

/Return:

  • Option: A functional option that configures the client’s transport.

Example Usage:

client := New(WithTransport(http.DefaultTransport))
Example

ExampleWithTransport demonstrates how to use the Client with a custom transport.

// Create a test HTTP server.
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("Custom Transport OK"))
}))
defer ts.Close()

// Create a custom transport.
customTransport := &http.Transport{MaxIdleConns: 100}

// Initialize a client with the custom transport.
client := New(WithTransport(customTransport))

// Perform a GET request.
resp, err := client.Get(ts.URL)
if err != nil {
	fmt.Println("Error:", err)
	return
}
fmt.Println(string(resp.Body))
Output:

Custom Transport OK

func WithTransportConfig

func WithTransportConfig(tr *http.Transport) Option

WithTransportConfig applies a pre-configured HTTP transport.

This function sets a pre-configured `http.Transport` for the client, allowing customization of low-level transport options such as connection pooling and timeouts.

/Parameters:

  • tr (*http.Transport): The pre-configured HTTP transport.

/Return:

  • Option: A functional option that applies the transport settings.

Example Usage:

transport := &http.Transport{MaxIdleConns: 100}
client := New(WithTransportConfig(transport))
Example

ExampleWithTransportConfig demonstrates how to use the Client with a custom transport configuration.

// Create a test HTTP server.
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("Configured Transport OK"))
}))
defer ts.Close()

// Pre-configured transport.
preConfiguredTransport := &http.Transport{MaxConnsPerHost: 50}

// Initialize a client with the pre-configured transport.
client := New(WithTransportConfig(preConfiguredTransport))

// Perform a GET request.
resp, err := client.Get(ts.URL)
if err != nil {
	fmt.Println("Error:", err)
	return
}
fmt.Println(string(resp.Body))
Output:

Configured Transport OK

type RetryConfig

type RetryConfig struct {
	MaxRetries   int           // Maximum number of retry attempts
	Delay        time.Duration // Base delay between retries
	UseBackoff   bool          // Enable exponential backoff
	Statuses     []int         // HTTP status codes that trigger retry
	FailoverURLs []string      // Alternative URLs for failover
	EnableLog    bool          // Enable logging for retry attempts
}

RetryConfig defines configuration parameters for retry logic

type RetryTransport

type RetryTransport struct {
	Base          http.RoundTripper // Base transport implementation
	MaxRetries    int               // Maximum retry attempts
	RetryDelay    time.Duration     // Delay between retries
	UseBackoff    bool              // Use exponential backoff
	RetryStatuses []int             // Status codes triggering retry
	Logger        *slog.Logger      // Logger instance
	EnableLogger  bool              // Enable logging
	FailoverURLs  []string          // Alternative URLs for failover
}

RetryTransport implements http.RoundTripper with retry and failover logic

func (*RetryTransport) RoundTrip

func (rt *RetryTransport) RoundTrip(req *http.Request) (*http.Response, error)

RoundTrip executes an HTTP request with retry and failover support.

This function implements the http.RoundTripper interface and retries requests based on configured conditions.

/Parameters:

  • req (*http.Request): The HTTP request to send.

/Return:

  • *http.Response: The HTTP response from the server.
  • error: If all retry attempts fail, an error is returned.

Jump to

Keyboard shortcuts

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