Add Client with response wrapper, request helpers, and full middleware assembly

Implements the top-level httpx.Client that composes the full chain:
  Logging → User Middlewares → Retry → Circuit Breaker → Balancer → Transport

- Response wrapper with JSON/XML/Bytes decoding and body caching
- NewJSONRequest helper with Content-Type and GetBody support
- Functional options: WithBaseURL, WithTimeout, WithRetry, WithEndpoints, etc.
- Integration tests covering retry, balancing, error mapping, and JSON round-trips
This commit is contained in:
2026-03-20 14:22:22 +03:00
parent a90c4cd7fa
commit f9a05f5c57
8 changed files with 959 additions and 0 deletions

87
client_options.go Normal file
View File

@@ -0,0 +1,87 @@
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
}
// 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 }
}