HTTP包默认路由匹配规则
内容简介:最近看到 http 包的相关内容,写了几个路由发现规则好像不是正则匹配,下面从源码触发分析下路由匹配和执行的过程上面的代码的执行情况如下,对于一般中间件的结构如下
最近看到 http 包的相关内容,写了几个路由发现规则好像不是正则匹配,下面从源码触发分析下路由匹配和执行的过程
问题引入
//路由1 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "/") }) //路由2 http.HandleFunc("/path/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "/path/") }) //路由3 http.HandleFunc("/path/subpath", func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "/path/subpath") }) // 传入nil,使用默认的DefaultServeMux中间件 http.ListenAndServe(":8080", nil)
上面的代码的执行情况如下,对于 /path
和 /path/subpath/
的结果是不是很意外
127.0.0.1:8080 输出 / 127.0.0.1:8080/abc/def 输出 / 127.0.0.1:8080/path 输出 /path/ 127.0.0.1:8080/path/ 输出 /path/ 127.0.0.1:8080/path/subpath 输出 /path/subpath 127.0.0.1:8080/path/subpath/ 输出 /path/
源码解读
一般中间件的结构如下
type ServeMux struct { mu sync.RWMutex m map[string]muxEntry // 所有注册的路由 es []muxEntry // 所有以/结尾的路由规则,有长到短排序 hosts bool }
先从 http.HandleFunc
路由注册追踪,一层一层往下点,找到如下代码
func (mux *ServeMux) Handle(pattern string, handler Handler) { mux.mu.Lock() defer mux.mu.Unlock() if pattern == "" { panic("http: invalid pattern") } if handler == nil { panic("http: nil handler") } if _, exist := mux.m[pattern]; exist { panic("http: multiple registrations for " + pattern) } // 懒加载 if mux.m == nil { mux.m = make(map[string]muxEntry) } e := muxEntry{h: handler, pattern: pattern} // 所有注册的路由存放到mux.m里面 mux.m[pattern] = e // 如果路由以/做结尾,则在mux.es中存放一份规则,同时做好从长到短的排序,便于以后 if pattern[len(pattern)-1] == '/' { mux.es = appendSorted(mux.es, e) } if pattern[0] != '/' { mux.hosts = true } }
路由注册完后进入到 ListenAndServe
中,层层往下找,找到如下循环等待请求的代码
func (c *conn) serve(ctx context.Context)
中又执行了 serverHandler{c.server}.ServeHTTP(w, w.req)
func (srv *Server) Serve(l net.Listener) error { // ... for { // ... // 启动一个goroutine处理请求 go c.serve(ctx) } }