# HandleFunc 解析 * Listing 3.8 ```go= func hello(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello") } func main() { http.HandleFunc("/hello", hello) } ``` ```shell= http.HandleFunc("/hello", hello) func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) at: mux.Handle(pattern, HandlerFunc(handler)) call: func (mux *ServeMux) Handle(pattern string, handler Handler) ``` * `http.HandleFunc("/hello", hello)` call HandleFunc * `HandleFunc` at line `2470` * mux.Handle(pattern, HandlerFunc(handler)) * 把 handler (也就是 `hello()`) 轉成 `HandlerFunc` 型態 * [`pkg net/http`](https://golang.org/pkg/net/http/#HandlerFunc): HandlerFunc(f) is a Handler that calls f. * `http.HandleFunc("/hello", hello)` * `func HandleFunc(pattern string, handler func(ResponseWriter, *Request))` * `func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request))` * `func (*ServeMux) Handle` * Handle registers the handler for the given pattern in the DefaultServeMux. ## HandleFunc * 單純是個 wrapper,讓我們直接使用 `DefaultServeMux` 這個 內建的 instance ```go= var defaultServeMux ServeMux var DefaultServeMux = &defaultServeMux func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler) } ``` * `DefaultServeMux` 只是一個 `ServeMux` 型態的 instance * 因為我們呼叫 function 版本的` HandleFunc()` 時可以不用自己指定 ServeMux * 如果需要的話也可以透過以下來宣告自己的 ServeMux 型態的 instance~ * 並直接用 Method 版本的 HandleFunc 註冊 handler ```go= var myMux *ServeMux // Register myMux.HandleFunc(pattern, handler) ``` ## `(mux *ServeMux) HandleFunc` * method 版本的 HandleFunc * 對 handler 做檢查 * 如果 handler not exist -> report error * call `(*ServeMux).Handle()` 來執行 pattern & handler 的實際對應 * 把 `handler` (e.g. `hello()`) 轉成 `HandlerFunc` 型態 * `hello()` 在轉換前只是一個 * 沒有 type * 擁有與 `HandlerFunc` 相同 funtion signature 的 function * Why 需要轉換? * 因為 `HandlerFunc` 這個 function value 具有 `ServeHTTP` 這個 method, 原本沒有註冊的 `hello()` 沒有 ```go= type HandlerFunc func(ResponseWriter, *Request) func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { if handler == nil { panic("http: nil handler") } mux.Handle(pattern, HandlerFunc(handler)) } ``` ```go= // ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) } ``` ## `(mux *ServeMux) Handle` * 真正進行註冊的 function * 透過 `Servemux.m` 這個 map 來執行註冊這個動作 * 透過 `Servemux.m` 這個 `map[string]muxEntry` 來把對應的 pattern map 到對應的 handler 上 ```go= type ServeMux struct { mu sync.RWMutex m map[string]muxEntry es []muxEntry // slice of entries sorted from longest to shortest. hosts bool // whether any patterns contain hostnames } type muxEntry struct { h Handler pattern string } ``` ```go= 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[pattern] = e if pattern[len(pattern)-1] == '/' { mux.es = appendSorted(mux.es, e) } if pattern[0] != '/' { mux.hosts = true } } ``` :::success :bulb: TODO: 透過 `map[string]handler` 的方法來做 fib counting 的 handler function selector ! ::: ## 執行 * :question: 有哪些 object implement `ServeHTTP()` method ? ```go= func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) ``` ## Handler 與 HandlerFunc 差異? * Handler 是個 interface * HandlerFunc 是個 function value * 具有 `HandlerFunc.ServeHTTP()` method ```go= type Handler interface { ServeHTTP(ResponseWriter, *Request) } type HandlerFunc func(ResponseWriter, *Request) func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) ``` * 為甚麼需要 `Handler` interface? ## How to serve? ### ListenAndServe ```go= // Listen and serve (function) func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe() } // Listen and serve (method) // ListenAndServe listens on the TCP network address srv.Addr and then // calls Serve to handle requests on incoming connections. // Accepted connections are configured to enable TCP keep-alives. // // If srv.Addr is blank, ":http" is used. // // ListenAndServe always returns a non-nil error. After Shutdown or Close, // the returned error is ErrServerClosed. func (srv *Server) ListenAndServe() error { ... ln, err := net.Listen("tcp", addr) // handle error... return srv.Serve(ln) } ``` * What is `ln` ? * `(net.Listener)` interface * A Listener is a generic network listener for stream-oriented protocols.` ### `(src *Server).Serve(net.Listener)` * accept connection from Listener `ln` * create service goroutine for each connection ```go= // Serve accepts incoming connections on the Listener l, creating a // new service goroutine for each. The service goroutines read requests and // then call srv.Handler to reply to them. // // HTTP/2 support is only enabled if the Listener returns *tls.Conn // connections and they were configured with "h2" in the TLS // Config.NextProtos. // // Serve always returns a non-nil error and closes l. // After Shutdown or Close, the returned error is ErrServerClosed. func (srv *Server) Serve(l net.Listener) error { //... for { rw, err := l.Accept() // handle err connCtx := ctx if cc := srv.ConnContext; cc != nil { connCtx = cc(connCtx, rw) if connCtx == nil { panic("ConnContext returned nil") } } tempDelay = 0 c := srv.newConn(rw) c.setState(c.rwc, StateNew) // before Serve can return go c.serve(connCtx) } } func (srv *Server) newConn(rwc net.Conn) *conn { c := &conn{ server: srv, rwc: rwc, } if debugServerConnections { c.rwc = newLoggingConn("server", c.rwc) } return c } ``` * What does `srv.newConn(rw)` do? * `func (srv *Server) newConn(rwc net.Conn) *conn` * Create new connection from rwc. * rwc: type: `type Conn interface` * Conn is a generic stream-oriented network connection. * ref: [pkg http - server.go](https://golang.org/src/net/http/server.go?s=78188:82485#L2510) * `go c.serve(connCtx)` * `c`: * type `*conn` * What is `conn`? * `type conn struct` * A conn represents the server side of an HTTP connection. * [`func (c *conn) serve(ctx context.Context)`](https://golang.org/src/net/http/server.go?s=78188:82485#L1794) * Serve a new connection. * call [serverHandler{c.server}.ServeHTTP(w, w.req) ](https://golang.org/src/net/http/server.go?s=78188:82485#L1794) ```go= func (c *conn) serve(ctx context.Context) { ... // HTTP/1.x from here on. for { ... serverHandler{c.server}.ServeHTTP(w, w.req) } ... } // serverHandler delegates to either the server's Handler or // DefaultServeMux and also handles "OPTIONS *" requests. type serverHandler struct { srv *Server } func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { handler := sh.srv.Handler //serverHandler.Server.Handler (default is http.DefaultServeMux) if handler == nil { handler = DefaultServeMux } if req.RequestURI == "*" && req.Method == "OPTIONS" { handler = globalOptionsHandler{} } handler.ServeHTTP(rw, req) // (Serve) } ``` * 在 line 8: * `handler := sh.srv.Handler` * 就是把第一步 * `func ListenAndServe(addr string, handler Handler)` 註冊的 handler assign 到 `handler` 這個變數 * 如果一開始呼叫 `ListenAndServe()` 時沒有傳入 handler, * 就會在 line 10 註冊成 `DefaultServeMux` 這個 `*ServeMux` 型態的 multiplexer * 在 line 15: * 呼叫 (*ServeMux).ServeHTTP(rw, req) :::success TODO: * What is `sh.srv.Handler`? * [`handler.ServeHTTP(rw, req)`]() ::: ## `(mux *ServeMux) ServeHTTP(rw, req)` ```go= // 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 - type: Handler, kind: interface h.ServeHTTP(w, r) } ``` * line 11: * [`func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string)` ](https://golang.org/src/net/http/server.go?s=78188:82485#L1794) * 作用 * Handler returns the handler to use for the given request, * 也就是透過呼叫 `(mux *ServeMux) handler()` 來取出對應 `pattern` 的 handler,並 assign 給 `h` * `(mux *ServeMux) handler(host, path string) (h Handler, pattern string)` * `handler()` 呼叫 `(mux *ServeMux) match(path string) (h Handler, pattern string)` * `match()` 會從 `ServeMux.m` 這個 map 取出對應 `pattern` 的 `Handler` `h` * h - type: Handler, kind: interface * 最後在 line 12 透過 h 這個 Handler 型態的 interface 呼叫 * `(f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request` * 他會呼叫我們已經註冊的 對應 pattern 的 handler ### (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) ```go= // 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 es []muxEntry // slice of entries sorted from longest to shortest. hosts bool // whether any patterns contain hostnames } func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) { // CONNECT requests are not canonicalized. if r.Method == "CONNECT" { // If r.URL.Path is /tree and its handler is not registered, // the /tree -> /tree/ redirect applies to CONNECT requests // but the path canonicalization does not. if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok { return RedirectHandler(u.String(), StatusMovedPermanently), u.Path } return mux.handler(r.Host, r.URL.Path) } // All other requests have any port stripped and path cleaned // before passing to mux.handler. host := stripHostPort(r.Host) path := cleanPath(r.URL.Path) // If the given path is /tree and its handler is not registered, // redirect for /tree/. if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok { return RedirectHandler(u.String(), StatusMovedPermanently), u.Path } if path != r.URL.Path { _, pattern = mux.handler(host, path) url := *r.URL url.Path = path return RedirectHandler(url.String(), StatusMovedPermanently), pattern } return mux.handler(host, r.URL.Path) } // 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. 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, "" } // ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) } ```