Documentation ¶
Index ¶
- Constants
- Variables
- func CleanPath(p string) string
- func DefaultHandleRecovery(c Context, _ any)
- func NewTestContext(w http.ResponseWriter, r *http.Request) (*Router, Context)
- func Walk(tree *Tree, fn WalkFunc) error
- type ClientIPStrategy
- type ClientIPStrategyFunc
- type Context
- type ContextCloser
- type HandlerFunc
- type Iterator
- func (it *Iterator) Handler() HandlerFunc
- func (it *Iterator) Method() string
- func (it *Iterator) Next()
- func (it *Iterator) Path() string
- func (it *Iterator) Rewind()
- func (it *Iterator) SeekMethod(method string)
- func (it *Iterator) SeekMethodPrefix(method, key string)
- func (it *Iterator) SeekPrefix(key string)
- func (it *Iterator) Valid() bool
- type MiddlewareFunc
- type MiddlewareScope
- type Option
- func DefaultOptions() Option
- func WithAutoOptions(enable bool) Option
- func WithClientIPStrategy(strategy ClientIPStrategy) Option
- func WithIgnoreTrailingSlash(enable bool) Option
- func WithMiddleware(m ...MiddlewareFunc) Option
- func WithMiddlewareFor(scope MiddlewareScope, m ...MiddlewareFunc) Option
- func WithNoMethod(enable bool) Option
- func WithNoMethodHandler(handler HandlerFunc) Option
- func WithNoRouteHandler(handler HandlerFunc) Option
- func WithOptionsHandler(handler HandlerFunc) Option
- func WithRedirectTrailingSlash(enable bool) Option
- type Param
- type Params
- type RecoveryFunc
- type ResponseWriter
- type RouteConflictError
- type Router
- func (fox *Router) AutoOptionsEnabled() bool
- func (fox *Router) ClientIPStrategyEnabled() bool
- func (fox *Router) Handle(method, path string, handler HandlerFunc) error
- func (fox *Router) IgnoreTrailingSlashEnabled() bool
- func (fox *Router) Lookup(w ResponseWriter, r *http.Request) (handler HandlerFunc, cc ContextCloser, tsr bool)
- func (fox *Router) MethodNotAllowedEnabled() bool
- func (fox *Router) MustHandle(method, path string, handler HandlerFunc)
- func (fox *Router) NewTree() *Tree
- func (fox *Router) RedirectTrailingSlashEnabled() bool
- func (fox *Router) Remove(method, path string) error
- func (fox *Router) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (fox *Router) Swap(new *Tree) (old *Tree)
- func (fox *Router) Tree() *Tree
- func (fox *Router) Update(method, path string, handler HandlerFunc) error
- type Tree
- func (t *Tree) Handle(method, path string, handler HandlerFunc) error
- func (t *Tree) Has(method, path string) bool
- func (t *Tree) Lookup(w ResponseWriter, r *http.Request) (handler HandlerFunc, cc ContextCloser, tsr bool)
- func (t *Tree) Match(method, path string) string
- func (t *Tree) Methods(path string) []string
- func (t *Tree) Remove(method, path string) error
- func (t *Tree) Update(method, path string, handler HandlerFunc) error
- type WalkFunc
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 is the name of the "Allow" header field used to list the set of methods // advertised as supported by the target resource. Returning an Allow header is mandatory // for status 405 (method not found) and useful for the OPTIONS method in responses. // See RFC 7231: https://datatracker.ietf.org/doc/html/rfc7231#section-7.4.1 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" HeaderXRealIP = "X-Real-Ip" 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" 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" )
Headers
Variables ¶
var ( ErrRouteNotFound = errors.New("route not found") ErrRouteExist = errors.New("route already registered") ErrRouteConflict = errors.New("route conflict") ErrInvalidRoute = errors.New("invalid route") ErrDiscardedResponseWriter = errors.New("discarded response writer") ErrInvalidRedirectCode = errors.New("invalid redirect code") ErrNoClientIPStrategy = errors.New("no client ip strategy") )
var SkipMethod = errors.New("skip method")
SkipMethod is used as a return value from WalkFunc to indicate that the method named in the call is to be skipped.
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 NewTestContext ¶ added in v0.7.0
NewTestContext returns a new Router and its associated Context, designed only for testing purpose.
func Walk ¶ added in v0.5.0
Walk allow to walk over all registered route in lexicographical order. If the function return the special value SkipMethod, Walk skips the current method. This function is safe for concurrent use by multiple goroutine and while mutation are ongoing. This API is EXPERIMENTAL and is likely to change in future release.
Types ¶
type ClientIPStrategy ¶ added in v0.14.0
type ClientIPStrategy interface { // ClientIP returns the "real" client IP according to the implemented strategy. It returns an error if no valid IP // address can be derived using the strategy. This is typically considered a misconfiguration error, unless the strategy // involves obtaining an untrustworthy or optional value. ClientIP(c Context) (*net.IPAddr, error) }
ClientIPStrategy define a strategy for obtaining the "real" client IP from HTTP requests. The strategy used must be chosen and tuned for your network configuration. This should result in the strategy 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 strategies can be found in the github.com/tigerwill90/fox/strategy package. See https://adam-p.ca/blog/2022/03/x-forwarded-for/ for more details on how to choose the right strategy for your use-case and network.
type ClientIPStrategyFunc ¶ added in v0.14.0
The ClientIPStrategyFunc type is an adapter to allow the use of ordinary functions as ClientIPStrategy. If f is a function with the appropriate signature, ClientIPStrategyFunc(f) is a ClientIPStrategyFunc that calls f.
type Context ¶ added in v0.7.0
type Context interface { // Request returns the current *http.Request. Request() *http.Request // SetRequest sets the *http.Request. SetRequest(r *http.Request) // Writer method returns a custom ResponseWriter implementation. Writer() ResponseWriter // SetWriter sets the ResponseWriter. SetWriter(w ResponseWriter) // RemoteIP parses the IP from 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 ClientIPStrategy. // The strategy is set using the WithClientIPStrategy option. There is no sane default, so if no strategy is configured, // the method returns ErrNoClientIPStrategy. // // The strategy used must be chosen and tuned for your network configuration. This should result // in the strategy 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. // // This api is EXPERIMENTAL and is likely to change in future release. ClientIP() (*net.IPAddr, error) // Path returns the registered path for the handler. Path() string // Params returns a Params slice containing the matched // wildcard parameters. Params() Params // Param retrieve a matching wildcard parameter by name. Param(name string) string // QueryParams parses the Request RawQuery and returns the corresponding values. QueryParams() url.Values // QueryParam returns the first query value associated with the given key. QueryParam(name string) string // SetHeader sets the response header for the given key to the specified value. SetHeader(key, value string) // Header retrieves the value of the request header for the given key. Header(key string) string // String sends a formatted string with the specified status code. String(code int, format string, values ...any) error // Blob sends a byte slice with the specified status code and content type. Blob(code int, contentType string, buf []byte) error // Stream sends data from an io.Reader with the specified status code and content type. Stream(code int, contentType string, r io.Reader) error // Redirect sends an HTTP redirect response with the given status code and URL. Redirect(code int, url string) error // Clone returns a copy of the Context that is safe to use after the HandlerFunc returns. Clone() Context // CloneWith returns a 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 returned ContextCloser must be closed once 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. CloneWith(w ResponseWriter, r *http.Request) ContextCloser // Tree is a local copy of the Tree in use to serve the request. Tree() *Tree // Fox returns the Router instance. Fox() *Router // Reset resets the Context to its initial state, attaching the provided ResponseWriter and http.Request. Reset(w ResponseWriter, r *http.Request) }
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 underlying implementation may be reused a soon as the handler return. (see Clone method).
func NewTestContextOnly ¶ added in v0.7.0
NewTestContextOnly returns a new Context associated with the provided Router, designed only for testing purpose.
type ContextCloser ¶ added in v0.7.0
type ContextCloser interface { Context Close() }
ContextCloser extends Context for manually created instances, adding a Close method to release resources after use.
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 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 DefaultMethodNotAllowedHandler ¶ added in v0.7.6
func DefaultMethodNotAllowedHandler() HandlerFunc
DefaultMethodNotAllowedHandler returns a simple HandlerFunc that replies to each request with a “405 Method Not Allowed” reply.
func DefaultNotFoundHandler ¶ added in v0.7.6
func DefaultNotFoundHandler() HandlerFunc
DefaultNotFoundHandler returns a simple HandlerFunc that replies to each request with a “404 page not found” reply.
func DefaultOptionsHandler ¶ added in v0.13.0
func DefaultOptionsHandler() HandlerFunc
DefaultOptionsHandler returns a simple HandlerFunc that replies to each request with a "200 OK" reply.
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 Iterator ¶ added in v0.3.0
type Iterator struct {
// contains filtered or unexported fields
}
func NewIterator ¶ added in v0.5.0
NewIterator returns an Iterator that traverses all registered routes in lexicographic order. An Iterator is safe to use when the router is serving request, when routing updates are ongoing or in parallel with other Iterators. Note that changes that happen while iterating over routes may not be reflected by the Iterator. This api is EXPERIMENTAL and is likely to change in future release.
Example ¶
r := New() it := NewIterator(r.Tree()) // Iterate over all routes for it.Rewind(); it.Valid(); it.Next() { fmt.Println(it.Method(), it.Path()) } // Iterate over all routes for the GET method for it.SeekMethod(http.MethodGet); it.Valid(); it.Next() { fmt.Println(it.Method(), it.Path()) } // Iterate over all routes starting with /users for it.SeekPrefix("/users"); it.Valid(); it.Next() { fmt.Println(it.Method(), it.Path()) } // Iterate over all route starting with /users for the GET method for it.SeekMethodPrefix(http.MethodGet, "/user"); it.Valid(); it.Next() { fmt.Println(it.Method(), it.Path()) }
Output:
func (*Iterator) Handler ¶ added in v0.3.0
func (it *Iterator) Handler() HandlerFunc
Handler return the registered handler for the current route.
func (*Iterator) Next ¶ added in v0.3.0
func (it *Iterator) Next()
Next advance the iterator to the next route. Always check it.Valid() after a it.Next().
func (*Iterator) Rewind ¶ added in v0.3.0
func (it *Iterator) Rewind()
Rewind reset the iterator cursor all the way to zero-th position which is the first method and route. It does not keep track of whether the cursor started with SeekPrefix, SeekMethod or SeekMethodPrefix.
func (*Iterator) SeekMethod ¶ added in v0.3.0
SeekMethod reset the iterator cursor to the first route for the given method. It does not keep tracking of previous seek.
func (*Iterator) SeekMethodPrefix ¶ added in v0.3.0
SeekMethodPrefix reset the iterator cursor to the first route starting with key for the given method. It does not keep tracking of previous seek.
func (*Iterator) SeekPrefix ¶ added in v0.3.0
SeekPrefix reset the iterator cursor to the first route starting with key. It does not keep tracking of previous seek.
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 CustomRecovery ¶ added in v0.14.0
func CustomRecovery(handle RecoveryFunc) MiddlewareFunc
CustomRecovery returns a middleware that recovers from any panics, logs the error, request details, and stack trace, and then calls the provided handle function to handle the recovery.
func CustomRecoveryWithLogHandler ¶ added in v0.14.0
func CustomRecoveryWithLogHandler(handler slog.Handler, handle RecoveryFunc) MiddlewareFunc
CustomRecoveryWithLogHandler returns a middleware for a given slog.Handler that recovers from any panics, logs the error, request details, and stack trace, and then calls the provided handle function to handle the recovery.
func Logger ¶ added in v0.14.0
func Logger() MiddlewareFunc
Logger returns a middleware that logs request information to os.Stdout or os.Stderr (for ERROR level). It logs details such as the remote or client IP, HTTP method, request path, status code and latency.
func LoggerWithHandler ¶ added in v0.14.0
func LoggerWithHandler(handler slog.Handler) MiddlewareFunc
LoggerWithHandler 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.
func Recovery ¶ added in v0.7.0
func Recovery() MiddlewareFunc
Recovery returns a middleware that recovers from any panics, logs the error, request details, and stack trace, and writes a 500 status code response if a panic occurs.
type MiddlewareScope ¶ added in v0.7.6
type MiddlewareScope uint8
MiddlewareScope is a type that represents different scopes for applying middleware.
const ( // RouteHandlers scope applies middleware only to regular routes registered in the router. RouteHandlers MiddlewareScope = 1 << (8 - 1 - iota) // NoRouteHandler scope applies middleware to the NoRoute handler. NoRouteHandler // NoMethodHandler scope applies middleware to the NoMethod handler. NoMethodHandler // RedirectHandler scope applies middleware to the internal redirect trailing slash handler. RedirectHandler // OptionsHandler scope applies middleware to the automatic OPTIONS handler. OptionsHandler // AllHandlers is a combination of all the above scopes, which means the middleware will be applied to all types of handlers. AllHandlers = RouteHandlers | NoRouteHandler | NoMethodHandler | RedirectHandler | OptionsHandler )
type Option ¶ added in v0.5.0
type Option interface {
// contains filtered or unexported methods
}
func DefaultOptions ¶ added in v0.7.0
func DefaultOptions() Option
DefaultOptions configure the router to use the Recovery middleware for the RouteHandlers scope, the Logger middleware for AllHandlers scope and enable automatic OPTIONS response. Note that DefaultOptions push the Recovery and Logger middleware respectively to the first and second position of the middleware chains.
func WithAutoOptions ¶ added in v0.9.0
WithAutoOptions enables automatic response to OPTIONS requests with, by default, a 200 OK status code. Use the WithOptionsHandler option to customize the response. When this option is enabled, the router automatically determines the "Allow" header value based on the methods registered for the given route. Note that custom OPTIONS handler take priority over automatic replies. This option is automatically enabled when providing a custom handler with the option WithOptionsHandler. This api is EXPERIMENTAL and is likely to change in future release.
func WithClientIPStrategy ¶ added in v0.14.0
func WithClientIPStrategy(strategy ClientIPStrategy) Option
WithClientIPStrategy sets the strategy for obtaining the "real" client IP address from HTTP requests. This strategy is used by the Context.ClientIP method. The strategy 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 strategy is configured, Context.ClientIP returns ErrNoClientIPStrategy. This API is EXPERIMENTAL and is likely to change in future releases.
func WithIgnoreTrailingSlash ¶ added in v0.14.0
WithIgnoreTrailingSlash allows the router to match routes regardless of whether a trailing slash is present or not. E.g. /foo/bar/ and /foo/bar would both match the same handler. This option prevents the router from issuing a redirect and instead matches the request directly. Note that this option is mutually exclusive with WithRedirectTrailingSlash, and if both are enabled, WithIgnoreTrailingSlash takes precedence. This api is EXPERIMENTAL and is likely to change in future release.
func WithMiddleware ¶ added in v0.7.0
func WithMiddleware(m ...MiddlewareFunc) Option
WithMiddleware attaches a global middleware to the router. Middlewares provided will be chained in the order they were added. Note that this option apply middleware to all handler, including NotFound, MethodNotAllowed and the internal redirect handler.
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.Path(), time.Since(start), c.Writer().Status(), ) } } f := New(WithMiddleware(metrics)) f.MustHandle(http.MethodGet, "/hello/{name}", func(c Context) { _ = c.String(200, "Hello %s\n", c.Param("name")) })
Output:
func WithMiddlewareFor ¶ added in v0.7.6
func WithMiddlewareFor(scope MiddlewareScope, m ...MiddlewareFunc) Option
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 RouteHandlers (regular routes), NoRouteHandler, NoMethodHandler, RedirectHandler, OptionsHandler, and any combination of these. Use this option when you need fine-grained control over where the middleware is applied. This api is EXPERIMENTAL and is likely to change in future release.
func WithNoMethod ¶ added in v0.9.0
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) Option
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) Option
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) Option
WithOptionsHandler register an HandlerFunc which is called on automatic OPTIONS requests. By default, the router respond with a 200 OK status code. The "Allow" header it automatically set before calling the handler. Note that custom OPTIONS handler take priority over automatic replies. By default, DefaultOptionsHandler is used. Note that this option automatically enable WithAutoOptions. This api is EXPERIMENTAL and is likely to change in future release.
func WithRedirectTrailingSlash ¶ added in v0.5.0
WithRedirectTrailingSlash enable automatic redirection fallback when the current request does not match but another handler is found with/without an additional trailing slash. E.g. /foo/bar/ request does not match but /foo/bar would match. The client is redirected with a http status code 301 for GET requests and 308 for all other methods. Note that this option is mutually exclusive with WithIgnoreTrailingSlash, and if both are enabled, WithIgnoreTrailingSlash takes precedence.
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 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 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 }
ResponseWriter extends http.ResponseWriter and provides methods to retrieve the recorded status code, written state, and response size.
type RouteConflictError ¶
type RouteConflictError struct { Method string Path string Matched []string // contains filtered or unexported fields }
RouteConflictError is a custom error type used to represent conflicts when registering or updating routes in the router. It holds information about the conflicting method, path, and the matched routes that caused the conflict.
func (*RouteConflictError) Error ¶
func (e *RouteConflictError) Error() string
Error returns a formatted error message for the RouteConflictError.
func (*RouteConflictError) Unwrap ¶
func (e *RouteConflictError) Unwrap() error
Unwrap returns the sentinel value ErrRouteConflict.
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 New ¶
New returns a ready to use instance of Fox router.
Example ¶
This example demonstrates how to create a simple router using the default options, which include the Recovery and Logger middleware. A basic route is defined, along with a custom middleware to log the request metrics.
// Create a new router with default options, which include the Recovery and Logger middleware r := New(DefaultOptions()) // 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.Path(), time.Since(start), c.Writer().Status()) } } // Define a route with the path "/hello/{name}", apply the custom "metrics" middleware, // and set a simple handler that greets the user by their name. r.MustHandle(http.MethodGet, "/hello/{name}", metrics(func(c Context) { _ = c.String(200, "Hello %s\n", c.Param("name")) })) // Start the HTTP server using the router as the handler and listen on port 8080 log.Fatalln(http.ListenAndServe(":8080", r))
Output:
func (*Router) AutoOptionsEnabled ¶ added in v0.14.0
AutoOptionsEnabled returns whether the router is configured to automatically respond to OPTIONS requests. This api is EXPERIMENTAL and is likely to change in future release.
func (*Router) ClientIPStrategyEnabled ¶ added in v0.14.0
ClientIPStrategyEnabled returns whether the router is configured with a ClientIPStrategy. This api is EXPERIMENTAL and is likely to change in future release.
func (*Router) Handle ¶ added in v0.7.0
func (fox *Router) Handle(method, path string, handler HandlerFunc) error
Handle registers a new handler for the given method and path. This function return an error if the route is already registered or conflict with another. It's perfectly safe to add a new handler while the tree is in use for serving requests. This function is safe for concurrent use by multiple goroutine. To override an existing route, use Update.
func (*Router) IgnoreTrailingSlashEnabled ¶ added in v0.14.0
IgnoreTrailingSlashEnabled returns whether the router is configured to ignore trailing slashes in requests when matching routes. This api is EXPERIMENTAL and is likely to change in future release.
func (*Router) Lookup ¶
func (fox *Router) Lookup(w ResponseWriter, r *http.Request) (handler HandlerFunc, cc ContextCloser, tsr bool)
Lookup is a helper that calls Tree.Lookup. For more details, refer to Tree.Lookup. It performs a manual route lookup for a given http.Request, returning the matched HandlerFunc along with a ContextCloser, and a boolean indicating if a trailing slash action (e.g. redirect) is recommended (tsr). The ContextCloser should always be closed if non-nil. This API is EXPERIMENTAL and is likely to change in future release.
Example ¶
This example demonstrates how to create a custom middleware that cleans the request path and performs a manual lookup on the tree. If the cleaned path matches a registered route, the client is redirected to the valid path.
redirectFixedPath := MiddlewareFunc(func(next HandlerFunc) HandlerFunc { return func(c Context) { req := c.Request() target := req.URL.Path cleanedPath := CleanPath(target) // Nothing to clean, call next handler or middleware. if cleanedPath == target { next(c) return } req.URL.Path = cleanedPath handler, cc, tsr := c.Fox().Lookup(c.Writer(), req) if handler != nil { defer cc.Close() code := http.StatusMovedPermanently if req.Method != http.MethodGet { code = http.StatusPermanentRedirect } // Redirect the client if direct match or indirect match. if !tsr || c.Fox().IgnoreTrailingSlashEnabled() { if err := c.Redirect(code, cleanedPath); err != nil { // Only if not in the range 300..308, so not possible here! panic(err) } return } // Add or remove an extra trailing slash and redirect the client. if c.Fox().RedirectTrailingSlashEnabled() { if err := c.Redirect(code, fixTrailingSlash(cleanedPath)); err != nil { // Only if not in the range 300..308, so not possible here panic(err) } return } } // rollback to the original path before calling the // next handler or middleware. req.URL.Path = target next(c) } }) f := New( // Register the middleware for the NoRouteHandler scope. WithMiddlewareFor(NoRouteHandler|NoMethodHandler, redirectFixedPath), ) f.MustHandle(http.MethodGet, "/hello/{name}", func(c Context) { _ = c.String(200, "Hello %s\n", c.Param("name")) })
Output:
func (*Router) MethodNotAllowedEnabled ¶ added in v0.14.0
MethodNotAllowedEnabled returns whether the router is configured to handle requests with methods that are not allowed. This api is EXPERIMENTAL and is likely to change in future release.
func (*Router) MustHandle ¶ added in v0.7.0
func (fox *Router) MustHandle(method, path string, handler HandlerFunc)
MustHandle registers a new handler for the given method and path. This function is a convenience wrapper for the Handle function. It will panic if the route is already registered or conflicts with another route. It's perfectly safe to add a new handler while the tree is in use for serving requests. This function is safe for concurrent use by multiple goroutines. To override an existing route, use Update.
func (*Router) NewTree ¶ added in v0.5.0
NewTree returns a fresh routing Tree that inherits all registered router options. It's safe to create multiple Tree concurrently. However, a Tree itself is not thread-safe and all its APIs that perform write operations should be run serially. Note that a Tree give direct access to the underlying sync.Mutex. This API is EXPERIMENTAL and is likely to change in future release.
func (*Router) RedirectTrailingSlashEnabled ¶ added in v0.14.0
RedirectTrailingSlashEnabled returns whether the router is configured to automatically redirect requests that include or omit a trailing slash. This api is EXPERIMENTAL and is likely to change in future release.
func (*Router) Remove ¶
Remove delete an existing handler for the given method and path. If the route does not exist, the function return an ErrRouteNotFound. It's perfectly safe to remove a handler while the tree is in use for serving requests. This function is safe for concurrent use by multiple goroutine.
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) Swap ¶ added in v0.5.0
Swap atomically replaces the currently in-use routing tree with the provided new tree, and returns the previous tree. This API is EXPERIMENTAL and is likely to change in future release.
func (*Router) Tree ¶ added in v0.5.0
Tree atomically loads and return the currently in-use routing tree. This API is EXPERIMENTAL and is likely to change in future release.
Example ¶
This example demonstrates some important considerations when using the Tree API.
r := New() // Each tree as its own sync.Mutex that is used to lock write on the tree. Since the router tree may be swapped at // any given time, you MUST always copy the pointer locally, This ensures that you do not inadvertently cause a // deadlock by locking/unlocking the wrong tree. tree := r.Tree() upsert := func(method, path string, handler HandlerFunc) error { tree.Lock() defer tree.Unlock() if tree.Has(method, path) { return tree.Update(method, path, handler) } return tree.Handle(method, path, handler) } _ = upsert(http.MethodGet, "/foo/bar", func(c Context) { // Note the tree accessible from fox.Context is already a local copy so the golden rule above does not apply. c.Tree().Lock() defer c.Tree().Unlock() _ = c.String(200, "foo bar") }) // Bad, instead make a local copy of the tree! _ = func(method, path string, handler HandlerFunc) error { r.Tree().Lock() defer r.Tree().Unlock() if r.Tree().Has(method, path) { return r.Tree().Update(method, path, handler) } return r.Tree().Handle(method, path, handler) }
Output:
func (*Router) Update ¶
func (fox *Router) Update(method, path string, handler HandlerFunc) error
Update override an existing handler for the given method and path. If the route does not exist, the function return an ErrRouteNotFound. It's perfectly safe to update a handler while the tree is in use for serving requests. This function is safe for concurrent use by multiple goroutine. To add new handler, use Handle method.
type Tree ¶ added in v0.5.0
Tree implements a Concurrent Radix Tree that supports lock-free reads while allowing concurrent writes. The caller is responsible for ensuring that all writes are run serially.
IMPORTANT: Each tree as its own sync.Mutex that may be used to serialize write. Since the router tree may be swapped at any given time, you MUST always copy the pointer locally to avoid inadvertently causing a deadlock by locking/unlocking the wrong Tree.
Good: t := fox.Tree() t.Lock() defer t.Unlock()
Dramatically bad, may cause deadlock fox.Tree().Lock() defer fox.Tree().Unlock()
func (*Tree) Handle ¶ added in v0.7.0
func (t *Tree) Handle(method, path string, handler HandlerFunc) error
Handle registers a new handler for the given method and path. This function return an error if the route is already registered or conflict with another. It's perfectly safe to add a new handler while the tree is in use for serving requests. However, this function is NOT thread-safe and should be run serially, along with all other Tree APIs that perform write operations. To override an existing route, use Update.
func (*Tree) Has ¶ added in v0.8.0
Has allows to check if the given method and path exactly match a registered route. This function is safe for concurrent use by multiple goroutine and while mutation on Tree are ongoing. This API is EXPERIMENTAL and is likely to change in future release.
Example ¶
This example demonstrates how to check if a given route is registered in the tree.
f := New() f.MustHandle(http.MethodGet, "/hello/{name}", emptyHandler) tree := f.Tree() exist := tree.Match(http.MethodGet, "/hello/{name}") fmt.Println(exist) // true
Output:
func (*Tree) Lookup ¶ added in v0.8.0
func (t *Tree) Lookup(w ResponseWriter, r *http.Request) (handler HandlerFunc, cc ContextCloser, tsr bool)
Lookup performs a manual route lookup for a given http.Request, returning the matched HandlerFunc along with a ContextCloser, and a boolean indicating if the handler was matched by adding or removing a trailing slash (trailing slash action is recommended). The ContextCloser should always be closed if non-nil. This method is primarily intended for integrating the fox router into custom routing solutions or middleware. This function is safe for concurrent use by multiple goroutine and while mutation on Tree are ongoing. If there is a direct match or a tsr is possible, Lookup always return a HandlerFunc and a ContextCloser. This API is EXPERIMENTAL and is likely to change in future release.
func (*Tree) Match ¶ added in v0.13.0
Match perform a reverse lookup on the tree for the given method and path and return the matching registered route if any. When WithIgnoreTrailingSlash or WithRedirectTrailingSlash are enabled, Match will match a registered route regardless of an extra or missing trailing slash. This function is safe for concurrent use by multiple goroutine and while mutation on Tree are ongoing. See also Tree.Lookup as an alternative. This API is EXPERIMENTAL and is likely to change in future release.
Example ¶
This example demonstrates how to do a reverse lookup on the tree.
f := New() f.MustHandle(http.MethodGet, "/hello/{name}", emptyHandler) tree := f.Tree() matched := tree.Match(http.MethodGet, "/hello/fox") fmt.Println(matched) // /hello/{name}
Output:
func (*Tree) Methods ¶ added in v0.7.4
Methods returns a sorted list of HTTP methods associated with a given path in the routing tree. If the path is "*", it returns all HTTP methods that have at least one route registered in the tree. For a specific path, it returns the methods that can route requests to that path. When WithIgnoreTrailingSlash or WithRedirectTrailingSlash are enabled, Methods will match a registered route regardless of an extra or missing trailing slash. This function is safe for concurrent use by multiple goroutine and while mutation on Tree are ongoing. This API is EXPERIMENTAL and is likely to change in future release.
func (*Tree) Remove ¶ added in v0.5.0
Remove delete an existing handler for the given method and path. If the route does not exist, the function return an ErrRouteNotFound. It's perfectly safe to remove a handler while the tree is in use for serving requests. However, this function is NOT thread-safe and should be run serially, along with all other Tree APIs that perform write operations.
func (*Tree) Update ¶ added in v0.5.0
func (t *Tree) Update(method, path string, handler HandlerFunc) error
Update override an existing handler for the given method and path. If the route does not exist, the function return an ErrRouteNotFound. It's perfectly safe to update a handler while the tree is in use for serving requests. However, this function is NOT thread-safe and should be run serially, along with all other Tree APIs that perform write operations. To add a new handler, use Handle method.
type WalkFunc ¶
type WalkFunc func(method, path string, handler HandlerFunc) error
WalkFunc is the type of the function called by Walk to visit each registered routes.