Skip to content

Commit d4548e1

Browse files
committed
feat(matcher): seal Context from matcher API via onlyRequestContext
1 parent 6a1c4df commit d4548e1

2 files changed

Lines changed: 21 additions & 3 deletions

File tree

context.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,23 @@ func (c *Context) getQueries() url.Values {
415415
return c.cachedQueries
416416
}
417417

418+
// onlyRequestContext wraps *Context so matchers can only reach the [RequestContext] interface and cannot
419+
// misuse the full Context API.
420+
type onlyRequestContext struct {
421+
c *Context
422+
}
423+
424+
func (s onlyRequestContext) Request() *http.Request { return s.c.Request() }
425+
func (s onlyRequestContext) RemoteIP() *net.IPAddr { return s.c.RemoteIP() }
426+
func (s onlyRequestContext) ClientIP() (*net.IPAddr, error) { return s.c.ClientIP() }
427+
func (s onlyRequestContext) Method() string { return s.c.Method() }
428+
func (s onlyRequestContext) Path() string { return s.c.Path() }
429+
func (s onlyRequestContext) EscapedPath() string { return s.c.EscapedPath() }
430+
func (s onlyRequestContext) Host() string { return s.c.Host() }
431+
func (s onlyRequestContext) QueryParams() url.Values { return s.c.QueryParams() }
432+
func (s onlyRequestContext) QueryParam(name string) string { return s.c.QueryParam(name) }
433+
func (s onlyRequestContext) Header(key string) string { return s.c.Header(key) }
434+
418435
// WrapF is an adapter for wrapping [http.HandlerFunc] and returns a [HandlerFunc] function.
419436
// The route parameters are being accessed by the wrapped handler through the context.
420437
func WrapF(f http.HandlerFunc) HandlerFunc {

route.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ func (r *Route) String() string {
135135

136136
// match reports whether the request satisfies this route's method constraint (if any)
137137
// and all attached matchers.
138-
func (r *Route) match(method string, c RequestContext) bool {
138+
func (r *Route) match(method string, c *Context) bool {
139139
// Fast path: routes with exactly one method and no matchers cache that
140140
// method in methodFast.
141141
if r.methodFast == method {
@@ -149,7 +149,7 @@ func (r *Route) match(method string, c RequestContext) bool {
149149
// inlinable.
150150
//
151151
//go:noinline
152-
func (r *Route) matchSlow(method string, c RequestContext) bool {
152+
func (r *Route) matchSlow(method string, c *Context) bool {
153153
methods := r.methods
154154
switch len(methods) {
155155
case 0:
@@ -165,8 +165,9 @@ func (r *Route) matchSlow(method string, c RequestContext) bool {
165165
return false
166166
}
167167
}
168+
sealed := onlyRequestContext{c}
168169
for _, m := range r.matchers {
169-
if !m.Match(c) {
170+
if !m.Match(sealed) {
170171
return false
171172
}
172173
}

0 commit comments

Comments
 (0)