Documentation
¶
Overview ¶
Package client provides a high-performance HTTP/1.1 client with pipelining, per-host connection pooling, object reuse, retry, DNS caching, metrics, streaming, proxy support, and graceful shutdown. Zero external dependencies.
Create a client with NewClient and a Config (or use DefaultConfig, HighThroughputConfig, ResilientConfig presets). Use convenience methods like Client.Get and Client.Post for common operations, or the low-level Client.Do / Client.DoWithContext for full control with pooled requests.
cfg := client.DefaultConfig()
cfg.Host = "api.example.com"
cfg.Port = 443
cfg.UseTLS = true
c, err := client.NewClient(cfg)
if err != nil {
log.Fatal(err)
}
defer c.Stop()
resp, err := c.Get("/v1/users", nil)
if err != nil {
log.Fatal(err)
}
defer c.ReleaseResponse(resp)
Index ¶
- Constants
- Variables
- func GetVersion() string
- func IsRetryable(err error) bool
- func IsTimeout(err error) bool
- func LogErrorWithFlag(errType ErrorType, message string, err error, context map[string]interface{}, ...) error
- func ReleaseRequest(req *Request)
- func ReleaseResponse(resp *Response)
- func WrapError(errType ErrorType, message string, err error) error
- type BuiltinMetrics
- func (m *BuiltinMetrics) RecordPoolEvent(event PoolEvent, host string)
- func (m *BuiltinMetrics) RecordRequest(method, host string) time.Time
- func (m *BuiltinMetrics) RecordResponse(method, host string, statusCode int, err error, start time.Time, ...)
- func (m *BuiltinMetrics) RecordRetry()
- func (m *BuiltinMetrics) Snapshot() MetricsSnapshot
- type Client
- func (c *Client) AcquireRequest() *Request
- func (c *Client) BuildPreEncodedHeaderPrefix(req *Request, host string, port int, useTLS bool) ([]byte, error)
- func (c *Client) Delete(path string, headers []Header) (*Response, error)
- func (c *Client) Do(req *Request) (*Response, error)
- func (c *Client) DoReader(ctx context.Context, method, urlOrPath string, bodyReader io.Reader, ...) (*Response, error)
- func (c *Client) DoStreaming(ctx context.Context, req *Request) (*StreamingResponse, error)
- func (c *Client) DoWithContext(ctx context.Context, req *Request) (*Response, error)
- func (c *Client) Get(path string, headers []Header) (*Response, error)
- func (c *Client) GetHealthyConnections() int
- func (c *Client) GetURL(rawURL string, headers []Header) (*Response, error)
- func (c *Client) GracefulStop(timeout time.Duration) bool
- func (c *Client) Head(path string, headers []Header) (*Response, error)
- func (c *Client) Options(path string, headers []Header) (*Response, error)
- func (c *Client) Patch(path string, body []byte, headers []Header) (*Response, error)
- func (c *Client) Post(path string, body []byte, headers []Header) (*Response, error)
- func (c *Client) PostURL(rawURL string, body []byte, headers []Header) (*Response, error)
- func (c *Client) Put(path string, body []byte, headers []Header) (*Response, error)
- func (c *Client) ReleaseRequest(req *Request)
- func (c *Client) ReleaseResponse(resp *Response)
- func (c *Client) Start() error
- func (c *Client) StartN(n int) error
- func (c *Client) Stats() ClientStats
- func (c *Client) Stop()
- type ClientStats
- type Compressor
- type Config
- type Connection
- type DNSCache
- type DetailedError
- type Dialer
- type ErrorType
- type Header
- type HostPool
- type MetricsCollector
- type MetricsSnapshot
- type MultipartBuilder
- func (mb *MultipartBuilder) AddField(name, value string) error
- func (mb *MultipartBuilder) AddFileFromBytes(fieldName, fileName string, data []byte) error
- func (mb *MultipartBuilder) AddFileFromPath(fieldName, filePath string) error
- func (mb *MultipartBuilder) AddFileFromReader(fieldName, fileName string, reader io.Reader) error
- func (mb *MultipartBuilder) ContentType() string
- func (mb *MultipartBuilder) Finish() (body []byte, contentType string, err error)
- type PendingRequest
- type Pool
- type PoolEvent
- type ProxyDialer
- type Request
- func (r *Request) Context() context.Context
- func (r *Request) Reset()
- func (r *Request) SetBody(body []byte)
- func (r *Request) SetContext(ctx context.Context)
- func (r *Request) SetHeader(key, value string) bool
- func (r *Request) WithBody(body []byte) *Request
- func (r *Request) WithCompression() *Request
- func (r *Request) WithContext(ctx context.Context) *Request
- func (r *Request) WithExpectContinue() *Request
- func (r *Request) WithHeader(key, value string) *Request
- func (r *Request) WithMethod(method string) *Request
- func (r *Request) WithPath(path string) *Request
- func (r *Request) WithReadTimeout(d time.Duration) *Request
- func (r *Request) WithURL(rawURL string) *Request
- func (r *Request) WithWriteTimeout(d time.Duration) *Request
- type Response
- type ResponseReader
- type Retryer
- type SOCKS5Dialer
- type Scheduler
- type StreamingResponse
- type TLSConfig
Examples ¶
- BuiltinMetrics.Snapshot
- Client.DoWithContext
- Client.Get
- Client.GracefulStop
- Client.Post
- Client.Stats
- DefaultConfig
- GetVersion
- HighThroughputConfig
- IsRetryable
- IsTimeout
- MultipartBuilder
- NewBuiltinMetrics
- NewClient
- NewDNSCache
- NewRetryer
- Request (Fluent)
- ResilientConfig
- Response.Header
- Response.HeaderValues
Constants ¶
const ( Version = "v0.1.4" VersionMajor = 0 VersionMinor = 1 VersionPatch = 4 VersionPreRelease = "" )
Variables ¶
var ( ErrConnectFailed = errors.New("connect failed") ErrWriteFailed = errors.New("write failed") ErrReadFailed = errors.New("read failed") ErrHeaderTooLarge = errors.New("response header too large") ErrInvalidResponse = errors.New("invalid response") ErrResponseTooLarge = errors.New("response too large") ErrRequestTooLarge = errors.New("request body too large") ErrHeaderBufferSmall = errors.New("header buffer too small") ErrTimeout = errors.New("operation timeout") ErrConnectionClosed = errors.New("connection closed") ErrProxyFailed = errors.New("proxy connection failed") ErrInvalidURL = errors.New("invalid URL") ErrInvalidRequest = errors.New("invalid request") )
Pre-allocated sentinel errors returned by the library. Use IsTimeout and IsRetryable to classify errors without direct comparison.
Functions ¶
func GetVersion ¶
func GetVersion() string
GetVersion returns the library version.
Example ¶
package main
import (
"fmt"
client "github.com/muxover/bursthttp"
)
func main() {
v := client.GetVersion()
fmt.Printf("starts with v: %v\n", v[0] == 'v')
}
Output: starts with v: true
func IsRetryable ¶
IsRetryable checks if an error is retryable.
Example ¶
package main
import (
"fmt"
client "github.com/muxover/bursthttp"
)
func main() {
fmt.Println(client.IsRetryable(client.ErrConnectFailed))
fmt.Println(client.IsRetryable(client.ErrInvalidURL))
}
Output: true false
func IsTimeout ¶
IsTimeout checks if an error is a timeout error. It handles net.Error timeouts, context.DeadlineExceeded, ErrTimeout, and DetailedError with ErrorTypeTimeout — including wrapped errors.
Example ¶
package main
import (
"fmt"
client "github.com/muxover/bursthttp"
)
func main() {
err := client.ErrTimeout
fmt.Println(client.IsTimeout(err))
fmt.Println(client.IsTimeout(nil))
}
Output: true false
func LogErrorWithFlag ¶
func LogErrorWithFlag(errType ErrorType, message string, err error, context map[string]interface{}, enableLogging bool) error
LogErrorWithFlag logs a detailed error with type and context if enableLogging is true.
func ReleaseRequest ¶
func ReleaseRequest(req *Request)
ReleaseRequest returns req to the global pool.
func ReleaseResponse ¶
func ReleaseResponse(resp *Response)
ReleaseResponse returns a response to the pool.
Types ¶
type BuiltinMetrics ¶
type BuiltinMetrics struct {
// contains filtered or unexported fields
}
BuiltinMetrics is a thread-safe in-memory MetricsCollector included with bursthttp. It provides request counts, latency percentiles, byte counters, and connection pool statistics.
cfg.Metrics = bursthttp.NewBuiltinMetrics()
func NewBuiltinMetrics ¶
func NewBuiltinMetrics() *BuiltinMetrics
NewBuiltinMetrics creates a ready-to-use BuiltinMetrics instance.
Example ¶
package main
import (
"fmt"
client "github.com/muxover/bursthttp"
)
func main() {
m := client.NewBuiltinMetrics()
cfg := client.DefaultConfig()
cfg.Metrics = m
fmt.Println("metrics configured")
}
Output: metrics configured
func (*BuiltinMetrics) RecordPoolEvent ¶
func (m *BuiltinMetrics) RecordPoolEvent(event PoolEvent, host string)
RecordPoolEvent implements MetricsCollector.
func (*BuiltinMetrics) RecordRequest ¶
func (m *BuiltinMetrics) RecordRequest(method, host string) time.Time
RecordRequest implements MetricsCollector.
func (*BuiltinMetrics) RecordResponse ¶
func (m *BuiltinMetrics) RecordResponse(method, host string, statusCode int, err error, start time.Time, bytesWritten, bytesRead int64)
RecordResponse implements MetricsCollector.
func (*BuiltinMetrics) RecordRetry ¶
func (m *BuiltinMetrics) RecordRetry()
RecordRetry increments the retry counter. Called by the retry loop.
func (*BuiltinMetrics) Snapshot ¶
func (m *BuiltinMetrics) Snapshot() MetricsSnapshot
Snapshot implements MetricsCollector.
Example ¶
package main
import (
"fmt"
client "github.com/muxover/bursthttp"
)
func main() {
m := client.NewBuiltinMetrics()
start := m.RecordRequest("GET", "example.com")
m.RecordResponse("GET", "example.com", 200, nil, start, 50, 1024)
m.RecordPoolEvent(client.PoolEventConnCreated, "example.com")
snap := m.Snapshot()
fmt.Printf("total: %d, ok: %d\n", snap.RequestsTotal, snap.RequestsOK)
fmt.Printf("bytes written: %d, read: %d\n", snap.BytesWritten, snap.BytesRead)
fmt.Printf("conns created: %d\n", snap.ConnsCreated)
}
Output: total: 1, ok: 1 bytes written: 50, read: 1024 conns created: 1
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is the main HTTP client. Safe for concurrent use.
A single Client is designed for requests to one primary host (configured via Config.Host / Config.Port). For multi-host usage, set Request.URL and the client will route to the correct host pool automatically.
func NewClient ¶
NewClient creates a new HTTP client with the given configuration. Pass nil to use DefaultConfig.
Example ¶
package main
import (
"fmt"
"log"
client "github.com/muxover/bursthttp"
)
func main() {
cfg := client.DefaultConfig()
cfg.Host = "httpbin.org"
cfg.Port = 443
cfg.UseTLS = true
c, err := client.NewClient(cfg)
if err != nil {
log.Fatal(err)
}
defer c.Stop()
fmt.Println("client created")
}
Output: client created
func (*Client) AcquireRequest ¶
AcquireRequest gets a Request from the client's pool.
func (*Client) BuildPreEncodedHeaderPrefix ¶ added in v0.1.2
func (c *Client) BuildPreEncodedHeaderPrefix(req *Request, host string, port int, useTLS bool) ([]byte, error)
BuildPreEncodedHeaderPrefix encodes the request line and headers (without Content-Length) for the given host/port/useTLS. The returned slice may be stored on Request.PreEncodedHeaderPrefix to send multiple requests with the same headers without re-encoding. Only the body may change between sends. The request must have Method and Path (or URL) set; host and port should match the target used when sending (e.g. from Config or from URL parsing).
func (*Client) DoReader ¶
func (c *Client) DoReader(ctx context.Context, method, urlOrPath string, bodyReader io.Reader, contentLength int64, headers []Header) (*Response, error)
DoReader executes a request whose body is read from an io.Reader. contentLength must be the exact number of bytes that bodyReader will produce.
func (*Client) DoStreaming ¶
DoStreaming executes a request and returns a StreamingResponse whose Body is an io.ReadCloser. The caller MUST call StreamingResponse.Close() when done to return the underlying response to the pool.
func (*Client) DoWithContext ¶
DoWithContext executes an HTTP request with the given context. If retries are configured, transient failures are retried automatically.
Example ¶
package main
import (
"context"
"fmt"
"log"
"time"
client "github.com/muxover/bursthttp"
)
func main() {
cfg := client.DefaultConfig()
cfg.Host = "httpbin.org"
cfg.Port = 443
cfg.UseTLS = true
c, err := client.NewClient(cfg)
if err != nil {
log.Fatal(err)
}
defer c.Stop()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
req := c.AcquireRequest()
defer c.ReleaseRequest(req)
req.WithMethod("GET").
WithURL("https://httpbin.org/get").
WithHeader("Accept", "application/json")
resp, err := c.DoWithContext(ctx, req)
if err != nil {
log.Fatal(err)
}
defer c.ReleaseResponse(resp)
fmt.Printf("status: %d\n", resp.StatusCode)
}
Output:
func (*Client) Get ¶
Get performs a GET request to path using the configured host.
Example ¶
package main
import (
"fmt"
"log"
client "github.com/muxover/bursthttp"
)
func main() {
cfg := client.DefaultConfig()
cfg.Host = "httpbin.org"
cfg.Port = 443
cfg.UseTLS = true
c, err := client.NewClient(cfg)
if err != nil {
log.Fatal(err)
}
defer c.Stop()
resp, err := c.Get("/get", nil)
if err != nil {
log.Fatal(err)
}
defer c.ReleaseResponse(resp)
fmt.Printf("status: %d\n", resp.StatusCode)
}
Output:
func (*Client) GetHealthyConnections ¶
GetHealthyConnections returns the total number of healthy pooled connections.
func (*Client) GracefulStop ¶
GracefulStop waits for in-flight requests to complete (up to timeout) before closing all connections. Returns true if all requests drained.
Example ¶
package main
import (
"fmt"
"log"
"time"
client "github.com/muxover/bursthttp"
)
func main() {
cfg := client.DefaultConfig()
cfg.Host = "httpbin.org"
cfg.Port = 443
cfg.UseTLS = true
c, err := client.NewClient(cfg)
if err != nil {
log.Fatal(err)
}
drained := c.GracefulStop(5 * time.Second)
fmt.Printf("drained: %v\n", drained)
}
Output: drained: true
func (*Client) Post ¶
Post performs a POST request to path with the given body.
Example ¶
package main
import (
"fmt"
"log"
client "github.com/muxover/bursthttp"
)
func main() {
cfg := client.DefaultConfig()
cfg.Host = "httpbin.org"
cfg.Port = 443
cfg.UseTLS = true
c, err := client.NewClient(cfg)
if err != nil {
log.Fatal(err)
}
defer c.Stop()
resp, err := c.Post("/post", []byte(`{"key":"value"}`), []client.Header{
{Key: "Content-Type", Value: "application/json"},
})
if err != nil {
log.Fatal(err)
}
defer c.ReleaseResponse(resp)
fmt.Printf("status: %d\n", resp.StatusCode)
}
Output:
func (*Client) ReleaseRequest ¶
ReleaseRequest returns a Request to the client's pool.
func (*Client) ReleaseResponse ¶
ReleaseResponse returns a Response to the client's pool. Always call this after processing a response to avoid memory leaks.
func (*Client) Start ¶
Start pre-establishes connections to the primary host. The number of connections created is min(n, PoolSize) where n defaults to PoolSize/4 when no argument is given. Returns the number of connections established. Calling Start is optional — connections are created lazily on first request when Start is not called.
func (*Client) Stats ¶
func (c *Client) Stats() ClientStats
Stats returns a snapshot of pool and metrics state.
Example ¶
package main
import (
"fmt"
"log"
client "github.com/muxover/bursthttp"
)
func main() {
m := client.NewBuiltinMetrics()
cfg := client.DefaultConfig()
cfg.Host = "httpbin.org"
cfg.Port = 443
cfg.UseTLS = true
cfg.Metrics = m
c, err := client.NewClient(cfg)
if err != nil {
log.Fatal(err)
}
defer c.Stop()
stats := c.Stats()
fmt.Printf("healthy: %d, total requests: %d\n", stats.HealthyConnections, stats.Metrics.RequestsTotal)
}
Output: healthy: 0, total requests: 0
type ClientStats ¶
type ClientStats struct {
HealthyConnections int
Metrics MetricsSnapshot
}
ClientStats holds a point-in-time snapshot of client state.
type Compressor ¶
type Compressor struct {
// contains filtered or unexported fields
}
Compressor manages a pool of gzip writers for request body compression.
func NewCompressor ¶
func NewCompressor(level int) *Compressor
NewCompressor creates a new compressor with the specified compression level.
func (*Compressor) CompressInto ¶
func (c *Compressor) CompressInto(dst []byte, src []byte) ([]byte, error)
CompressInto compresses data into the provided buffer.
type Config ¶
type Config struct {
Host string
Port int
UseTLS bool
PoolSize int
MaxRequestsPerConn int
IdleTimeout time.Duration
DialTimeout time.Duration
ReadTimeout time.Duration
WriteTimeout time.Duration
TLSHandshakeTimeout time.Duration
ReadBufferSize int
WriteBufferSize int
HeaderBufferSize int
HeaderReadChunkSize int
BodyReadChunkSize int
MaxRequestBodySize int
MaxResponseBodySize int
EnableCompression bool
CompressionLevel int
TLSConfig *tls.Config
TLSMinVersion uint16
TLSMaxVersion uint16
InsecureSkipVerify bool
TLSClientSessionCacheSize int
ProxyURL string
ProxyUsername string
ProxyPassword string
ProxyConnectTimeout time.Duration
ProxyReadTimeout time.Duration
SOCKS5Addr string // Takes precedence over ProxyURL.
SOCKS5Username string
SOCKS5Password string
KeepAlive bool
DisableKeepAlive bool
MaxIdleConnsPerHost int
EnablePipelining bool
MaxPipelinedRequests int
MaxRetries int
RetryBaseDelay time.Duration
RetryMaxDelay time.Duration
RetryMultiplier float64
RetryJitter bool
RetryableStatus []int
EnableDNSCache bool
DNSCacheTTL time.Duration
DNSNegativeTTL time.Duration // how long to cache failed lookups (default 5s)
// Scheduler: opt-in request scheduler for stable latency under load.
// When true, DoWithContext routes through per-host worker queues.
EnableScheduler bool
SchedulerWorkers int // workers per host (default: PoolSize)
SchedulerQueueDepth int // max queued requests per host (default: workers*4)
// Connection health scoring: track per-connection latency EWMA and error
// rate; prefer lower-latency connections during pool selection.
// Enabled by default.
EnableHealthScoring bool
IdleCheckInterval time.Duration
EnableResponseStreaming bool
TCPNoDelay bool
TCPKeepAlive bool
TCPKeepAlivePeriod time.Duration
TCPFastOpen bool // Linux only: TCP Fast Open (TFO)
TCPReusePort bool // Linux only: SO_REUSEPORT
// Pipeline auto-tuning: dynamically adjusts pipeline depth per connection
// based on measured round-trip latency. <10ms → grow; >100ms → shrink.
EnablePipelineAutoTune bool
EnableLogging bool
// Set to a MetricsCollector implementation to receive per-request telemetry.
// nil disables metrics (default).
Metrics MetricsCollector
}
Config holds all configuration for the HTTP client. All fields are read-only after initialization for thread safety.
func DefaultConfig ¶
func DefaultConfig() *Config
DefaultConfig returns a default configuration optimized for high performance.
Example ¶
package main
import (
"fmt"
client "github.com/muxover/bursthttp"
)
func main() {
cfg := client.DefaultConfig()
fmt.Printf("host: %s, pool: %d, pipelining: %v\n", cfg.Host, cfg.PoolSize, cfg.EnablePipelining)
}
Output: host: localhost, pool: 512, pipelining: true
func HighThroughputConfig ¶
func HighThroughputConfig() *Config
HighThroughputConfig returns a config tuned for sustained high RPS. Use this for large-scale workloads (100K+ RPS) with adequate hardware.
Example ¶
package main
import (
"fmt"
client "github.com/muxover/bursthttp"
)
func main() {
cfg := client.HighThroughputConfig()
cfg.Host = "api.example.com"
cfg.Port = 443
cfg.UseTLS = true
fmt.Printf("pool size: %d, pipelining: %v\n", cfg.PoolSize, cfg.EnablePipelining)
}
Output: pool size: 1024, pipelining: true
func ResilientConfig ¶
func ResilientConfig() *Config
ResilientConfig returns a config with retry and resilience features enabled.
Example ¶
package main
import (
"fmt"
client "github.com/muxover/bursthttp"
)
func main() {
cfg := client.ResilientConfig()
fmt.Printf("retries: %d, base delay: %v\n", cfg.MaxRetries, cfg.RetryBaseDelay)
}
Output: retries: 3, base delay: 200ms
func (*Config) BuildTLSConfig ¶
BuildTLSConfig creates a TLS config from the configuration.
type Connection ¶
type Connection struct {
// contains filtered or unexported fields
}
Connection owns a single TCP/TLS connection with optional pipelining.
func NewConnection ¶
func NewConnection(id int, host string, port int, config *Config, dialer *Dialer, tlsConfig *TLSConfig, compressor *Compressor) *Connection
NewConnection creates a Connection. TCP is not established until first use.
func (*Connection) CanAcceptRequest ¶
func (c *Connection) CanAcceptRequest() bool
CanAcceptRequest returns true when the connection can take another request.
func (*Connection) Do ¶
func (c *Connection) Do(req *Request) (*Response, error)
Do executes a request on this connection.
func (*Connection) HealthScore ¶ added in v0.1.3
func (c *Connection) HealthScore() int32
HealthScore returns the current health score (0–100, higher is better). Returns 100 for new connections with no data yet.
func (*Connection) IsHealthy ¶
func (c *Connection) IsHealthy() bool
IsHealthy returns true if the connection is alive and not stopped.
type DNSCache ¶
type DNSCache struct {
// contains filtered or unexported fields
}
DNSCache is a thread-safe, TTL-based DNS resolution cache with async refresh, in-flight deduplication, round-robin IP selection, and negative caching.
func NewDNSCache ¶
NewDNSCache creates a new DNS cache with the given TTL and starts background prefetch and janitor goroutines.
Example ¶
package main
import (
"fmt"
"log"
"time"
client "github.com/muxover/bursthttp"
)
func main() {
cache := client.NewDNSCache(5*time.Minute, 5*time.Second)
defer cache.Stop()
addrs, err := cache.LookupHost("localhost")
if err != nil {
log.Fatal(err)
}
fmt.Printf("resolved: %v\n", len(addrs) > 0)
}
Output: resolved: true
func (*DNSCache) Invalidate ¶
Invalidate removes a host from the cache.
func (*DNSCache) LookupHost ¶
LookupHost resolves a hostname to an IP address using the cache. Multiple concurrent misses for the same host share one lookup (singleflight). Returns a single IP selected via round-robin when multiple IPs are cached.
type DetailedError ¶
type DetailedError struct {
Type ErrorType
Message string
Err error
Context map[string]interface{}
}
DetailedError wraps an error with type and context for detailed logging.
func (*DetailedError) Error ¶
func (e *DetailedError) Error() string
Error implements the error interface.
func (*DetailedError) Unwrap ¶
func (e *DetailedError) Unwrap() error
Unwrap returns the underlying error.
type Dialer ¶
type Dialer struct {
// contains filtered or unexported fields
}
Dialer handles TCP connection establishment with optimised socket settings.
func (*Dialer) Dial ¶
Dial establishes a connection to the given address ("host:port"). Falls back to config Host/Port when address is empty.
func (*Dialer) DialAddr ¶
DialAddr establishes a connection to host:port. When host is empty, d.config.Host is used; when port is 0, d.config.Port is used.
func (*Dialer) DialForward ¶ added in v0.1.1
DialForward connects to the proxy's TCP address without issuing a CONNECT request, then applies the same socket options as DialAddr. Used when IsForwardProxy returns true.
func (*Dialer) IsForwardProxy ¶ added in v0.1.1
IsForwardProxy reports whether HTTP requests to non-TLS targets should be sent via forward-proxy mode (absolute URI + Proxy-Authorization), rather than through an HTTP CONNECT tunnel.
func (*Dialer) ProxyAuthHeader ¶ added in v0.1.1
ProxyAuthHeader returns the pre-encoded "Proxy-Authorization: Basic …\r\n" header bytes, or nil when no proxy credentials are configured.
type ErrorType ¶
type ErrorType string
ErrorType represents the category of error for logging and handling.
type HostPool ¶
type HostPool struct {
// contains filtered or unexported fields
}
HostPool manages connections for a single scheme+host+port key. connections is updated with atomic.Pointer for lock-free get path.
type MetricsCollector ¶
type MetricsCollector interface {
// RecordRequest is called when a request starts. Returns a timestamp
// that should be passed to RecordResponse for latency tracking.
RecordRequest(method, host string) time.Time
// RecordResponse is called after every completed request.
// statusCode is 0 if the request failed before a response was received.
// err is non-nil when the request failed.
// start is the timestamp returned by RecordRequest.
// bytesWritten and bytesRead are wire-level byte counts.
RecordResponse(method, host string, statusCode int, err error, start time.Time, bytesWritten, bytesRead int64)
// RecordPoolEvent is called for connection pool events.
RecordPoolEvent(event PoolEvent, host string)
// Snapshot returns a point-in-time copy of collected metrics.
Snapshot() MetricsSnapshot
}
MetricsCollector is the interface for receiving request/response events. Implement it and set Config.Metrics to plug in any metrics backend (Prometheus, statsd, in-memory, etc.).
All methods must be safe for concurrent use.
type MetricsSnapshot ¶
type MetricsSnapshot struct {
RequestsTotal int64
RequestsOK int64
RequestsError int64
Requests4xx int64
Requests5xx int64
Requests1xx int64
Requests3xx int64
RetriesTotal int64
BytesWritten int64
BytesRead int64
LatencyP50 time.Duration
LatencyP95 time.Duration
LatencyP99 time.Duration
LatencyAvg time.Duration
LatencyMin time.Duration
LatencyMax time.Duration
ConnsCreated int64
ConnsClosed int64
ConnsReused int64
ConnsFailed int64
}
MetricsSnapshot is a point-in-time copy of collected metric values.
type MultipartBuilder ¶
type MultipartBuilder struct {
// contains filtered or unexported fields
}
MultipartBuilder constructs a multipart/form-data body incrementally. Use NewMultipartBuilder, add fields/files, then call Finish to get the body bytes and Content-Type header value.
Example ¶
package main
import (
"fmt"
"log"
client "github.com/muxover/bursthttp"
)
func main() {
mb := client.NewMultipartBuilder()
mb.AddField("username", "alice")
mb.AddField("email", "alice@example.com")
mb.AddFileFromBytes("avatar", "avatar.png", []byte("fake-png-data"))
body, contentType, err := mb.Finish()
if err != nil {
log.Fatal(err)
}
fmt.Printf("has body: %v, has boundary: %v\n", len(body) > 0, len(contentType) > 0)
}
Output: has body: true, has boundary: true
func NewMultipartBuilder ¶
func NewMultipartBuilder() *MultipartBuilder
NewMultipartBuilder creates a new multipart body builder.
func (*MultipartBuilder) AddField ¶
func (mb *MultipartBuilder) AddField(name, value string) error
AddField adds a text form field.
func (*MultipartBuilder) AddFileFromBytes ¶
func (mb *MultipartBuilder) AddFileFromBytes(fieldName, fileName string, data []byte) error
AddFileFromBytes adds a file field from an in-memory byte slice.
func (*MultipartBuilder) AddFileFromPath ¶
func (mb *MultipartBuilder) AddFileFromPath(fieldName, filePath string) error
AddFileFromPath adds a file field by reading from the filesystem.
func (*MultipartBuilder) AddFileFromReader ¶
func (mb *MultipartBuilder) AddFileFromReader(fieldName, fileName string, reader io.Reader) error
AddFileFromReader adds a file field by reading from an io.Reader.
func (*MultipartBuilder) ContentType ¶
func (mb *MultipartBuilder) ContentType() string
ContentType returns the Content-Type header value including the boundary. Must be called after Finish (or at any point for the current boundary).
type PendingRequest ¶
type PendingRequest struct {
// contains filtered or unexported fields
}
PendingRequest represents a request queued for a pipelined response. It is added to pendingReqs only AFTER the request has been written to the wire.
type Pool ¶
type Pool struct {
// contains filtered or unexported fields
}
Pool manages per-host connection pools. Host pool lookup and connection list access are lock-free (sync.Map + atomic.Pointer for the connection slice).
func NewPool ¶
func NewPool(config *Config, dialer *Dialer, tlsConfig *TLSConfig, compressor *Compressor) *Pool
NewPool creates a new connection pool.
func (*Pool) GetConnection ¶
func (p *Pool) GetConnection(key string, useTLS bool) *Connection
GetConnection returns an available connection for the given pool key. key is "scheme://host:port" (e.g. "https://api.example.com:443"). useTLS controls whether new connections use TLS.
func (*Pool) GetHealthyConnections ¶
GetHealthyConnections returns the total healthy connection count across all pools.
func (*Pool) GracefulStop ¶
GracefulStop waits for in-flight requests to complete (up to timeout) before closing all connections. Returns true if all requests drained cleanly.
type ProxyDialer ¶
type ProxyDialer struct {
// contains filtered or unexported fields
}
ProxyDialer handles HTTP CONNECT proxy connections with authentication. Optimized for zero-allocation and minimal syscalls.
func NewProxyDialer ¶
func NewProxyDialer(config *Config) (*ProxyDialer, error)
NewProxyDialer creates a new proxy dialer from configuration.
func (*ProxyDialer) Dial ¶
Dial establishes a connection through the proxy using HTTP CONNECT. Each CONNECT tunnel is bound to a single target; the main pool handles reuse.
func (*ProxyDialer) DialForward ¶ added in v0.1.1
func (p *ProxyDialer) DialForward() (net.Conn, error)
DialForward connects to the proxy over plain TCP without issuing a CONNECT request. Used for HTTP (non-TLS) targets where the proxy acts as a forward proxy: the client sends the absolute-form request directly to the proxy TCP socket and the proxy forwards it to the origin.
type Request ¶
type Request struct {
// Method is the HTTP verb (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS).
Method string
// URL is a full URL e.g. "https://api.example.com/v1/users".
// When set, Host/Port/TLS are derived from it; Path is ignored.
URL string
// Path is the request path used when URL is empty (e.g. "/api/v1/users").
// Deprecated in favour of URL for multi-host clients.
Path string
// Body is the raw request body.
Body []byte
// Compressed, if true, gzip-compresses Body before sending
// (requires Compressor configured in Client).
Compressed bool
// Per-request timeout overrides (0 = use client config).
ReadTimeout time.Duration
WriteTimeout time.Duration
// ExpectContinue, when true, sends "Expect: 100-continue" and waits
// for a 100 response before transmitting the body.
ExpectContinue bool
// PreEncodedHeaderPrefix, when non-nil, is the pre-encoded request header
// (request line + all headers) up to but not including "Content-Length: N\r\n\r\n".
// The connection will append Content-Length and body. Build via BuildPreEncodedHeaderPrefix.
PreEncodedHeaderPrefix []byte
// contains filtered or unexported fields
}
Request represents an HTTP request with pre-allocated buffers. Acquire via Client.AcquireRequest() or the package-level AcquireRequest(). Release after Do() returns via Client.ReleaseRequest() or ReleaseRequest().
Example (Fluent) ¶
package main
import (
"context"
"fmt"
client "github.com/muxover/bursthttp"
)
func main() {
req := client.AcquireRequest()
defer client.ReleaseRequest(req)
req.WithMethod("POST").
WithPath("/api/v1/data").
WithBody([]byte(`{"key":"value"}`)).
WithHeader("Content-Type", "application/json").
WithContext(context.Background())
fmt.Printf("method: %s, path: %s\n", req.Method, req.Path)
}
Output: method: POST, path: /api/v1/data
func AcquireRequest ¶
func AcquireRequest() *Request
AcquireRequest gets a Request from the global pool.
func BuildMultipartRequest ¶
func BuildMultipartRequest(client *Client, method, urlOrPath string, fields map[string]string, files map[string][]byte) (*Request, error)
BuildMultipartRequest is a convenience that creates a Request with a multipart body from field and file data.
func (*Request) Reset ¶
func (r *Request) Reset()
Reset clears the request for reuse, preserving underlying buffer capacity.
func (*Request) SetContext ¶
SetContext sets the context for the request.
func (*Request) SetHeader ¶
SetHeader appends a header in wire format ("Key: Value\r\n") to the internal buffer. Returns false if key/value contain CR or LF (header injection protection) or if the buffer cannot grow beyond 1 MB.
func (*Request) WithCompression ¶
WithCompression enables gzip compression of the request body.
func (*Request) WithContext ¶
WithContext sets the cancellation context and returns r for chaining.
func (*Request) WithExpectContinue ¶
WithExpectContinue enables the Expect: 100-continue protocol. The client sends headers first, waits for a 100 response, then sends the body.
func (*Request) WithHeader ¶
WithHeader adds a header in wire format and returns r for chaining. Returns r unchanged if the buffer is full (grows automatically up to 1 MB).
func (*Request) WithMethod ¶
WithMethod sets the HTTP method and returns r for chaining.
func (*Request) WithReadTimeout ¶
WithReadTimeout overrides the client read timeout for this request.
func (*Request) WithURL ¶
WithURL sets the full URL and returns r for chaining. Example: req.WithURL("https://api.example.com/v1/items")
type Response ¶
type Response struct {
StatusCode int
ContentLength int
Body []byte
Headers []Header
// contains filtered or unexported fields
}
Response represents an HTTP response with pre-allocated body buffer.
func AcquireResponse ¶
func AcquireResponse() *Response
AcquireResponse gets a response from the pool.
func AcquireResponseWithMaxSize ¶
AcquireResponseWithMaxSize gets a response from the pool with a maximum size limit. This allows per-client configuration while maintaining pool efficiency.
func (*Response) HasHeader ¶
HasHeader reports whether the response contains the named header (case-insensitive).
func (*Response) Header ¶
Header returns the first value for the given header key (case-insensitive). Returns empty string if the header is not present.
Example ¶
package main
import (
"fmt"
client "github.com/muxover/bursthttp"
)
func main() {
resp := &client.Response{
StatusCode: 200,
Headers: []client.Header{
{Key: "Content-Type", Value: "application/json"},
{Key: "X-Request-Id", Value: "abc123"},
},
}
fmt.Println(resp.Header("content-type"))
fmt.Println(resp.HasHeader("X-Request-Id"))
}
Output: application/json true
func (*Response) HeaderBytes ¶ added in v0.1.2
HeaderBytes returns the first value for the given header key as a slice into the response's raw header buffer (zero-copy). The slice is valid only until ReleaseResponse is called. Returns nil if the header is not present. Case-insensitive header name match.
func (*Response) HeaderValues ¶
HeaderValues returns all values for the given header key (case-insensitive). Returns nil if the header is not present.
Example ¶
package main
import (
"fmt"
client "github.com/muxover/bursthttp"
)
func main() {
resp := &client.Response{
StatusCode: 200,
Headers: []client.Header{
{Key: "Set-Cookie", Value: "a=1"},
{Key: "Set-Cookie", Value: "b=2"},
},
}
for _, v := range resp.HeaderValues("Set-Cookie") {
fmt.Println(v)
}
}
Output: a=1 b=2
type ResponseReader ¶
type ResponseReader struct {
// contains filtered or unexported fields
}
ResponseReader is a per-connection goroutine that reads responses in FIFO order. It uses a persistent bufio.Reader to preserve any leftover bytes that arrived in the same TCP segment as a previous response (essential for pipelining).
type Retryer ¶
type Retryer struct {
// contains filtered or unexported fields
}
Retryer executes a request with configurable retry logic. It is created from the client's Config and reused across requests.
func NewRetryer ¶
NewRetryer creates a Retryer from configuration. Returns nil when retries are disabled (MaxRetries == 0).
Example ¶
package main
import (
"fmt"
"time"
client "github.com/muxover/bursthttp"
)
func main() {
cfg := client.DefaultConfig()
cfg.MaxRetries = 3
cfg.RetryBaseDelay = 100 * time.Millisecond
cfg.RetryMaxDelay = 5 * time.Second
cfg.RetryMultiplier = 2.0
cfg.RetryJitter = true
r := client.NewRetryer(cfg)
if r != nil {
fmt.Printf("backoff attempt 0: %v\n", r.Backoff(0) > 0)
fmt.Printf("backoff attempt 2: %v\n", r.Backoff(2) > 0)
}
}
Output: backoff attempt 0: true backoff attempt 2: true
func (*Retryer) Backoff ¶
Backoff returns the duration to wait before the given retry attempt. Uses exponential backoff with optional jitter.
func (*Retryer) ShouldRetry ¶
ShouldRetry reports whether the request should be retried based on the response status code or error. attempt is 0-indexed (first retry = 0).
type SOCKS5Dialer ¶
type SOCKS5Dialer struct {
// contains filtered or unexported fields
}
SOCKS5Dialer handles connections through a SOCKS5 proxy.
func NewSOCKS5Dialer ¶
func NewSOCKS5Dialer(config *Config) (*SOCKS5Dialer, error)
NewSOCKS5Dialer creates a SOCKS5 dialer from configuration.
type Scheduler ¶ added in v0.1.3
type Scheduler struct {
// contains filtered or unexported fields
}
Scheduler routes requests through per-host queues processed by a bounded worker pool. This replaces the spin-wait in GetConnection with a proper blocking queue, giving stable latency under overload and eliminating busy-wait CPU overhead when all connections are busy.
func NewScheduler ¶ added in v0.1.3
NewScheduler creates a Scheduler backed by the given client. workers is the number of worker goroutines per host (default: PoolSize). queueCap is the max queued requests per host before Do blocks (default: workers*4).
type StreamingResponse ¶
type StreamingResponse struct {
StatusCode int
ContentLength int
Headers []Header
Body io.ReadCloser
// contains filtered or unexported fields
}
StreamingResponse wraps a Response with an io.ReadCloser for the body. Use Client.DoStreaming to obtain one. The caller MUST call Close() when done.
func (*StreamingResponse) Close ¶
func (sr *StreamingResponse) Close() error
Close releases the underlying response back to the pool. Must be called after the body has been fully consumed or abandoned.
func (*StreamingResponse) HasHeader ¶
func (sr *StreamingResponse) HasHeader(key string) bool
HasHeader reports whether the streaming response contains the named header.
func (*StreamingResponse) Header ¶
func (sr *StreamingResponse) Header(key string) string
Header returns the first value for the given header key (case-insensitive).
type TLSConfig ¶
type TLSConfig struct {
// contains filtered or unexported fields
}
TLSConfig wraps tls.Config with optimized session caching and connection management.
func NewTLSConfig ¶
NewTLSConfig creates a new TLS configuration with session caching enabled.