Files
httpx/client_options.go
Aleksey Shakhmatov 21274c178a Add WithMaxResponseBody option to prevent client-side OOM
Wraps response body with io.LimitedReader when configured, preventing
unbounded reads from io.ReadAll in Response.Bytes(). Protects against
upstream services returning unexpectedly large responses.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 21:48:05 +03:00

97 lines
2.8 KiB
Go

package httpx
import (
"log/slog"
"net/http"
"time"
"git.codelab.vc/pkg/httpx/balancer"
"git.codelab.vc/pkg/httpx/circuitbreaker"
"git.codelab.vc/pkg/httpx/middleware"
"git.codelab.vc/pkg/httpx/retry"
)
type clientOptions struct {
baseURL string
timeout time.Duration
transport http.RoundTripper
logger *slog.Logger
errorMapper ErrorMapper
middlewares []middleware.Middleware
retryOpts []retry.Option
enableRetry bool
cbOpts []circuitbreaker.Option
enableCB bool
endpoints []balancer.Endpoint
balancerOpts []balancer.Option
maxResponseBody int64
}
// Option configures a Client.
type Option func(*clientOptions)
// WithBaseURL sets the base URL prepended to all relative request paths.
func WithBaseURL(url string) Option {
return func(o *clientOptions) { o.baseURL = url }
}
// WithTimeout sets the overall request timeout.
func WithTimeout(d time.Duration) Option {
return func(o *clientOptions) { o.timeout = d }
}
// WithTransport sets the base http.RoundTripper. Defaults to http.DefaultTransport.
func WithTransport(rt http.RoundTripper) Option {
return func(o *clientOptions) { o.transport = rt }
}
// WithLogger enables structured logging of requests and responses.
func WithLogger(l *slog.Logger) Option {
return func(o *clientOptions) { o.logger = l }
}
// WithErrorMapper sets a function that maps HTTP responses to errors.
func WithErrorMapper(m ErrorMapper) Option {
return func(o *clientOptions) { o.errorMapper = m }
}
// WithMiddleware appends user middlewares to the chain.
// These run between logging and retry in the middleware stack.
func WithMiddleware(mws ...middleware.Middleware) Option {
return func(o *clientOptions) { o.middlewares = append(o.middlewares, mws...) }
}
// WithRetry enables retry with the given options.
func WithRetry(opts ...retry.Option) Option {
return func(o *clientOptions) {
o.enableRetry = true
o.retryOpts = opts
}
}
// WithCircuitBreaker enables per-host circuit breaking.
func WithCircuitBreaker(opts ...circuitbreaker.Option) Option {
return func(o *clientOptions) {
o.enableCB = true
o.cbOpts = opts
}
}
// WithEndpoints sets the endpoints for load balancing.
func WithEndpoints(eps ...balancer.Endpoint) Option {
return func(o *clientOptions) { o.endpoints = eps }
}
// WithBalancer configures the load balancer strategy and options.
func WithBalancer(opts ...balancer.Option) Option {
return func(o *clientOptions) { o.balancerOpts = opts }
}
// WithMaxResponseBody limits the number of bytes read from response bodies
// by Response.Bytes (and by extension String, JSON, XML). If the response
// body exceeds n bytes, reading stops and returns an error.
// A value of 0 means no limit (the default).
func WithMaxResponseBody(n int64) Option {
return func(o *clientOptions) { o.maxResponseBody = n }
}