golang net之http server


golang 版本:1.12.9

簡單的HTTP服務器代碼:

package main

import (
    "net/http"
)

type TestHandler struct {
    str string
}

func (th *TestHandler)ServeHTTP(w http.ResponseWriter, r *http.Request){
    w.Write([]byte(string(th.str+",welcome")))
}

func main(){
    http.Handle("/", &TestHandler{"Hi,Stranger"})
    http.HandleFunc("/test",func(w http.ResponseWriter,r *http.Request){
        w.Write([]byte("Hi,Tester"))
    })
    http.ListenAndServe(":8000",nil)}

在瀏覽器輸入“http://127.0.0.1:8000”得到輸出“Hi,Stranger,welcome”;輸入“http://127.0.0.1:8000/test”得到輸出“Hi,Tester”

handler的注冊

handler的相關方法如下:

func NewServeMux() *ServeMux func (mux *ServeMux) Handle(pattern string, handler Handler) //注冊handler func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) //注冊handler func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) //在mux.m中根據pattern查找handler func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) //handler的具體實現

http使用handler定義請求的路徑以及請求的處理。每個handler都必須實現ServeHTTP方法,該方法將請求分發到不同的handler進行處理,每個handler處理一條請求路徑。有兩種注冊handler的方式:http.Handlehttp.HandleFunc,兩種實現本質上是一致的,前者需要明確寫出ServeHTTP方法的實現,后者由內置方法實現(見下文)。

Handler的接口定義如下:

// net/http/server.go
type Handler interface { ServeHTTP(ResponseWriter, *Request) }

http.HandleFunc的第二個參數被定義為HandlerFunc,實現了Handler接口。

// net/http/server.go
type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) }

當http.ListenAndServe的第二個參數為nil,則使用http.Handle和http.HandleFunc方法注冊的handler,默認保存在http.DefaultServeMux.m中(注冊方法為ServeMux.Handle/ServeMux.HandleFunc)。當http server接收到一個request時,會在serverHandler.ServeHTTP中調用DefaultServeMux.ServeHTTP來處理接收到的request,分為兩步:

    • 調用ServeMux.Handler函數,在ServeMux.m中根據pattern遍歷查合適的handler
    • 調用handler的ServeHTTP方法

serverHandler.ServeHTTP的源碼如下:

// net/http/server.go
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
// 如果有自注冊的handler則使用自注冊的,否則使用默認的handler處理請求 handler :
= sh.srv.Handler if handler == nil { handler = DefaultServeMux } if req.RequestURI == "*" && req.Method == "OPTIONS" { handler = globalOptionsHandler{} } handler.ServeHTTP(rw, req) }

DefaultServeMux的結構體定義如下:

 
         
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
// net/http/server.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
}

默認的handler的ServeHTTP方法實現如下,主要實現查找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
    }
// 根據請求的路徑查找注冊的handler h, _ :
= mux.Handler(r)
// 調用注冊的handler處理請求,對應上面例子的
// http.HandleFunc("/test",func(w http.ResponseWriter,r *http.Request){w.Write([]byte("Hi,Tester"))}) h.ServeHTTP(
w, r) }
// 本函數根據請求中的路徑找到合適的handler或者重定向(請求路徑格式不正確)
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) { // CONNECT requests are not canonicalized.
// 對CONNECT請求的處理,代理場景 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.
// redirectToPathSlash函數主要用於自動檢測是否重定向URL並修改重定向URL路徑,當注冊的URL路徑為/tree/,而請求URL路徑為/tree,
// redirectToPathSlash函數無法在mux.m中查找注冊的handler,則將設請求URL設置為/tree/
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) // 非代理場景重定向的處理,與"CONNECT"邏輯相同 if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok { return RedirectHandler(u.String(), StatusMovedPermanently), u.Path } // 如果請求路徑不等於處理后的路徑,如請求路徑為"//test/",處理后的路徑為"/test/",執行重定向並返回URL路徑,重定向
// 通過http.redirectHandler.ServeHTTP函數進行處理,如下:
/*

        < HTTP/1.1 301 Moved Permanently
        < Content-Type: text/html; charset=utf-8
        < Location: /test/
        < Date: Fri, 06 Dec 2019 03:35:59 GMT
        < Content-Length: 41
        <
        <a href="/test/">Moved Permanently</a>.

      */

if path != r.URL.Path {
        _, pattern = mux.handler(host, path)
        url := *r.URL
        url.Path = path
        return RedirectHandler(url.String(), StatusMovedPermanently), pattern
    }
    // 在mux.m和mux.es中根據host/url.path找到對應的handler
    return mux.handler(host, r.URL.Path)
}
func (rh *redirectHandler) ServeHTTP(w ResponseWriter, r *Request) {
    Redirect(w, r, rh.url, rh.code)
}

通常使用http.HandleFunc注冊handler,使用DefaultServeMux的方法分發處理請求即可。也可以通過http.NewServeMux()創建一個自定義的serverHandler,並實現Serve HTTP方法。

import (
    "net/http"
)

type TestHandler struct {
    str string
}

func (th *TestHandler)ServeHTTP(w http.ResponseWriter, r *http.Request){
    w.Write([]byte(string(th.str+",welcome")))
}

func main(){
 serverHandler := http.NewServeMux()
    serverHandler.Handle("/", &TestHandler{"Hi,Stranger"})
    serverHandler.HandleFunc("/test",func(w http.ResponseWriter,r *http.Request){
        w.Write([]byte("Hi,Tester"))
    })

    http.ListenAndServe(":8000",serverHandler)
}

http.server

調用下面函數進行監聽,主要創建監聽socket並接收該socket上的連接。通常調用如下接口即可:

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

一個Server結構體表示一個啟用監聽端口的真實服務

type Server struct {
    Addr    string  // TCP address to listen on, ":http" if empty
    Handler Handler // handler to invoke, http.DefaultServeMux if nil

    // TLSConfig optionally provides a TLS configuration for use
    // by ServeTLS and ListenAndServeTLS. Note that this value is
    // cloned by ServeTLS and ListenAndServeTLS, so it's not
    // possible to modify the configuration with methods like
    // tls.Config.SetSessionTicketKeys. To use
    // SetSessionTicketKeys, use Server.Serve with a TLS Listener
    // instead.
    TLSConfig *tls.Config

    // ReadTimeout is the maximum duration for reading the entire
    // request, including the body.
    //
    // Because ReadTimeout does not let Handlers make per-request
    // decisions on each request body's acceptable deadline or
    // upload rate, most users will prefer to use
    // ReadHeaderTimeout. It is valid to use them both.
    ReadTimeout time.Duration

    // ReadHeaderTimeout is the amount of time allowed to read
    // request headers. The connection's read deadline is reset
    // after reading the headers and the Handler can decide what
    // is considered too slow for the body. If ReadHeaderTimeout
    // is zero, the value of ReadTimeout is used. If both are
    // zero, there is no timeout.
    ReadHeaderTimeout time.Duration

    // WriteTimeout is the maximum duration before timing out
    // writes of the response. It is reset whenever a new
    // request's header is read. Like ReadTimeout, it does not
    // let Handlers make decisions on a per-request basis.
    WriteTimeout time.Duration

    // IdleTimeout is the maximum amount of time to wait for the
    // next request when keep-alives are enabled. If IdleTimeout
    // is zero, the value of ReadTimeout is used. If both are
    // zero, there is no timeout.
    IdleTimeout time.Duration

    // MaxHeaderBytes controls the maximum number of bytes the
    // server will read parsing the request header's keys and
    // values, including the request line. It does not limit the
    // size of the request body.
    // If zero, DefaultMaxHeaderBytes is used.
    MaxHeaderBytes int

    // TLSNextProto optionally specifies a function to take over
    // ownership of the provided TLS connection when an NPN/ALPN
    // protocol upgrade has occurred. The map key is the protocol
    // name negotiated. The Handler argument should be used to
    // handle HTTP requests and will initialize the Request's TLS
    // and RemoteAddr if not already set. The connection is
    // automatically closed when the function returns.
    // If TLSNextProto is not nil, HTTP/2 support is not enabled
    // automatically.
    TLSNextProto map[string]func(*Server, *tls.Conn, Handler)

    // ConnState specifies an optional callback function that is
    // called when a client connection changes state. See the
    // ConnState type and associated constants for details.
    ConnState func(net.Conn, ConnState)

    // ErrorLog specifies an optional logger for errors accepting
    // connections, unexpected behavior from handlers, and
    // underlying FileSystem errors.
    // If nil, logging is done via the log package's standard logger.
    ErrorLog *log.Logger

    // BaseContext optionally specifies a function that returns
    // the base context for incoming requests on this server.
    // The provided Listener is the specific Listener that's
    // about to start accepting requests.
    // If BaseContext is nil, the default is context.Background().
    // If non-nil, it must return a non-nil context.
    BaseContext func(net.Listener) context.Context

    // ConnContext optionally specifies a function that modifies
    // the context used for a new connection c. The provided ctx
    // is derived from the base context and has a ServerContextKey
    // value.
    ConnContext func(ctx context.Context, c net.Conn) context.Context

    disableKeepAlives int32     // accessed atomically.
    inShutdown        int32     // accessed atomically (non-zero means we're in Shutdown)
    nextProtoOnce     sync.Once // guards setupHTTP2_* init
    nextProtoErr      error     // result of http2.ConfigureServer if used

    mu         sync.Mutex
    listeners  map[*net.Listener]struct{}
    activeConn map[*conn]struct{}
    doneChan   chan struct{}
    onShutdown []func()
}

 ListenAndServe在創建監聽socket后調用Serve等待連接

func (srv *Server) ListenAndServe() error {
// 服務器調用Server.Close或Server.Shutdown關閉連接時會設置shuttingDown為1,表示該服務正在停止,不可提供服務。
// Close會直接關閉底層tcp連接,Shutdown
則會調用服務提供的函數Server.onShutdown平滑關閉。推薦使用Shutdown
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) }

ListenAndServeTLS與ListenAndServe類似,只是入參多了證書參數

func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error {
    if srv.shuttingDown() { return ErrServerClosed } addr := srv.Addr if addr == "" { addr = ":https" } ln, err := net.Listen("tcp", addr) if err != nil { return err } defer ln.Close() return srv.ServeTLS(ln, certFile, keyFile) }

ServeTLS函數中會調用tls.NewListener創建一個tls類型的監聽socket,后續會調用tls的Accetp函數接收客戶端連接

func (srv *Server) ServeTLS(l net.Listener, certFile, keyFile string) error {
    // Setup HTTP/2 before srv.Serve, to initialize srv.TLSConfig
    // before we clone it and create the TLS Listener.
    if err := srv.setupHTTP2_ServeTLS(); err != nil { return err } config := cloneTLSConfig(srv.TLSConfig) if !strSliceContains(config.NextProtos, "http/1.1") { config.NextProtos = append(config.NextProtos, "http/1.1") } configHasCert := len(config.Certificates) > 0 || config.GetCertificate != nil if !configHasCert || certFile != "" || keyFile != "" { var err error config.Certificates = make([]tls.Certificate, 1) config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile) if err != nil { return err } } tlsListener := tls.NewListener(l, config) return srv.Serve(tlsListener) }
// src/crypto/tls/tls.go
// tls的Accept僅僅在處理Server函數是增加了證書相關的參數

func (l *listener) Accept() (net.Conn, error) { c, err := l.Listener.Accept() if err != nil { return nil, err } return Server(c, l.config), nil }

Serve主要實現如下。通過Accept與客戶端創建連接后,通過newConn函數初始化一個HTTP連接,該連接包含HTTP的描述(監聽地址,URL等)和一個TCP連接,然后處理來自客戶的HTTP請求。

func (srv *Server) Serve(l net.Listener) error {
    ...
    ctx := context.WithValue(baseCtx, ServerContextKey, srv)
    for {
// Accept()返回底層TCP的連接 rw, e :
= l.Accept() if e != nil { select { case <-srv.getDoneChan(): return ErrServerClosed default: } if ne, ok := e.(net.Error); ok && ne.Temporary() {
// 處理accept因為網絡失敗之后的等待時間
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 } if cc := srv.ConnContext; cc != nil { ctx = cc(ctx, rw) if ctx == nil { panic("ConnContext returned nil") } } tempDelay = 0
//構造HTTP連接
c := srv.newConn(rw) c.setState(c.rwc, StateNew) // before Serve can return
//在另外的goroutine中處理基於該TCP的HTTP請求,本goroutine可以繼續accept TCP連接
go c.serve(ctx) } }

Accept返回的底層的連接結構如下

type Conn interface {
    // Read reads data from the connection.
    // Read can be made to time out and return an Error with Timeout() == true
    // after a fixed time limit; see SetDeadline and SetReadDeadline.
    Read(b []byte) (n int, err error)

    // Write writes data to the connection.
    // Write can be made to time out and return an Error with Timeout() == true
    // after a fixed time limit; see SetDeadline and SetWriteDeadline.
    Write(b []byte) (n int, err error)

    // Close closes the connection.
    // Any blocked Read or Write operations will be unblocked and return errors.
    Close() error

    // LocalAddr returns the local network address.
    LocalAddr() Addr

    // RemoteAddr returns the remote network address.
    RemoteAddr() Addr

    // SetDeadline sets the read and write deadlines associated
    // with the connection. It is equivalent to calling both
    // SetReadDeadline and SetWriteDeadline.
    //
    // A deadline is an absolute time after which I/O operations
    // fail with a timeout (see type Error) instead of
    // blocking. The deadline applies to all future and pending
    // I/O, not just the immediately following call to Read or
    // Write. After a deadline has been exceeded, the connection
    // can be refreshed by setting a deadline in the future.
    //
    // An idle timeout can be implemented by repeatedly extending
    // the deadline after successful Read or Write calls.
    //
    // A zero value for t means I/O operations will not time out.
    //
    // Note that if a TCP connection has keep-alive turned on,
    // which is the default unless overridden by Dialer.KeepAlive
    // or ListenConfig.KeepAlive, then a keep-alive failure may
    // also return a timeout error. On Unix systems a keep-alive
    // failure on I/O can be detected using
    // errors.Is(err, syscall.ETIMEDOUT).
    SetDeadline(t time.Time) error

    // SetReadDeadline sets the deadline for future Read calls
    // and any currently-blocked Read call.
    // A zero value for t means Read will not time out.
    SetReadDeadline(t time.Time) error

    // SetWriteDeadline sets the deadline for future Write calls
    // and any currently-blocked Write call.
    // Even if write times out, it may return n > 0, indicating that
    // some of the data was successfully written.
    // A zero value for t means Write will not time out.
    SetWriteDeadline(t time.Time) error
}

實現如上接口的有tcpsock的TCPConn以及unixsock的UnixConn,通常使用TCPConn

type TCPConn struct {
    conn
}
type UnixConn struct {
    conn
}

newConn生成的HTTP結構體如下,它表示一條基於TCP的HTTP連接,封裝了3個重要的數據結構:server表示HTTP server的"server";rwc表示底層連接結構體rwc net.Conn;r用於讀取http數據的connReader(從rwc讀取數據)。后續的request和response都基於該結構體

type conn struct {
    // server is the server on which the connection arrived.
    // Immutable; never nil.
 server *Server // cancelCtx cancels the connection-level context.
    cancelCtx context.CancelFunc

    // rwc is the underlying network connection.
    // This is never wrapped by other types and is the value given out
    // to CloseNotifier callers. It is usually of type *net.TCPConn or
    // *tls.Conn.
 rwc net.Conn // remoteAddr is rwc.RemoteAddr().String(). It is not populated synchronously
    // inside the Listener's Accept goroutine, as some implementations block.
    // It is populated immediately inside the (*conn).serve goroutine.
    // This is the value of a Handler's (*Request).RemoteAddr.
    remoteAddr string

    // tlsState is the TLS connection state when using TLS.
    // nil means not TLS.
    tlsState *tls.ConnectionState

    // werr is set to the first write error to rwc.
    // It is set via checkConnErrorWriter{w}, where bufw writes.
    werr error

    // r is bufr's read source. It's a wrapper around rwc that provides
    // io.LimitedReader-style limiting (while reading request headers)
    // and functionality to support CloseNotifier. See *connReader docs.
 r *connReader // bufr reads from r.
    bufr *bufio.Reader

    // bufw writes to checkConnErrorWriter{c}, which populates werr on error.
    bufw *bufio.Writer

    // lastMethod is the method of the most recent request
    // on this connection, if any.
    lastMethod string

    curReq atomic.Value // of *response (which has a Request in it)

    curState struct{ atomic uint64 } // packed (unixtime<<8|uint8(ConnState))

    // mu guards hijackedv
    mu sync.Mutex

    // hijackedv is whether this connection has been hijacked
    // by a Handler with the Hijacker interface.
    // It is guarded by mu.
    hijackedv bool
}

connReader中的conn就是上面表示http連接的結構體

type connReader struct {
 conn *conn

    mu      sync.Mutex // guards following
    hasByte bool
    byteBuf [1]byte
    cond    *sync.Cond
    inRead  bool
    aborted bool  // set true before conn.rwc deadline is set to past
    remain  int64 // bytes remaining
}

在下面的server函數中處理請求並返回響應

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

// 處理ServeTLS accept的連接
if tlsConn, ok := c.rwc.(*tls.Conn); ok { if d := c.server.ReadTimeout; d != 0 {
// 設置TCP的讀超時時間 c.rwc.SetReadDeadline(time.Now().Add(d)) }
if d := c.server.WriteTimeout; d != 0 {
// 設置TCP的寫超時時間 c.rwc.SetWriteDeadline(time.Now().Add(d)) }
// tls協商並判斷協商結果
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()
// 用於判斷是否使用TLS的NPN擴展協商出非http/1.1和http/1.0的上層協議,如果存在則使用server.TLSNextProto處理請求
if proto := c.tlsState.NegotiatedProtocol; validNPN(proto) { if fn := c.server.TLSNextProto[proto]; fn != nil { h := initNPNRequest{ctx, tlsConn, serverHandler{c.server}} fn(c.server, tlsConn, h) } return } } // 下面處理HTTP/1.x的請求 ctx, cancelCtx := context.WithCancel(ctx) c.cancelCtx = cancelCtx defer cancelCtx()   
   // 為c.bufr創建read源,使用sync.pool提高存取效率 c.r
= &connReader{conn: c}
// read buf長度默認為4096,創建ioReader為c.r的bufio.Reader。用於讀取HTTP的request c.bufr
= newBufioReader(c.r)
   // c.bufw默認長度為4096,4<<10=4096,用於發送response c.bufw
= newBufioWriterSize(checkConnErrorWriter{c}, 4<<10) // 循環處理HTTP請求 for {
     // 處理請求並返回封裝好的響應 w, err :
= c.readRequest(ctx)
// 判斷是否有讀取過數據,如果讀取過數據則設置TCP狀態為active
if c.r.remain != c.server.initialReadLimitSize() { // If we read any bytes off the wire, we're active. c.setState(c.rwc, StateActive) }
// 處理http請求錯誤
if err != nil { const errorHeaders = "\r\nContent-Type: text/plain; charset=utf-8\r\nConnection: close\r\n\r\n" switch { case 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. const publicErr = "431 Request Header Fields Too Large" fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr) c.closeWriteAndWait() return // 直接return會斷開底層TCP連接(GC?) case isUnsupportedTEError(err): // Respond as per RFC 7230 Section 3.3.1 which says, // A server that receives a request message with a // transfer coding it does not understand SHOULD // respond with 501 (Unimplemented). code := StatusNotImplemented // We purposefully aren't echoing back the transfer-encoding's value, // so as to mitigate the risk of cross side scripting by an attacker. 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 } } // Expect 100 Continue support req := w.req
// 如果http首部包含"100-continue"請求
if req.expectsContinue() {
// "100-continue"的首部要求http1.1版本以上,且http.body長度不為0
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} }
// 非"100-continue"但首部包含"Expect"字段的請求為非法請求 }
else if req.Header.get("Expect") != "" { w.sendExpectationFailed() return } // curReq保存了當前的response,當前代碼中主要用於在讀失敗后調用response中的closeNotifyCh傳遞信號,此時連接斷開 c.curReq.Store(w) // 判斷是否有后續的數據,req.Body在http.readTransfer函數中設置為http.body類型,registerOnHitEOF注冊的就是
// 遇到EOF時執行的函數http.body.onHitEOF
if requestBodyRemains(req.Body) { registerOnHitEOF(req.Body, w.conn.r.startBackgroundRead) } else {
// 如果沒有后續的數據,調用下面函數在新的goroutine中阻塞等待數據的到來,通知finishRequest w.conn.r.startBackgroundRead() }
// 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. // But we're not going to implement HTTP pipelining because it // was never deployed in the wild and the answer is HTTP/2.
// 通過請求找到匹配的handler,然后處理請求並發送響應 serverHandler{c.server}.ServeHTTP(w, w.req) w.cancelCtx() if c.hijacked() { return }
// 該函數中會結束HTTP請求,發送response w.finishRequest()
// 判斷是否需要重用底層TCP連接,即是否退出本函數的for循環,推出for循環將斷開連接
if !w.shouldReuseConnection() {
// 不可重用底層連接時,如果請求數據過大或設置提前取消讀取數據,則調用closeWriteAndWait平滑關閉TCP連接
if w.requestBodyLimitHit || w.closedRequestBodyEarly() { c.closeWriteAndWait() } return }
// 重用連接,設置底層狀態為idle c.setState(c.rwc, StateIdle) c.curReq.Store((
*response)(nil))
// 如果沒有通過SetKeepAlivesEnabled設置HTTP keepalive或底層連接已經通過如Server.Close關閉,則直接退出
if !w.conn.server.doKeepAlives() { // We're in shutdown mode. We might've replied // to the user without "Connection: close" and // they might think they can send another // request, but such is life with HTTP/1.1. return } if d := c.server.idleTimeout(); d != 0 {
// 如果設置了idle狀態超時時間,則調用SetReadDeadline設置底層連接deadline,並調用bufr.Peek等待請求 c.rwc.SetReadDeadline(time.Now().Add(d))
if _, err := c.bufr.Peek(4); err != nil { return } } c.rwc.SetReadDeadline(time.Time{}) } }

readRequest函數處理http請求

func (c *conn) readRequest(ctx context.Context) (w *response, err error) {
    if c.hijacked() {
        return nil, ErrHijacked
    }

    var (
        wholeReqDeadline time.Time // or zero if none
        hdrDeadline      time.Time // or zero if none
    )
    t0 := time.Now()
// 設置讀取HTTP的超時時間
if d := c.server.readHeaderTimeout(); d != 0 { hdrDeadline = t0.Add(d) }
// 設置讀取整個HTTP的超時時間
if d := c.server.ReadTimeout; d != 0 { wholeReqDeadline = t0.Add(d) }
// 通過SetReadDeadline設置TCP讀超時時間 c.rwc.SetReadDeadline(hdrDeadline)
if d := c.server.WriteTimeout; d != 0 {
// 通過defer設置TCP寫超時時間,本函數主要處理讀請求,在本函數處理完request之后再設置寫超時時間 defer func() { c.rwc.SetWriteDeadline(
time.Now().Add(d)) }() } // 設置讀取請求的最大字節數,為DefaultMaxHeaderBytes+4096=1052672,用於防止超大報文攻擊 c.r.setReadLimit(c.server.initialReadLimitSize())
// 處理老設備的client
if c.lastMethod == "POST" { // RFC 7230 section 3.5 Message Parsing Robustness tolerance for old buggy clients. peek, _ := c.bufr.Peek(4) // ReadRequest will get err below c.bufr.Discard(numLeadingCRorLF(peek)) }
// 從bufr讀取request,並返回結構體格式的請求 req, err :
= readRequest(c.bufr, keepHostHeader) if err != nil {
// 如果讀取的報文超過限制,則返回錯誤
if c.r.hitReadLimit() { return nil, errTooLarge } return nil, err } // 判斷是否是go服務所支持的HTTP/1.x的請求 if !http1ServerSupportsRequest(req) { return nil, badRequestError("unsupported protocol version") } c.lastMethod = req.Method c.r.setInfiniteReadLimit() hosts, haveHost := req.Header["Host"] isH2Upgrade := req.isH2Upgrade()
// 判斷是否需要Host首部字段
if req.ProtoAtLeast(1, 1) && (!haveHost || len(hosts) == 0) && !isH2Upgrade && req.Method != "CONNECT" { return nil, badRequestError("missing required Host header") }
// 多個Host首部字段
if len(hosts) > 1 { return nil, badRequestError("too many Host headers") }
// 非法Host首部字段值
if len(hosts) == 1 && !httpguts.ValidHostHeader(hosts[0]) { return nil, badRequestError("malformed Host header") }
// 判斷首部字段值是否有非法字符
for k, vv := range req.Header { if !httpguts.ValidHeaderFieldName(k) { return nil, badRequestError("invalid header name") } for _, v := range vv { if !httpguts.ValidHeaderFieldValue(v) { return nil, badRequestError("invalid header value") } } }
// 響應報文中不包含Host字段 delete(req.Header,
"Host") ctx, cancelCtx := context.WithCancel(ctx) req.ctx = ctx req.RemoteAddr = c.remoteAddr req.TLS = c.tlsState if body, ok := req.Body.(*body); ok { body.doEarlyClose = true } // 判斷是否超過請求的最大值 if !hdrDeadline.Equal(wholeReqDeadline) { c.rwc.SetReadDeadline(wholeReqDeadline) } w = &response{ conn: c, cancelCtx: cancelCtx, req: req, reqBody: req.Body, handlerHeader: make(Header), contentLength: -1, closeNotifyCh: make(chan bool, 1), // We populate these ahead of time so we're not // reading from req.Header after their Handler starts // and maybe mutates it (Issue 14940) wants10KeepAlive: req.wantsHttp10KeepAlive(), wantsClose: req.wantsClose(), } if isH2Upgrade { w.closeAfterReply = true }
// w.cw.res中保存了response的信息,而response中又保存了底層連接conn,后續將通過w.cw.res.conn寫數據
w.cw.res = w
// 創建2048字節的寫bufio,用於發送response
w.w = newBufioWriterSize(&w.cw, bufferBeforeChunkingSize) return w, nil }

讀取HTTP請求,並將其結構化為http.Request

func readRequest(b *bufio.Reader, deleteHostHeader bool) (req *Request, err error) {
    // 封裝為textproto.Reader,該結構體實現了讀取HTTP的相關方法
tp :
= newTextprotoReader(b)
// 初始化一個Request結構體,該函數后續工作就是填充該變量並返回 req
= new(Request) // First line: GET /index.html HTTP/1.0 var s string
// ReadLine會調用<textproto.(*Reader).ReadLine->textproto.(*Reader).readLineSlice->bufio.(*Reader).ReadLine->
// bufio.(*Reader).ReadSlic->bufio.(*Reader).fill->http.(*connReader).Read>讀取HTTP的請求並填充b.buf,並返回以"\n"作為
// 分隔符的首行字符串
if s, err = tp.ReadLine(); err != nil { return nil, err }
// putTextprotoReader函數使用sync.pool來保存textproto.Reader變量,通過重用內存來提升在大量HTTP請求下執行效率。
// 對應函數首部的newTextprotoReader
defer func() { putTextprotoReader(tp)
if err == io.EOF { err = io.ErrUnexpectedEOF } }() var ok bool
// 解析請求方法,請求URL,請求協議
req.Method, req.RequestURI, req.Proto, ok = parseRequestLine(s) if !ok { return nil, &badStringError{"malformed HTTP request", s} }
// 判斷方法是否包含非法字符
if !validMethod(req.Method) { return nil, &badStringError{"invalid method", req.Method} }
// 獲取請求路徑,如HTTP請求為"http://127.0.0.1:8000/test"時,rawurl為"/test" rawurl :
= req.RequestURI
// 判斷HTTP協議版本有效性,通常為支持HTTP/1.x
if req.ProtoMajor, req.ProtoMinor, ok = ParseHTTPVersion(req.Proto); !ok { return nil, &badStringError{"malformed HTTP version", req.Proto} } // CONNECT requests are used two different ways, and neither uses a full URL: // The standard use is to tunnel HTTPS through an HTTP proxy. // It looks like "CONNECT www.google.com:443 HTTP/1.1", and the parameter is // just the authority section of a URL. This information should go in req.URL.Host. // // The net/rpc package also uses CONNECT, but there the parameter is a path // that starts with a slash. It can be parsed with the regular URL parser, // and the path will end up in req.URL.Path, where it needs to be in order for // RPC to work.
// 處理代理場景,使用"CONNECT"與代理建立連接時會使用完整的URL(帶host)
justAuthority := req.Method == "CONNECT" && !strings.HasPrefix(rawurl, "/") if justAuthority { rawurl = "http://" + rawurl } if req.URL, err = url.ParseRequestURI(rawurl); err != nil { return nil, err } if justAuthority { // Strip the bogus "http://" back off. req.URL.Scheme = "" } // 解析request首部的key:value mimeHeader, err := tp.ReadMIMEHeader() if err != nil { return nil, err } req.Header = Header(mimeHeader) // RFC 7230, section 5.3: Must treat // GET /index.html HTTP/1.1 // Host: www.google.com // and // GET http://www.google.com/index.html HTTP/1.1 // Host: doesntmatter // the same. In the second case, any Host line is ignored. req.Host = req.URL.Host
// 如果是上面注釋中的第一種需要從req.Header中獲取"Host"字段
if req.Host == "" { req.Host = req.Header.get("Host") }
// "Host"字段僅存在於request中,在接收到之后需要刪除首部的Host字段,更多參見該變量注釋
if deleteHostHeader { delete(req.Header, "Host") } //處理"Cache-Control"首部 fixPragmaCacheControl(req.Header) // 判斷是否是長連接,如果是,則保持連接,反之則斷開並刪除"Connection"首部 req.Close = shouldClose(req.ProtoMajor, req.ProtoMinor, req.Header, false) // 解析首部字段並填充req內容 err = readTransfer(req, b) if err != nil { return nil, err } // 當HTTP1.1服務嘗試解析HTTP2的消息時使用"PRI"方法 if req.isH2Upgrade() { // Because it's neither chunked, nor declared: req.ContentLength = -1 // We want to give handlers a chance to hijack the // connection, but we need to prevent the Server from // dealing with the connection further if it's not // hijacked. Set Close to ensure that: req.Close = true } return req, nil }
func shouldClose(major, minor int, header Header, removeCloseHeader bool) bool {
    // HTTP/1.x以下不支持"connection"指定長連接
if major < 1 { return true } conv := header["Connection"]
// 如果首部包含"Connection: close"則斷開連接 hasClose :
= httpguts.HeaderValuesContainsToken(conv, "close")
// 使用HTTP/1.0時,如果包含"Connection: close"或不包含"Connection: keep-alive",則使用短連接;
// HTTP/1.1中不指定"Connection",默認使用長連接
if major == 1 && minor == 0 { return hasClose || !httpguts.HeaderValuesContainsToken(conv, "keep-alive") } // 如果使用非長連接,且需要刪除首部中的Connection字段。在經過proxy或gateway時必須移除Connection首部字段 if hasClose && removeCloseHeader { header.Del("Connection") } return hasClose }
func readTransfer(msg interface{}, r *bufio.Reader) (err error) {
    t := &transferReader{RequestMethod: "GET"}

    // Unify input
    isResponse := false
    switch rr := msg.(type) {
// 消息為響應時的賦值
case *Response: t.Header = rr.Header t.StatusCode = rr.StatusCode t.ProtoMajor = rr.ProtoMajor t.ProtoMinor = rr.ProtoMinor
// 響應中不需要Connection首部字段,下面函數最后一個參數設置為true,刪除該首部字段 t.Close
= shouldClose(t.ProtoMajor, t.ProtoMinor, t.Header, true) isResponse = true if rr.Request != nil { t.RequestMethod = rr.Request.Method }
// 消息為請求時的賦值
case *Request: t.Header = rr.Header t.RequestMethod = rr.Method t.ProtoMajor = rr.ProtoMajor t.ProtoMinor = rr.ProtoMinor // Transfer semantics for Requests are exactly like those for // Responses with status code 200, responding to a GET method t.StatusCode = 200 t.Close = rr.Close default: panic("unexpected type") } // Default to HTTP/1.1 if t.ProtoMajor == 0 && t.ProtoMinor == 0 { t.ProtoMajor, t.ProtoMinor = 1, 1 } // 處理"Transfer-Encoding"首部 err = t.fixTransferEncoding() if err != nil { return err } // 處理"Content-Length"首部,注意此處返回的是真實的消息載體長度 realLength, err := fixLength(isResponse, t.StatusCode, t.RequestMethod, t.Header, t.TransferEncoding) if err != nil { return err }
// 如果該消息為響應且對應的請求方法為HEAD,如果響應首部包含Content-Length字段,則將此作為響應的ContentLength的值,表示server
// 可以接收到的數據的最大長度,由於該響應沒有有效載體,此時不能使用fixLength返回的真實長度0
if isResponse && t.RequestMethod == "HEAD" { if n, err := parseContentLength(t.Header.get("Content-Length")); err != nil { return err } else { t.ContentLength = n } } else { t.ContentLength = realLength } // 處理Trailer首部字段,主要進行有消息校驗 t.Trailer, err = fixTrailer(t.Header, t.TransferEncoding) if err != nil { return err } // If there is no Content-Length or chunked Transfer-Encoding on a *Response // and the status is not 1xx, 204 or 304, then the body is unbounded. // See RFC 7230, section 3.3.
// 含body但不是chunked且不包含length字段的響應稱為unbounded(無法衡量長度的消息)消息,根據RFC 7230會被關閉
switch msg.(type) { case *Response: if realLength == -1 && !chunked(t.TransferEncoding) && bodyAllowedForStatus(t.StatusCode) { // Unbounded body. t.Close = true } } // Prepare body reader. ContentLength < 0 means chunked encoding // or close connection when finished, since multipart is not supported yet
// 給t.Body賦值
switch {
// chunked 場景處理
case chunked(t.TransferEncoding):
// 如果請求為HEAD或響應狀態碼為1xx, 204 or 304,則消息不包含有效載體
if noResponseBodyExpected(t.RequestMethod) || !bodyAllowedForStatus(t.StatusCode) { t.Body = NoBody } else {
// 下面會創建chunkedReader t.Body
= &body{src: internal.NewChunkedReader(r), hdr: msg, r: r, closing: t.Close} } case realLength == 0: t.Body = NoBody
// 非chunked且包含有效載體(對應Content-Length),創建limitReader
case realLength > 0: t.Body = &body{src: io.LimitReader(r, realLength), closing: t.Close} default: // realLength < 0, i.e. "Content-Length" not mentioned in header
// 此處對於消息有效載體unbounded場景,斷開底層連接
if t.Close { // Close semantics (i.e. HTTP/1.0) t.Body = &body{src: r, closing: t.Close} } else { // Persistent connection (i.e. HTTP/1.1) 好像走不到該分支。。。 t.Body = NoBody } } // 為請求/響應結構體賦值並通過指針返回 switch rr := msg.(type) { case *Request: rr.Body = t.Body rr.ContentLength = t.ContentLength rr.TransferEncoding = t.TransferEncoding rr.Close = t.Close rr.Trailer = t.Trailer case *Response: rr.Body = t.Body rr.ContentLength = t.ContentLength rr.TransferEncoding = t.TransferEncoding rr.Close = t.Close rr.Trailer = t.Trailer } return nil }
// 1.13.3版本的本函數描述有誤,下面代碼來自最新master分支
func (t *transferReader) fixTransferEncoding() error {
// 本函數主要處理"Transfer-Encoding"首部,如果不存在,則直接退出 raw, present :
= t.Header["Transfer-Encoding"] if !present { return nil } delete(t.Header, "Transfer-Encoding") // Issue 12785; ignore Transfer-Encoding on HTTP/1.0 requests.
// HTTP/1.0不處理此首部
if !t.protoAtLeast(1, 1) { return nil }
// "Transfer-Encoding"首部字段使用逗號分割 encodings :
= strings.Split(raw[0], ",") te := make([]string, 0, len(encodings)) // When adding new encodings, please maintain the invariant: // if chunked encoding is present, it must always // come last and it must be applied only once. // See RFC 7230 Section 3.3.1 Transfer-Encoding.
// 循環處理各個傳輸編碼,目前僅實現了"chunked"
for i, encoding := range encodings { encoding = strings.ToLower(strings.TrimSpace(encoding)) if encoding == "identity" { // "identity" should not be mixed with other transfer-encodings/compressions // because it means "no compression, no transformation". if len(encodings) != 1 { return &badStringError{`"identity" when present must be the only transfer encoding`, strings.Join(encodings, ",")} } // "identity" is not recorded. break } switch { case encoding == "chunked": // "chunked" MUST ALWAYS be the last // encoding as per the loop invariant. // That is: // Invalid: [chunked, gzip] // Valid: [gzip, chunked] if i+1 != len(encodings) { return &badStringError{"chunked must be applied only once, as the last encoding", strings.Join(encodings, ",")} } // Supported otherwise. case isGzipTransferEncoding(encoding): // Supported default: return &unsupportedTEError{fmt.Sprintf("unsupported transfer encoding: %q", encoding)} } te = te[0 : len(te)+1] te[len(te)-1] = encoding } if len(te) > 0 { // RFC 7230 3.3.2 says "A sender MUST NOT send a // Content-Length header field in any message that // contains a Transfer-Encoding header field." // // but also: // "If a message is received with both a // Transfer-Encoding and a Content-Length header // field, the Transfer-Encoding overrides the // Content-Length. Such a message might indicate an // attempt to perform request smuggling (Section 9.5) // or response splitting (Section 9.4) and ought to be // handled as an error. A sender MUST remove the // received Content-Length field prior to forwarding // such a message downstream." // // Reportedly, these appear in the wild.
// "Transfer-Encoding"就是為了解決"Content-Length"不存在才出現了,因此當存在"Transfer-Encoding"時無需處理"Content-Length",
// 此處刪除"Content-Length"首部,不在fixLength函數中處理
        delete(t.Header, "Content-Length")
        t.TransferEncoding = te
        return nil
    }

    return nil
}
// 本函數處理Content-Length首部,並返回真實的消息載體長度
func fixLength(isResponse bool, status int, requestMethod string, header Header, te []string) (int64, error) { isRequest := !isResponse contentLens := header["Content-Length"] // Hardening against HTTP request smuggling if len(contentLens) > 1 { // Per RFC 7230 Section 3.3.2, prevent multiple // Content-Length headers if they differ in value. // If there are dups of the value, remove the dups. // See Issue 16490.
// 下面按照RFC 7230的建議進行處理,如果一個Content-Length包含多個不同的value,則認為該消息無效
first := strings.TrimSpace(contentLens[0]) for _, ct := range contentLens[1:] { if first != strings.TrimSpace(ct) { return 0, fmt.Errorf("http: message cannot contain multiple Content-Length headers; got %q", contentLens) } } // 如果一個Content-Length包含多個相同的value,則僅保留一個 header.Del("Content-Length") header.Add("Content-Length", first) contentLens = header["Content-Length"] } // 處理HEAD請求 if noResponseBodyExpected(requestMethod) { // For HTTP requests, as part of hardening against request // smuggling (RFC 7230), don't allow a Content-Length header for // methods which don't permit bodies. As an exception, allow // exactly one Content-Length header if its value is "0".
// 當HEAD請求中的Content-Length為0時允許存在該字段
if isRequest && len(contentLens) > 0 && !(len(contentLens) == 1 && contentLens[0] == "0") { return 0, fmt.Errorf("http: method cannot contain a Content-Length; got %q", contentLens) } return 0, nil }
// 處理狀態碼為1xx的響應,不包含消息體
if status/100 == 1 { return 0, nil }
// 處理狀態碼為204和304的響應,不包含消息體 switch status {
case 204, 304: return 0, nil } // 包含Transfer-Encoding時無法衡量數據長度,以Transfer-Encoding為准,設置返回長度為-1,直接返回 if chunked(te) { return -1, nil } var cl string
// 獲取Content-Length字段值
if len(contentLens) == 1 { cl = strings.TrimSpace(contentLens[0]) }
// 對Content-Length字段的值進行有效性驗證,如果有效則返回該值的整型,無效返回錯誤
if cl != "" { n, err := parseContentLength(cl) if err != nil { return -1, err } return n, nil }
// 數值為空,刪除該首部字段 header.Del(
"Content-Length")
// 請求中沒有Content-Length且沒有Transfer-Encoding字段的請求被認為沒有有效載體
if isRequest { // RFC 7230 neither explicitly permits nor forbids an // entity-body on a GET request so we permit one if // declared, but we default to 0 here (not -1 below) // if there's no mention of a body. // Likewise, all other request methods are assumed to have // no body if neither Transfer-Encoding chunked nor a // Content-Length are set. return 0, nil } // Body-EOF logic based on other methods (like closing, or chunked coding)
// 消息為響應,該場景后續會在readTransfer被close處理
return -1, nil }
func (cr *connReader) startBackgroundRead() {
    cr.lock()
    defer cr.unlock()
// 表示該連接正在被讀取
if cr.inRead { panic("invalid concurrent Body.Read call") }
// 表示該連接上是否還有數據
if cr.hasByte { return } cr.inRead = true
// 設置底層連接deadline為1<<64 -1
cr.conn.rwc.SetReadDeadline(time.Time{})
// 在新的goroutine中等待數據 go cr.backgroundRead() }
func (cr *connReader) backgroundRead() {
// 阻塞等待讀取一個字節的數 n, err :
= cr.conn.rwc.Read(cr.byteBuf[:]) cr.lock()
// 如果存在數據則設置cr.hasByte為true,byteBuf容量為1
if n == 1 { cr.hasByte = true // We were past the end of the previous request's body already // (since we wouldn't be in a background read otherwise), so // this is a pipelined HTTP request. Prior to Go 1.11 we used to // send on the CloseNotify channel and cancel the context here, // but the behavior was documented as only "may", and we only // did that because that's how CloseNotify accidentally behaved // in very early Go releases prior to context support. Once we // added context support, people used a Handler's // Request.Context() and passed it along. Having that context // cancel on pipelined HTTP requests caused problems. // Fortunately, almost nothing uses HTTP/1.x pipelining. // Unfortunately, apt-get does, or sometimes does. // New Go 1.11 behavior: don't fire CloseNotify or cancel // contexts on pipelined requests. Shouldn't affect people, but // fixes cases like Issue 23921. This does mean that a client // closing their TCP connection after sending a pipelined // request won't cancel the context, but we'll catch that on any // write failure (in checkConnErrorWriter.Write). // If the server never writes, yes, there are still contrived // server & client behaviors where this fails to ever cancel the // context, but that's kinda why HTTP/1.x pipelining died // anyway. } if ne, ok := err.(net.Error); ok && cr.aborted && ne.Timeout() { // Ignore this error. It's the expected error from // another goroutine calling abortPendingRead. } else if err != nil { cr.handleReadError(err) } cr.aborted = false cr.inRead = false cr.unlock()
// 當有數據時,通知cr.cond.Wait解鎖 cr.cond.Broadcast() }
func (w *response) finishRequest() {
    w.handlerDone.setTrue()
    // wroteHeader表示是否已經將響應首部寫入,沒有則寫入 if !w.wroteHeader {
        w.WriteHeader(StatusOK)
    }
   // 此處調用w.cw.write(checkConnErrorWriter) -> c.rwc.write發送數據,即調用底層連接的write將buf中的數據發送出去 w.w.Flush()
// 將w.w重置並放入sync.pool中,待后續重用 putBufioWriter(
w.w)

// 主要構造chunked的結束符:"0\r\n","\r\n",通過cw.chunking判斷是否是chunked編碼
w.cw.close()
// 發送bufw緩存的數據
w.conn.bufw.Flush() // 用於等待處理未讀取完的數據,與connReader.backgroundRead中的cr.cond.Broadcast()對應 w.conn.r.abortPendingRead() // Close the body (regardless of w.closeAfterReply) so we can // re-use its bufio.Reader later safely. w.reqBody.Close() if w.req.MultipartForm != nil { w.req.MultipartForm.RemoveAll() } }
func (w *response) shouldReuseConnection() bool {
// 表示是否需要在響應之后關閉底層連接。requestTooLarge,isH2Upgrade或包含首部字段"Connection:close"時置位
if w.closeAfterReply { // The request or something set while executing the // handler indicated we shouldn't reuse this // connection. return false } // 寫入數據與"content-length"不匹配,為避免不同步,不重用連接 if w.req.Method != "HEAD" && w.contentLength != -1 && w.bodyAllowed() && w.contentLength != w.written { // Did not write enough. Avoid getting out of sync. return false } // There was some error writing to the underlying connection // during the request, so don't re-use this conn.
// 底層連接出現錯誤,不可重用
if w.conn.werr != nil { return false } // 判斷是否在讀取完數據前執行關閉 if w.closedRequestBodyEarly() { return false } return true }
// closeWrite flushes any outstanding data and sends a FIN packet (if
// client is connected via TCP), signalling that we're done. We then
// pause for a bit, hoping the client processes it before any
// subsequent RST.
//
// See https://golang.org/issue/3595
func (c *conn) closeWriteAndWait() {
// 在關閉寫之前將緩沖區中的數據發送出去
c.finalFlush()
if tcp, ok := c.rwc.(closeWriter); ok {
// 執行tcpsock.go中的TCPConn.CloseWrite,調用SHUT_WR關閉寫
tcp.CloseWrite()
}
time.Sleep(rstAvoidanceDelay)
}
func (c *conn) finalFlush() {
// 本函數中如果c.bufr或c.bufw不為空,都會重置並重用這部分內存
if c.bufr != nil { // Steal the bufio.Reader (~4KB worth of memory) and its associated // reader for a future connection. putBufioReader(c.bufr) c.bufr = nil } if c.bufw != nil {
// 將緩存區中的數據全部通過底層發送出去
// respose寫數據調用為c.bufw.wr.Write -> checkConnErrorWriter.write -> c.rwc.write,最終通過底層write發送數據
c.bufw.Flush()
// Steal the bufio.Writer (~4KB worth of memory) and its associated // writer for a future connection. putBufioWriter(c.bufw) c.bufw = nil } }

http.transport

 參見詳解transport

NetPoll

 參見詳解golang net之netpoll

 

參考:

https://golang.org/pkg/net/http/

https://lanre.wtf/blog/2017/07/24/roundtripper-go/

https://lanre.wtf/blog/2017/04/03/http-in-go/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM