httputil

package
v0.35.8 Latest Latest
Warning

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

Go to latest
Published: Feb 13, 2026 License: Unlicense Imports: 16 Imported by: 0

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

Examples

Constants

View Source
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

func CopyRequestTo(ctx context.Context, dst, src *http.Request)

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

func PanicHandler(v any) (h http.Handler)

PanicHandler returns an HTTP handler that panics with the provided value. v must not be nil.

func PassThrough added in v0.35.2

func PassThrough(h http.Handler) (wrapped http.Handler)

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

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

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

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

type MiddlewareFunc func(h http.Handler) (wrapped http.Handler)

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

func (r *RequestIDRoundTripper) RoundTrip(req *http.Request) (resp *http.Response, err error)

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

type RouterFunc func(pattern string, h http.Handler)

RouterFunc is a functional implementation of the Router interface.

func (RouterFunc) Handle

func (f RouterFunc) Handle(pattern string, h http.Handler)

Handle implements the Router interface for RouterFunc.

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.

Jump to

Keyboard shortcuts

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