源码阅读-golang-net/http

98 阅读4分钟

简单使用

package main

import "net/http"

func main(){
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {  
        w.Write([]byte("hello world"))  
    })  
    http.ListenAndServe("", nil)
}

通过 net/http 包调用 HandleFunc 进行路由注册,其中第一个参数 pattern 是请求路径, 第二个参数handler 是请求处理参数,

调用ListenAndServe 函数,并传入 网络地址请求处理器,当 网络地址 参数为空字符串,那么默认使用 80 端口进行连接;处理器参数为 nil 时服务端会使用默认的多路复用器 DefaultServeMux

源码分析

路由分配

http.HandleFunc()

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
   DefaultServeMux.HandleFunc(pattern, handler)
}

DefaultServeMux 是默认的请求处理器 ServeMuxServeMux 使用map保存了注册的所有路径和处理函数的对应关系,结构如下

type ServeMux struct {
    mu    sync.RWMutex  // 锁
    m     map[string]muxEntry // 路由规则,一个string对应一个mux实体,这里的string就是注册的路由表达式
    es    []muxEntry  // slice of entries sorted from longest to shortest.
    hosts bool        // 是否有任何路径包含主机名
}

其中 muxEntry 定义如下

type muxEntry struct {
    h       Handler // 路由对应哪个handler,handler是一个接口,包含ServeHTTP(ResponseWriter, *Request)方法
    pattern string  // 匹配字符串
    }

再看一下 ServeMux.HandleFunc()这个方法

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    if handler == nil {
        panic("http: nil handler")
    }
    // 将func表示的handler转换为net/http中的HandlerFunc,并在其接口ServerHTTP中调用handler.
    mux.Handle(pattern, HandlerFunc(handler))
}

HandlerFunc 实际上是以函数类型 func(ResponseWriter, *Request) 为底层类型,为 HandlerFunc 类型定义了方法 ServeHTTP()

type HandlerFunc func(ResponseWriter, *Request)

Go语言中允许为(基于)函数的类型定义方法,Serve.Handle() 方法只接受类型为接口Handler的参数:

// ServeHTTP calls f(w, r)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { 
        f(w, r) 
}

HandlerFunc(handler) 将我们自己的处理函数 handler 转换成了 HandlerFunc 类型,使结构体实现了net\http 下的 Handler 接口,并重写了ServerHTTP方法,在该方法内部直接调用我们自己的handler

最后调用 mux.Handle() 方法进行注册

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
    }
}

端口监听

首先看一下 http.ListenAndServe("", nil) 这个方法

func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}

生成 Server 结构体,调用其 ListenAndServe 方法

其中 Server 结构体的定义如下

type Server struct {
    Addr string
    Handler Handler 
    DisableGeneralOptionsHandler bool
    TLSConfig *tls.Config
    ReadTimeout time.Duration
    ReadHeaderTimeout time.Duration
    WriteTimeout time.Duration
    IdleTimeout time.Duration
    MaxHeaderBytes int
    TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
    ConnState func(net.Conn, ConnState)
    ErrorLog *log.Logger
    BaseContext func(net.Listener) context.Context
    ConnContext func(ctx context.Context, c net.Conn) context.Context
    inShutdown atomic.Bool
    disableKeepAlives atomic.Bool
    nextProtoOnce     sync.Once 
    nextProtoErr      error     
    mu         sync.Mutex
    listeners  map[*net.Listener]struct{}
    activeConn map[*conn]struct{}
    onShutdown []func()
    listenerGroup sync.WaitGroup
}

server.ListenAndServe()

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)
}

net.Listen("tcp", addr) 对地址进行监听,返回 Listener 作为参数传入 Server.Serve() 方法

func (srv *Server) Serve(l net.Listener) error {
    ...

    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 {
            if srv.shuttingDown() {
                return ErrServerClosed
            }
            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
        // 构造一个新的conn,然后开启goroutine去执行本次请求.
        c := srv.newConn(rw)
        c.setState(c.rwc, StateNew, runHooks) // before Serve can return
        go c.serve(connCtx)
    }
}

Serve 接受监听器 l 上传入的连接,为每个连接创建一个新的服务 goroutine ,读取请求并然后调用 srv.Handler 来回复它们

执行一个 for 死循环,通过 l.Accept() 来接受请求返回连接 Conn,根据该连接创建对应的 goroutine 来执行 conn.serve(ctx),同时 context 作为参数传入,用于管理这些生成的 goroutine

请求解析

func (c *conn) serve(ctx context.Context) {
    ...
    for {
        w, err := c.readRequest(ctx)
        ...
        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 赋值,如果是nil,则使用默认的 DefaultServeMux
    handler := sh.srv.Handler  
    if handler == nil {  
        handler = DefaultServeMux  
    }
    // 
    if !sh.srv.DisableGeneralOptionsHandler && req.RequestURI == "*" && req.Method == "OPTIONS" {  
        handler = globalOptionsHandler{}  
    }  
  
    if req.URL != nil && strings.Contains(req.URL.RawQuery, ";") {  
        var allowQuerySemicolonsInUse atomic.Bool  
        req = req.WithContext(context.WithValue(req.Context(),
            silenceSemWarnContextKey, func() {  
                allowQuerySemicolonsInUse.Store(true)  
        }))  
        defer func() {  
            if !allowQuerySemicolonsInUse.Load() {  
                sh.srv.logf("http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192")  
            }  
       }()  
    }  
  
    handler.ServeHTTP(rw, req)  
}

handler.ServeHTTP(rw, req)

// 根据request然后去ServeMux中找到属于当前请求的Handler
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) 
}

h, _ := mux.Handler(r)

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)
        u := &url.URL{Path: path, RawQuery: r.URL.RawQuery}
        return RedirectHandler(u.String(), StatusMovedPermanently), pattern
    }

    // 从map中取出自己的对应的handler并返回
    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, ""  
}