diff --git a/clientip/clientip.go b/clientip/clientip.go index 6f4a29e..1c91230 100644 --- a/clientip/clientip.go +++ b/clientip/clientip.go @@ -36,9 +36,9 @@ type TrustedIPRange interface { } // The IPRangeResolverFunc type is an adapter to allow the use of -// ordinary functions as TrustedIPRange. If f is a function +// ordinary functions as [TrustedIPRange]. If f is a function // with the appropriate signature, IPRangeResolverFunc() is a -// TrustedIPRange that calls f. +// [TrustedIPRange] that calls f. type IPRangeResolverFunc func() ([]net.IPNet, error) // TrustedIPRange calls f(). @@ -66,7 +66,7 @@ type Chain struct { strategies []fox.ClientIPStrategy } -// NewChain creates a Chain that attempts to use the given strategies to +// NewChain creates a [Chain] that attempts to use the given strategies to // derive the client IP, stopping when the first one succeeds. func NewChain(strategies ...fox.ClientIPStrategy) Chain { return Chain{strategies: strategies} @@ -100,9 +100,9 @@ func NewRemoteAddr() RemoteAddr { return RemoteAddr{} } -// ClientIP derives the client IP using the RemoteAddr strategy. The returned net.IPAddr may contain a zone identifier. +// ClientIP derives the client IP using the [RemoteAddr] strategy. The returned [net.IPAddr] may contain a zone identifier. // This should only happen if remoteAddr has been modified to something illegal, or if the server is accepting connections -// on a Unix domain socket (in which case RemoteAddr is "@"). If no valid IP can be derived, an error is returned. +// on a Unix domain socket (in which case [RemoteAddr] is "@"). If no valid IP can be derived, an error is returned. func (s RemoteAddr) ClientIP(c fox.Context) (*net.IPAddr, error) { ipAddr, err := ParseIPAddr(c.Request().RemoteAddr) if err != nil { @@ -121,7 +121,7 @@ type SingleIPHeader struct { headerName string } -// NewSingleIPHeader creates a SingleIPHeader strategy that uses the headerName request header to get the client IP. +// NewSingleIPHeader creates a [SingleIPHeader] strategy that uses the headerName request header to get the client IP. func NewSingleIPHeader(headerName string) SingleIPHeader { if headerName == "" { panic(errors.New("header must not be empty")) @@ -138,7 +138,7 @@ func NewSingleIPHeader(headerName string) SingleIPHeader { return SingleIPHeader{headerName: headerName} } -// ClientIP derives the client IP using the SingleIPHeader. The returned net.IPAddr may contain a zone identifier. +// ClientIP derives the client IP using the [SingleIPHeader]. The returned [net.IPAddr] may contain a zone identifier. // If no valid IP can be derived, an error is returned. func (s SingleIPHeader) ClientIP(c fox.Context) (*net.IPAddr, error) { // RFC 2616 does not allow multiple instances of single-IP headers (or any non-list header). @@ -164,7 +164,7 @@ type LeftmostNonPrivate struct { blacklistedRanges []net.IPNet } -// NewLeftmostNonPrivate creates a LeftmostNonPrivate strategy. By default, loopback, link local and private net ip range +// NewLeftmostNonPrivate creates a [LeftmostNonPrivate] strategy. By default, loopback, link local and private net ip range // are blacklisted. func NewLeftmostNonPrivate(key HeaderKey, opts ...BlacklistRangeOption) LeftmostNonPrivate { if key > 1 { @@ -179,8 +179,8 @@ func NewLeftmostNonPrivate(key HeaderKey, opts ...BlacklistRangeOption) Leftmost return LeftmostNonPrivate{headerName: key.String(), blacklistedRanges: orSlice(cfg.ipRanges, privateAndLocalRanges)} } -// ClientIP derives the client IP using the LeftmostNonPrivate. -// The returned net.IPAddr may contain a zone identifier. If no valid IP can be derived, an error returned. +// ClientIP derives the client IP using the [LeftmostNonPrivate]. +// The returned [net.IPAddr] may contain a zone identifier. If no valid IP can be derived, an error returned. func (s LeftmostNonPrivate) ClientIP(c fox.Context) (*net.IPAddr, error) { ipAddrs := getIPAddrList(c.Request().Header, s.headerName) for _, ip := range ipAddrs { @@ -202,7 +202,7 @@ type RightmostNonPrivate struct { trustedRanges []net.IPNet } -// NewRightmostNonPrivate creates a RightmostNonPrivate strategy. By default, loopback, link local and private net ip range +// NewRightmostNonPrivate creates a [RightmostNonPrivate] strategy. By default, loopback, link local and private net ip range // are trusted. func NewRightmostNonPrivate(key HeaderKey, opts ...TrustedRangeOption) RightmostNonPrivate { if key > 1 { @@ -220,8 +220,8 @@ func NewRightmostNonPrivate(key HeaderKey, opts ...TrustedRangeOption) Rightmost } } -// ClientIP derives the client IP using the RightmostNonPrivate. -// The returned net.IPAddr may contain a zone identifier. If no valid IP can be derived, an error returned. +// ClientIP derives the client IP using the [RightmostNonPrivate]. +// The returned [net.IPAddr] may contain a zone identifier. If no valid IP can be derived, an error returned. func (s RightmostNonPrivate) ClientIP(c fox.Context) (*net.IPAddr, error) { ipAddrs := getIPAddrList(c.Request().Header, s.headerName) // Look backwards through the list of IP addresses @@ -244,7 +244,7 @@ type RightmostTrustedCount struct { trustedCount int } -// NewRightmostTrustedCount creates a RightmostTrustedCount strategy. trustedCount is the number of trusted reverse proxies. +// NewRightmostTrustedCount creates a [RightmostTrustedCount] strategy. trustedCount is the number of trusted reverse proxies. // The IP returned will be the (trustedCount-1)th from the right. For example, if there's only one trusted proxy, this // strategy will return the last (rightmost) IP address. func NewRightmostTrustedCount(key HeaderKey, trustedCount int) RightmostTrustedCount { @@ -259,8 +259,8 @@ func NewRightmostTrustedCount(key HeaderKey, trustedCount int) RightmostTrustedC return RightmostTrustedCount{headerName: key.String(), trustedCount: trustedCount} } -// ClientIP derives the client IP using the RightmostTrustedCount. -// The returned net.IPAddr may contain a zone identifier. If no valid IP can be derived, an error returned. +// ClientIP derives the client IP using the [RightmostTrustedCount]. +// The returned [net.IPAddr] may contain a zone identifier. If no valid IP can be derived, an error returned. func (s RightmostTrustedCount) ClientIP(c fox.Context) (*net.IPAddr, error) { ipAddrs := getIPAddrList(c.Request().Header, s.headerName) @@ -297,7 +297,7 @@ type RightmostTrustedRange struct { headerName string } -// NewRightmostTrustedRange creates a RightmostTrustedRange strategy. headerName must be "X-Forwarded-For" +// NewRightmostTrustedRange creates a [RightmostTrustedRange] strategy. headerName must be "X-Forwarded-For" // or "Forwarded". trustedRanges must contain all trusted reverse proxies on the path to this server and can // be private/internal or external (for example, if a third-party reverse proxy is used). func NewRightmostTrustedRange(key HeaderKey, resolver TrustedIPRange) RightmostTrustedRange { @@ -312,8 +312,8 @@ func NewRightmostTrustedRange(key HeaderKey, resolver TrustedIPRange) RightmostT return RightmostTrustedRange{headerName: key.String(), resolver: resolver} } -// ClientIP derives the client IP using the RightmostTrustedRange. -// The returned net.IPAddr may contain a zone identifier. If no valid IP can be derived, an error is returned. +// ClientIP derives the client IP using the [RightmostTrustedRange]. +// The returned [net.IPAddr] may contain a zone identifier. If no valid IP can be derived, an error is returned. func (s RightmostTrustedRange) ClientIP(c fox.Context) (*net.IPAddr, error) { trustedRange, err := s.resolver.TrustedIPRange() if err != nil { @@ -340,7 +340,7 @@ func (s RightmostTrustedRange) ClientIP(c fox.Context) (*net.IPAddr, error) { return nil, fmt.Errorf("%w: unable to find a valid IP address", ErrRightmostTrustedRange) } -// MustParseIPAddr panics if ParseIPAddr fails. +// MustParseIPAddr panics if [ParseIPAddr] fails. func MustParseIPAddr(ipStr string) *net.IPAddr { ipAddr, err := ParseIPAddr(ipStr) if err != nil { @@ -349,12 +349,13 @@ func MustParseIPAddr(ipStr string) *net.IPAddr { return ipAddr } -// ParseIPAddr safely parses the given string into a net.IPAddr. It also returns an error for unspecified (like "::") and zero-value -// addresses (like "0.0.0.0"). These are nominally valid IPs (net.ParseIP will accept them), but they are never valid "real" client IPs. +// ParseIPAddr safely parses the given string into a [net.IPAddr]. It also returns an error for unspecified (like "::") +// and zero-value addresses (like "0.0.0.0"). These are nominally valid IPs ([net.ParseIP] will accept them), but they +// are never valid "real" client IPs. // // The function returns the following errors: -// - ErrInvalidIpAddress: if the IP address cannot be parsed. -// - ErrUnspecifiedIpAddress: if the IP address is unspecified (e.g., "::" or "0.0.0.0"). +// - [ErrInvalidIpAddress]: if the IP address cannot be parsed. +// - [ErrUnspecifiedIpAddress]: if the IP address is unspecified (e.g., "::" or "0.0.0.0"). func ParseIPAddr(ip string) (*net.IPAddr, error) { host, _, err := net.SplitHostPort(ip) if err == nil { @@ -387,7 +388,7 @@ func ParseIPAddr(ip string) (*net.IPAddr, error) { } // AddressesAndRangesToIPNets converts a slice of strings with IPv4 and IPv6 addresses and CIDR ranges (prefixes) to -// net.IPNet instances. If net.ParseCIDR or net.ParseIP fail, an error will be returned. Zones in addresses or ranges +// [net.IPNet] instances. If [net.ParseCIDR] or [net.ParseIP] fail, an error will be returned. Zones in addresses or ranges // are not allowed and will result in an error. func AddressesAndRangesToIPNets(ranges ...string) ([]net.IPNet, error) { var result []net.IPNet diff --git a/context.go b/context.go index df2b553..132f4d1 100644 --- a/context.go +++ b/context.go @@ -376,8 +376,8 @@ func copyWithResize[S ~[]T, T any](dst, src *S) { copy(*dst, *src) } -// 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. +// 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 (c *cTx) Scope() HandlerScope { return c.scope } @@ -398,7 +398,7 @@ func (c *cTx) getQueries() url.Values { return c.cachedQuery } -// WrapF is an adapter for wrapping http.HandlerFunc and returns a HandlerFunc function. +// 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 WrapF(f http.HandlerFunc) HandlerFunc { return func(c Context) { diff --git a/error.go b/error.go index 46958b1..0fd0334 100644 --- a/error.go +++ b/error.go @@ -42,7 +42,7 @@ func newConflictErr(method, path string, matched []string) *RouteConflictError { } } -// Error returns a formatted error message for the RouteConflictError. +// Error returns a formatted error message for the [RouteConflictError]. func (e *RouteConflictError) Error() string { if !e.isUpdate { return e.insertError() @@ -59,7 +59,7 @@ func (e *RouteConflictError) updateError() string { } -// Unwrap returns the sentinel value ErrRouteConflict. +// Unwrap returns the sentinel value [ErrRouteConflict]. func (e *RouteConflictError) Unwrap() error { return e.err } diff --git a/fox.go b/fox.go index dbf00be..402441a 100644 --- a/fox.go +++ b/fox.go @@ -5,6 +5,7 @@ package fox import ( + "cmp" "fmt" "math" "net" @@ -56,7 +57,7 @@ type MiddlewareFunc func(next HandlerFunc) HandlerFunc // 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 +// github.com/tigerwill90/fox/clientip 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 ClientIPStrategy interface { // ClientIP returns the "real" client IP according to the implemented strategy. It returns an error if no valid IP @@ -164,7 +165,7 @@ func (fox *Router) IgnoreTrailingSlashEnabled() bool { return fox.ignoreTrailingSlash } -// ClientIPStrategyEnabled returns whether the router is configured with a ClientIPStrategy. +// ClientIPStrategyEnabled returns whether the router is configured with a [ClientIPStrategy]. func (fox *Router) ClientIPStrategyEnabled() bool { _, ok := fox.ipStrategy.(noClientIPStrategy) return !ok @@ -191,7 +192,7 @@ func (fox *Router) Handle(method, pattern string, handler HandlerFunc, opts ...R // MustHandle registers a new handler for the given method and route pattern. On success, it returns the newly registered [Route] // This function is a convenience wrapper for the [Router.Handle] function and panics on error. It's perfectly safe to -// add a new handler while the router serving requests. This function is safe for concurrent use by multiple goroutines. +// add a new handler while the router is serving requests. This function is safe for concurrent use by multiple goroutines. // To override an existing route, use [Router.Update]. func (fox *Router) MustHandle(method, pattern string, handler HandlerFunc, opts ...RouteOption) *Route { rte, err := fox.Handle(method, pattern, handler, opts...) @@ -260,13 +261,14 @@ func (fox *Router) Route(method, pattern string) *Route { // Reverse perform a reverse lookup for the given method, host and path and return 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. +// (trailing slash action recommended). If the path is empty, a default slash is automatically added. This function +// is safe for concurrent use by multiple goroutine and while mutation on routes are ongoing. See also [Router.Lookup] +// as an alternative. func (fox *Router) Reverse(method, host, path string) (route *Route, tsr bool) { tree := fox.getRoot() c := tree.ctx.Get().(*cTx) c.resetNil() - n, tsr := tree.lookup(method, host, path, c, true) + n, tsr := tree.lookup(method, host, cmp.Or(path, "/"), c, true) tree.ctx.Put(c) if n != nil { return n.route, tsr diff --git a/helpers.go b/helpers.go index ca72f71..dd1b54c 100644 --- a/helpers.go +++ b/helpers.go @@ -9,14 +9,14 @@ import ( "testing" ) -// NewTestContext returns a new Router and its associated Context, designed only for testing purpose. +// NewTestContext returns a new [Router] and its associated [Context], designed only for testing purpose. func NewTestContext(w http.ResponseWriter, r *http.Request, opts ...GlobalOption) (*Router, Context) { fox := New(opts...) c := newTextContextOnly(fox, w, r) return fox, c } -// NewTestContextOnly returns a new Context associated with the provided Router, designed only for testing purpose. +// NewTestContextOnly returns a new [Context] designed only for testing purpose. func NewTestContextOnly(w http.ResponseWriter, r *http.Request, opts ...GlobalOption) Context { fox := New(opts...) return newTextContextOnly(fox, w, r) diff --git a/internal/netutil/netutil.go b/internal/netutil/netutil.go index 345c5c1..2f18e27 100644 --- a/internal/netutil/netutil.go +++ b/internal/netutil/netutil.go @@ -20,7 +20,7 @@ func SplitHostZone(s string) (host, zone string) { // SplitHostPort separates host and port. If the port is not valid, it returns // the entire input as host, and it doesn't check the validity of the host. -// Unlike net.SplitHostPort, but per RFC 3986, it requires ports to be numeric. +// Unlike [net.SplitHostPort], but per RFC 3986, it requires ports to be numeric. func SplitHostPort(hostPort string) (host, port string) { host = hostPort diff --git a/iter.go b/iter.go index f79cc6c..01855dc 100644 --- a/iter.go +++ b/iter.go @@ -5,6 +5,7 @@ package fox import ( + "cmp" "iter" ) @@ -99,12 +100,12 @@ func (it Iter) Routes(methods iter.Seq[string], pattern string) iter.Seq2[string } // Reverse returns a range iterator over all routes registered in the routing tree that match the given host and path -// for the provided HTTP methods. Unlike Routes, which matches an exact route, Reverse is used to match an url +// for the provided HTTP methods. Unlike [Iter.Routes], which matches an exact route, Reverse is used to match an url // (e.g., a path from an incoming request) to a registered routes in the tree. The iterator reflect a snapshot of the // routing tree at the time [Iter] is created. // -// If WithIgnoreTrailingSlash or WithRedirectTrailingSlash option is enabled on a route, Reverse will match it regardless -// of whether a trailing slash is present. +// If [WithIgnoreTrailingSlash] or [WithRedirectTrailingSlash] option is enabled on a route, Reverse will match it regardless +// of whether a trailing slash is present. If the path is empty, a default slash is automatically added. // // This function is safe for concurrent use by multiple goroutine and while mutation on routes are ongoing. func (it Iter) Reverse(methods iter.Seq[string], host, path string) iter.Seq2[string, *Route] { @@ -113,7 +114,7 @@ func (it Iter) Reverse(methods iter.Seq[string], host, path string) iter.Seq2[st defer c.Close() for method := range methods { c.resetNil() - n, tsr := it.root.lookup(it.tree, method, host, path, c, true) + n, tsr := it.root.lookup(it.tree, method, host, cmp.Or(path, "/"), c, true) if n != nil && (!tsr || n.route.redirectTrailingSlash || n.route.ignoreTrailingSlash) { if !yield(method, n.route) { return diff --git a/logger.go b/logger.go index cc7afa4..8b51c28 100644 --- a/logger.go +++ b/logger.go @@ -11,7 +11,7 @@ import ( "time" ) -// LoggerWithHandler returns a middleware that logs request information using the provided slog.Handler. +// 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 LoggerWithHandler(handler slog.Handler) MiddlewareFunc { log := slog.New(handler) @@ -66,7 +66,7 @@ func LoggerWithHandler(handler slog.Handler) MiddlewareFunc { } } -// Logger returns a middleware that logs request information to os.Stdout or os.Stderr (for ERROR level). +// 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 Logger() MiddlewareFunc { return LoggerWithHandler(slogpretty.DefaultHandler) diff --git a/options.go b/options.go index c5fe693..67e39dd 100644 --- a/options.go +++ b/options.go @@ -45,8 +45,8 @@ func (o optionFunc) applyRoute(r *Route) { o(nil, r) } -// WithNoRouteHandler register an HandlerFunc which is called when no matching route is found. -// By default, the DefaultNotFoundHandler is used. +// WithNoRouteHandler register an [HandlerFunc] which is called when no matching route is found. +// By default, the [DefaultNotFoundHandler] is used. func WithNoRouteHandler(handler HandlerFunc) GlobalOption { return globOptionFunc(func(r *Router) { if handler != nil { @@ -55,10 +55,10 @@ func WithNoRouteHandler(handler HandlerFunc) GlobalOption { }) } -// WithNoMethodHandler register an HandlerFunc which is called when the request cannot be routed, +// 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. +// handler. By default, the [DefaultMethodNotAllowedHandler] is used. Note that this option automatically +// enable [WithNoMethod]. func WithNoMethodHandler(handler HandlerFunc) GlobalOption { return globOptionFunc(func(r *Router) { if handler != nil { @@ -68,10 +68,10 @@ func WithNoMethodHandler(handler HandlerFunc) GlobalOption { }) } -// WithOptionsHandler register an HandlerFunc which is called on automatic OPTIONS requests. By default, the router +// 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. +// handler take priority over automatic replies. By default, [DefaultOptionsHandler] is used. Note that this option +// automatically enable [WithAutoOptions]. func WithOptionsHandler(handler HandlerFunc) GlobalOption { return globOptionFunc(func(r *Router) { if handler != nil { @@ -108,8 +108,9 @@ func WithMiddleware(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 RouteHandler (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. +// Possible scopes include [RouteHandler] (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. func WithMiddlewareFor(scope HandlerScope, m ...MiddlewareFunc) GlobalOption { return globOptionFunc(func(r *Router) { for i := range m { @@ -121,7 +122,7 @@ func WithMiddlewareFor(scope HandlerScope, m ...MiddlewareFunc) 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. +// option [WithNoMethodHandler]. func WithNoMethod(enable bool) GlobalOption { return globOptionFunc(func(r *Router) { r.handleMethodNotAllowed = enable @@ -129,10 +130,10 @@ func WithNoMethod(enable bool) GlobalOption { } // 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 +// 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. +// the option [WithOptionsHandler]. func WithAutoOptions(enable bool) GlobalOption { return globOptionFunc(func(r *Router) { r.handleOptions = enable @@ -150,8 +151,8 @@ func WithAutoOptions(enable bool) GlobalOption { // - The option must be explicitly reapplied when updating a route. If not, the route will fall back // to the global configuration for trailing slash behavior. // -// Note that this option is mutually exclusive with WithIgnoreTrailingSlash, and if enabled will -// automatically deactivate WithIgnoreTrailingSlash. +// Note that this option is mutually exclusive with [WithIgnoreTrailingSlash], and if enabled will +// automatically deactivate [WithIgnoreTrailingSlash]. func WithRedirectTrailingSlash(enable bool) Option { return optionFunc(func(router *Router, route *Route) { if router != nil { @@ -179,8 +180,8 @@ func WithRedirectTrailingSlash(enable bool) Option { // - The option must be explicitly reapplied when updating a route. If not, the route will fall back // to the global configuration for trailing slash behavior. // -// Note that this option is mutually exclusive with -// WithRedirectTrailingSlash, and if enabled will automatically deactivate WithRedirectTrailingSlash. +// Note that this option is mutually exclusive with [WithRedirectTrailingSlash], and if enabled will automatically +// deactivate [WithRedirectTrailingSlash]. func WithIgnoreTrailingSlash(enable bool) Option { return optionFunc(func(router *Router, route *Route) { if router != nil { @@ -199,10 +200,10 @@ func WithIgnoreTrailingSlash(enable bool) 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 +// 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. +// There is no sane default, so if no strategy is configured, [Context.ClientIP] returns [ErrNoClientIPStrategy]. // // This option can be applied on a per-route basis or globally: // - If applied globally, it affects all routes by default. @@ -233,9 +234,9 @@ func WithAnnotations(annotations ...Annotation) RouteOption { }) } -// DefaultOptions configure the router to use the Recovery middleware for the RouteHandler 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. +// DefaultOptions configure the router to use the [Recovery] middleware for the [RouteHandler] 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 DefaultOptions() GlobalOption { return globOptionFunc(func(r *Router) { r.mws = append([]middleware{ diff --git a/path.go b/path.go index f3d2a8a..ac8f75e 100644 --- a/path.go +++ b/path.go @@ -10,7 +10,7 @@ import ( "strings" ) -// CleanPath is the URL version of path.Clean, it returns a canonical URL path +// 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 diff --git a/recovery.go b/recovery.go index 5409c31..e03af69 100644 --- a/recovery.go +++ b/recovery.go @@ -23,7 +23,7 @@ import ( // handling of an HTTP request. type RecoveryFunc func(c Context, err any) -// CustomRecoveryWithLogHandler returns a middleware for a given slog.Handler that recovers from any panics, +// 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 CustomRecoveryWithLogHandler(handler slog.Handler, handle RecoveryFunc) MiddlewareFunc { slogger := slog.New(handler) @@ -47,7 +47,7 @@ func Recovery() MiddlewareFunc { return CustomRecovery(DefaultHandleRecovery) } -// DefaultHandleRecovery is a default implementation of the RecoveryFunc. +// DefaultHandleRecovery is a default implementation of the [RecoveryFunc]. // It responds with a status code 500 and writes a generic error message. func DefaultHandleRecovery(c Context, _ any) { http.Error(c.Writer(), http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError) diff --git a/response_writer.go b/response_writer.go index 5e56497..fda500f 100644 --- a/response_writer.go +++ b/response_writer.go @@ -33,7 +33,7 @@ var copyBufPool = sync.Pool{ }, } -// ResponseWriter extends http.ResponseWriter and provides methods to retrieve the recorded status code, +// ResponseWriter extends [http.ResponseWriter] and provides methods to retrieve the recorded status code, // written state, and response size. type ResponseWriter interface { http.ResponseWriter @@ -46,23 +46,23 @@ type ResponseWriter interface { // 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. + // 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. + // 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 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. + // 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. + // it returns an error matching [http.ErrNotSupported]. SetWriteDeadline(deadline time.Time) error } @@ -105,7 +105,7 @@ func (r *recorder) Unwrap() http.ResponseWriter { } // WriteHeader sends an HTTP response header with the provided -// status code. See http.ResponseWriter for more details. +// status code. See [http.ResponseWriter] for more details. func (r *recorder) WriteHeader(code int) { if r.hijacked { caller := relevantCaller() @@ -132,7 +132,7 @@ func (r *recorder) WriteHeader(code int) { } // Write writes the data to the connection as part of an HTTP reply. -// See http.ResponseWriter for more details. +// See [http.ResponseWriter] for more details. func (r *recorder) Write(buf []byte) (n int, err error) { if r.hijacked { if len(buf) > 0 { @@ -197,7 +197,7 @@ func (r *recorder) ReadFrom(src io.Reader) (n int64, err error) { } // 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. +// matching [http.ErrNotSupported]. See [http.Flusher] for more details. func (r *recorder) FlushError() error { switch flusher := r.ResponseWriter.(type) { case interface{ FlushError() error }: @@ -216,8 +216,8 @@ func (r *recorder) FlushError() 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 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. func (r *recorder) Push(target string, opts *http.PushOptions) error { if pusher, ok := r.ResponseWriter.(http.Pusher); ok { return pusher.Push(target, opts) @@ -226,7 +226,7 @@ func (r *recorder) Push(target string, opts *http.PushOptions) 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. +// an error matching [http.ErrNotSupported]. See [http.Hijacker] for more details. func (r *recorder) Hijack() (net.Conn, *bufio.ReadWriter, error) { if hijacker, ok := r.ResponseWriter.(http.Hijacker); ok { r.hijacked = true @@ -238,7 +238,7 @@ func (r *recorder) Hijack() (net.Conn, *bufio.ReadWriter, 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. +// an error matching [http.ErrNotSupported]. func (r *recorder) SetReadDeadline(deadline time.Time) error { if w, ok := r.ResponseWriter.(interface{ SetReadDeadline(time.Time) error }); ok { return w.SetReadDeadline(deadline) @@ -249,7 +249,7 @@ func (r *recorder) 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. +// it returns an error matching [http.ErrNotSupported]. func (r *recorder) SetWriteDeadline(deadline time.Time) error { if w, ok := r.ResponseWriter.(interface{ SetWriteDeadline(time.Time) error }); ok { return w.SetWriteDeadline(deadline) diff --git a/route.go b/route.go index d8e5c05..04160d2 100644 --- a/route.go +++ b/route.go @@ -26,7 +26,7 @@ type Route struct { ignoreTrailingSlash bool } -// Handle calls the handler with the provided [Context]. See also [HandleMiddleware]. +// Handle calls the handler with the provided [Context]. See also [Route.HandleMiddleware]. func (r *Route) Handle(c Context) { r.hbase(c) }