middleware

package
v1.0.3 Latest Latest
Warning

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

Go to latest
Published: Apr 29, 2024 License: Apache-2.0 Imports: 23 Imported by: 1

Documentation

Index

Constants

View Source
const RequestIDKey ctxKeyRequestID = 0

RequestIDKey is the key that holds the unique request ID in a request context.

Variables

View Source
var (
	// LogEntryCtxKey is the context.Context key to store the request log entry.
	LogEntryCtxKey = &contextKey{"LogEntry"}

	// DefaultLogger is called by the Logger middleware handler to log each request.
	// Its made a package-level variable so that it can be reconfigured for custom
	// logging configurations.
	DefaultLogger func(next http.Handler) http.Handler
)
View Source
var IsTTY bool
View Source
var RequestIDHeader = "X-Request-Id"

RequestIDHeader is the name of the HTTP Header which contains the request id. Exported so that it can be changed by developers

Functions

func AllowContentEncoding

func AllowContentEncoding(contentEncoding ...string) func(next http.Handler) http.Handler

AllowContentEncoding enforces a whitelist of request Content-Encoding otherwise responds with a 415 Unsupported Media Type status.

func AllowContentType

func AllowContentType(contentTypes ...string) func(next http.Handler) http.Handler

AllowContentType enforces a whitelist of request Content-Types otherwise responds with a 415 Unsupported Media Type status.

func BasicAuth

func BasicAuth(realm string, creds map[string]string) func(next http.Handler) http.Handler

BasicAuth implements a simple middleware handler for adding basic http auth to a route.

func Compress

func Compress(level int, types ...string) func(next http.Handler) http.Handler

Compress is a middleware that compresses response body of a given content types to a data format based on Accept-Encoding request header. It uses a given compression level.

NOTE: make sure to set the Content-Type header on your response otherwise this middleware will not compress the response body. For ex, in your handler you should set w.Header().Set("Content-Type", http.DetectContentType(yourBody)) or set it manually.

Passing a compression level of 5 is sensible value

func ContentCharset

func ContentCharset(charsets ...string) func(next http.Handler) http.Handler

ContentCharset generates a handler that writes a 415 Unsupported Media Type response if none of the charsets match. An empty charset will allow requests with no Content-Type header or no specified charset.

func CorsHandler

func CorsHandler(options CorsOptions) func(next http.Handler) http.Handler

CorsHandler creates a new Cors handler with passed options.

func GetReqID

func GetReqID(ctx context.Context) string

GetReqID returns a request ID from the given context if one is present. Returns the empty string if a request ID cannot be found.

func Heartbeat

func Heartbeat(endpoint string) func(http.Handler) http.Handler

Heartbeat endpoint middleware useful to setting up a path like `/ping` that load balancers or uptime testing external services can make a request before hitting any routes. It's also convenient to place this above ACL middlewares as well.

func Logger

func Logger(next http.Handler) http.Handler

Logger is a middleware that logs the start and end of each request, along with some useful data about what was requested, what the response status was, and how long it took to return. When standard output is a TTY, Logger will print in color, otherwise it will print in black and white. Logger prints a request ID if one is provided.

Alternatively, look at https://github.com/goware/httplog for a more in-depth http logger with structured logging support.

IMPORTANT NOTE: Logger should go before any other middleware that may change the response, such as middleware.Recoverer. Example:

r := chi.NewRouter()
r.Use(middleware.Logger)        // <--<< Logger should come before Recoverer
r.Use(middleware.Recoverer)
r.Get("/", handler)

func Maybe

func Maybe(mw func(http.Handler) http.Handler, maybeFn func(r *http.Request) bool) func(http.Handler) http.Handler

Maybe middleware will allow you to change the flow of the middleware stack execution depending on return value of maybeFn(request). This is useful for example if you'd like to skip a middleware handler if a request does not satisfy the maybeFn logic.

func New

func New(h http.Handler) func(next http.Handler) http.Handler

New will create a new middleware handler from a http.Handler.

func NextRequestID

func NextRequestID() uint64

NextRequestID generates the next request ID in the sequence.

func NoCache

func NoCache(h http.Handler) http.Handler

NoCache is a simple piece of middleware that sets a number of HTTP headers to prevent a router (or subrouter) from being cached by an upstream proxy and/or client.

As per http://wiki.nginx.org/HttpProxyModule - NoCache sets:

Expires: Thu, 01 Jan 1970 00:00:00 UTC
Cache-Control: no-cache, private, max-age=0
X-Accel-Expires: 0
Pragma: no-cache (for HTTP/1.0 proxies/clients)

func PageRoute

func PageRoute(path string, handler http.Handler) func(http.Handler) http.Handler

PageRoute is a simple middleware which allows you to route a static GET request at the middleware stack level.

func PathRewrite

func PathRewrite(old, new string) func(http.Handler) http.Handler

PathRewrite is a simple middleware which allows you to rewrite the request URL path.

func PrintPrettyStack

func PrintPrettyStack(rvr interface{})

func RealIP

func RealIP(h http.Handler) http.Handler

RealIP is a middleware that sets a http.Request's RemoteAddr to the results of parsing either the True-Client-IP, X-Real-IP or the X-Forwarded-For headers (in that order).

This middleware should be inserted fairly early in the middleware stack to ensure that subsequent layers (e.g., request loggers) which examine the RemoteAddr will see the intended value.

You should only use this middleware if you can trust the headers passed to you (in particular, the two headers this middleware uses), for example because you have placed a reverse proxy like HAProxy or nginx in front of chi. If your reverse proxies are configured to pass along arbitrary header values from the client, or if you use this middleware without a reverse proxy, malicious clients will be able to make you very sad (or, depending on how you're using RemoteAddr, vulnerable to an attack of some sort).

func Recoverer

func Recoverer(next http.Handler) http.Handler

Recoverer is a middleware that recovers from panics, logs the panic (and a backtrace), and returns a HTTP 500 (Internal Server Error) status if possible. Recoverer prints a request ID if one is provided.

Alternatively, look at https://github.com/go-chi/httplog middleware pkgs.

func RequestID

func RequestID(next http.Handler) http.Handler

RequestID is a middleware that injects a request ID into the context of each request. A request ID is a string of the form "host.example.com/random-0001", where "random" is a base62 random string that uniquely identifies this go process, and where the last number is an atomically incremented request counter.

func RequestLogger

func RequestLogger(f LogFormatter) func(next http.Handler) http.Handler

RequestLogger returns a logger handler using a custom LogFormatter.

func RequestSize

func RequestSize(bytes int64) func(http.Handler) http.Handler

RequestSize is a middleware that will limit request sizes to a specified number of bytes. It uses MaxBytesReader to do so.

func SetHeader

func SetHeader(key, value string) func(next http.Handler) http.Handler

SetHeader is a convenience handler to set a response header key/value

func Throttle

func Throttle(limit int) func(http.Handler) http.Handler

Throttle is a middleware that limits number of currently processed requests at a time across all users. Note: Throttle is not a rate-limiter per user, instead it just puts a ceiling on the number of currently in-flight requests being processed from the point from where the Throttle middleware is mounted.

func ThrottleBacklog

func ThrottleBacklog(limit, backlogLimit int, backlogTimeout time.Duration) func(http.Handler) http.Handler

ThrottleBacklog is a middleware that limits number of currently processed requests at a time and provides a backlog for holding a finite number of pending requests.

func ThrottleWithOpts

func ThrottleWithOpts(opts ThrottleOpts) func(http.Handler) http.Handler

ThrottleWithOpts is a middleware that limits number of currently processed requests using passed ThrottleOpts.

func Timeout

func Timeout(timeout time.Duration) func(next http.Handler) http.Handler

Timeout is a middleware that cancels ctx after a given timeout and return a 504 Gateway Timeout error to the client.

It's required that you select the ctx.Done() channel to check for the signal if the context has reached its deadline and return, otherwise the timeout signal will be just ignored.

ie. a route/handler may look like:

r.Get("/long", func(w http.ResponseWriter, r *http.Request) {
	ctx := r.Context()
	processTime := time.Duration(rand.Intn(4)+1) * time.Second

	select {
	case <-ctx.Done():
		return

	case <-time.After(processTime):
		// The above channel simulates some hard work.
	}

	w.Write([]byte("done"))
})

func WithLogEntry

func WithLogEntry(r *http.Request, entry LogEntry) *http.Request

WithLogEntry sets the in-context LogEntry for a request.

func WithValue

func WithValue(key, val interface{}) func(next http.Handler) http.Handler

WithValue is a middleware that sets a given key/value in a context chain.

Types

type Compressor

type Compressor struct {
	// contains filtered or unexported fields
}

Compressor represents a set of encoding configurations.

func NewCompressor

func NewCompressor(level int, types ...string) *Compressor

NewCompressor creates a new Compressor that will handle encoding responses.

The level should be one of the ones defined in the flate package. The types are the content types that are allowed to be compressed.

func (*Compressor) Handler

func (c *Compressor) Handler(next http.Handler) http.Handler

Handler returns a new middleware that will compress the response based on the current Compressor.

func (*Compressor) SetEncoder

func (c *Compressor) SetEncoder(encoding string, fn EncoderFunc)

SetEncoder can be used to set the implementation of a compression algorithm.

The encoding should be a standardised identifier. See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding

For example, add the Brotli algorithm:

import brotli_enc "gopkg.in/kothar/brotli-go.v0/enc"

compressor := middleware.NewCompressor(5, "text/html")
compressor.SetEncoder("br", func(w io.Writer, level int) io.Writer {
	params := brotli_enc.NewBrotliParams()
	params.SetQuality(level)
	return brotli_enc.NewBrotliWriter(params, w)
})

type Cors

type Cors struct {
	// Debug logger
	Log Log
	// contains filtered or unexported fields
}

Cors http handler

func AllowAll

func AllowAll() *Cors

AllowAll create a new Cors handler with permissive configuration allowing all origins with all standard methods with any header and credentials.

func NewCORS

func NewCORS(options CorsOptions) *Cors

NewCORS creates a new Cors handler with the provided options.

func (*Cors) Handler

func (c *Cors) Handler(next http.Handler) http.Handler

Handler apply the CORS specification on the request, and add relevant CORS headers as necessary.

type CorsOptions

type CorsOptions struct {
	// Debug logger
	Log Log

	// AllowedOrigins is a list of origins a cross-domain request can be executed from.
	// If the special "*" value is present in the list, all origins will be allowed.
	// An origin may contain a wildcard (*) to replace 0 or more characters
	// (i.e.: http://*.domain.com). Usage of wildcards implies a small performance penalty.
	// Only one wildcard can be used per origin.
	// Default value is ["*"]
	AllowedOrigins []string

	// AllowOriginFunc is a custom function to validate the origin. It takes the origin
	// as argument and returns true if allowed or false otherwise. If this option is
	// set, the content of AllowedOrigins is ignored.
	AllowOriginFunc func(r *http.Request, origin string) bool

	// AllowedMethods is a list of methods the client is allowed to use with
	// cross-domain requests. Default value is simple methods (HEAD, GET and POST).
	AllowedMethods []string

	// AllowedHeaders is list of non simple headers the client is allowed to use with
	// cross-domain requests.
	// If the special "*" value is present in the list, all headers will be allowed.
	// Default value is [] but "Origin" is always appended to the list.
	AllowedHeaders []string

	// ExposedHeaders indicates which headers are safe to expose to the API of a CORS
	// API specification
	ExposedHeaders []string

	// AllowCredentials indicates whether the request can include user credentials like
	// cookies, HTTP authentication or client side SSL certificates.
	AllowCredentials bool

	// MaxAge indicates how long (in seconds) the results of a preflight request
	// can be cached
	MaxAge int

	// OptionsPassthrough instructs preflight to let other potential next handlers to
	// process the OPTIONS method. Turn this on if your application handles OPTIONS.
	OptionsPassthrough bool

	// Debugging flag adds additional output to debug server side CORS issues
	Debug bool
}

CorsOptions is a configuration container to setup the CORS middleware.

type DefaultLogFormatter

type DefaultLogFormatter struct {
	Logger  LoggerInterface
	NoColor bool
}

DefaultLogFormatter is a simple logger that implements a LogFormatter.

func (*DefaultLogFormatter) NewLogEntry

func (l *DefaultLogFormatter) NewLogEntry(r *http.Request) LogEntry

NewLogEntry creates a new LogEntry for the request.

type EncoderFunc

type EncoderFunc func(w io.Writer, level int) io.Writer

An EncoderFunc is a function that wraps the provided io.Writer with a streaming compression algorithm and returns it.

In case of failure, the function should return nil.

type HeaderRoute

type HeaderRoute struct {
	Middleware func(next http.Handler) http.Handler
	MatchOne   Pattern
	MatchAny   []Pattern
}

func (HeaderRoute) IsMatch

func (r HeaderRoute) IsMatch(value string) bool

type HeaderRouter

type HeaderRouter map[string][]HeaderRoute

func RouteHeaders

func RouteHeaders() HeaderRouter

RouteHeaders is a neat little header-based router that allows you to direct the flow of a request through a middleware stack based on a request header.

For example, lets say you'd like to setup multiple routers depending on the request Host header, you could then do something as so:

r := chi.NewRouter()
rSubdomain := chi.NewRouter()
r.Use(middleware.RouteHeaders().
	Route("Host", "example.com", middleware.New(r)).
	Route("Host", "*.example.com", middleware.New(rSubdomain)).
	Handler)
r.Get("/", h)
rSubdomain.Get("/", h2)

Another example, imagine you want to setup multiple CORS handlers, where for your origin servers you allow authorized requests, but for third-party public requests, authorization is disabled.

r := chi.NewRouter()
r.Use(middleware.RouteHeaders().
	Route("Origin", "https://app.skyweaver.net", cors.Handler(cors.Options{
		AllowedOrigins:   []string{"https://api.skyweaver.net"},
		AllowedMethods:   []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
		AllowedHeaders:   []string{"Accept", "Authorization", "Content-Type"},
		AllowCredentials: true, // <----------<<< allow credentials
	})).
	Route("Origin", "*", cors.Handler(cors.Options{
		AllowedOrigins:   []string{"*"},
		AllowedMethods:   []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"},
		AllowedHeaders:   []string{"Accept", "Content-Type"},
		AllowCredentials: false, // <----------<<< do not allow credentials
	})).
	Handler)

func (HeaderRouter) Handler

func (hr HeaderRouter) Handler(next http.Handler) http.Handler

func (HeaderRouter) Route

func (hr HeaderRouter) Route(header, match string, middlewareHandler func(next http.Handler) http.Handler) HeaderRouter

func (HeaderRouter) RouteAny

func (hr HeaderRouter) RouteAny(header string, match []string, middlewareHandler func(next http.Handler) http.Handler) HeaderRouter

func (HeaderRouter) RouteDefault

func (hr HeaderRouter) RouteDefault(handler func(next http.Handler) http.Handler) HeaderRouter

type Log

type Log interface {
	Printf(string, ...interface{})
}

Log generic interface for logger

type LogEntry

type LogEntry interface {
	Write(status, bytes int, header http.Header, elapsed time.Duration, extra interface{})
	Panic(v interface{}, stack []byte)
}

LogEntry records the final log when a request completes. See defaultLogEntry for an example implementation.

func GetLogEntry

func GetLogEntry(r *http.Request) LogEntry

GetLogEntry returns the in-context LogEntry for a request.

type LogFormatter

type LogFormatter interface {
	NewLogEntry(r *http.Request) LogEntry
}

LogFormatter initiates the beginning of a new LogEntry per request. See DefaultLogFormatter for an example implementation.

type LoggerInterface

type LoggerInterface interface {
	Print(v ...interface{})
}

LoggerInterface accepts printing to stdlib logger or compatible logger.

type Pattern

type Pattern struct {
	// contains filtered or unexported fields
}

func NewPattern

func NewPattern(value string) Pattern

func (Pattern) Match

func (p Pattern) Match(v string) bool

type ThrottleOpts

type ThrottleOpts struct {
	RetryAfterFn   func(ctxDone bool) time.Duration
	Limit          int
	BacklogLimit   int
	BacklogTimeout time.Duration
}

ThrottleOpts represents a set of throttling options.

type WrapResponseWriter

type WrapResponseWriter interface {
	http.ResponseWriter
	// Status returns the HTTP status of the request, or 0 if one has not
	// yet been sent.
	Status() int
	// BytesWritten returns the total number of bytes sent to the client.
	BytesWritten() int
	// Tee causes the response body to be written to the given io.Writer in
	// addition to proxying the writes through. Only one io.Writer can be
	// tee'd to at once: setting a second one will overwrite the first.
	// Writes will be sent to the proxy before being written to this
	// io.Writer. It is illegal for the tee'd writer to be modified
	// concurrently with writes.
	Tee(io.Writer)
	// Unwrap returns the original proxied target.
	Unwrap() http.ResponseWriter
}

WrapResponseWriter is a proxy around an http.ResponseWriter that allows you to hook into various parts of the response process.

func NewWrapResponseWriter

func NewWrapResponseWriter(w http.ResponseWriter, protoMajor int) WrapResponseWriter

NewWrapResponseWriter wraps an http.ResponseWriter, returning a proxy that allows you to hook into various parts of the response process.

Jump to

Keyboard shortcuts

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