這是一個建立於
的文章,其中的資訊可能已經有所發展或是發生改變。
golang http的handle模組(一般也稱為鉤子模組),通過進階語言的匿名函數很容易實現這種內嵌功能的handle
我們一般這樣使用golang的http HandleFunc來為http的server端做相應的處理
/*********************************************/ http.HandleFunc("/", xxx_FUN)err := http.ListenAndServe(":8080", nil)if err != nil {log.Fatal("ListenAndServe: ", err)} /*********************************************/
我們再深入源碼仔細看看http.HandleFunc的實現
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {DefaultServeMux.HandleFunc(pattern, handler)}// NewServeMux allocates and returns a new ServeMux.var DefaultServeMux = NewServeMux()func NewServeMux() *ServeMux { return &ServeMux{m: make(map[string]muxEntry)} }type ServeMux struct {mu sync.RWMutex //一個讀寫鎖m map[string]muxEntry //一個path(patterns)的映射maphosts bool // whether any patterns contain hostnames}
再來看看ListenAndServe的具體實現
func ListenAndServe(addr string, handler Handler) error {server := &Server{Addr: addr, Handler: handler}return server.ListenAndServe()}func (srv *Server) ListenAndServe() error {addr := srv.Addrif addr == "" {addr = ":http"}ln, err := net.Listen("tcp", addr)if err != nil {return err}return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})}// 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.func (srv *Server) Serve(l net.Listener) error {defer l.Close()var tempDelay time.Duration // how long to sleep on accept failurefor {rw, e := l.Accept()if e != nil {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 = 0c, err := srv.newConn(rw)if err != nil {continue}c.setState(c.rwc, StateNew) // before Serve can returngo c.serve() //看來這個c.serve是處理的入口}}
看來這個c.serve是處理的入口
// Serve a new connection.func (c *conn) serve() {origConn := c.rwc // copy it before it's set nil on Close or Hijackdefer func() {if err := recover(); err != nil {const size = 64 << 10buf := make([]byte, size)buf = buf[:runtime.Stack(buf, false)]c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)}if !c.hijacked() {c.close()c.setState(origConn, StateClosed)}}()if tlsConn, ok := c.rwc.(*tls.Conn); ok {if d := c.server.ReadTimeout; d != 0 {c.rwc.SetReadDeadline(time.Now().Add(d))}if d := c.server.WriteTimeout; d != 0 {c.rwc.SetWriteDeadline(time.Now().Add(d))}if err := tlsConn.Handshake(); err != nil {c.server.logf("http: TLS handshake error from %s: %v", c.rwc.RemoteAddr(), err)return}c.tlsState = new(tls.ConnectionState)*c.tlsState = tlsConn.ConnectionState()if proto := c.tlsState.NegotiatedProtocol; validNPN(proto) {if fn := c.server.TLSNextProto[proto]; fn != nil {h := initNPNRequest{tlsConn, serverHandler{c.server}}fn(c.server, tlsConn, h)}return}}for {w, err := c.readRequest()if c.lr.N != c.server.initialLimitedReaderSize() {// If we read any bytes off the wire, we're active.c.setState(c.rwc, StateActive)}if err != nil {if err == errTooLarge {// Their HTTP client may or may not be// able to read this if we're// responding to them and hanging up// while they're still writing their// request. Undefined behavior.io.WriteString(c.rwc, "HTTP/1.1 413 Request Entity Too Large\r\n\r\n")c.closeWriteAndWait()break} else if err == io.EOF {break // Don't reply} else if neterr, ok := err.(net.Error); ok && neterr.Timeout() {break // Don't reply}io.WriteString(c.rwc, "HTTP/1.1 400 Bad Request\r\n\r\n")break}// Expect 100 Continue supportreq := w.reqif req.expectsContinue() {if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {// Wrap the Body reader with one that replies on the connectionreq.Body = &expectContinueReader{readCloser: req.Body, resp: w}}req.Header.Del("Expect")} else if req.Header.get("Expect") != "" {w.sendExpectationFailed()break}// HTTP cannot have multiple simultaneous active requests.[*]// Until the server replies to this request, it can't read another,// so we might as well run the handler in this goroutine.// [*] Not strictly true: HTTP pipelining. We could let them all process// in parallel even if their responses need to be serialized.serverHandler{c.server}.ServeHTTP(w, w.req) //這個是入口if c.hijacked() {return}w.finishRequest()if w.closeAfterReply {if w.requestBodyLimitHit {c.closeWriteAndWait()}break}c.setState(c.rwc, StateIdle)}}
Handler處理的入口就是serverHandler{c.server}.ServerHTTP(w,w.req),最終到HandleFunc的執行
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {if r.Method != "CONNECT" {if p := cleanPath(r.URL.Path); p != r.URL.Path {_, pattern = mux.handler(r.Host, p) //接下來處理url := *r.URLurl.Path = preturn RedirectHandler(url.String(), StatusMovedPermanently), pattern}}return mux.handler(r.Host, r.URL.Path) //接下來處理}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 onesif mux.hosts {h, pattern = mux.match(host + path)}if h == nil {h, pattern = mux.match(path)}if h == nil {h, pattern = NotFoundHandler(), "" //如果handler對應的匿名函數為空白,則返回預設的匿名函數}return}// 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) //接下來處理}//接下來就初始時候執行的操作func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {mux.Handle(pattern, HandlerFunc(handler))}func (mux *ServeMux) Handle(pattern string, handler Handler) { //處理patternmux.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)}mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern} //設定ServeMux的mapif 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 := patternif pattern[0] != '/' {// In pattern, at least the last character is a '/', so// strings.Index can't be -1.path = pattern[strings.Index(pattern, "/"):]}mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(path, StatusMovedPermanently), pattern: pattern}}}
最後再看看通過mux匹配擷取對應的map的操作:
func (mux *ServeMux) match(path string) (h Handler, pattern string) {var n = 0for k, v := range mux.m {if !pathMatch(k, path) { //匹配continue}if h == nil || len(k) > n {n = len(k)h = v.hpattern = v.pattern}}return}