Documentation
¶
Index ¶
- Constants
- Variables
- func CleanPath(p string) string
- func DefaultHandleRecovery(c *Context, _ any)
- func DefaultMethodNotAllowedHandler(c *Context)
- func DefaultNotFoundHandler(c *Context)
- func DefaultOptionsHandler(c *Context)
- func ErrNotSupported() error
- func NewTestContext(w http.ResponseWriter, r *http.Request, opts ...GlobalOption) (*Router, *Context)
- func WithClientIPMatcher(ip string) interface{ ... }
- func WithClientIPResolver(resolver ClientIPResolver) interface{ ... }
- func WithHandleTrailingSlash(opt TrailingSlashOption) interface{ ... }
- func WithHeaderMatcher(key, value string) interface{ ... }
- func WithHeaderRegexpMatcher(key, expr string) interface{ ... }
- func WithMatcher(matchers ...Matcher) interface{ ... }
- func WithMiddleware(m ...MiddlewareFunc) interface{ ... }
- func WithQueryMatcher(key, value string) interface{ ... }
- func WithQueryRegexpMatcher(key, expr string) interface{ ... }
- type ClientIPResolver
- type ClientIPResolverFunc
- type ClientIpMatcher
- type Context
- func (c *Context) AddHeader(key, value string)
- func (c *Context) Blob(code int, contentType string, buf []byte) (err error)
- func (c *Context) ClientIP() (*net.IPAddr, error)
- func (c *Context) Clone() *Context
- func (c *Context) CloneWith(w ResponseWriter, r *http.Request) *Context
- func (c *Context) Close()
- func (c *Context) Header(key string) string
- func (c *Context) Host() string
- func (c *Context) Method() string
- func (c *Context) Param(name string) string
- func (c *Context) Params() iter.Seq[Param]
- func (c *Context) Path() string
- func (c *Context) Pattern() string
- func (c *Context) QueryParam(name string) string
- func (c *Context) QueryParams() url.Values
- func (c *Context) RemoteIP() *net.IPAddr
- func (c *Context) Request() *http.Request
- func (c *Context) Route() *Route
- func (c *Context) Router() *Router
- func (c *Context) Scope() HandlerScope
- func (c *Context) SetHeader(key, value string)
- func (c *Context) SetRequest(r *http.Request)
- func (c *Context) SetWriter(w ResponseWriter)
- func (c *Context) Stream(code int, contentType string, r io.Reader) (err error)
- func (c *Context) String(code int, s string) (err error)
- func (c *Context) Writer() ResponseWriter
- type FixedPathOption
- type GlobalOption
- func AllowRegexpParam(enable bool) GlobalOption
- func DefaultOptions() GlobalOption
- func WithAutoOptions(enable bool) GlobalOption
- func WithHandleFixedPath(opt FixedPathOption) GlobalOption
- func WithMaxRouteMatchers(max int) GlobalOption
- func WithMaxRouteParamKeyBytes(max int) GlobalOption
- func WithMaxRouteParams(max int) GlobalOption
- func WithMiddlewareFor(scope HandlerScope, m ...MiddlewareFunc) GlobalOption
- func WithNoMethod(enable bool) GlobalOption
- func WithNoMethodHandler(handler HandlerFunc) GlobalOption
- func WithNoRouteHandler(handler HandlerFunc) GlobalOption
- func WithOptionsHandler(handler HandlerFunc) GlobalOption
- func WithPrettyLogs() GlobalOption
- func WithSystemWideOptions(enable bool) GlobalOption
- type HandlerFunc
- type HandlerScope
- type HeaderMatcher
- type HeaderRegexpMatcher
- type Iter
- type Matcher
- type MatcherOption
- type MiddlewareFunc
- type Param
- type Params
- type QueryMatcher
- type QueryRegexpMatcher
- type RecoveryFunc
- type RequestContext
- type ResponseWriter
- type Route
- func (r *Route) Annotation(key any) any
- func (r *Route) ClientIPResolver() ClientIPResolver
- func (r *Route) Handle(c *Context)
- func (r *Route) HandleMiddleware(c *Context, _ ...struct{})
- func (r *Route) Hostname() string
- func (r *Route) Matchers() iter.Seq[Matcher]
- func (r *Route) MatchersLen() int
- func (r *Route) MatchersPriority() uint
- func (r *Route) Methods() iter.Seq[string]
- func (r *Route) Name() string
- func (r *Route) Params() iter.Seq[string]
- func (r *Route) ParamsLen() int
- func (r *Route) Path() string
- func (r *Route) Pattern() string
- func (r *Route) String() string
- func (r *Route) TrailingSlashOption() TrailingSlashOption
- type RouteConflictError
- type RouteMatch
- type RouteNameConflictError
- type RouteOption
- type Router
- func (fox *Router) Add(methods []string, pattern string, handler HandlerFunc, opts ...RouteOption) (*Route, error)
- func (fox *Router) AddRoute(route *Route) error
- func (fox *Router) Delete(methods []string, pattern string, opts ...MatcherOption) (*Route, error)
- func (fox *Router) DeleteRoute(route *Route) (*Route, error)
- func (fox *Router) HandleNoRoute(c *Context)
- func (fox *Router) Has(methods []string, pattern string, matchers ...Matcher) bool
- func (fox *Router) Iter() Iter
- func (fox *Router) Len() int
- func (fox *Router) Lookup(w ResponseWriter, r *http.Request) (route *Route, cc *Context, tsr bool)
- func (fox *Router) Match(method string, r *http.Request) (route *Route, tsr bool)
- func (fox *Router) Mount() HandlerFunc
- func (fox *Router) MustAdd(methods []string, pattern string, handler HandlerFunc, opts ...RouteOption) *Route
- func (fox *Router) Name(name string) *Route
- func (fox *Router) NewRoute(methods []string, pattern string, handler HandlerFunc, opts ...RouteOption) (*Route, error)
- func (fox *Router) Route(methods []string, pattern string, matchers ...Matcher) *Route
- func (fox *Router) RouterInfo() RouterInfo
- func (fox *Router) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (fox *Router) Txn(write bool) *Txn
- func (fox *Router) Update(methods []string, pattern string, handler HandlerFunc, opts ...RouteOption) (*Route, error)
- func (fox *Router) UpdateRoute(route *Route) error
- func (fox *Router) Updates(fn func(txn *Txn) error) error
- func (fox *Router) View(fn func(txn *Txn) error) error
- type RouterInfo
- type TrailingSlashOption
- type Txn
- func (txn *Txn) Abort()
- func (txn *Txn) Add(methods []string, pattern string, handler HandlerFunc, opts ...RouteOption) (*Route, error)
- func (txn *Txn) AddRoute(route *Route) error
- func (txn *Txn) Commit()
- func (txn *Txn) Delete(methods []string, pattern string, opts ...MatcherOption) (*Route, error)
- func (txn *Txn) DeleteRoute(route *Route) (*Route, error)
- func (txn *Txn) Has(methods []string, pattern string, matchers ...Matcher) bool
- func (txn *Txn) Iter() Iter
- func (txn *Txn) Len() int
- func (txn *Txn) Lookup(w ResponseWriter, r *http.Request) (route *Route, cc *Context, tsr bool)
- func (txn *Txn) Match(method string, r *http.Request) (route *Route, tsr bool)
- func (txn *Txn) Name(name string) *Route
- func (txn *Txn) Route(methods []string, pattern string, matchers ...Matcher) *Route
- func (txn *Txn) Snapshot() *Txn
- func (txn *Txn) Truncate() error
- func (txn *Txn) Update(methods []string, pattern string, handler HandlerFunc, opts ...RouteOption) (*Route, error)
- func (txn *Txn) UpdateRoute(route *Route) error
Examples ¶
Constants ¶
const ( MIMEApplicationJSON = "application/json" MIMEApplicationJSONCharsetUTF8 = MIMEApplicationJSON + "; " + charsetUTF8 MIMEApplicationJavaScript = "application/javascript" MIMEApplicationJavaScriptCharsetUTF8 = MIMEApplicationJavaScript + "; " + charsetUTF8 MIMEApplicationXML = "application/xml" MIMEApplicationXMLCharsetUTF8 = MIMEApplicationXML + "; " + charsetUTF8 MIMETextXML = "text/xml" MIMETextXMLCharsetUTF8 = MIMETextXML + "; " + charsetUTF8 MIMEApplicationForm = "application/x-www-form-urlencoded" MIMEApplicationProtobuf = "application/protobuf" MIMEApplicationMsgpack = "application/msgpack" MIMETextHTML = "text/html" MIMETextHTMLCharsetUTF8 = MIMETextHTML + "; " + charsetUTF8 MIMETextPlain = "text/plain" MIMETextPlainCharsetUTF8 = MIMETextPlain + "; " + charsetUTF8 MIMEMultipartForm = "multipart/form-data" MIMEOctetStream = "application/octet-stream" )
MIME types
const ( HeaderAccept = "Accept" HeaderAcceptEncoding = "Accept-Encoding" HeaderAllow = "Allow" HeaderAuthorization = "Authorization" HeaderProxyAuthorization = "Proxy-Authorization" HeaderContentDisposition = "Content-Disposition" HeaderContentEncoding = "Content-Encoding" HeaderContentLength = "Content-Length" HeaderContentType = "Content-Type" HeaderCookie = "Cookie" HeaderSetCookie = "Set-Cookie" HeaderIfModifiedSince = "If-Modified-Since" HeaderLastModified = "Last-Modified" HeaderLocation = "Location" HeaderRetryAfter = "Retry-After" HeaderUpgrade = "Upgrade" HeaderVary = "Vary" HeaderWWWAuthenticate = "WWW-Authenticate" HeaderXForwardedFor = "X-Forwarded-For" HeaderForwarded = "Forwarded" HeaderXForwardedProto = "X-Forwarded-Proto" HeaderXForwardedProtocol = "X-Forwarded-Protocol" HeaderXForwardedSsl = "X-Forwarded-Ssl" HeaderXUrlScheme = "X-Url-Scheme" HeaderXHTTPMethodOverride = "X-HTTP-Method-Override" HeaderXRequestID = "X-Request-Id" HeaderXCorrelationID = "X-Correlation-Id" HeaderXRequestedWith = "X-Requested-With" HeaderServer = "Server" HeaderOrigin = "Origin" HeaderHost = "Host" HeaderCacheControl = "Cache-Control" HeaderConnection = "Connection" HeaderETag = "ETag" // Access control HeaderAccessControlRequestMethod = "Access-Control-Request-Method" HeaderAccessControlRequestHeaders = "Access-Control-Request-Headers" HeaderAccessControlAllowOrigin = "Access-Control-Allow-Origin" HeaderAccessControlAllowMethods = "Access-Control-Allow-Methods" HeaderAccessControlAllowHeaders = "Access-Control-Allow-Headers" HeaderAccessControlAllowCredentials = "Access-Control-Allow-Credentials" HeaderAccessControlExposeHeaders = "Access-Control-Expose-Headers" HeaderAccessControlMaxAge = "Access-Control-Max-Age" // Security HeaderStrictTransportSecurity = "Strict-Transport-Security" HeaderXContentTypeOptions = "X-Content-Type-Options" HeaderXXSSProtection = "X-XSS-Protection" HeaderXFrameOptions = "X-Frame-Options" HeaderContentSecurityPolicy = "Content-Security-Policy" HeaderContentSecurityPolicyReportOnly = "Content-Security-Policy-Report-Only" // nolint:gosec HeaderXCSRFToken = "X-CSRF-Token" HeaderReferrerPolicy = "Referrer-Policy" // Platform Header for single IP HeaderCFConnectionIP = "CF-Connecting-IP" HeaderTrueClientIP = "True-Client-IP" HeaderFastClientIP = "Fastly-Client-IP" HeaderXAzureClientIP = "X-Azure-ClientIP" HeaderXAzureSocketIP = "X-Azure-SocketIP" HeaderXAppengineRemoteAddr = "X-Appengine-Remote-Addr" HeaderFlyClientIP = "Fly-Client-IP" HeaderXRealIP = "X-Real-Ip" )
Headers
const ( // LoggerStatusKey is the key used by the built-in logger middleware for the HTTP response status code // when the log method is called. The associated [slog.Value] is a string. LoggerStatusKey = "status" // LoggerMethodKey is the key used by the built-in logger middleware for the HTTP request method. // The associated [slog.Value] is a string. LoggerMethodKey = "method" // LoggerHostKey is the key used by the built-in logger middleware for the request host. // The associated [slog.Value] is a string. LoggerHostKey = "host" // LoggerPathKey is the key used by the built-in logger middleware for the request path. // The associated [slog.Value] is a string. LoggerPathKey = "path" // LoggerLatencyKey is the key used by the built-in logger middleware for the request processing duration. // The associated [slog.Value] is a time.Duration. LoggerLatencyKey = "latency" // LoggerSizeKey is the key used by the built-in logger middleware for the response body size. // The associated [slog.Value] is an int. LoggerSizeKey = "size" // LoggerLocationKey is the key used by the built-in logger middleware for redirect location header. // The associated [slog.Value] is a string. LoggerLocationKey = "location" )
Keys for "built-in" logger attribute for the logger middleware.
const ( // LoggerRouteKey is the key used by the built-in recovery middleware for the matched route // when the log method is called. The associated [slog.Value] is a string. LoggerRouteKey = "route" // LoggerParamsKey is the key used by the built-in recovery middleware for route parameters // when the log method is called. The associated [slog.Value] is a [slog.GroupValue] containing parameter // key-value pairs. LoggerParamsKey = "params" // LoggerPanicKey is the key used by the built-in recovery middleware for the panic value // when the log method is called. The associated [slog.Value] is any. LoggerPanicKey = "panic" )
Keys for "built-in" logger attribute for the recovery middleware. Keys for "built-in" logger attributes used by the recovery middleware.
Variables ¶
var ( ErrRouteNotFound = errors.New("route not found") ErrRouteConflict = errors.New("route conflict") ErrRouteNameExist = errors.New("route name already registered") ErrInvalidRoute = errors.New("invalid route") ErrDiscardedResponseWriter = errors.New("discarded response writer") ErrNoClientIPResolver = errors.New("no client ip resolver") ErrReadOnlyTxn = errors.New("write on read-only transaction") ErrSettledTxn = errors.New("transaction settled") ErrParamKeyTooLarge = errors.New("parameter key too large") ErrTooManyParams = errors.New("too many params") ErrTooManyMatchers = errors.New("too many matchers") ErrRegexpNotAllowed = errors.New("regexp not allowed") ErrInvalidConfig = errors.New("invalid config") ErrInvalidMatcher = errors.New("invalid matcher") )
var ( MethodGet = []string{http.MethodGet} MethodHead = []string{http.MethodHead} MethodPost = []string{http.MethodPost} MethodPut = []string{http.MethodPut} MethodPatch = []string{http.MethodPatch} MethodDelete = []string{http.MethodDelete} MethodConnect = []string{http.MethodConnect} MethodOptions = []string{http.MethodOptions} MethodTrace = []string{http.MethodTrace} )
Common HTTP methods.
var MethodAny []string
Functions ¶
func CleanPath ¶
CleanPath is the URL version of path.Clean, it returns a canonical URL path for p, eliminating . and .. elements.
The following rules are applied iteratively until no further processing can be done:
- Replace multiple slashes with a single slash.
- Eliminate each . path name element (the current directory).
- Eliminate each inner .. path name element (the parent directory) along with the non-.. element that precedes it.
- Eliminate .. elements that begin a rooted path: that is, replace "/.." by "/" at the beginning of a path.
If the result of this process is an empty string, "/" is returned
func DefaultHandleRecovery ¶ added in v0.7.2
DefaultHandleRecovery is a default implementation of the RecoveryFunc. It responds with a status code 500 and writes a generic error message.
func DefaultMethodNotAllowedHandler ¶ added in v0.7.6
func DefaultMethodNotAllowedHandler(c *Context)
DefaultMethodNotAllowedHandler is a simple HandlerFunc that replies to each request with a “405 Method Not Allowed” reply.
func DefaultNotFoundHandler ¶ added in v0.7.6
func DefaultNotFoundHandler(c *Context)
DefaultNotFoundHandler is a simple HandlerFunc that replies to each request with a “404 page not found” reply.
func DefaultOptionsHandler ¶ added in v0.13.0
func DefaultOptionsHandler(c *Context)
DefaultOptionsHandler is a simple HandlerFunc that replies to each request with a "200 OK" reply.
func ErrNotSupported ¶ added in v0.16.0
func ErrNotSupported() error
ErrNotSupported returns an error that Is http.ErrNotSupported, but is not == to it.
func NewTestContext ¶ added in v0.7.0
func NewTestContext(w http.ResponseWriter, r *http.Request, opts ...GlobalOption) (*Router, *Context)
NewTestContext returns a new Router and its associated Context, designed only for testing purpose.
func WithClientIPMatcher ¶ added in v0.26.0
func WithClientIPMatcher(ip string) interface { RouteOption MatcherOption }
WithClientIPMatcher attaches a client IP address matcher to a route. The matcher ensures that requests are only routed to the handler if the client IP address matches the specified CIDR notation or IP address. The ip parameter accepts both single IP addresses (e.g., "192.168.1.1") and CIDR ranges (e.g., "192.168.1.0/24"). Multiple matchers can be attached to the same route. All matchers must match for the route to be eligible. See WithClientIPResolver to configure a resolver for obtaining the "real" client IP.
func WithClientIPResolver ¶ added in v0.19.0
func WithClientIPResolver(resolver ClientIPResolver) interface { GlobalOption RouteOption }
WithClientIPResolver sets the resolver for obtaining the "real" client IP address from HTTP requests. This resolver is used by the Context.ClientIP method. The resolver must be chosen and tuned for your network configuration to ensure it never returns an error -- i.e., never fails to find a candidate for the "real" IP. Consequently, getting an error result should be treated as an application error, perhaps even worthy of panicking. There is no sane default, so if no resolver is configured, Context.ClientIP returns ErrNoClientIPResolver.
This option can be applied on a per-route basis or globally:
- If applied globally, it affects all routes by default.
- If applied to a specific route, it will override the global setting for that route.
- Setting the resolver to nil is equivalent to no resolver configured.
func WithHandleTrailingSlash ¶ added in v0.24.0
func WithHandleTrailingSlash(opt TrailingSlashOption) interface { GlobalOption RouteOption }
WithHandleTrailingSlash configures how the router handles trailing slashes in request paths.
Available slash handling modes:
- StrictSlash: Routes are matched exactly as registered. /foo/bar and /foo/bar/ are treated as different routes.
- RelaxedSlash: Routes match regardless of trailing slash. Both /foo/bar and /foo/bar/ match the same route.
- RedirectSlash: When a route is not found, but exists with/without a trailing slash, issues a redirect to the correct path.
Redirects use URL.RawPath if set, otherwise URL.Path.
This option can be applied on a per-route basis or globally:
- If applied globally, it affects all routes by default.
- If applied to a specific route, it will override the global setting for that route.
If both /foo/bar and /foo/bar/ are explicitly registered, the exact match always takes precedence. The trailing slash handling logic only applies when there is no direct match but a match would be possible by adding or removing a trailing slash.
func WithHeaderMatcher ¶ added in v0.26.0
func WithHeaderMatcher(key, value string) interface { RouteOption MatcherOption }
WithHeaderMatcher attaches an HTTP header matcher to a route. The matcher ensures that requests are only routed to the handler if the specified header matches the given value. Multiple matchers can be attached to the same route. All matchers must match for the route to be eligible.
func WithHeaderRegexpMatcher ¶ added in v0.26.0
func WithHeaderRegexpMatcher(key, expr string) interface { RouteOption MatcherOption }
WithHeaderRegexpMatcher attaches an HTTP header matcher with regular expression support to a route. The matcher ensures that requests are only routed to the handler if the specified header value matches the given regular expression. The expression is automatically anchored at both ends, requiring a full match of the header value. Multiple matchers can be attached to the same route. All matchers must match for the route to be eligible.
func WithMatcher ¶ added in v0.26.0
func WithMatcher(matchers ...Matcher) interface { RouteOption MatcherOption }
WithMatcher attaches a custom matcher to a route. Matchers allow for advanced request routing based on conditions beyond the request host, path and method. Multiple matchers can be attached to the same route. All matchers must match for the route to be eligible.
func WithMiddleware ¶ added in v0.7.0
func WithMiddleware(m ...MiddlewareFunc) interface { GlobalOption RouteOption }
WithMiddleware attaches middleware to the router or to a specific route. The middlewares are executed in the order they are added. When applied globally, the middleware affects all handlers, including special handlers such as NotFound, MethodNotAllowed, AutoOption, and the internal redirect handler.
This option can be applied on a per-route basis or globally: - If applied globally, the middleware will be applied to all routes and handlers by default. - If applied to a specific route, the middleware will only apply to that route and will be chained after any global middleware.
Example ¶
This example demonstrates how to register a global middleware that will be applied to all routes.
// Define a custom middleware to measure the time taken for request processing and
// log the URL, route, time elapsed, and status code.
metrics := func(next HandlerFunc) HandlerFunc {
return func(c *Context) {
start := time.Now()
next(c)
log.Printf(
"url=%s; route=%s; time=%d; status=%d",
c.Request().URL,
c.Pattern(),
time.Since(start),
c.Writer().Status(),
)
}
}
f, _ := NewRouter(WithMiddleware(metrics))
f.MustAdd([]string{http.MethodGet, http.MethodHead}, "/hello/{name}", func(c *Context) {
_ = c.String(200, fmt.Sprintf("Hello %s\n", c.Param("name")))
})
func WithQueryMatcher ¶ added in v0.26.0
func WithQueryMatcher(key, value string) interface { RouteOption MatcherOption }
WithQueryMatcher attaches a query parameter matcher to a route. The matcher ensures that requests are only routed to the handler if the specified query parameter matches the given value. Multiple matchers can be attached to the same route. All matchers must match for the route to be eligible.
func WithQueryRegexpMatcher ¶ added in v0.26.0
func WithQueryRegexpMatcher(key, expr string) interface { RouteOption MatcherOption }
WithQueryRegexpMatcher attaches a query parameter matcher with regular expression support to a route. The matcher ensures that requests are only routed to the handler if the specified query parameter value matches the given regular expression. The expression is automatically anchored at both ends, requiring a full match of the parameter value. Multiple matchers can be attached to the same route. All matchers must match for the route to be eligible.
Types ¶
type ClientIPResolver ¶ added in v0.19.0
type ClientIPResolver interface {
// ClientIP returns the "real" client IP according to the implemented resolver. It returns an error if no valid IP
// address can be derived. This is typically considered a misconfiguration error, unless the resolver involves
// obtaining an untrustworthy or optional value.
ClientIP(c RequestContext) (*net.IPAddr, error)
}
ClientIPResolver define a resolver for obtaining the "real" client IP from HTTP requests. The resolver used must be chosen and tuned for your network configuration. This should result in a resolver never returning an error i.e., never failing to find a candidate for the "real" IP. Consequently, getting an error result should be treated as an application error, perhaps even worthy of panicking. Builtin best practices resolver can be found in the github.com/tigerwill90/fox/clientip package.
type ClientIPResolverFunc ¶ added in v0.19.0
type ClientIPResolverFunc func(c RequestContext) (*net.IPAddr, error)
The ClientIPResolverFunc type is an adapter to allow the use of ordinary functions as ClientIPResolver. If f is a function with the appropriate signature, ClientIPResolverFunc(f) is a ClientIPResolverFunc that calls f.
func (ClientIPResolverFunc) ClientIP ¶ added in v0.19.0
func (f ClientIPResolverFunc) ClientIP(c RequestContext) (*net.IPAddr, error)
ClientIP calls f(c).
type ClientIpMatcher ¶ added in v0.26.0
type ClientIpMatcher struct {
// contains filtered or unexported fields
}
func MatchClientIP ¶ added in v0.26.0
func MatchClientIP(ip string) (ClientIpMatcher, error)
func (ClientIpMatcher) Equal ¶ added in v0.26.0
func (m ClientIpMatcher) Equal(matcher Matcher) bool
func (ClientIpMatcher) IPNet ¶ added in v0.26.0
func (m ClientIpMatcher) IPNet() *net.IPNet
func (ClientIpMatcher) Match ¶ added in v0.26.0
func (m ClientIpMatcher) Match(c RequestContext) bool
func (ClientIpMatcher) String ¶ added in v0.26.0
func (m ClientIpMatcher) String() string
type Context ¶ added in v0.7.0
type Context struct {
// contains filtered or unexported fields
}
Context represents the context of the current HTTP request. It provides methods to access request data and to write a response. Be aware that the Context API is not thread-safe and its lifetime should be limited to the duration of the HandlerFunc execution, as the Context may be reused as soon as the handler returns.
func NewTestContextOnly ¶ added in v0.7.0
func NewTestContextOnly(w http.ResponseWriter, r *http.Request, opts ...GlobalOption) *Context
NewTestContextOnly returns a new Context designed only for testing purpose.
func (*Context) AddHeader ¶ added in v0.18.0
AddHeader add the response header for the given key to the specified value.
func (*Context) Blob ¶ added in v0.7.0
Blob sends a byte slice with the specified status code and content type.
func (*Context) ClientIP ¶ added in v0.14.0
ClientIP returns the "real" client IP address based on the configured ClientIPResolver. The resolver is set using the WithClientIPResolver option. If no resolver is configured, the method returns error ErrNoClientIPResolver.
The resolver used must be chosen and tuned for your network configuration. This should result in a resolver never returning an error -- i.e., never failing to find a candidate for the "real" IP. Consequently, getting an error result should be treated as an application error, perhaps even worthy of panicking.
func (*Context) Clone ¶ added in v0.7.0
Clone returns a deep copy of the Context that is safe to use after the HandlerFunc returns. Any attempt to write on the ResponseWriter will panic with the error ErrDiscardedResponseWriter.
func (*Context) CloneWith ¶ added in v0.11.0
func (c *Context) CloneWith(w ResponseWriter, r *http.Request) *Context
CloneWith returns a shallow copy of the current Context, substituting its ResponseWriter and http.Request with the provided ones. The method is designed for zero allocation during the copy process. The caller is responsible for closing the returned Context by calling Context.Close when it is no longer needed. This functionality is particularly beneficial for middlewares that need to wrap their custom ResponseWriter while preserving the state of the original Context.
func (*Context) Close ¶ added in v0.26.0
func (c *Context) Close()
Close releases the context to be reused later. This method must be called for contexts obtained via Context.CloneWith, Router.Lookup, or Txn.Lookup. Contexts passed to a HandlerFunc are managed automatically by the router and should not be closed manually. See also Context for more details.
func (*Context) Header ¶ added in v0.7.2
Header retrieves the value of the request header for the given key.
func (*Context) Params ¶ added in v0.7.0
Params returns an iterator over the matched wildcard parameters for the current route.
func (*Context) Path ¶ added in v0.7.0
Path returns the request url.URL.RawPath if not empty, or fallback to the url.URL.Path.
func (*Context) Pattern ¶ added in v0.18.0
Pattern returns the registered route pattern or an empty string if the handler is called in a scope other than RouteHandler.
func (*Context) QueryParam ¶ added in v0.7.0
QueryParam returns the first query value associated with the given key. The query parameters are parsed and cached on first access.
func (*Context) QueryParams ¶ added in v0.7.0
QueryParams parses the http.Request raw query and returns the corresponding values. The result is cached after the first call.
func (*Context) RemoteIP ¶ added in v0.14.0
RemoteIP parses the IP from http.Request.RemoteAddr, normalizes it, and returns a net.IPAddr. It never returns nil, even if parsing the IP fails.
func (*Context) Request ¶ added in v0.7.0
Request returns the http.Request.
func (*Context) Route ¶ added in v0.17.0
Route returns the registered Route or nil if the handler is called in a scope other than RouteHandler.
func (*Context) Scope ¶ added in v0.17.0
func (c *Context) Scope() HandlerScope
Scope returns the HandlerScope associated with the current Context. This indicates the scope in which the handler is being executed, such as RouteHandler, NoRouteHandler, etc.
func (*Context) SetHeader ¶ added in v0.7.3
SetHeader sets the response header for the given key to the specified value.
func (*Context) SetRequest ¶ added in v0.7.0
SetRequest sets the http.Request.
func (*Context) SetWriter ¶ added in v0.7.0
func (c *Context) SetWriter(w ResponseWriter)
SetWriter sets the ResponseWriter.
func (*Context) Stream ¶ added in v0.7.0
Stream sends data from an io.Reader with the specified status code and content type.
func (*Context) String ¶ added in v0.7.0
String sends a formatted string with the specified status code.
func (*Context) Writer ¶ added in v0.7.0
func (c *Context) Writer() ResponseWriter
Writer returns the ResponseWriter.
type FixedPathOption ¶ added in v0.24.0
type FixedPathOption uint8
const ( StrictPath FixedPathOption = iota RelaxedPath RedirectPath )
type GlobalOption ¶ added in v0.16.0
type GlobalOption interface {
// contains filtered or unexported methods
}
func AllowRegexpParam ¶ added in v0.25.0
func AllowRegexpParam(enable bool) GlobalOption
AllowRegexpParam enables support for regular expressions in route parameters. When enabled, parameters can include regex patterns (e.g., {id:[0-9]+}). When disabled, routes containing regex patterns will fail with and error that Is ErrInvalidRoute and ErrRegexpNotAllowed.
func DefaultOptions ¶ added in v0.7.0
func DefaultOptions() GlobalOption
DefaultOptions configures the router with sensible production defaults:
- Enables automatic OPTIONS responses (WithAutoOptions)
- Enables 405 Method Not Allowed responses (WithNoMethod)
- Enables regular expression support in route parameters (AllowRegexpParam)
- Enables redirect-based path correction for trailing slashes (WithHandleTrailingSlash with RedirectSlash)
- Enables redirect-based path correction for non-canonical paths (WithHandleFixedPath with RedirectPath)
For development, consider combining this with WithPrettyLogs to add debugging middleware.
func WithAutoOptions ¶ added in v0.9.0
func WithAutoOptions(enable bool) GlobalOption
WithAutoOptions enables automatic responses to OPTIONS requests with, by default, a 204 status code. For regular OPTIONS requests, the router responds with the "Allow" header listing methods registered for the matched routes, or calls the NoRoute handler if no route matches. For CORS preflight requests, the router always responds to all request by calling the OPTIONS handler. Note that custom OPTIONS handler take priority over automatic replies. This option is automatically enabled when providing a custom handler with the option WithOptionsHandler.
func WithHandleFixedPath ¶ added in v0.24.0
func WithHandleFixedPath(opt FixedPathOption) GlobalOption
WithHandleFixedPath configures how the router handles non-canonical request paths containing extraneous elements like double slashes, dots, or parent directory references.
Available path handling modes:
- StrictPath: No path cleaning is performed. Routes are matched only as requested (disables this feature).
- RelaxedPath: After normal lookup fails, tries matching with a cleaned path. If found, serves the handler directly.
- RedirectPath: After normal lookup fails, tries matching with a cleaned path. If found, redirects to the clean path.
Redirects use URL.RawPath if set, otherwise URL.Path.
This option applies globally to all routes and cannot be configured per-route. See CleanPath for details on how paths are cleaned.
func WithMaxRouteMatchers ¶ added in v0.26.0
func WithMaxRouteMatchers(max int) GlobalOption
WithMaxRouteMatchers set the maximum number of matchers allowed in a route. The default max is math.MaxUint8. Routes exceeding this limit will fail with an error that Is ErrInvalidRoute and ErrTooManyMatchers.
func WithMaxRouteParamKeyBytes ¶ added in v0.19.0
func WithMaxRouteParamKeyBytes(max int) GlobalOption
WithMaxRouteParamKeyBytes set the maximum number of bytes allowed per parameter key in a route. The default max is math.MaxUint8. Routes with parameter keys exceeding this limit will fail with an error that Is ErrInvalidRoute and ErrParamKeyTooLarge.
func WithMaxRouteParams ¶ added in v0.19.0
func WithMaxRouteParams(max int) GlobalOption
WithMaxRouteParams set the maximum number of parameters allowed in a route. The default max is math.MaxUint8. Routes exceeding this limit will fail with an error that is ErrInvalidRoute and ErrTooManyParams.
func WithMiddlewareFor ¶ added in v0.7.6
func WithMiddlewareFor(scope HandlerScope, m ...MiddlewareFunc) GlobalOption
WithMiddlewareFor attaches middleware to the router for a specified scope. Middlewares provided will be chained in the order they were added. The scope parameter determines which types of handlers the middleware will be applied to. Possible scopes include RouteHandler (regular routes), NoRouteHandler, NoMethodHandler, RedirectSlashHandler, RedirectPathHandler, OptionsHandler, and any combination of these. Use this option when you need fine-grained control over where the middleware is applied.
func WithNoMethod ¶ added in v0.9.0
func WithNoMethod(enable bool) GlobalOption
WithNoMethod enable to returns 405 Method Not Allowed instead of 404 Not Found when the route exist for another http verb. The "Allow" header it automatically set before calling the handler. Note that this option is automatically enabled when providing a custom handler with the option WithNoMethodHandler.
func WithNoMethodHandler ¶ added in v0.9.0
func WithNoMethodHandler(handler HandlerFunc) GlobalOption
WithNoMethodHandler register an HandlerFunc which is called when the request cannot be routed, but the same route exist for other methods. The "Allow" header it automatically set before calling the handler. By default, the DefaultMethodNotAllowedHandler is used. Note that this option automatically enable WithNoMethod.
func WithNoRouteHandler ¶ added in v0.9.0
func WithNoRouteHandler(handler HandlerFunc) GlobalOption
WithNoRouteHandler register an HandlerFunc which is called when no matching route is found. By default, the DefaultNotFoundHandler is used.
func WithOptionsHandler ¶ added in v0.9.0
func WithOptionsHandler(handler HandlerFunc) GlobalOption
WithOptionsHandler register an HandlerFunc which is called on automatic OPTIONS requests. By default, the router respond with a 204 status code. The "Allow" header it automatically set before calling the handler (except for CORS preflight request). Note that custom OPTIONS handler take priority over automatic replies. By default, DefaultOptionsHandler is used. Note that this option automatically enable WithAutoOptions.
func WithPrettyLogs ¶ added in v0.26.0
func WithPrettyLogs() GlobalOption
WithPrettyLogs configures the router with human-readable, colorized logging optimized for terminal output. It registers the following middleware at the front of the chain:
- Recovery middleware for the RouteHandler scope, which catches panics and logs stack traces
- Logger middleware for AllHandlers scope, which logs request details
This option prioritizes readability over performance and is not recommended for high-throughput applications. For production workloads, prefer structured logging with a performance-oriented slog.Handler such as zerolog or zap.
func WithSystemWideOptions ¶ added in v0.26.0
func WithSystemWideOptions(enable bool) GlobalOption
WithSystemWideOptions enable automatic response for system-wide OPTIONS request (OPTIONS *). When this option is enabled, the router return a 200 OK status code with the "Allow" header containing all registered HTTP methods. This option is enabled by default.
type HandlerFunc ¶
type HandlerFunc func(c *Context)
HandlerFunc is a function type that responds to an HTTP request. It enforces the same contract as http.Handler but provides additional feature like matched wildcard route segments via the Context type. The Context is freed once the HandlerFunc returns and may be reused later to save resources. If you need to hold the context longer, you have to copy it (see Context.Clone method).
Similar to http.Handler, to abort a HandlerFunc so the client sees an interrupted response, panic with the value http.ErrAbortHandler.
HandlerFunc functions should be thread-safe, as they will be called concurrently.
func WrapF ¶
func WrapF(f http.HandlerFunc) HandlerFunc
WrapF is an adapter for wrapping http.HandlerFunc and returns a HandlerFunc function. The route parameters are being accessed by the wrapped handler through the context.
func WrapH ¶
func WrapH(h http.Handler) HandlerFunc
WrapH is an adapter for wrapping http.Handler and returns a HandlerFunc function. The route parameters are being accessed by the wrapped handler through the context.
type HandlerScope ¶ added in v0.17.0
type HandlerScope uint8
HandlerScope represents different scopes where a handler may be called. It also allows for fine-grained control over where middleware is applied.
const ( // RouteHandler scope applies to regular routes registered in the router. RouteHandler HandlerScope = 1 << (8 - 1 - iota) // NoRouteHandler scope applies to the NoRoute handler, which is invoked when no route matches the request. NoRouteHandler // NoMethodHandler scope applies to the NoMethod handler, which is invoked when a route exists, but the method is not allowed. NoMethodHandler // RedirectSlashHandler scope applies to the internal redirect trailing slash handler, used for handling requests with trailing slashes. RedirectSlashHandler // RedirectPathHandler scope applies to the internal redirect fixed path handler, used for handling requests that need path cleaning. RedirectPathHandler // OptionsHandler scope applies to the automatic OPTIONS handler, which handles pre-flight or cross-origin requests. OptionsHandler // AllHandlers is a combination of all the above scopes, which can be used to apply middlewares to all types of handlers. AllHandlers = RouteHandler | NoRouteHandler | NoMethodHandler | RedirectSlashHandler | RedirectPathHandler | OptionsHandler )
type HeaderMatcher ¶ added in v0.26.0
type HeaderMatcher struct {
// contains filtered or unexported fields
}
func MatchHeader ¶ added in v0.26.0
func MatchHeader(key, value string) (HeaderMatcher, error)
func (HeaderMatcher) Equal ¶ added in v0.26.0
func (m HeaderMatcher) Equal(matcher Matcher) bool
func (HeaderMatcher) Key ¶ added in v0.26.0
func (m HeaderMatcher) Key() string
func (HeaderMatcher) Match ¶ added in v0.26.0
func (m HeaderMatcher) Match(c RequestContext) bool
func (HeaderMatcher) String ¶ added in v0.26.0
func (m HeaderMatcher) String() string
func (HeaderMatcher) Value ¶ added in v0.26.0
func (m HeaderMatcher) Value() string
type HeaderRegexpMatcher ¶ added in v0.26.0
type HeaderRegexpMatcher struct {
// contains filtered or unexported fields
}
func MatchHeaderRegexp ¶ added in v0.26.0
func MatchHeaderRegexp(key, expr string) (HeaderRegexpMatcher, error)
func (HeaderRegexpMatcher) Equal ¶ added in v0.26.0
func (m HeaderRegexpMatcher) Equal(matcher Matcher) bool
func (HeaderRegexpMatcher) Key ¶ added in v0.26.0
func (m HeaderRegexpMatcher) Key() string
func (HeaderRegexpMatcher) Match ¶ added in v0.26.0
func (m HeaderRegexpMatcher) Match(c RequestContext) bool
func (HeaderRegexpMatcher) Regex ¶ added in v0.26.0
func (m HeaderRegexpMatcher) Regex() *regexp.Regexp
func (HeaderRegexpMatcher) String ¶ added in v0.26.0
func (m HeaderRegexpMatcher) String() string
type Iter ¶ added in v0.16.0
type Iter struct {
// contains filtered or unexported fields
}
Iter provide a set of range iterators for traversing registered methods and routes. Iter capture a point-in-time snapshot of the routing tree. Therefore, all iterators returned by Iter will not observe subsequent write on the router or on the transaction from which the Iter is created.
func (Iter) All ¶ added in v0.16.0
All returns a range iterator over all routes registered in the routing tree. The iterator reflect a snapshot of the routing tree at the time Iter is created. This function is safe for concurrent use by multiple goroutine and while mutation on routes are ongoing. See also Iter.PatternPrefix as an alternative.
Example ¶
f, _ := NewRouter()
it := f.Iter()
for route := range it.All() {
fmt.Println(slices.Collect(route.Methods()), route.Pattern())
}
func (Iter) Methods ¶ added in v0.16.0
Methods returns a range iterator over all HTTP methods registered in the routing tree. The iterator reflect a snapshot of the routing tree at the time Iter is created. This function is safe for concurrent use by multiple goroutine and while mutation on routes are ongoing.
Example ¶
f, _ := NewRouter()
it := f.Iter()
for method := range it.Methods() {
fmt.Println(method)
}
func (Iter) NamePrefix ¶ added in v0.26.0
NamePrefix returns a range iterator over all routes in the routing tree that match a given name prefix. The iterator reflect a snapshot of the routing tree at the time Iter is created. This function is safe for concurrent use by multiple goroutine and while mutation on routes are ongoing.
Example ¶
f, _ := NewRouter()
it := f.Iter()
for route := range it.NamePrefix("ns:default/") {
fmt.Println(slices.Collect(route.Methods()), route.Name())
}
func (Iter) Names ¶ added in v0.26.0
Names returns a range iterator over all routes registered in the routing tree with a name. The iterator reflect a snapshot of the routing tree at the time Iter is created. This function is safe for concurrent use by multiple goroutine and while mutation on routes are ongoing. See also Iter.NamePrefix as an alternative.
Example ¶
f, _ := NewRouter()
it := f.Iter()
for route := range it.Names() {
fmt.Println(slices.Collect(route.Methods()), route.Pattern())
}
func (Iter) PatternPrefix ¶ added in v0.26.0
PatternPrefix returns a range iterator over all routes in the routing tree that match a given pattern prefix. The iterator reflect a snapshot of the routing tree at the time Iter is created. This function is safe for concurrent use by multiple goroutine and while mutation on routes are ongoing. Note: Partial parameter syntax (e.g., /users/{name:) is not supported and will not match any routes.
Example ¶
f, _ := NewRouter()
it := f.Iter()
for route := range it.PatternPrefix("/v1/") {
fmt.Println(slices.Collect(route.Methods()), route.Pattern())
}
func (Iter) Routes ¶ added in v0.16.0
Routes returns a range iterator over all registered routes in the routing tree that exactly match the provided route pattern. The iterator reflect a snapshot of the routing tree at the time Iter is created. This function is safe for concurrent use by multiple goroutine and while mutation on routes are ongoing.
Example ¶
f, _ := NewRouter()
it := f.Iter()
for route := range it.Routes("/hello/{name}") {
fmt.Println(slices.Collect(route.Methods()), route.Pattern())
}
type Matcher ¶ added in v0.26.0
type Matcher interface {
// Match evaluates if the [RequestContext] satisfies this matcher.
Match(c RequestContext) bool
// Equal reports whether this matcher is semantically equivalent to another. Implementation must
// - Handle type checking: matchers of different types are not equal
// - Be reflexive: m.Equal(m) == true
// - Be symmetric: m.Equal(n) == n.Equal(m)
Equal(m Matcher) bool
}
Matcher evaluates if an HTTP request satisfies specific conditions. Matchers are evaluated after hostname and path matching succeeds. All matchers associated with a route must match for the route to be selected. Matcher implementations must be safe for concurrent use by multiple goroutines.
type MatcherOption ¶ added in v0.26.0
type MatcherOption interface {
// contains filtered or unexported methods
}
type MiddlewareFunc ¶ added in v0.7.0
type MiddlewareFunc func(next HandlerFunc) HandlerFunc
MiddlewareFunc is a function type for implementing HandlerFunc middleware. The returned HandlerFunc usually wraps the input HandlerFunc, allowing you to perform operations before and/or after the wrapped HandlerFunc is executed. MiddlewareFunc functions should be thread-safe, as they will be called concurrently.
func Logger ¶ added in v0.14.0
func Logger(handler slog.Handler) MiddlewareFunc
Logger returns a middleware that logs request information using the provided slog.Handler. It logs details such as the remote or client IP, HTTP method, request path, status code and latency. Status codes are logged at different levels: 2xx at INFO, 3xx at DEBUG (with Location header if present), 4xx at WARN, and 5xx at ERROR.
func Recovery ¶ added in v0.7.0
func Recovery(handler slog.Handler) MiddlewareFunc
Recovery returns a middleware that recovers from any panics, logs the error, request details, and stack trace using the provided slog.Handler and writes a 500 status code response if a panic occurs.
func RecoveryWithFunc ¶ added in v0.26.0
func RecoveryWithFunc(handler slog.Handler, handle RecoveryFunc) MiddlewareFunc
RecoveryWithFunc returns a middleware that recovers from any panics, logs the error, request details, and stack trace using the provided slog.Handler and then calls the handle function to handle the recovery.
func WrapM ¶ added in v0.7.0
func WrapM(m func(http.Handler) http.Handler) MiddlewareFunc
WrapM is an adapter for wrapping http.Handler middleware and returns a MiddlewareFunc function. The route parameters are being accessed by the wrapped handler through the context.
type Params ¶
type Params []Param
func ParamsFromContext ¶
ParamsFromContext is a helper to retrieve params from context.Context when a http.Handler is registered using WrapF or WrapH.
type QueryMatcher ¶ added in v0.26.0
type QueryMatcher struct {
// contains filtered or unexported fields
}
func MatchQuery ¶ added in v0.26.0
func MatchQuery(key, value string) (QueryMatcher, error)
func (QueryMatcher) Equal ¶ added in v0.26.0
func (m QueryMatcher) Equal(matcher Matcher) bool
func (QueryMatcher) Key ¶ added in v0.26.0
func (m QueryMatcher) Key() string
func (QueryMatcher) Match ¶ added in v0.26.0
func (m QueryMatcher) Match(c RequestContext) bool
func (QueryMatcher) String ¶ added in v0.26.0
func (m QueryMatcher) String() string
func (QueryMatcher) Value ¶ added in v0.26.0
func (m QueryMatcher) Value() string
type QueryRegexpMatcher ¶ added in v0.26.0
type QueryRegexpMatcher struct {
// contains filtered or unexported fields
}
func MatchQueryRegexp ¶ added in v0.26.0
func MatchQueryRegexp(key, expr string) (QueryRegexpMatcher, error)
func (QueryRegexpMatcher) Equal ¶ added in v0.26.0
func (m QueryRegexpMatcher) Equal(matcher Matcher) bool
func (QueryRegexpMatcher) Key ¶ added in v0.26.0
func (m QueryRegexpMatcher) Key() string
func (QueryRegexpMatcher) Match ¶ added in v0.26.0
func (m QueryRegexpMatcher) Match(c RequestContext) bool
func (QueryRegexpMatcher) Regex ¶ added in v0.26.0
func (m QueryRegexpMatcher) Regex() *regexp.Regexp
func (QueryRegexpMatcher) String ¶ added in v0.26.0
func (m QueryRegexpMatcher) String() string
type RecoveryFunc ¶ added in v0.7.0
RecoveryFunc is a function type that defines how to handle panics that occur during the handling of an HTTP request.
type RequestContext ¶ added in v0.26.0
type RequestContext interface {
// Request returns the current [http.Request].
Request() *http.Request
// RemoteIP parses the IP from [http.Request.RemoteAddr], normalizes it, and returns an IP address. The returned [net.IPAddr]
// may contain a zone identifier. RemoteIP never returns nil, even if parsing the IP fails.
RemoteIP() *net.IPAddr
// ClientIP returns the "real" client IP address based on the configured [ClientIPResolver].
// The resolver is set using the [WithClientIPResolver] option. There is no sane default, so if no resolver is configured,
// the method returns [ErrNoClientIPResolver].
//
// The resolver used must be chosen and tuned for your network configuration. This should result
// in a resolver never returning an error -- i.e., never failing to find a candidate for the "real" IP.
// Consequently, getting an error result should be treated as an application error, perhaps even
// worthy of panicking.
//
// The returned [net.IPAddr] may contain a zone identifier.
ClientIP() (*net.IPAddr, error)
// Method returns the request method.
Method() string
// Path returns the request [url.URL.RawPath] if not empty, or fallback to the [url.URL.Path].
Path() string
// Host returns the request host.
Host() string
// QueryParams parses the [http.Request] raw query and returns the corresponding values. The result is cached after
// the first call.
QueryParams() url.Values
// QueryParam returns the first query value associated with the given key. The query parameters are parsed and
// cached on first access.
QueryParam(name string) string
// Header retrieves the value of the request header for the given key.
Header(key string) string
// Pattern returns the registered route pattern or an empty string if the handler is called in a scope other than [RouteHandler].
Pattern() string
}
RequestContext provides read-only access to incoming HTTP request data, including request properties, headers, query parameters, and client IP information. It is implemented by Context.
The RequestContext API is not thread-safe. Its lifetime is limited to the scope of its caller: within a Matcher, it is valid only for the duration of the [Matcher.Match] call; within a HandlerFunc, it is valid only for the duration of the handler execution. The underlying context may be reused after the call returns.
type ResponseWriter ¶ added in v0.7.0
type ResponseWriter interface {
http.ResponseWriter
io.StringWriter
io.ReaderFrom
// Status recorded after Write and WriteHeader.
Status() int
// Written returns true if the response has been written.
Written() bool
// Size returns the size of the written response.
Size() int
// FlushError flushes buffered data to the client. If flush is not supported, FlushError returns an error
// matching [http.ErrNotSupported]. See [http.Flusher] for more details.
FlushError() error
// Hijack lets the caller take over the connection. If hijacking the connection is not supported, Hijack returns
// an error matching [http.ErrNotSupported]. See [http.Hijacker] for more details.
Hijack() (net.Conn, *bufio.ReadWriter, error)
// Push initiates an HTTP/2 server push. Push returns [http.ErrNotSupported] if the client has disabled push or if push
// is not supported on the underlying connection. See [http.Pusher] for more details.
Push(target string, opts *http.PushOptions) error
// SetReadDeadline sets the deadline for reading the entire request, including the body. Reads from the request
// body after the deadline has been exceeded will return an error. A zero value means no deadline. Setting the read
// deadline after it has been exceeded will not extend it. If SetReadDeadline is not supported, it returns
// an error matching [http.ErrNotSupported].
SetReadDeadline(deadline time.Time) error
// SetWriteDeadline sets the deadline for writing the response. Writes to the response body after the deadline has
// been exceeded will not block, but may succeed if the data has been buffered. A zero value means no deadline.
// Setting the write deadline after it has been exceeded will not extend it. If SetWriteDeadline is not supported,
// it returns an error matching [http.ErrNotSupported].
SetWriteDeadline(deadline time.Time) error
// EnableFullDuplex indicates that the request handler will interleave reads from [http.Request.Body]
// with writes to the [ResponseWriter].
//
// For HTTP/1 requests, the Go HTTP server by default consumes any unread portion of
// the request body before beginning to write the response, preventing handlers from
// concurrently reading from the request and writing the response.
// Calling EnableFullDuplex disables this behavior and permits handlers to continue to read
// from the request while concurrently writing the response.
//
// For HTTP/2 requests, the Go HTTP server always permits concurrent reads and responses.
// If EnableFullDuplex is not supported, it returns an error matching [http.ErrNotSupported].
EnableFullDuplex() error
}
ResponseWriter extends http.ResponseWriter and provides methods to retrieve the recorded status code, written state, and response size.
type Route ¶ added in v0.16.0
type Route struct {
// contains filtered or unexported fields
}
Route represents an immutable HTTP route with associated handlers and settings.
func (*Route) Annotation ¶ added in v0.20.0
Annotation returns the value associated with this Route for key, or nil if no value is associated with key. Successive calls to Annotation with the same key returns the same result.
func (*Route) ClientIPResolver ¶ added in v0.21.0
func (r *Route) ClientIPResolver() ClientIPResolver
ClientIPResolver returns the ClientIPResolver configured for this Route, if any.
func (*Route) Handle ¶ added in v0.16.0
Handle calls the handler with the provided Context. See also Route.HandleMiddleware.
func (*Route) HandleMiddleware ¶ added in v0.17.0
HandleMiddleware calls the handler with route-specific middleware applied, using the provided Context. This method is not intended to be used as the handler for another route, as the middleware chain would be duplicated when registered. To reuse a route's handler, use Route.Handle directly or register a new route via Router.AddRoute or Router.UpdateRoute.
func (*Route) Hostname ¶ added in v0.18.0
Hostname returns the hostname part of the registered pattern if any.
func (*Route) Matchers ¶ added in v0.26.0
Matchers returns an iterator over all matchers attached to this Route.
func (*Route) MatchersLen ¶ added in v0.26.0
MatchersLen returns the number of matchers for this Route.
func (*Route) MatchersPriority ¶ added in v0.26.0
MatchersPriority returns the matchers priority for this Route.
func (*Route) Methods ¶ added in v0.26.0
Methods returns an iterator over all HTTP methods this route responds to (if any), in lexicographical order.
func (*Route) Params ¶ added in v0.26.0
Params returns an iterator over all parameters name for this Route.
func (*Route) ParamsLen ¶ added in v0.21.0
ParamsLen returns the number of parameters for this Route.
func (*Route) TrailingSlashOption ¶ added in v0.24.0
func (r *Route) TrailingSlashOption() TrailingSlashOption
TrailingSlashOption returns the configured TrailingSlashOption for this Route.
type RouteConflictError ¶
type RouteConflictError struct {
// New is the route that was being registered when the conflict was detected.
New *Route
// Conflicts contains the previously registered routes that conflict with New.
Conflicts []*Route
// contains filtered or unexported fields
}
RouteConflictError represents a conflict that occurred during route registration. It contains the route being registered, and the existing routes that caused the conflict.
func (*RouteConflictError) Error ¶
func (e *RouteConflictError) Error() string
func (*RouteConflictError) Unwrap ¶
func (e *RouteConflictError) Unwrap() error
Unwrap returns the sentinel value ErrRouteConflict.
type RouteMatch ¶ added in v0.26.0
type RouteMatch struct {
*Route
// Tsr is true when the match required trailing slash adjustment.
Tsr bool
}
RouteMatch represents a route matched by a reverse lookup operation.
type RouteNameConflictError ¶ added in v0.26.0
type RouteNameConflictError struct {
// New is the route that was being registered when the conflict was detected.
New *Route
// Conflict is the previously registered route that conflict with New.
Conflict *Route
}
RouteNameConflictError represents a conflict that occurred during route name registration. It contains the route being registered, and the existing route that caused the conflict.
func (*RouteNameConflictError) Error ¶ added in v0.26.0
func (e *RouteNameConflictError) Error() string
func (*RouteNameConflictError) Unwrap ¶ added in v0.26.0
func (e *RouteNameConflictError) Unwrap() error
Unwrap returns the sentinel value ErrRouteNameExist.
type RouteOption ¶ added in v0.18.0
type RouteOption interface {
// contains filtered or unexported methods
}
func WithAnnotation ¶ added in v0.17.0
func WithAnnotation(key, value any) RouteOption
WithAnnotation attach arbitrary metadata to routes. Annotations are key-value pairs that allow middleware, handler or any other components to modify behavior based on the attached metadata. Unlike context-based metadata, which is tied to the request lifetime, annotations are bound to the route's lifetime and remain static across all requests for that route. The provided key must be comparable and should not be of type string or any other built-in type to avoid collisions between packages that use route annotation. See also [WithAnnotationFunc]
func WithMatcherPriority ¶ added in v0.26.0
func WithMatcherPriority(priority uint) RouteOption
WithMatcherPriority sets the priority for a route with matchers. When multiple routes share the same pattern (regardless of param names) and have overlapping methods, matchers are evaluated by priority (highest first). Routes with equal priority may be evaluated in any order. Routes without matchers are always evaluated last. If unset or 0, the priority defaults to the number of matchers. Note that routes with specific methods are always evaluated before method-less routes, regardless of priority.
func WithName ¶ added in v0.26.0
func WithName(name string) RouteOption
WithName assigns a name to a route for identification and lookup purposes. The name must be unique among all other routes registered.
type Router ¶
type Router struct {
// contains filtered or unexported fields
}
Router is a lightweight high performance HTTP request router that support mutation on its routing tree while handling request concurrently.
func MustRouter ¶ added in v0.26.0
func MustRouter(opts ...GlobalOption) *Router
MustRouter returns a ready to use instance of Fox router. This function is a convenience wrapper for NewRouter and panics on error.
func NewRouter ¶ added in v0.26.0
func NewRouter(opts ...GlobalOption) (*Router, error)
NewRouter returns a ready to use instance of Fox router.
func (*Router) Add ¶ added in v0.26.0
func (fox *Router) Add(methods []string, pattern string, handler HandlerFunc, opts ...RouteOption) (*Route, error)
Add registers a new route for the given methods, pattern and matchers. On success, it returns the newly registered Route. If an error occurs, it returns one of the following:
- ErrRouteConflict: If the route conflict with others.
- ErrRouteNameExist: If the route name is already registered.
- ErrInvalidRoute: If the provided method or pattern is invalid.
- ErrInvalidConfig: If the provided route options are invalid.
- ErrInvalidMatcher: If the provided matcher options are invalid.
It's safe to add a new handler while the router is serving requests. This function is safe for concurrent use by multiple goroutine. To override an existing handler, use Router.Update.
func (*Router) AddRoute ¶ added in v0.26.0
AddRoute registers a new Route. If an error occurs, it returns one of the following:
- ErrRouteConflict: If the route conflict with others.
- ErrRouteNameExist: If the route name is already registered.
- ErrInvalidRoute: If the route is missing.
It's safe to add a new route while the router is serving requests. This function is safe for concurrent use by multiple goroutine. To override an existing route, use Router.UpdateRoute.
func (*Router) Delete ¶
Delete deletes an existing route for the given methods, pattern and matchers. On success, it returns the deleted Route.
- ErrRouteNotFound: If the route does not exist.
- ErrInvalidRoute: If the provided method or pattern is invalid.
- ErrInvalidMatcher: If the provided matcher options are invalid.
It's safe to delete a handler while the router is serving requests. This function is safe for concurrent use by multiple goroutine.
func (*Router) DeleteRoute ¶ added in v0.26.0
DeleteRoute deletes an existing route that match the provided Route pattern and matchers. On success, it returns the deleted Route. If an error occurs, it returns one of the following:
- ErrRouteNotFound: If the route does not exist.
- ErrInvalidRoute: If the route is missing.
It's safe to delete a handler while the router is serving requests. This function is safe for concurrent use by multiple goroutine.
func (*Router) HandleNoRoute ¶ added in v0.23.1
HandleNoRoute calls the no route handler with the provided Context. Note that this bypasses any middleware attached to the no route handler.
func (*Router) Has ¶ added in v0.18.0
Has allows to check if the given methods, pattern and matchers exactly match a registered route. This function is safe for concurrent use by multiple goroutine and while mutation on routes are ongoing. See also Router.Route as an alternative.
Example ¶
f, _ := NewRouter()
f.MustAdd([]string{http.MethodGet, http.MethodHead}, "/hello/{name}", emptyHandler)
exist := f.Has([]string{http.MethodGet, http.MethodHead}, "/hello/{name}")
fmt.Println(exist) // true
func (*Router) Iter ¶ added in v0.16.0
Iter returns a collection of range iterators for traversing registered methods and routes. It creates a point-in-time snapshot of the routing tree. Therefore, all iterators returned by Iter will not observe subsequent write on the router. This function is safe for concurrent use by multiple goroutine and while mutation on routes are ongoing.
func (*Router) Lookup ¶
Lookup performs a manual route lookup for a given http.Request, returning the matched Route along with a Context, and a boolean indicating if the route was matched by adding or removing a trailing slash (trailing slash action recommended). If there is a direct match or a tsr is possible, Lookup always return a Route and a Context. The Context should always be closed if non-nil. This function is safe for concurrent use by multiple goroutine and while mutation on routes are ongoing. See also Router.Match as an alternative.
func (*Router) Match ¶
Match perform a reverse lookup for the given method and http.Request. It returns the matching registered Route (if any) along with a boolean indicating if the route was matched by adding or removing a trailing slash (trailing slash action recommended). This function is safe for concurrent use by multiple goroutine and while mutation on routes are ongoing. See also Router.Lookup as an alternative.
Example ¶
f, _ := NewRouter()
f.MustAdd([]string{http.MethodGet, http.MethodHead}, "exemple.com/hello/{name}", emptyHandler)
req := httptest.NewRequest(http.MethodGet, "/hello/fox", nil)
route, tsr := f.Match(req.Method, req)
fmt.Println(route.Pattern(), tsr) // exemple.com/hello/{name} false
func (*Router) Mount ¶ added in v0.26.0
func (fox *Router) Mount() HandlerFunc
Mount returns a HandlerFunc for mounting this Router as a sub-router. Requests matching the parent route prefix are delegated to the sub-router which handles the remaining path. The parent route pattern should end with a catch-all. Parameters captured by the parent route are preserved and accessible alongside any parameters matched by the sub-router. Similarly, http.Request.Pattern is the concatenation of the parent and sub-router patterns. See also Router.Add for registering the handler.
func (*Router) MustAdd ¶ added in v0.26.0
func (fox *Router) MustAdd(methods []string, pattern string, handler HandlerFunc, opts ...RouteOption) *Route
MustAdd registers a new route for the given methods, pattern and matchers. On success, it returns the newly registered Route. This function is a convenience wrapper for the Router.Add function and panics on error.
func (*Router) Name ¶ added in v0.26.0
Name performs a lookup for a registered route matching the given method and route name. It returns the Route if a match is found or nil otherwise. This function is safe for concurrent use by multiple goroutines and while mutations on routes are ongoing. See also Router.Route as an alternative.
func (*Router) NewRoute ¶ added in v0.21.0
func (fox *Router) NewRoute(methods []string, pattern string, handler HandlerFunc, opts ...RouteOption) (*Route, error)
NewRoute create a new Route, configured with the provided options. If an error occurs, it returns one of the following:
- ErrInvalidRoute: If the provided method or pattern is invalid.
- ErrInvalidConfig: If the provided route options are invalid.
- ErrInvalidMatcher: If the provided matcher options are invalid.
func (*Router) Route ¶ added in v0.18.0
Route performs a lookup for a registered route matching the given methods, pattern and matchers. It returns the Route if a match is found or nil otherwise. This function is safe for concurrent use by multiple goroutine and while mutation on route are ongoing. See also Router.Has or Iter.Routes as an alternative.
func (*Router) RouterInfo ¶ added in v0.26.0
func (fox *Router) RouterInfo() RouterInfo
RouterInfo returns information on the configured global option.
func (*Router) ServeHTTP ¶
func (fox *Router) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP is the main entry point to serve a request. It handles all incoming HTTP requests and dispatches them to the appropriate handler function based on the request's method and path.
func (*Router) Txn ¶ added in v0.18.0
Txn create a new read-write or read-only transaction. Each Txn must be finalized with Txn.Commit or Txn.Abort. It's safe to create transaction from multiple goroutine and while the router is serving request. However, the returned Txn itself is NOT tread-safe. See also Router.Updates and Router.View for managed read-write and read-only transaction.
Example ¶
This example demonstrate how to create an unmanaged read-write transaction.
f, _ := NewRouter()
// Txn create an unmanaged read-write or read-only transaction.
txn := f.Txn(true)
defer txn.Abort()
if _, err := txn.Add([]string{http.MethodGet, http.MethodHead}, "exemple.com/hello/{name}", func(c *Context) {
_ = c.String(http.StatusOK, fmt.Sprintf("Hello %s\n", c.Param("name")))
}); err != nil {
log.Printf("error inserting route: %s", err)
return
}
// Iter returns a collection of range iterators for traversing registered routes.
it := txn.Iter()
// When Iter() is called on a write transaction, it creates a point-in-time snapshot of the transaction state.
// It means that writing on the current transaction while iterating is allowed, but the mutation will not be
// observed in the result returned by PatternPrefix (or any other iterator).
for route := range it.PatternPrefix("tmp.exemple.com/") {
if _, err := f.Delete(slices.Collect(route.Methods()), route.Pattern()); err != nil {
log.Printf("error deleting route: %s", err)
return
}
}
// Finalize the transaction
txn.Commit()
func (*Router) Update ¶
func (fox *Router) Update(methods []string, pattern string, handler HandlerFunc, opts ...RouteOption) (*Route, error)
Update override an existing route for the given methods, pattern and matchers. On success, it returns the newly registered Route. If an error occurs, it returns one of the following:
- ErrRouteNotFound: If the route does not exist.
- ErrRouteNameExist: If the route name is already registered.
- ErrInvalidRoute: If the provided method or pattern is invalid.
- ErrInvalidConfig: If the provided route options are invalid.
- ErrInvalidMatcher: If the provided matcher options are invalid.
Route-specific option and middleware must be reapplied when updating a route. if not, any middleware and option will be removed (or reset to their default value), and the route will fall back to using global configuration (if any). It's safe to update a handler while the router is serving requests. This function is safe for concurrent use by multiple goroutine. To add new handler, use Router.Add method.
func (*Router) UpdateRoute ¶ added in v0.21.0
UpdateRoute override an existing Route for the given new Route. If an error occurs, it returns one of the following:
- ErrRouteNotFound: If the route does not exist.
- ErrRouteNameExist: If the route name is already registered.
- ErrInvalidRoute: If the route is missing.
It's safe to update a handler while the router is serving requests. This function is safe for concurrent use by multiple goroutine. To add new route, use Router.AddRoute method.
func (*Router) Updates ¶ added in v0.18.0
Updates executes a function within the context of a read-write managed transaction. If no error is returned from the function then the transaction is committed. If an error is returned then the entire transaction is aborted. Updates returns any error returned by fn. This function is safe for concurrent use by multiple goroutine and while the router is serving request. However Txn itself is NOT tread-safe. See also Router.Txn for unmanaged transaction and Router.View for managed read-only transaction.
Example ¶
This example demonstrate how to create a managed read-write transaction.
f, _ := NewRouter()
// Updates executes a function within the context of a read-write managed transaction. If no error is returned
// from the function then the transaction is committed. If an error is returned then the entire transaction is
// aborted.
if err := f.Updates(func(txn *Txn) error {
if _, err := txn.Add([]string{http.MethodGet, http.MethodHead}, "exemple.com/hello/{name}", func(c *Context) {
_ = c.String(http.StatusOK, fmt.Sprintf("Hello %s\n", c.Param("name")))
}); err != nil {
return err
}
// Iter returns a collection of range iterators for traversing registered routes.
it := txn.Iter()
// When Iter() is called on a write transaction, it creates a point-in-time snapshot of the transaction state.
// It means that writing on the current transaction while iterating is allowed, but the mutation will not be
// observed in the result returned by PatternPrefix (or any other iterator).
for route := range it.PatternPrefix("tmp.exemple.com/") {
if _, err := f.Delete(slices.Collect(route.Methods()), route.Pattern()); err != nil {
return err
}
}
return nil
}); err != nil {
log.Printf("transaction aborted: %s", err)
}
func (*Router) View ¶ added in v0.18.0
View executes a function within the context of a read-only managed transaction. View returns any error returned by fn. This function is safe for concurrent use by multiple goroutine and while mutation on routes are ongoing. However Txn itself is NOT tread-safe. See also Router.Txn for unmanaged transaction and Router.Updates for managed read-write transaction.
Example ¶
This example demonstrate how to create a managed read-only transaction.
f, _ := NewRouter()
// View executes a function within the context of a read-only managed transaction.
_ = f.View(func(txn *Txn) error {
if txn.Has([]string{http.MethodGet}, "/foo") && txn.Has([]string{http.MethodGet}, "/bar") {
// Do something
}
return nil
})
type RouterInfo ¶ added in v0.19.0
type RouterInfo struct {
MaxRouteParams int
MaxRouteParamKeyBytes int
MaxRouteMatchers int
TrailingSlashOption TrailingSlashOption
FixedPathOption FixedPathOption
MethodNotAllowed bool
AutoOptions bool
SystemWideOptions bool
ClientIP bool
AllowRegexp bool
}
RouterInfo hold information on the configured global options.
type TrailingSlashOption ¶ added in v0.24.0
type TrailingSlashOption uint8
const ( StrictSlash TrailingSlashOption = iota RelaxedSlash RedirectSlash )
type Txn ¶ added in v0.18.0
type Txn struct {
// contains filtered or unexported fields
}
Txn is a read or write transaction on the routing tree.
func (*Txn) Abort ¶ added in v0.18.0
func (txn *Txn) Abort()
Abort cancel the transaction. This is a noop for read transactions, already aborted or committed transactions. This function is NOT thread-safe and should be run serially, along with all other Txn APIs.
func (*Txn) Add ¶ added in v0.26.0
func (txn *Txn) Add(methods []string, pattern string, handler HandlerFunc, opts ...RouteOption) (*Route, error)
Add registers a new route for the given methods, pattern and matchers. On success, it returns the newly registered Route. If an error occurs, it returns one of the following:
- ErrRouteConflict: If the route conflict with others.
- ErrRouteNameExist: If the route name is already registered.
- ErrInvalidRoute: If the provided method or pattern is invalid.
- ErrInvalidConfig: If the provided route options are invalid.
- ErrInvalidMatcher: If the provided matcher options are invalid.
- ErrReadOnlyTxn: On write in a read-only transaction.
This function is NOT thread-safe and should be run serially, along with all other Txn APIs. To override an existing handler, use Txn.Update.
func (*Txn) AddRoute ¶ added in v0.26.0
AddRoute registers a new Route. If an error occurs, it returns one of the following:
- ErrRouteConflict: If the route conflict with others.
- ErrRouteNameExist: If the route name is already registered.
- ErrInvalidRoute: If the route is missing.
- ErrReadOnlyTxn: On write in a read-only transaction.
This function is NOT thread-safe and should be run serially, along with all other Txn APIs. To override an existing route, use Txn.UpdateRoute.
func (*Txn) Commit ¶ added in v0.18.0
func (txn *Txn) Commit()
Commit finalize the transaction. This is a noop for read transactions, already aborted or committed transactions. This function is NOT thread-safe and should be run serially, along with all other Txn APIs.
func (*Txn) Delete ¶ added in v0.18.0
Delete deletes an existing route for the given methods, pattern and matchers. On success, it returns the deleted Route. If an error occurs, it returns one of the following:
- ErrRouteNotFound: If the route does not exist.
- ErrInvalidRoute: If the provided method or pattern is invalid.
- ErrInvalidMatcher: If the provided matcher options are invalid.
- ErrReadOnlyTxn: On write in a read-only transaction.
This function is NOT thread-safe and should be run serially, along with all other Txn APIs.
func (*Txn) DeleteRoute ¶ added in v0.26.0
DeleteRoute deletes an existing route that match the provided Route pattern and matchers. On success, it returns the deleted Route. If an error occurs, it returns one of the following:
- ErrRouteNotFound: If the route does not exist.
- ErrInvalidRoute: If the route is missing.
- ErrReadOnlyTxn: On write in a read-only transaction.
This function is NOT thread-safe and should be run serially, along with all other Txn APIs.
func (*Txn) Has ¶ added in v0.18.0
Has allows to check if the given methods, pattern and matchers exactly match a registered route. This function is NOT thread-safe and should be run serially, along with all other Txn APIs. See also Txn.Route as an alternative.
func (*Txn) Iter ¶ added in v0.18.0
Iter returns a collection of range iterators for traversing registered routes. When called on a write transaction, Iter creates a point-in-time snapshot of the transaction state. Therefore, writing on the current transaction while iterating is allowed, but the mutation will not be observed in the result returned by iterators collection. This function is NOT thread-safe and should be run serially, along with all other Txn APIs.
func (*Txn) Lookup ¶ added in v0.18.0
Lookup performs a manual route lookup for a given http.Request, returning the matched Route along with a Context, and a boolean indicating if the route was matched by adding or removing a trailing slash (trailing slash action recommended). If there is a direct match or a tsr is possible, Lookup always return a Route and a Context. The Context should always be closed if non-nil. This function is NOT thread-safe and should be run serially, along with all other Txn APIs. See also Txn.Match as an alternative.
func (*Txn) Match ¶ added in v0.26.0
Match perform a reverse lookup for the given method and http.Request. It returns the matching registered Route (if any) along with a boolean indicating if the route was matched by adding or removing a trailing slash (trailing slash action recommended). This function is NOT thread-safe and should be run serially, along with all other Txn APIs. See also Txn.Lookup as an alternative.
func (*Txn) Name ¶ added in v0.26.0
Name performs a lookup for a registered route matching the given method and route name. It returns the Route if a match is found or nil otherwise. This function is NOT thread-safe and should be run serially, along with all other Txn APIs. See also Txn.Route as an alternative.
func (*Txn) Route ¶ added in v0.18.0
Route performs a lookup for a registered route matching the given methods, pattern and matchers. It returns the Route if a match is found or nil otherwise. This function is NOT thread-safe and should be run serially, along with all other Txn APIs. See also Txn.Has or Iter.Routes as an alternative.
func (*Txn) Snapshot ¶ added in v0.18.0
Snapshot returns a point in time snapshot of the current state of the transaction. Returns a new read-only transaction or nil if the transaction is already aborted or commited.
func (*Txn) Truncate ¶ added in v0.18.0
Truncate remove all routes registered in the router. Truncating on a read-only transaction returns ErrReadOnlyTxn.
func (*Txn) Update ¶ added in v0.18.0
func (txn *Txn) Update(methods []string, pattern string, handler HandlerFunc, opts ...RouteOption) (*Route, error)
Update override an existing route for the given methods, pattern and matchers. On success, it returns the newly registered Route. If an error occurs, it returns one of the following:
- ErrRouteNotFound: If the route does not exist.
- ErrRouteNameExist: If the route name is already registered.
- ErrInvalidRoute: If the provided method or pattern is invalid.
- ErrInvalidConfig: If the provided route options are invalid.
- ErrInvalidMatcher: If the provided matcher options are invalid.
- ErrReadOnlyTxn: On write in a read-only transaction.
Route-specific option and middleware must be reapplied when updating a route. if not, any middleware and option will be removed (or reset to their default value), and the route will fall back to using global configuration (if any). This function is NOT thread-safe and should be run serially, along with all other Txn APIs. To add a new handler, use Txn.Add.
func (*Txn) UpdateRoute ¶ added in v0.21.0
UpdateRoute override an existing Route for the given new Route. If an error occurs, it returns one of the following:
- ErrRouteNotFound: If the route does not exist.
- ErrRouteNameExist: If the route name is already registered.
- ErrInvalidRoute: If the route is missing.
- ErrReadOnlyTxn: On write in a read-only transaction.
This function is NOT thread-safe and should be run serially, along with all other Txn APIs. To add a new route, use Txn.AddRoute.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
constraints
Package constraints defines a set of useful constraints to be used with type parameters.
|
Package constraints defines a set of useful constraints to be used with type parameters. |