畅远数码|万字手撕Go http源码server.go( 三 )


func appendSorted(es []muxEntry, e muxEntry) []muxEntry {n := len(es)i := sort.Search(n, func(i int) bool {return len(es[i].pattern) < len(e.pattern)})if i == n {return append(es, e)}// we now know that i points at where we want to insertes = append(es, muxEntry{}) // try to grow the slice in place, any entry works.copy(es[i+1:], es[i:])// Move shorter entries downes[i] = ereturn es}复制代码mux.HandleFunc()
mux.HandleFunc()是我认为最重要的一个方法 , 同样是将handler注册到路由表中 , 我们应该对比mux.HandleFunc()和mux.Handle()的区别 , 其实从函数体来看mux.HandleFunc()算是对mux.Handle()函数的一个再封装 , 调用了HandlerFunc()这个适配器函数 , 本质上是将一个普通函数作为HTTP请求handler的语法糖 , 我们不再需要实现ServeHTTP()方法 , 取而代之的是传入的普通函数只要为func(ResponseWriter, *Request)类型的 , 就可以进行函数的路由 , 基本上一行代码就可以搞定 , 这也是为什么在官网示例中我们可以轻而易举的构建简单的web程序的原因 。 在官网示例的HandleFunc()函数中调用了默认复用器的DefaultServeMux.HandleFunc()方法 , 开发者只需要自己定义普通函数即可:
// HandleFunc registers the handler function for the given pattern.func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {if handler == nil {panic("http: nil handler")}mux.Handle(pattern, HandlerFunc(handler))}复制代码私有方法mux.match()
当调用mux.Handler()返回Handler类时在mux.handler()内部会调用mux.match()函数 , 本质上可以看作是路由查找的过程(Handle()是路由注册的过程)
// Find a handler on a handler map given a path string.// Most-specific (longest) pattern wins.func (mux *ServeMux) match(path string) (h Handler, pattern string) {// Check for exact match first.v, ok := mux.m[path]if ok {return v.h, v.pattern}// Check for longest valid match.mux.es contains all patterns// that end in / sorted from longest to shortest.for _, e := range mux.es {if strings.HasPrefix(path, e.pattern) {return e.h, e.pattern}}return nil, ""}复制代码在进行匹配的过程中:
1.首先在路由表中进行精确匹配 , 匹配到muxEntry后返回
2.如果在路由表中没有查询到 , 则在有序数组es中进行匹配 , 从strings.HasPrefix()可以看出 , 本质上这是一种模糊匹配 , 只匹配了相应的前缀 , 就认定匹配成功
3.如果相应前缀无法查询 , 则认为匹配失败 , 返回nil handler
总结匹配规则一句话描述是: Longer patterns take precedence over shorter ones , 长字符串模式优先级大于短字符串模式 , 优先匹配长字符串
mux.shouldRedirectRLocked()
mux.shouldRedirectRLocked()方法的作用较为简单 , 判断是否需要对像"/tree/"这种路由的重定向(在ServeMux中对于"/tree"会自动重定向到"/tree/" , 除非路由表中已有"/tree" , 此过程在mux.Handler()中调用mux.redirectToPathSlash()完成)
1.判断路由表中是否存在host+path或者path的组合 , 如果存在则不需要重定向
2.如果path为空字符串 , 则不需要重定向
3.如果当前路由表中存在path+“/” , 则需要重定向(例如在注册时将"/tree/"注册到表中 , 则对于"/tree"的路由重定向到了"/tree/" , )
func (mux *ServeMux) shouldRedirectRLocked(host, path string) bool {p := []string{path, host + path}for _, c := range p {if _, exist := mux.m[c]; exist {return false}}n := len(path)if n == 0 {return false}for _, c := range p {if _, exist := mux.m[c+"/"]; exist {return path[n-1] != '/'}}return false}复制代码


推荐阅读