func (srv *Server) Serve(l ) error { ...... for { rw, err := () if err != nil { select { case <-(): return ErrServerClosed default: } if ne, ok := err.(); ok && () { if tempDelay == 0 { tempDelay = 5 * } else { tempDelay *= 2 } if max := 1 * ; tempDelay > max { tempDelay = max } ("http: Accept error: %v; retrying in %v", err, tempDelay) (tempDelay) continue } return err } connCtx := ctx if cc := ; cc != nil { connCtx = cc(connCtx, rw) if connCtx == nil { panic("ConnContext returned nil") } } tempDelay = 0 c := (rw) (, StateNew, runHooks) // before Serve can return go (connCtx) } }
func (c *conn) serve(ctx ) { ...... // HTTP/ from here on. ctx, cancelCtx := (ctx) = cancelCtx defer cancelCtx() = &connReader{conn: c} = newBufioReader() = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10) for { w, err := (ctx) if != () { // If we read any bytes off the wire, we're active. (, StateActive, runHooks) } 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" (, "HTTP/1.1 "+publicErr+errorHeaders+publicErr) () return 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. (, "HTTP/1.1 %d %s%sUnsupported transfer encoding", code, StatusText(code), errorHeaders) return case isCommonNetReadError(err): return // don't reply default: if v, ok := err.(statusError); ok { (, "HTTP/1.1 %d %s: %s%s%d %s: %s", , StatusText(), , errorHeaders, , StatusText(), ) return } publicErr := "400 Bad Request" (, "HTTP/1.1 "+publicErr+errorHeaders+publicErr) return } } // Expect 100 Continue support req := if () { if (1, 1) && != 0 { // Wrap the Body reader with one that replies on the connection = &expectContinueReader{readCloser: , resp: w} () } } else if ("Expect") != "" { () return } (w) if requestBodyRemains() { registerOnHitEOF(, ) } else { () } // 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. inFlightResponse = w serverHandler{}.ServeHTTP(w, ) inFlightResponse = nil () if () { return } () if !() { if || () { () } return } (, StateIdle, runHooks) ((*response)(nil)) if !() { // 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 := (); d != 0 { (().Add(d)) if _, err := (4); err != nil { return } } ({}) } }
1、(ctx)
Put it in the for loop, for HTTP Keep-Alive, the TCP connection can be multiplexed, and it is serial. Only after the previous request is processed will it read the data of the next request. If the connection is disconnected by the client, then(ctx)
Will exit due to a read error.
By continuing to track the source code, I found that this is just reading the header and making some judgments, so there will bereadHeaderDeadline
For this configuration, then set the Body type. There is a blank line between the Header and the Body. This is a flag for the header to finish reading. Through Content-Length, you can know whether there is Body content and how much content is there.
switch { case : if noResponseBodyExpected() || !bodyAllowedForStatus() { = NoBody } else { = &body{src: (r), hdr: msg, r: r, closing: } } case realLength == 0: = NoBody case realLength > 0: = &body{src: (r, realLength), closing: } default: // realLength < 0, . "Content-Length" not mentioned in header if { // Close semantics (. HTTP/1.0) = &body{src: r, closing: } } else { // Persistent connection (. HTTP/1.1) = NoBody } }
func (l *LimitedReader) Read(p []byte) (n int, err error) { if <= 0 { return 0, EOF } if int64(len(p)) > { p = p[0:] } n, err = (p) -= int64(n) return }
After reading the specified length, an EOF error will be returned, indicating that the reading is completed.
2、
if requestBodyRemains() { registerOnHitEOF(, ) } else { () }
It will be turned on after the Body is read.startBackgroundRead
。
func (cr *connReader) backgroundRead() { n, err := ([:]) () if n == 1 { = true ...... } if ne, ok := err.(); ok && && () { // Ignore this error. It's the expected error from // another goroutine calling abortPendingRead. } else if err != nil { (err) } = false = false () () } func (cr *connReader) handleReadError(_ error) { () () } // may be called from multiple goroutines. func (cr *connReader) closeNotify() { res, _ := ().(*response) if res != nil && atomic.CompareAndSwapInt32(&, 0, 1) { <- true } }
actuallystartBackgroundRead
It is to monitor whether the client has closed the connection. It cannot affect the reading of business data. Therefore, it needs to wait until the Body is read before it is turned on. It symbolically reads a byte. If the client is closed, the corresponding fd is readable, and it will write data like a channel. The life cycle of this coroutine is the current request, not the current connection. Its function is to interrupt the Handler processing stage of the current request. It believes that the client has given up the request, and the server does not need to do too much business processing, but this is difficult to implement in actual business, or it is redundant. In our opinion, as long as the request arrives, the server has the obligation to correctly handle it and should not interrupt it.
When the request is processed, it will be calledabortPendingRead
, makestartBackgroundRead
Coroutine exits. WhystartBackgroundRead
The life cycle of a coroutine is not connected accordingly, because the Keep-Alive connection will last for a period of time, even if no request comes, this will causestartBackgroundRead
The coroutine is running all the time.
So when will the server close this connection? After all, the client is untrusted, it is set upSetReadDeadline
forReadHeaderTimeout
To modify the timer time, of course, if not setReadHeaderTimeout
, then it will be usedReadTimeout
Instead, the timeout has not sent a request, it can be considered that the client has not reused this connection. The for loop exits and the connection is closed in defer.
In fact, the client will only reuse the connection if it has to send multiple requests in a short time. For example, when the page is initialized, the browser will reuse the connection depending on the situation.
ReadDeadline
is a total time, a deadline, which is the total time to read the header and the Body.
3、serverHandler{}.ServeHTTP(w, )
Later, I started calling Handler. If I need to use Body information, I need to read the Body content. It can be seen that the Header and Body are read separately. The second read will not block because there is content in fd. Of course, if someone maliciously attacks and only sends the request header and does not fill in the Body, it will also block.
This is the end of this article about in-depth analysis of the Golang Server source code implementation process. For more related Go Server content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!