前言
在本文中以及下篇文章中,我们会研习Golang 的源码来探究Golang 是如何实现HTTP URL 匹配的,并对比 mux的实现。
本人水平有限,如有疏漏和不正确的地方,还请各位不吝赐教,多谢!
Golang 源码基于1.9.2
正文
我们有这样一个HTTP 服务器程序:
func main() {
http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello")
})
http.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "World")
})
http.ListenAndServe(":8080", nil)
}
http://localhost:8080/barhttp://localhost:8080/foo/bar/foo
注册
跟随几步代码进去,会发现Golang 定义了这样一个结构
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry
hosts bool // whether any patterns contain hostnames
}
muxEntry
type muxEntry struct {
explicit bool
h Handler
pattern string
}
mmuxEntrym
// 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)
}
if mux.m == nil {
mux.m = make(map[string]muxEntry)
}
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, "/"):]
}
url := &url.URL{Path: path}
mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(url.String(), StatusMovedPermanently), pattern: pattern}
}
}
Helpful behaviorHelpful behavior/bar//bar/bar/bar/bar/
func main() {
http.HandleFunc("/bar/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello")
})
http.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "World")
})
http.ListenAndServe(":8080", nil)
}
http://localhost:8080/barhttp://localhost:8080/bar/Hello
Request URL: http://127.0.0.1:8080/bar
Request Method: GET
Status Code: 301 Moved Permanently
Remote Address: 127.0.0.1:8080
Request URL: http://localhost:8080/bar/
Request Method: GET
Status Code: 200 OK (from disk cache)
Remote Address: [::1]:8080
/bar
匹配
我们都知道HTTP 协议是基于TCP 实现的,我们先来看一个TCP echo 服务器
func main() {
fmt.Println("Launching server...")
// listen on all interfaces
ln, _ := net.Listen("tcp", ":8081")
for {
// accept connection on port
conn, _ := ln.Accept()
// will listen for message to process ending in newline (\n)
message, _ := bufio.NewReader(conn).ReadString('\n')
// output message received
fmt.Print("Message Received:", string(message))
// sample process for string received
newmessage := strings.ToUpper(message)
// send new string back to client
conn.Write([]byte(newmessage + "\n"))
}
}
net.Listensocket()bind()listenerAccept()Accept()
func (srv *Server) Serve(l net.Listener) error {
defer l.Close()
if fn := testHookServerServe; fn != nil {
fn(srv, l)
}
var tempDelay time.Duration // how long to sleep on accept failure
if err := srv.setupHTTP2_Serve(); err != nil {
return err
}
srv.trackListener(l, true)
defer srv.trackListener(l, false)
baseCtx := context.Background() // base is always background, per Issue 16220
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
for {
rw, e := l.Accept()
if e != nil {
select {
case <-srv.getDoneChan():
return ErrServerClosed
default:
}
if ne, ok := e.(net.Error); ok && ne.Temporary() {
if tempDelay == 0 {
tempDelay = 5 * time.Millisecond
} else {
tempDelay *= 2
}
if max := 1 * time.Second; tempDelay > max {
tempDelay = max
}
srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
time.Sleep(tempDelay)
continue
}
return e
}
tempDelay = 0
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx)
}
}
从这也可以看到,对于每一个HTTP 请求,服务端都会起一个goroutine 来serve.
跟随者源码一路追溯下去,发现调用了这样一个函数:
// parseRequestLine parses "GET /foo HTTP/1.1" into its three parts.
func parseRequestLine(line string) (method, requestURI, proto string, ok bool) {
s1 := strings.Index(line, " ")
s2 := strings.Index(line[s1+1:], " ")
if s1 < 0 || s2 < 0 {
return
}
s2 += s1 + 1
return line[:s1], line[s1+1 : s2], line[s2+1:], true
}
对连接发送的内容进行HTTP 协议解析,得到 HTTP 方法和URI。我们略过其他协议解析和验证的部分,直接看serve request 的函数:
serverHandler{c.server}.ServeHTTP(w, w.req)
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
if req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}
handler.ServeHTTP(rw, req)
}
handlernilDefaultServeMux
http.ListenAndServe(":8080", nil)
nilDefaultServeMuxhttp.HandleFuncDefaultServeMuxDefaultServeMuxServeHTTP
// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
}
w.WriteHeader(StatusBadRequest)
return
}
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
mux.Handler(r)
// handler is the main implementation of Handler.
// The path is known to be in canonical form, except for CONNECT methods.
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
mux.mu.RLock()
defer mux.mu.RUnlock()
// Host-specific pattern takes precedence over generic ones
if mux.hosts {
h, pattern = mux.match(host + path)
}
if h == nil {
h, pattern = mux.match(path)
}
if h == nil {
h, pattern = NotFoundHandler(), ""
}
return
}
// 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.
var n = 0
for k, v := range mux.m {
if !pathMatch(k, path) {
continue
}
if h == nil || len(k) > n {
n = len(k)
h = v.h
pattern = v.pattern
}
}
return
}
// Does path match pattern?
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
}
matchpathMatchpathMatchmain
func main() {
http.HandleFunc("/bar/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello")
})
http.HandleFunc("/bar/bbb/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "bbb")
})
http.HandleFunc("/foo", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "World")
})
http.ListenAndServe(":8080", nil)
}
http://localhost:8080/foo/aaa404 page not foundhttp://localhost:8080/bar/aaaHellohttp://localhost:8080/bar/bbb/ccc/bar//bar/bbb//bar/bbb/bbb
总结
map