...

Source file src/github.com/go-chi/chi/context.go

Documentation: github.com/go-chi/chi

     1  package chi
     2  
     3  import (
     4  	"context"
     5  	"net"
     6  	"net/http"
     7  	"strings"
     8  )
     9  
    10  // URLParam returns the url parameter from a http.Request object.
    11  func URLParam(r *http.Request, key string) string {
    12  	if rctx := RouteContext(r.Context()); rctx != nil {
    13  		return rctx.URLParam(key)
    14  	}
    15  	return ""
    16  }
    17  
    18  // URLParamFromCtx returns the url parameter from a http.Request Context.
    19  func URLParamFromCtx(ctx context.Context, key string) string {
    20  	if rctx := RouteContext(ctx); rctx != nil {
    21  		return rctx.URLParam(key)
    22  	}
    23  	return ""
    24  }
    25  
    26  // RouteContext returns chi's routing Context object from a
    27  // http.Request Context.
    28  func RouteContext(ctx context.Context) *Context {
    29  	val, _ := ctx.Value(RouteCtxKey).(*Context)
    30  	return val
    31  }
    32  
    33  // ServerBaseContext wraps an http.Handler to set the request context to the
    34  // `baseCtx`.
    35  func ServerBaseContext(baseCtx context.Context, h http.Handler) http.Handler {
    36  	fn := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    37  		ctx := r.Context()
    38  		baseCtx := baseCtx
    39  
    40  		// Copy over default net/http server context keys
    41  		if v, ok := ctx.Value(http.ServerContextKey).(*http.Server); ok {
    42  			baseCtx = context.WithValue(baseCtx, http.ServerContextKey, v)
    43  		}
    44  		if v, ok := ctx.Value(http.LocalAddrContextKey).(net.Addr); ok {
    45  			baseCtx = context.WithValue(baseCtx, http.LocalAddrContextKey, v)
    46  		}
    47  
    48  		h.ServeHTTP(w, r.WithContext(baseCtx))
    49  	})
    50  	return fn
    51  }
    52  
    53  // NewRouteContext returns a new routing Context object.
    54  func NewRouteContext() *Context {
    55  	return &Context{}
    56  }
    57  
    58  var (
    59  	// RouteCtxKey is the context.Context key to store the request context.
    60  	RouteCtxKey = &contextKey{"RouteContext"}
    61  )
    62  
    63  // Context is the default routing context set on the root node of a
    64  // request context to track route patterns, URL parameters and
    65  // an optional routing path.
    66  type Context struct {
    67  	Routes Routes
    68  
    69  	// Routing path/method override used during the route search.
    70  	// See Mux#routeHTTP method.
    71  	RoutePath   string
    72  	RouteMethod string
    73  
    74  	// Routing pattern stack throughout the lifecycle of the request,
    75  	// across all connected routers. It is a record of all matching
    76  	// patterns across a stack of sub-routers.
    77  	RoutePatterns []string
    78  
    79  	// URLParams are the stack of routeParams captured during the
    80  	// routing lifecycle across a stack of sub-routers.
    81  	URLParams RouteParams
    82  
    83  	// The endpoint routing pattern that matched the request URI path
    84  	// or `RoutePath` of the current sub-router. This value will update
    85  	// during the lifecycle of a request passing through a stack of
    86  	// sub-routers.
    87  	routePattern string
    88  
    89  	// Route parameters matched for the current sub-router. It is
    90  	// intentionally unexported so it cant be tampered.
    91  	routeParams RouteParams
    92  
    93  	// methodNotAllowed hint
    94  	methodNotAllowed bool
    95  }
    96  
    97  // Reset a routing context to its initial state.
    98  func (x *Context) Reset() {
    99  	x.Routes = nil
   100  	x.RoutePath = ""
   101  	x.RouteMethod = ""
   102  	x.RoutePatterns = x.RoutePatterns[:0]
   103  	x.URLParams.Keys = x.URLParams.Keys[:0]
   104  	x.URLParams.Values = x.URLParams.Values[:0]
   105  
   106  	x.routePattern = ""
   107  	x.routeParams.Keys = x.routeParams.Keys[:0]
   108  	x.routeParams.Values = x.routeParams.Values[:0]
   109  	x.methodNotAllowed = false
   110  }
   111  
   112  // URLParam returns the corresponding URL parameter value from the request
   113  // routing context.
   114  func (x *Context) URLParam(key string) string {
   115  	for k := len(x.URLParams.Keys) - 1; k >= 0; k-- {
   116  		if x.URLParams.Keys[k] == key {
   117  			return x.URLParams.Values[k]
   118  		}
   119  	}
   120  	return ""
   121  }
   122  
   123  // RoutePattern builds the routing pattern string for the particular
   124  // request, at the particular point during routing. This means, the value
   125  // will change throughout the execution of a request in a router. That is
   126  // why its advised to only use this value after calling the next handler.
   127  //
   128  // For example,
   129  //
   130  //   func Instrument(next http.Handler) http.Handler {
   131  //     return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   132  //       next.ServeHTTP(w, r)
   133  //       routePattern := chi.RouteContext(r.Context()).RoutePattern()
   134  //       measure(w, r, routePattern)
   135  //   	 })
   136  //   }
   137  func (x *Context) RoutePattern() string {
   138  	routePattern := strings.Join(x.RoutePatterns, "")
   139  	return replaceWildcards(routePattern)
   140  }
   141  
   142  // replaceWildcards takes a route pattern and recursively replaces all
   143  // occurrences of "/*/" to "/".
   144  func replaceWildcards(p string) string {
   145  	if strings.Contains(p, "/*/") {
   146  		return replaceWildcards(strings.Replace(p, "/*/", "/", -1))
   147  	}
   148  
   149  	return p
   150  }
   151  
   152  // RouteParams is a structure to track URL routing parameters efficiently.
   153  type RouteParams struct {
   154  	Keys, Values []string
   155  }
   156  
   157  // Add will append a URL parameter to the end of the route param
   158  func (s *RouteParams) Add(key, value string) {
   159  	s.Keys = append(s.Keys, key)
   160  	s.Values = append(s.Values, value)
   161  }
   162  
   163  // contextKey is a value for use with context.WithValue. It's used as
   164  // a pointer so it fits in an interface{} without allocation. This technique
   165  // for defining context keys was copied from Go 1.7's new use of context in net/http.
   166  type contextKey struct {
   167  	name string
   168  }
   169  
   170  func (k *contextKey) String() string {
   171  	return "chi context value " + k.name
   172  }
   173  

View as plain text