Documentation
¶
Overview ¶
Package httputil contains common constants, functions, and types for working with HTTP.
Example ¶
package main
import (
"bytes"
"context"
"fmt"
"io"
"net/http"
"net/http/httptest"
"net/url"
"time"
"github.com/AdguardTeam/golibs/netutil/httputil"
)
// must is a helper for tests.
func must[T any](v T, err error) (res T) {
if err != nil {
panic(err)
}
return v
}
func main() {
mux := http.NewServeMux()
httputil.RoutePprof(mux)
srv := httptest.NewServer(mux)
defer srv.Close()
u := must(url.Parse(srv.URL)).JoinPath(httputil.PprofBasePath)
req := must(http.NewRequest(http.MethodGet, u.String(), nil))
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
req = req.WithContext(ctx)
resp := must(http.DefaultClient.Do(req))
respBody := must(io.ReadAll(resp.Body))
lines := bytes.Split(respBody, []byte("\n"))
for i := range 15 {
fmt.Printf("%s\n", lines[i])
}
fmt.Println("…")
}
Output: <html> <head> <title>/debug/pprof/</title> <style> .profile-name{ display:inline-block; width:6rem; } </style> </head> <body> /debug/pprof/ <br> <p>Set debug=1 as a query parameter to export in legacy text format</p> <br> …
Index ¶
- Constants
- func CopyRequestTo(ctx context.Context, dst, src *http.Request)
- func PanicHandler(v any) (h http.Handler)
- func PassThrough(h http.Handler) (wrapped http.Handler)
- func RoutePprof(r Router)
- func Wrap(h http.Handler, middlewares ...Middleware) (wrapped http.Handler)
- type CodeRecorderResponseWriter
- func (w *CodeRecorderResponseWriter) Code() (code int)
- func (w *CodeRecorderResponseWriter) Header() (h http.Header)
- func (w *CodeRecorderResponseWriter) Hijack() (conn net.Conn, rw *bufio.ReadWriter, err error)
- func (w *CodeRecorderResponseWriter) Reset(rw http.ResponseWriter)
- func (w *CodeRecorderResponseWriter) SetImplicitSuccess()
- func (w *CodeRecorderResponseWriter) Unwrap() (rw http.ResponseWriter)
- func (w *CodeRecorderResponseWriter) Write(b []byte) (n int, err error)
- func (w *CodeRecorderResponseWriter) WriteHeader(code int)
- type EmptyRequestMetrics
- type HeaderPool
- type HeaderPoolEntry
- type LogMiddleware
- type MetricsMiddleware
- type MetricsMiddlewareConfig
- type Middleware
- type MiddlewareFunc
- type PlainTextHandler
- type RequestIDMiddleware
- type RequestIDRoundTripper
- type RequestIDRoundTripperConfig
- type RequestMetrics
- type Router
- type RouterFunc
- type ServerHeaderMiddleware
- type Wrapper
Examples ¶
Constants ¶
const PprofBasePath = "/debug/pprof/"
PprofBasePath is the default base path used by RoutePprof.
Variables ¶
This section is empty.
Functions ¶
func CopyRequestTo ¶ added in v0.27.0
CopyRequestTo is an optimized version of http.Request.WithContext that uses compiler optimizations to allow reducing allocations with a pool. ctx, dst, and src must not be nil.
See https://github.com/golang/go/issues/68501#issuecomment-2234069762.
func PanicHandler ¶ added in v0.35.4
PanicHandler returns an HTTP handler that panics with the provided value. v must not be nil.
func PassThrough ¶ added in v0.35.2
PassThrough is a MiddlewareFunc that returns h as-is.
func RoutePprof ¶
func RoutePprof(r Router)
RoutePprof adds all pprof handlers to r under the paths within PprofBasePath.
func Wrap ¶
func Wrap(h http.Handler, middlewares ...Middleware) (wrapped http.Handler)
Wrap is a helper function that wraps h with the specified middlewares. middlewares are wrapped, and thus called, in the same order in which they were specified. That is, the first middleware will be the first to receive the request, and so on.
Example ¶
package main
import (
"context"
"fmt"
"net/http"
"net/http/httptest"
"time"
"github.com/AdguardTeam/golibs/netutil/httputil"
)
// testMiddleware is a fake [httputil.Middleware] implementation for tests.
type testMiddleware struct {
OnWrap func(h http.Handler) (wrapped http.Handler)
}
// type check
var _ httputil.Middleware = (*testMiddleware)(nil)
// Wrap implements the [Middleware] interface for *testMiddleware.
func (mw *testMiddleware) Wrap(h http.Handler) (wrapped http.Handler) {
return mw.OnWrap(h)
}
func main() {
mw1 := &testMiddleware{
OnWrap: func(h http.Handler) (wrapped http.Handler) {
f := func(w http.ResponseWriter, r *http.Request) {
fmt.Println("first middleware start")
h.ServeHTTP(w, r)
fmt.Println("first middleware end")
}
return http.HandlerFunc(f)
},
}
mw2 := &testMiddleware{
OnWrap: func(h http.Handler) (wrapped http.Handler) {
f := func(w http.ResponseWriter, r *http.Request) {
fmt.Println("second middleware start")
h.ServeHTTP(w, r)
fmt.Println("second middleware end")
}
return http.HandlerFunc(f)
},
}
var h http.Handler = http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {
fmt.Println("handler")
})
h = httputil.Wrap(h, mw1, mw2)
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
w := httptest.NewRecorder()
r := httptest.NewRequestWithContext(ctx, http.MethodGet, testPath, nil)
h.ServeHTTP(w, r)
}
Output: first middleware start second middleware start handler second middleware end first middleware end
Types ¶
type CodeRecorderResponseWriter ¶
type CodeRecorderResponseWriter struct {
// contains filtered or unexported fields
}
CodeRecorderResponseWriter wraps an http.ResponseWriter allowing to save the response code.
Instances must be initialized either with NewCodeRecorderResponseWriter or by calling CodeRecorderResponseWriter.Reset.
func NewCodeRecorderResponseWriter ¶
func NewCodeRecorderResponseWriter(rw http.ResponseWriter) (w *CodeRecorderResponseWriter)
NewCodeRecorderResponseWriter returns a new *CodeRecorderResponseWriter which uses the given response writer as its base.
func (*CodeRecorderResponseWriter) Code ¶
func (w *CodeRecorderResponseWriter) Code() (code int)
Code returns the status code that was set. It expects that CodeRecorderResponseWriter.SetImplicitSuccess has already been called.
func (*CodeRecorderResponseWriter) Header ¶
func (w *CodeRecorderResponseWriter) Header() (h http.Header)
Header implements http.ResponseWriter for *CodeRecorderResponseWriter.
func (*CodeRecorderResponseWriter) Hijack ¶ added in v0.32.3
func (w *CodeRecorderResponseWriter) Hijack() (conn net.Conn, rw *bufio.ReadWriter, err error)
Hijack implements http.Hijacker for *CodeRecorderResponseWriter.
This can be necessary for older packages that don't support response controllers and require the Hijack method on the response writer itself, such as golang.org/x/net/http2/h2c.
func (*CodeRecorderResponseWriter) Reset ¶ added in v0.27.0
func (w *CodeRecorderResponseWriter) Reset(rw http.ResponseWriter)
Reset allows reusing w by setting rw as the response writer and setting the code to zero.
func (*CodeRecorderResponseWriter) SetImplicitSuccess ¶
func (w *CodeRecorderResponseWriter) SetImplicitSuccess()
SetImplicitSuccess should be called after the handler has finished to set the status code to http.StatusOK if it hadn't been set explicitly. This can be used to detect panics within handlers, as when a handler panics before calling w.WriteHeader, SetImplicitSuccess isn't reached, and w.Code returns 0 and false.
func (*CodeRecorderResponseWriter) Unwrap ¶
func (w *CodeRecorderResponseWriter) Unwrap() (rw http.ResponseWriter)
Unwrap implements the Wrapper interface for *CodeRecorderResponseWriter.
func (*CodeRecorderResponseWriter) Write ¶
func (w *CodeRecorderResponseWriter) Write(b []byte) (n int, err error)
Write implements http.ResponseWriter for *CodeRecorderResponseWriter.
func (*CodeRecorderResponseWriter) WriteHeader ¶
func (w *CodeRecorderResponseWriter) WriteHeader(code int)
WriteHeader implements http.ResponseWriter for *CodeRecorderResponseWriter.
type EmptyRequestMetrics ¶ added in v0.33.1
type EmptyRequestMetrics struct{}
EmptyRequestMetrics is an implementation of the RequestMetrics interface that does nothing.
func (EmptyRequestMetrics) ObserveRequest ¶ added in v0.33.1
func (EmptyRequestMetrics) ObserveRequest( _ context.Context, _ http.ResponseWriter, _ *http.Request, _ time.Duration, )
ObserveRequest implements the RequestMetrics interface for EmptyRequestMetrics.
type HeaderPool ¶ added in v0.35.6
type HeaderPool struct {
// contains filtered or unexported fields
}
HeaderPool allows reducing allocations when making many clones of http.Header maps.
Example ¶
package main
import (
"fmt"
"net/http"
"github.com/AdguardTeam/golibs/netutil/httputil"
)
func main() {
original := http.Header{
"Accept": {"*/*"},
"Content-Type": {"application/json"},
"Host": {"example.com"},
"Nil-Header": nil,
"User-Agent": {"MyHTTPClient/1.0"},
}
p := httputil.NewHeaderPool()
cloneEntry := p.Get(original)
defer p.Put(cloneEntry)
clone := cloneEntry.Header()
fmt.Printf("before mutation:\noriginal: %#v\nclone: %#v\n", original, clone)
clone.Set("New-Header", "value")
fmt.Printf("after mutation:\noriginal: %#v\nclone: %#v\n", original, clone)
}
Output: before mutation: original: http.Header{"Accept":[]string{"*/*"}, "Content-Type":[]string{"application/json"}, "Host":[]string{"example.com"}, "Nil-Header":[]string(nil), "User-Agent":[]string{"MyHTTPClient/1.0"}} clone: http.Header{"Accept":[]string{"*/*"}, "Content-Type":[]string{"application/json"}, "Host":[]string{"example.com"}, "Nil-Header":[]string(nil), "User-Agent":[]string{"MyHTTPClient/1.0"}} after mutation: original: http.Header{"Accept":[]string{"*/*"}, "Content-Type":[]string{"application/json"}, "Host":[]string{"example.com"}, "Nil-Header":[]string(nil), "User-Agent":[]string{"MyHTTPClient/1.0"}} clone: http.Header{"Accept":[]string{"*/*"}, "Content-Type":[]string{"application/json"}, "Host":[]string{"example.com"}, "New-Header":[]string{"value"}, "Nil-Header":[]string(nil), "User-Agent":[]string{"MyHTTPClient/1.0"}}
func NewHeaderPool ¶ added in v0.35.6
func NewHeaderPool() (p *HeaderPool)
NewHeaderPool returns a new properly initialized *HeaderPool.
func (*HeaderPool) Get ¶ added in v0.35.6
func (p *HeaderPool) Get(orig http.Header) (e *HeaderPoolEntry)
Get returns an entry that contains a deep clone of orig. e should be returned to the pool by using HeaderPool.Put.
func (*HeaderPool) Put ¶ added in v0.35.6
func (p *HeaderPool) Put(e *HeaderPoolEntry)
Put returns e to the pool for later reuse.
type HeaderPoolEntry ¶ added in v0.35.6
type HeaderPoolEntry struct {
// contains filtered or unexported fields
}
HeaderPoolEntry is an entry in the HeaderPool.
func (*HeaderPoolEntry) Header ¶ added in v0.35.6
func (e *HeaderPoolEntry) Header() (h http.Header)
Header returns the cloned header.
type LogMiddleware ¶
type LogMiddleware struct {
// contains filtered or unexported fields
}
LogMiddleware adds a logger using slogutil.ContextWithLogger and logs the starts and ends of queries at a given level.
func NewLogMiddleware ¶
func NewLogMiddleware(l *slog.Logger, lvl slog.Level) (mw *LogMiddleware)
NewLogMiddleware returns a new *LogMiddleware with l as the base logger.
func (*LogMiddleware) Wrap ¶
func (mw *LogMiddleware) Wrap(h http.Handler) (wrapped http.Handler)
Wrap implements the Middleware interface for *LogMiddleware.
type MetricsMiddleware ¶ added in v0.33.1
type MetricsMiddleware struct {
// contains filtered or unexported fields
}
MetricsMiddleware wraps a handler with RequestMetrics.
func NewMetricsMiddleware ¶ added in v0.33.1
func NewMetricsMiddleware(c *MetricsMiddlewareConfig) (mw *MetricsMiddleware)
NewMetricsMiddleware returns a new properly initialized *MetricsMiddleware. c must not be nil and must be valid.
func (*MetricsMiddleware) Wrap ¶ added in v0.33.1
func (mw *MetricsMiddleware) Wrap(h http.Handler) (wrapped http.Handler)
Wrap implements the Middleware interface for *MetricsMiddleware.
type MetricsMiddlewareConfig ¶ added in v0.33.1
type MetricsMiddlewareConfig struct {
// Clock is used to measure the times for requests' durations. If Clock is
// nil, [timeutil.SystemClock] is used.
Clock timeutil.Clock
// Metrics is used to observe HTTP requests. It must not be nil.
Metrics RequestMetrics
}
MetricsMiddlewareConfig is the configuration structure for a *MetricsMiddleware.
type Middleware ¶
type Middleware interface {
// Wrap returns a new handler that wraps the specified handler.
Wrap(handler http.Handler) (wrapped http.Handler)
}
Middleware is a common HTTP middleware interface.
type MiddlewareFunc ¶ added in v0.35.2
MiddlewareFunc is a function that implements the Middleware interface.
func (MiddlewareFunc) Wrap ¶ added in v0.35.2
func (f MiddlewareFunc) Wrap(h http.Handler) (wrapped http.Handler)
Wrap implements the Middleware interface for MiddlewareFunc.
type PlainTextHandler ¶
type PlainTextHandler string
PlainTextHandler is a simple handler that returns the value of the underlying string with a "text/plain" content type. If there is an error during the response writing, it gets the logger from the context, if any, and writes the error there at the debug level.
const HealthCheckHandler PlainTextHandler = "OK\n"
HealthCheckHandler is the common healthcheck HTTP handler that writes the text "OK\n" into the response. These are typically used for the GET /health-check HTTP API.
func (PlainTextHandler) ServeHTTP ¶
func (text PlainTextHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP implements the http.Handler interface for PlainTextHandler.
type RequestIDMiddleware ¶ added in v0.35.5
type RequestIDMiddleware struct {
// contains filtered or unexported fields
}
RequestIDMiddleware reads request ID from headers and puts it in context.
func NewRequestIDMiddleware ¶ added in v0.35.5
func NewRequestIDMiddleware() (r *RequestIDMiddleware)
NewRequestIDMiddleware returns properly initialized RequestIDMiddleware.
func (*RequestIDMiddleware) Wrap ¶ added in v0.35.5
func (mw *RequestIDMiddleware) Wrap(h http.Handler) (wrapped http.Handler)
Wrap implements the Middleware interface for RequestIDMiddleware.
type RequestIDRoundTripper ¶ added in v0.35.5
type RequestIDRoundTripper struct {
// contains filtered or unexported fields
}
RequestIDRoundTripper is an implementation of http.RoundTripper that puts the request ID from the context into the X-Request-ID header. If the request already contains an X-Request-ID header, then the round tripper does nothing.
RequestIDRoundTripper does not support http.Request.Cancel.
func NewRequestIDRoundTripper ¶ added in v0.35.5
func NewRequestIDRoundTripper(c *RequestIDRoundTripperConfig) (r *RequestIDRoundTripper)
NewRequestIDRoundTripper returns properly initialized *RequestIDRoundTripper. c must be valid.
func (*RequestIDRoundTripper) RoundTrip ¶ added in v0.35.5
RoundTrip implements the http.RoundTripper for *RequestIDRoundTripper.
type RequestIDRoundTripperConfig ¶ added in v0.35.5
type RequestIDRoundTripperConfig struct {
// Transport is a round tripper that will be used to perform request after
// ID substitution. It must not be nil.
Transport http.RoundTripper
// Generate indicates whether a new request ID should be generated if the
// context doesn't contain one.
Generate bool
}
RequestIDRoundTripperConfig is a configuration structure for RequestIDRoundTripper.
type RequestMetrics ¶ added in v0.33.1
type RequestMetrics interface {
// ObserveRequest observes a single HTTP request. It should be called after
// the request has finished. w and r must not be nil, dur must be positive.
ObserveRequest(ctx context.Context, w http.ResponseWriter, r *http.Request, dur time.Duration)
}
RequestMetrics is an interface for collection of HTTP request statistics.
type Router ¶
type Router interface {
// Handle registers the handler for the given pattern.
Handle(pattern string, h http.Handler)
}
Router is the interface for HTTP routers, such as http.ServeMux.
type RouterFunc ¶
RouterFunc is a functional implementation of the Router interface.
type ServerHeaderMiddleware ¶
type ServerHeaderMiddleware string
ServerHeaderMiddleware adds a Server HTTP header to all responses.
func (ServerHeaderMiddleware) Wrap ¶
func (mw ServerHeaderMiddleware) Wrap(h http.Handler) (wrapped http.Handler)
Wrap implements the Middleware interface for *ServerHeaderMiddleware.
type Wrapper ¶
type Wrapper interface {
Unwrap() (rw http.ResponseWriter)
}
Wrapper is a copy of the hidden rwUnwrapper interface from the Go standard library. It is added here for tests, linting, etc.