golang中ServeMux解析

ServeMux解析

总览

原本是想作一个UML出来让这篇解析更清晰一点的,可是markdown的UML语法我一直捣鼓不出来。试了几个软件感受也没有
想象中的好用和方便,看来是时候本身开发一个了(笑).
口述一个流程,具体的函数你们能够页内跳转去看.
首先咱们是经过ListenAndServe来监听本地端口的,以后ListenAndServe将收到的新建一个Response连同收到的Request
做为参数调用ServeMux结构体ServeHTTP(省略了中间过程).ServeHTTP将
Request做为参数调用
Handler函数,Handler的返回值为一个Handler类型的接口,ServeHTTP会调用接口实现的ServeHTTP处理Response.golang

若是Request.URL.Path中有不合法的内容,则调用cleanPath清理,随后将Request.Host以及清理后的
内容传入handler函数,随后返回一个RedirectHandler以及handler所返回的路径。若是
Request.URL.Path合法,那么
直接调用handler,返回值与handler返回值相同。markdown

handler中经过判断ServeMux.hosts来决定是否实现pattern = r.Host + r.URL.Path.以后将pattern做为参数调用match,并将
match的返回值返回.app

match的判别方式比较"有趣",它虽然没实现为树形结构(只是用了映射),可是搜索的方法就是树形,由于URL路径就是个树形.它按照树的根节点
与子节点的关系进行判断,譬如路径"/home/select/usercourse",match在匹配的时候会首先匹配到"/"(假如咱们注册了),其次是"/home",
以后逐层匹配下来,假如咱们没注册过"/home/select/usercourse",可是注册了"/home/select/",那么match就会匹配到这一层.而后返回
"/home/select/"的Handler以及url(pattern).match函数的匹配规则实如今pathMatch函数

ServeMux结构体

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
    }

这样看起来还蛮直观的,mu是一个互斥锁,m则是咱们所要用的路由(router),其中的键(key)则是咱们挂载的路径,
对应的值则是响应的muxEntry,hosts指明了咱们是否在每一个路径中都声明了hostnames.好比咱们日常用'/'来表示
挂载在域名根目录下的HandlerFunc,可是若是hosts=true,那么咱们就须要'127.0.0.1/'来作一样的事情了.url

下面咱们来看muxEntry,h比较明确,就是咱们注册的处理过程.pattern与ServeMux中m的key相同,也就是说是咱们
注册的路径,而explicit的用途实际上是比较隐晦的。程序会根据explicit判别这个路径的Handler是不是用户注册的。
若是是程序为了Redirect(详细点击这里)而设定的,那么在它是能够被覆盖的,不然就是不能够被覆盖的.spa

NewServeMux()

DefaultServeMux变量就是直接调用的这个函数。那么这个函数只make了一个变量m,也就是为map分配了空间。
在golang的语法中,结构体里未声明的变量也是存在的(即分配了内存),只不过是该类型的默认值,好比hosts就
被设置成了false.这个问题不过多解释,有兴趣的话能够看一下golang中的相关内容code

pathMatch()

这个函数是ServeMux用来匹配路径的主要函数,因此看一下策略仍是很重要的.
函数中的参数pattern是咱们注册的路径,path是用户请求的路径router

func pathMatch(pattern, path string) bool {
    if len(pattern) == 0 {
        // should not happen
        return false
    }
    n := len(pattern)
    if pattern[n-1] != '/' {
        return pattern == path
    }
    return len(path) >= n && path[0:n] == pattern
}

若是咱们挂载的路径不是以'/'结尾的,那么就直接判断两个参数是否相同。若是是以'/'结尾的,只要path的路径包含
pattern那么就被断定是匹配。也就是说,若是我注册了/home/select/,那么/home/select/hello也会被定位到/home/select/
挂载的HandlerFunc上.这样作至关于为路径设置了一个index,不符合规则的URL都会被Redirect到这个index上接口

* ServeMux.Handler()

注释写的很清楚,这个函数就是处理URL,而后调用*ServeMux.handler().首先调用cleanPath清理请求URL中的不合法内容。若是存在不合法内容,
则将清理过的URL交由*ServeMux.handler()处理并得到匹配到的pattern,而后修改url.Path的内容并调用RedirectHandler.
若是内容合法,则直接调用*ServeMux.handler()并返回结果内存

* ServeMux.handler()

调用ServeMux.match()(封装了pathMatch函数)来得到匹配到的Handler以及对应pattern,若是ServeMux.hosts==true,那么
传入的参数为host + path,若是找不到的话,调用NotFoundHandler函数,并将其结果返回.

* ServeMux.Handle()

Handle函数是用来注册路径与处理过程的.若是该路径已经存在了一个用户注册的Handler则会panic(意思就是说不支持覆盖).判别了合法参数之后就将
pattern做为key,新建一个muxEntry类型变量做为value加入到map中。

if pattern[0] != '/' {
    mux.hosts = true
}

这是这个函数中比较有意思的一个部分,经过这里咱们能够看到若是注册路径的时候并非以'/'开头的,那么ServeMux就会开启hosts,而后会在
请求到达的时候将URL.Host和URL.Path链接在一块儿放入match中寻找,具体信息请看这里

接下来是关于路径的处理,也就是关于"/home"与"/home/"的区别.咱们先来看看做者怎么说

// Helpful behavior:
    // If pattern is /tree/, insert an implicit permanent redirect for /tree.
    // It can be overridden by an explicit registration.

若是路径的末尾是以'/'结尾而且该路径去掉末尾的'/'之后并无被注册.那么将会去掉'/'而且为其绑定一个Redirect到如今的路径.
我本身写起来都以为绕,举个例子就清楚了.
我注册了一个路径"/home/",可是没有注册"/home",那么若是用户访问了"/home"会发生什么呢?是的,会被Redirect到"/home/".
须要注意的是,这里的muxEntry中的explicit没有填,也就是说是false,那么便是能够覆盖的.

* ServeMux.ServeHTTP()

ServeHTTP会检测非法的URI(* ) 若是经过检测就会调用自身的Handler()来返回注册的Handler,随后调用Handler的ServeHTTP方法

相关文章
相关标签/搜索