package router // Copyright 2013 Julien Schmidt. All rights reserved. // Use of this source code is governed by a BSD-style license that can be found // in the LICENSE file. // https://github.com/julienschmidt/httprouter import ( "context" ) // Handle is a function that can be registered to a route to handle HTTP // requests. Like http.HandlerFunc, but has a third parameter for the values of // wildcards (variables). type Handle func(ns, path string, ps Params) // Param is a single URL parameter, consisting of a key and a value. type Param struct { Key string Value string } // Params is a Param-slice, as returned by the router. // The slice is ordered, the first URL parameter is also the first slice value. // It is therefore safe to read values by the index. type Params []Param // ByName returns the value of the first Param which key matches the given name. // If no matching Param is found, an empty string is returned. func (ps Params) ByName(name string) string { for i := range ps { if ps[i].Key == name { return ps[i].Value } } return "" } type paramsKey struct{} // ParamsKey is the request context key under which URL params are stored. var ParamsKey = paramsKey{} // ParamsFromContext pulls the URL parameters from a request context, // or returns nil if none are present. func ParamsFromContext(ctx context.Context) Params { p, _ := ctx.Value(ParamsKey).(Params) return p } // Router is a http.Handler which can be used to dispatch requests to different // handler functions via configurable routes type Router struct { trees map[string]*node // Function to handle panics recovered from handlers. PanicHandler func(namespace, path string, rev any) } // New returns a new initialized Router. // Path auto-correction, including trailing slashes, is enabled by default. func New() *Router { return &Router{ // trees: make(map[string]*node), } } // Route registers a new request handle with the given path and method. func (r *Router) Route(namespace, path string, handle Handle) { if len(path) < 1 || path[0] != '/' { panic("path must begin with '/' in path '" + path + "'") } if r.trees == nil { r.trees = make(map[string]*node) } root := r.trees[namespace] if root == nil { root = new(node) r.trees[namespace] = root } root.addRoute(path, handle) } // Lookup allows the manual lookup of a namespace + path combo. func (r *Router) Lookup(namespace, path string) (Handle, Params) { if r.trees != nil { if root := r.trees[namespace]; root != nil { handle, params, _ := root.getValue(path) return handle, params } } return nil, nil } func (r *Router) recv(ns, path string) { if rcv := recover(); rcv != nil { r.PanicHandler(ns, path, rcv) } } // Handle router and execute handle. func (r *Router) Handle(ns, path string) (found bool) { if r.PanicHandler != nil { defer r.recv(ns, path) } if handle, ps := r.Lookup(ns, path); handle != nil { found = true handle(ns, path, ps) return } return false }