golang net/http Server主要流程源碼分析

net/http

golang net/http Server主要流程源碼分析。golang

時間:2019年3月14日app

處理一個http請求大體思路以下:tcp

  • 建立一個net.Listener對象
  • net.Listener對象Accept一個tcp鏈接
  • 從tcp鏈接讀取一個請求並建立一個響應體
  • 調用接口處理這個鏈接的請求並寫入數據udao響應

主要堆棧:函數

http.ListenAndServe(addr string, handler Handler) error源碼分析

http.*Server.ListenAndServe() error

	net.Listen(network, address string) (net.Listener, error)

	http.*Server.Serve(l net.Listener) error

		http.*Server.setupHTTP2_Serve()

		net.Listener.Accept() (net.Conn, error)

		http.*Server.newConn(rwc net.Conn) *http.conn

		http.*conn.setState(nc net.Conn, state ConnState)

		http.*conn.serve(ctx context.Context)

			defer http.*conn.serve.func()

			http.*conn.rwc.(*tls.Conn)

				tls.*Conn.Handshake()

				tls.ConnectionState.NegotiatedProtocol

			http.*conn.readRequest(ctx context.Context) (w *http.response, err error)

			http.serverHandler{http.*conn.server}.ServeHTTP(w, w.req)
複製代碼

Start

啓動一個Server測試

package main

import "fmt"
import "net/http"

func main() {
	http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello, %q", r.URL.Path)
	})

	http.ListenAndServe(":8080", nil)
}
複製代碼

http.ListenAndServe

首先http.ListenAndServe會建立一個Server,設置地址和Handler,而後調用Server對象的ListenAndServe方法啓動。ui

ListenAndServe方法先檢查srv的狀態是不是關閉,服務是關閉的就直接退出this

而後設置默認地址,利用地址監聽tcp鏈接,最後調用Server對象的Serve的方法,處理這個監聽。spa

// ListenAndServe listens on the TCP network address addr and then calls
// Serve with handler to handle requests on incoming connections.
// Accepted connections are configured to enable TCP keep-alives.
//
// The handler is typically nil, in which case the DefaultServeMux is used.
//
// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {
	server := &Server{Addr: addr, Handler: handler}
	return 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(tcpKeepAliveListener{ln.(*net.TCPListener)})
複製代碼

Serve

Server對象的Serve方法纔是處理個監聽的主要過程,啓動順序函數以下:日誌

testHookServerServe執行net/http庫默認的測試函數。

if fn := testHookServerServe; fn != nil {
		fn(srv, l) // call hook with unwrapped listener
	}
複製代碼

onceCloseListener對象封裝net.Listener對象的Close方法,使用sync.Once對象確保net.Listener只會關閉一次。

l = &onceCloseListener{Listener: l}
	defer l.Close()
複製代碼

setupHTTP2_Serve設置http2,若是啓用了https默認就是http2,h2能夠使用環境變量設置是否啓動,具體不分析。

if err := srv.setupHTTP2_Serve(); err != nil {
		return err
	}
複製代碼

trackListener設置track日誌,忽略。

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

baseCtx是Server一個監聽的根Context。

baseCtx := context.Background() // base is always background, per Issue 16220
	ctx := context.WithValue(baseCtx, ServerContextKey, srv)
複製代碼

for循環處理Accept到的鏈接。

for {
		rw, e := l.Accept()
		...
	}
複製代碼

若是Accept返回err,會srv.getDoneChan()方法檢測Server是否結束,後序忽略。

if e != nil {
			select {
			case <-srv.getDoneChan():
				return ErrServerClosed
			default:
			}
			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
		}
複製代碼

Accept得到了一個net.Conn鏈接對象,使用srv.newConn方法建立一個http.conn鏈接。

http.conn鏈接就是http鏈接,設置鏈接狀態用於鏈接複用,而後c.serve處理這個http鏈接。

c := srv.newConn(rw)
		c.setState(c.rwc, StateNew) // before Serve can return
		go c.serve(ctx)
複製代碼

http.Server.Serve完整源碼以下:

func (srv *Server) Serve(l net.Listener) error {
	if fn := testHookServerServe; fn != nil {
		fn(srv, l) // call hook with unwrapped listener
	}

	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)

	var tempDelay time.Duration     // how long to sleep on accept failure
	baseCtx := context.Background() // base is always background, per Issue 16220
	ctx := context.WithValue(baseCtx, ServerContextKey, srv)
	for {
		rw, e := l.Accept()
		if e != nil {
			select {
			case <-srv.getDoneChan():
				return ErrServerClosed
			default:
			}
			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 = 0
		c := srv.newConn(rw)
		c.setState(c.rwc, StateNew) // before Serve can return
		go c.serve(ctx)
	}
}
複製代碼

net.conn.serve

net.conn.serve處理一個http鏈接。

WithValue設置Context,忽略。

c.remoteAddr = c.rwc.RemoteAddr().String()
	ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())
複製代碼

defer部分捕捉panic拋出的錯誤,而後Server對象輸出,若是Server對象設置了log.Logger,就輸出到log,不然輸出到默認。

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)
		}
		...
	}()
複製代碼

defer後部分,檢測http鏈接的狀態,非劫持狀態就關閉鏈接,設置http狀態成關閉;劫持狀態通常在Websock下,使用Hijack方法獲取了tcp鏈接,而後自定義處理。

if !c.hijacked() {
			c.close()
			c.setState(c.rwc, StateClosed)
		}
複製代碼

c.rwc就是net.Conn的鏈接,net.Conn實現了Reader、Writer、Closer接口,因此縮寫rwc。

rwc鏈接斷言判斷是不是tls.Conn鏈接,判斷是不是https鏈接;若是是就設置rwc的超時,

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))
		}
		...
	}
複製代碼

tlsConn.Handshake()是檢測tls握手是否正常,不正常就返回http 400的響應並關閉鏈接;

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
		}
複製代碼

獲取NegotiatedProtocol信息,就是NextProto的值,若是值是h2,就使用h2的鏈接處理;h2詳細見h2握手分析,此tls部分可忽略,是tls的ALPN擴展的支持。

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
		}
複製代碼

注意:tlsConn.Handshake()必定要執行,是驗證tls握手,而後纔會有NegotiatedProtocol等tls鏈接信息。

注意:NegotiatedProtocol是tls的ALPN擴展的關鍵,h2協議握手下的值就是h2

tls部分完整以下:

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; validNPN(proto) {
			if fn := c.server.TLSNextProto[proto]; fn != nil {
				h := initNPNRequest{tlsConn, serverHandler{c.server}}
				fn(c.server, tlsConn, h)
			}
			return
		}
	}
複製代碼

tls部分沒將請求變成h2就繼續按http/1.1處理請求。

newBufioReader部分對rwc,使用bufio變成緩衝讀寫。

// 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循環調用net.conn.readRequest讀取一個請求,並建立ResponseWriter對象。

for {
		w, err := c.readRequest(ctx)
		...
	}
複製代碼

若是讀取請求錯誤,就直接返回4xx錯誤響應並關閉鏈接。

if err != nil {
			const errorHeaders = "\r\nContent-Type: text/plain; charset=utf-8\r\nConnection: close\r\n\r\n"

			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.
				const publicErr = "431 Request Header Fields Too Large"
				fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr)
				c.closeWriteAndWait()
				return
			}
			if isCommonNetReadError(err) {
				return // don't reply
			}

			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
		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}
			}
		} 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處理當前的請求rw,serverHandler就檢測Server是否設置了默認處理者,和響應Option方法,可忽略。

而後判斷鏈接狀態是否劫持,劫持直接結束。

...

注意:serverHandler{c.server}.ServeHTTP(w, w.req),就是用鏈接先建立request和response對象,然使用http.Handler對象來處理這個請求。

// 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.
		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))

複製代碼

http.serverHandler定義:

// 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
	if handler == nil {
		handler = DefaultServeMux
	}
	if req.RequestURI == "*" && req.Method == "OPTIONS" {
		handler = globalOptionsHandler{}
	}
	handler.ServeHTTP(rw, req)
}
複製代碼

...

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 {
			c.rwc.SetReadDeadline(time.Now().Add(d))
			if _, err := c.bufr.Peek(4); err != nil {
				return
			}
		}
		c.rwc.SetReadDeadline(time.Time{})
複製代碼

net.conn.serve完整定義以下:

// Serve a new connection.
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; validNPN(proto) {
			if fn := c.server.TLSNextProto[proto]; fn != nil {
				h := initNPNRequest{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"

			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.
				const publicErr = "431 Request Header Fields Too Large"
				fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr)
				c.closeWriteAndWait()
				return
			}
			if isCommonNetReadError(err) {
				return // don't reply
			}

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

		// 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.
		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() {
			// 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 {
			c.rwc.SetReadDeadline(time.Now().Add(d))
			if _, err := c.bufr.Peek(4); err != nil {
				return
			}
		}
		c.rwc.SetReadDeadline(time.Time{})
	}
}
複製代碼

net.conn.readRequest

readRequest方法就是根據鏈接建立http.Request和http.ResponseWriter兩個對象供http.Handler接口使用,處理一個請求。

end

建立一個Server並處理http請求到此就結束。

http.HandleFunc

http.HandleFunc使用http.DefaultServeMux這個默認路由調用HandleFunc方法註冊一個路由。

// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) 複製代碼

http.Handler

http.Handler是net/http庫處理請求的接口,http.Server直接調用Handler處理請求。

http.ServeMux是net/http庫內置的路由器,執行了基本匹配,可是實現了http.Handler接口,Server就直接使用Mux。

HandlerFunc是處理函數,可是這個類型實現了http.Handler接口,就將一個函數轉換成了接口。

type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}


// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request) // ServeHTTP calls f(w, r). func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
	f(w, r)
}
複製代碼

一下兩種方法都將func(ResponseWriter, *Request){}這樣一個函數轉換成了http.Handler接口

http.HandlerFunc(func(ResponseWriter, *Request){})

func Convert(h http.HandlerFunc) http.Handler {
	return h
}
複製代碼

Middleware

基於net/http簡單實現中間件,使用http.Handler接口,使用裝飾器模式嵌套一層。

Logger對象實現了http.Handler接口,會先輸出請求信息,而後調用路由處理這個請求。

http.ServeMux是標準庫實現的路由器,會匹配並處理請求。

package main

import "fmt"
import "net/http"

func main() {
	// 建立並註冊路由
	mux := &http.ServeMux{}
	mux.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello, %q", r.URL.Path)
	})

	// 啓動服務,給予處理者是Logger
	http.ListenAndServe(":8080", &Logger{mux})
}

type Logger struct {
	h 	http.Handler
}

// 實現http.Handler接口
func (log *Logger) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// 輸出日誌信息
	fmt.Printf("%s %s\n", r.Method, r.URL.Path)
	// 使用下一個處理者處理請求
	log.h.ServeHTTP(w, r)
}
複製代碼
相關文章
相關標籤/搜索