1. router in golang
1). sample code
1 package main 2 3 import ( 4 "fmt" 5 "net/http" 6 ) 7 8 func main() { 9 10 http.HandleFunc("/handlefunc", func(w http.ResponseWriter, r *http.Request) { 11 fmt.Fprintf(w, "http.HandleFunc") 12 }) 13 14 http.Handle("/handle", &myHandler{}) 15 16 http.ListenAndServe(":8080", nil) 17 } 18 19 type myHandler struct{} 20 21 func (this *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 22 fmt.Fprintf(w, "http.Handle") 23 }
2). implement
a) top layer:
// Objects implementing the Handler interface can be // registered to serve a particular path or subtree // in the HTTP server. // // ServeHTTP should write reply headers and data to the ResponseWriter // and then return. Returning signals that the request is finished // and that the HTTP server can move on to the next request on // the connection. type Handler interface { ServeHTTP(ResponseWriter, *Request) }
// The HandlerFunc type is an adapter to allow the use of // ordinary functions as HTTP handlers. If f is a function // with the appropriate signature, HandlerFunc(f) is a // Handler object that calls f. type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r)
// Handle registers the handler for the given pattern // in the DefaultServeMux. // The documentation for ServeMux explains how patterns are matched. func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) } // HandleFunc registers the handler function for the given pattern // in the DefaultServeMux. // The documentation for ServeMux explains how patterns are matched. func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler) }
Now we know that both of http.HandleFunc and http.Handle invoke function of DefaultServeMux.
b) DefaultServeMux
// NewServeMux allocates and returns a new ServeMux. func NewServeMux() *ServeMux { return &ServeMux{m: make(map[string]muxEntry)} } // DefaultServeMux is the default ServeMux used by Serve. var DefaultServeMux = NewServeMux()
// ServeMux is an HTTP request multiplexer. // It matches the URL of each incoming request against a list of registered // patterns and calls the handler for the pattern that // most closely matches the URL. // type ServeMux struct { mu sync.RWMutex m map[string]muxEntry hosts bool // whether any patterns contain hostnames } type muxEntry struct { explicit bool h Handler pattern string }
// HandleFunc registers the handler function for the given pattern. func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { mux.Handle(pattern, HandlerFunc(handler)) }
// Handle registers the handler for the given pattern. // If a handler already exists for pattern, Handle panics. func (mux *ServeMux) Handle(pattern string, handler Handler) { mux.mu.Lock() defer mux.mu.Unlock() if pattern == "" { panic("http: invalid pattern " + pattern) } if handler == nil { panic("http: nil handler") } if mux.m[pattern].explicit { panic("http: multiple registrations for " + pattern) } mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern} if pattern[0] != '/' { mux.hosts = true } // Helpful behavior: // If pattern is /tree/, insert an implicit permanent redirect for /tree. // It can be overridden by an explicit registration. n := len(pattern) if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit { // If pattern contains a host name, strip it and use remaining // path for redirect. path := pattern if pattern[0] != '/' { // In pattern, at least the last character is a '/', so // strings.Index can't be -1. path = pattern[strings.Index(pattern, "/"):] } mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(path, StatusMovedPermanently), pattern: pattern} } }
2. rounter in beego
1) Get and Post router
(i) sample code
beego.Get("/",func(ctx *context.Context){ ctx.Output.Body([]byte("hello world")) })
beego.Post("/alice",func(ctx *context.Context){ ctx.Output.Body([]byte("bob")) })
(ii) implementation of Get router
(a) func Get(rootpath string, f FilterFunc) *App
// FilterFunc defines filter function type. type FilterFunc func(*context.Context) // register router for Get method // usage: // beego.Get("/", func(ctx *context.Context){ // ctx.Output.Body("hello world") // }) func Get(rootpath string, f FilterFunc) *App { BeeApp.Handlers.Get(rootpath, f) return BeeApp } // create beego application BeeApp = NewApp() // NewApp returns a new beego application. func NewApp() *App { cr := NewControllerRegister() app := &App{Handlers: cr, Server: &http.Server{}} return app } // App defines beego application with a new PatternServeMux. type App struct { Handlers *ControllerRegistor Server *http.Server }
(b) ControllerRegistor
// ControllerRegistor containers registered router rules, controller handlers and filters. type ControllerRegistor struct { routers map[string]*Tree enableFilter bool filters map[int][]*FilterRouter } // NewControllerRegister returns a new ControllerRegistor. func NewControllerRegister() *ControllerRegistor { return &ControllerRegistor{ routers: make(map[string]*Tree), filters: make(map[int][]*FilterRouter), } }
// add get method // usage: // Get("/", func(ctx *context.Context){ // ctx.Output.Body("hello world") // }) func (p *ControllerRegistor) Get(pattern string, f FilterFunc) { p.AddMethod("get", pattern, f) }
// add http method router // usage: // AddMethod("get","/api/:id", func(ctx *context.Context){ // ctx.Output.Body("hello world") // }) func (p *ControllerRegistor) AddMethod(method, pattern string, f FilterFunc) { if _, ok := HTTPMETHOD[strings.ToUpper(method)]; method != "*" && !ok { panic("not support http method: " + method) } route := &controllerInfo{} route.pattern = pattern route.routerType = routerTypeRESTFul route.runfunction = f methods := make(map[string]string) if method == "*" { for _, val := range HTTPMETHOD { methods[val] = val } } else { methods[strings.ToUpper(method)] = strings.ToUpper(method) } route.methods = methods for k, _ := range methods { if k == "*" { for _, m := range HTTPMETHOD { p.addToRouter(m, pattern, route) } } else { p.addToRouter(k, pattern, route) } } }
func (p *ControllerRegistor) addToRouter(method, pattern string, r *controllerInfo) { if t, ok := p.routers[method]; ok { t.AddRouter(pattern, r) } else { t := NewTree() t.AddRouter(pattern, r) p.routers[method] = t } }
appendix:
// supported http methods. HTTPMETHOD = map[string]string{ "GET": "GET", "POST": "POST", "PUT": "PUT", "DELETE": "DELETE", "PATCH": "PATCH", "OPTIONS": "OPTIONS", "HEAD": "HEAD", "TRACE": "TRACE", "CONNECT": "CONNECT", }
type controllerInfo struct { pattern string controllerType reflect.Type methods map[string]string handler http.Handler runfunction FilterFunc routerType int }
c) Tree
type Tree struct { //search fix route first fixrouters map[string]*Tree //if set, failure to match fixrouters search then search wildcard wildcard *Tree //if set, failure to match wildcard search leaves []*leafInfo } func NewTree() *Tree { return &Tree{ fixrouters: make(map[string]*Tree), } } // add Tree to the exist Tree // prefix should has no params func (t *Tree) AddTree(prefix string, tree *Tree) { t.addtree(splitPath(prefix), tree, nil, "") }
func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg string) { if len(segments) == 0 { panic("prefix should has path") } seg := segments[0] iswild, params, regexpStr := splitSegment(seg) if len(segments) == 1 { if iswild { if regexpStr != "" { if reg == "" { rr := "" for _, w := range wildcards { if w == "." || w == ":" { continue } if w == ":splat" { rr = rr + "(.+)/" } else { rr = rr + "([^/]+)/" } } regexpStr = rr + regexpStr } else { regexpStr = "/" + regexpStr } } else { for _, w := range wildcards { if w == "." || w == ":" { continue } regexpStr = "([^/]+)/" + regexpStr } } reg = strings.Trim(reg+regexpStr, "/") filterTreeWithPrefix(tree, append(wildcards, params...), reg) t.wildcard = tree } else { filterTreeWithPrefix(tree, append(wildcards, params...), reg) t.fixrouters[seg] = tree } return } if iswild { if t.wildcard == nil { t.wildcard = NewTree() } if regexpStr != "" { if reg == "" { rr := "" for _, w := range wildcards { if w == "." || w == ":" { continue } if w == ":splat" { rr = rr + "(.+)/" } else { rr = rr + "([^/]+)/" } } regexpStr = rr + regexpStr + "/" } else { regexpStr = "/" + regexpStr + "/" } } else { for _, w := range wildcards { if w == "." || w == ":" { continue } if w == ":splat" { regexpStr = "(.+)/" + regexpStr } else { regexpStr = "([^/]+)/" + regexpStr } } } reg = reg + regexpStr t.wildcard.addtree(segments[1:], tree, append(wildcards, params...), reg) } else { subTree := NewTree() t.fixrouters[seg] = subTree subTree.addtree(segments[1:], tree, append(wildcards, params...), reg) } }
I don't know what the Tree presents now.
I will figure it out later.