http源码

ServeHTTP函数调用在三个地方,它们三个都实现了Handler接口,

一是serverHandler{c.server}.ServeHTTP(w, w.req),它是serverHandler调用的,里面有ListenAndServe初始化时生成的Server对象,因为Server中有注册的路由。

二是handler.ServeHTTP(rw, req),handler是个接口对象,它绑定了注册的路由,在http中它是DefaultServeMux,在gin中它是Engine。

三是h.ServeHTTP(w, r),h是个接口,这里绑定的是注册的路由函数,但注册的路由函数没有实现这个接口,所以在注册的时候进行了类型强转mux.Handle(pattern, HandlerFunc(handler)),在gin中没有调用这个,估计是不需要,

1 分析以下代码

package main
import (
    "fmt"
    "log"
    "net/http"
)
func handleRequest(w http.ResponseWriter, r *http.Request) {
    fmt.Println("aaaaa")
}
func main() {
    // set route
    http.HandleFunc("/a/", handleRequest)
    fmt.Println("Listening at port 9090...")
    // listen at port 9090 and serve requests
    err := http.ListenAndServe(":9090", nil)
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}
View Code

2 http.HandleFunc路由的注册

注册前会先把输入的路由函数做类型转换,转为HandlerFunc类型,而该类型实现了handler接口,之后通过注册的路由找到对应的路由函数时,会直接调用handler接口的ServeHTTP方法,来实现该利用函数,ServeHTTP里只有一个f(w, r)

var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
// 传入的pattern是URL,handler是路由函数
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    DefaultServeMux.HandleFunc(pattern, handler)
}
// 调用ServerMux的Handle方法注册路由,
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    if handler == nil {
        panic("http: nil handler")
    }
    // 注意这里的进行了类型的强制转换,handler是传入的函数类型,HandlerFunc是自定义的类型它实现了ServeHTTP方法,所以实现了Handler接口
    mux.Handle(pattern, HandlerFunc(handler))
}
// 特别注意这里虽然传入的handler函数和这里定义的类型形式上一样,但它不是HandlerFunc类型,因为它没有实现ServeHTTP接口,
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}
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}
    // 注册的路由实际上就是个map,从这里也可以看出http的路由注册非常简单,所以有了后来的httprouter,
    mux.m[pattern] = e
    if pattern[len(pattern)-1] == '/' {
        mux.es = appendSorted(mux.es, e)
    }
    if pattern[0] != '/' {
        mux.hosts = true
    }
}
// m用于存储路由的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
}
View Code

3 http.ListenAndServe,监听端口,收到请求,处理请求,返回结果

// 启动监听,Handler是个接口,它里面只有一个ServeHttp方法,直接用http包时,这个置为nil,
func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}
// 调用Server的Serve方法,
func (srv *Server) ListenAndServe() error {
    if srv.shuttingDown() {
        return ErrServerClosed
    }
    addr := srv.Addr
    if addr == "" {
        addr = ":http"
    }
    ln, err := net.Listen("tcp", addr)
    if err != nil {
        return err
    }
    return srv.Serve(ln)
}
//
func (srv *Server) Serve(l net.Listener) error {
    if fn := testHookServerServe; fn != nil {
        fn(srv, l) // call hook with unwrapped listener
    }

    origListener := l
    l = &onceCloseListener{Listener: l}
    defer l.Close()

    if err := srv.setupHTTP2_Serve(); err != nil {
        return err
    }

    if !srv.trackListener(&l, true) {
        return ErrServerClosed
    }
    defer srv.trackListener(&l, false)

    baseCtx := context.Background()
    if srv.BaseContext != nil {
        baseCtx = srv.BaseContext(origListener)
        if baseCtx == nil {
            panic("BaseContext returned a nil context")
        }
    }

    var tempDelay time.Duration // how long to sleep on accept failure
    ctx := context.WithValue(baseCtx, ServerContextKey, srv)
    for {
        rw, err := l.Accept()
        if err != nil {
            select {
            case <-srv.getDoneChan():
                return ErrServerClosed
            default:
            }
            if ne, ok := err.(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", err, tempDelay)
                time.Sleep(tempDelay)
                continue
            }
            return 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)
    }
}
// 上述for循环可简化为,
for {
    rw, e := l.Accept() // 这里就是监听端口收到的请求,当收到请求后,建立新连接,
    ...
    c, err := srv.newConn(rw)
    ...
    go c.serve()  // 对新建的连接开启一个协程进行处理
}
// 注意在新连接生成的时候,将之前ListenAndServe中初始化的Server也保存在了里面,
func (srv *Server) newConn(rwc net.Conn) *conn {
    c := &conn{
        server: srv,
        rwc:    rwc,
    }
    if debugServerConnections {
        c.rwc = newLoggingConn("server", c.rwc)
    }
    return c
}
// 调用serve处理收到的Request,
func (c *conn) serve(ctx context.Context) {
    c.remoteAddr = c.rwc.RemoteAddr().String()
    ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())
    defer func() {
        if err := recover(); err != nil && err != ErrAbortHandler {
            const size = 64 << 10
            buf := 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(c.rwc, 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 {
            // If the handshake failed due to the client not speaking
            // TLS, assume they're speaking plaintext HTTP and write a
            // 400 response on the TLS conn's underlying net.Conn.
            if re, ok := err.(tls.RecordHeaderError); ok && re.Conn != nil && tlsRecordHeaderLooksLikeHTTP(re.RecordHeader) {
                io.WriteString(re.Conn, "HTTP/1.0 400 Bad Request\r\n\r\nClient sent an HTTP request to an HTTPS server.\n")
                re.Conn.Close()
                return
            }
            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; validNextProto(proto) {
            if fn := c.server.TLSNextProto[proto]; fn != nil {
                h := initALPNRequest{ctx, tlsConn, serverHandler{c.server}}
                fn(c.server, tlsConn, h)
            }
            return
        }
    }
    // HTTP/1.x from here on.
    ctx, cancelCtx := context.WithCancel(ctx)
    c.cancelCtx = cancelCtx
    defer cancelCtx()
    c.r = &connReader{conn: c}
    c.bufr = newBufioReader(c.r)
    c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)
    for {
        w, err := c.readRequest(ctx)
        if c.r.remain != c.server.initialReadLimitSize() {
            // If we read any bytes off the wire, we're active.
            c.setState(c.rwc, StateActive)
        }
        if err != nil {
            const errorHeaders = "\r\nContent-Type: text/plain; charset=utf-8\r\nConnection: close\r\n\r\n"
            switch {
            case err == errTooLarge:
                const publicErr = "431 Request Header Fields Too Large"
                fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr)
                c.closeWriteAndWait()
                return
            case isUnsupportedTEError(err):
                code := StatusNotImplemented
                fmt.Fprintf(c.rwc, "HTTP/1.1 %d %s%sUnsupported transfer encoding", code, StatusText(code), errorHeaders)
                return
            case isCommonNetReadError(err):
                return // don't reply
            default:
                publicErr := "400 Bad Request"
                if v, ok := err.(badRequestError); ok {
                    publicErr = publicErr + ": " + string(v)
                }
                fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr)
                return
            }
        }
        req := w.req
        if req.expectsContinue() {
            if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {
                // Wrap the Body reader with one that replies on the connection
                req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
                w.canWriteContinue.setTrue()
            }
        } else if req.Header.get("Expect") != "" {
            w.sendExpectationFailed()
            return
        }
        c.curReq.Store(w)
        if requestBodyRemains(req.Body) {
            registerOnHitEOF(req.Body, w.conn.r.startBackgroundRead)
        } else {
            w.conn.r.startBackgroundRead()
        }
        serverHandler{c.server}.ServeHTTP(w, w.req)
        w.cancelCtx()
        if c.hijacked() {
            return
        }
        w.finishRequest()
        if !w.shouldReuseConnection() {
            if w.requestBodyLimitHit || w.closedRequestBodyEarly() {
                c.closeWriteAndWait()
            }
            return
        }
        c.setState(c.rwc, StateIdle)
        c.curReq.Store((*response)(nil))
        if !w.conn.server.doKeepAlives() {
            return
        }
        if d := c.server.idleTimeout(); d != 0 {
            c.rwc.SetReadDeadline(time.Now().Add(d))
            if _, err := c.bufr.Peek(4); err != nil {
                return
            }
        }
        c.rwc.SetReadDeadline(time.Time{})
    }
}
//对于同一个连接, 循环地执行读取请求, 处理请求, 完成请求三个操作
for{
    w, err := c.readRequest()
    ...
    serverHandler{c.server}.ServeHTTP(w, w.req) // 这里直接初始化了一个serverHandler对象,
    ...
    w.finishRequest()
    ...
}
// rw req都是之前在readRequest中读取的,
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
    // serverHandler只有一个属性*Server,Server的属性Handler即之前http.ListenAndServer中传入的handler,
    // Server的属性Handler是接口Handler,它只有一个方法ServeHTTP,只要实现了ServeHTTP方法,就实现了Handler接口,
    if handler == nil {
        handler = DefaultServeMux
    }
    if req.RequestURI == "*" && req.Method == "OPTIONS" {
        handler = globalOptionsHandler{}
    }
    // 若handler接口绑定的类型为Engine,则ServeHTTP在gin中实现,
    handler.ServeHTTP(rw, req)
}
// 在gin中,上面的ServeHTTP会调用这个,
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    c := engine.pool.Get().(*Context)
    c.writermem.reset(w)
    c.Request = req
    c.reset()
    engine.handleHTTPRequest(c)
    engine.pool.Put(c)
}
// 在直接调用http包时,上面的handler接口绑定的类型是之前初始化的ServeMux对象
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
    }
    // 在之前注册的ServeMux中找到URL对应的路由函数,
    h, _ := mux.Handler(r)
    h.ServeHTTP(w, r)
}
// 在Handler中调用了handler
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
    mux.mu.RLock()
    defer mux.mu.RUnlock()
    if mux.hosts {
        h, pattern = mux.match(host + path)
    }
    if h == nil {
        h, pattern = mux.match(path)
    }
    if h == nil {
        h, pattern = NotFoundHandler(), ""
    }
    return
}
// match就是直接在map中寻找对应URL的路由函数,
func (mux *ServeMux) match(path string) (h Handler, pattern string) {
    v, ok := mux.m[path]
    if ok {
        return v.h, v.pattern
    }
    for _, e := range mux.es {
        if strings.HasPrefix(path, e.pattern) {
            return e.h, e.pattern
        }
    }
    return nil, ""
}
// 上面返回的h类型是HandlerFunc,即type HandlerFunc func(ResponseWriter, *Request),
// 因为之前在路由注册前进行类型强制转换,HanderFunc类型实现了Handler接口,所以这里可以直接调用,
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}
View Code

总体流程

一 调用http.HandleFunc("/a/", handleRequest)按如下顺序执行:
1 首先调用Http.HandleFunc,
2 调用了DefaultServerMux的HandleFunc
3 调用了DefaultServerMux的Handle,这里进行了类型的强转,因为传入的函数并没有实现ServeHTTP方法,
  往DefaultServerMux的map[string] muxEntry中增加对应的handler和路由规则。

二 调用http.ListenAndServe(":9090",nil),按如下顺序执行:
1 实例化Server。                        server := &Server{Addr: addr, Handler: handler}  Handler为实现了ServeHTTP的接口,
2 调用Server的ListenAndServe()。        server.ListenAndServe()
3 调用net.Listen("tcp",addr)监听端口。   ln, err := net.Listen("tcp", addr)
4 启动一个for死循环,在循环体中监听请求。    rw, err := l.Accept()
5 一旦收到请求,对该请求实例化一个Conn,并且开启一个goroutine为这个请求进行服务go c.serve()。  c := srv.newConn(rw)  go c.serve(connCtx)
6 在c.serve()中仍是启动一个for死循环,读取请求的内容。readRequest是读取数据,解析请求的地方,包括解析请求的header、body,和一些基本的校验,
  比如header头信息,请求method等          w, err := c.readRequest(ctx)
7 调用业务层定义的路由,调用最开始在http.ListenAndServe中实例化的Server,它也实现了Handler接口,并将处理好的Request、Response对象作为参数传入
  serverHandler{c.server}.ServeHTTP(w, w.req)
8 在Server的ServeHTTP中,将handler设置为1中的handler,若为nil设置为DefaultServeMux
  handler := sh.srv.Handler    if handler == nil {handler = DefaultServeMux}
9 调用handler的ServeHttp。              handler.ServeHTTP(rw, req)
10在之前注册的路由中找到处理请求的路由函数,  h, _ := mux.Handler(r)
11如果找到了路由函数,调用这个路由handler的ServeHttp。    h.ServeHTTP(w, r)
  否则返回"404 page not found"return HandlerFunc(NotFound)
View Code

参考:https://james-yip.github.io/2017/11/16/go-http-src-analysis/

https://www.huweihuang.com/golang-notes/web/golang-http-execution-flow.html

https://www.shipengqi.top/2019/11/12/go-http-resouce-code-analysis/

https://zhuanlan.zhihu.com/p/101995755

 

posted on 2020-12-19 19:44  吃我一枪  阅读(622)  评论(0编辑  收藏  举报

导航