go ListenAndServe

Article directory

  • foreword
  • 1. http.ListenAndServe
  • 2. server.ListenAndServe
  • 3. server.Serve
  • 4. http.conn.serve
  • Summarize

Foreword

go http.ListenAndServe link tracking

go can easily start a service as follows

package main
import("http")

func main() {<!-- -->
http.HandleFunc("/ping", ping)
http.ListenAndServe(":8080", nil)
}
func ping(w http.ResponseWriter, r *http.Request) {<!-- -->
w.Write([]byte("pong"))
return
}

This article mainly studies what the ListenAndServe method does

1. http. ListenAndServe

Listen to the tcp-based network address addr
After the client connects, the connect callback serves with a handler to handle
Set keep-alives to keep tcp connection.

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

2. server. ListenAndServe

func (srv *Server) ListenAndServe() error {<!-- -->
// Determine whether the service is closed
if srv.shuttingDown() {<!-- -->
return ErrServerClosed
}
addr := srv.Addr
if addr == "" {<!-- -->
addr = ":http"
}
// Listen to tcp service port (create socket, bind port, listen to tcp)
ln, err := net. Listen("tcp", addr)
if err != nil {<!-- -->
return err
}
//
return srv. Serve(ln)
}

3. server. Serve

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

origListener := l
l = &onceCloseListener{<!-- -->Listener: l}
defer l. Close()

// Check if http2 needs to be upgraded
if err := srv.setupHTTP2_Serve(); err != nil {<!-- -->
return err
}

// sync.WaitGroup add(1)
if !srv.trackListener( & amp;l, true) {<!-- -->
return ErrServerClosed
}
// sync. WaitGroup Done()
defer srv. trackListener( &l, false)

// create context
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 {<!-- -->
// handle client request
rw, err := l.Accept()
if err != nil {<!-- -->
select {<!-- -->
case <- srv. getDoneChan():
return ErrServerClosed
default:
}
if ne, ok := err.(net.Error); ok & amp; & amp; 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
// Generally start http.ListenAndServe() ConnContext is nil
if cc := srv.ConnContext; cc != nil {<!-- -->
connCtx = cc(connCtx, rw)
if connCtx == nil {<!-- -->
panic("ConnContext returned nil")
}
}
tempDelay = 0
// Encapsulate its own Server.conn
c := srv.newConn(rw)
c.setState(c.rwc, StateNew, runHooks) // before Serve can return
// Handle the Handle method
go c.serve(connCtx)
}
}

4. http.conn.serve

func (c *conn) serve(ctx context.Context) {<!-- -->
c.remoteAddr = c.rwc.RemoteAddr().String()
ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())
var inFlightResponse *response
defer func() {<!-- -->
// receive painc
// Recycle goroutine resources
// close connect
}()

if tlsConn, ok := c.rwc.(*tls.Conn); ok {<!-- -->
// handle http2 request
}

// 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, runHooks)
}
if err != nil {<!-- -->
// Handle the error of reading the request
}

// support Header Expect
req := w.req
if req. expectsContinue() {<!-- -->
if req.ProtoAtLeast(1, 1) & amp; & amp; 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()
}

// 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
// handle handle logic
serverHandler{<!-- -->c.server}.ServeHTTP(w, w.req)
inFlightResponse = nil
w. cancelCtx()
if c.hijacked() {<!-- -->
return
}
w. finishRequest()
if !w.shouldReuseConnection() {<!-- -->
if w.requestBodyLimitHit || w.closedRequestBodyEarly() {<!-- -->
c. closeWriteAndWait()
}
return
}
c.setState(c.rwc, StateIdle, runHooks)
// store the response
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{<!-- -->})
}
}

Summary

The above is http.ListenAndServe link tracking. This article only introduces the implementation process of serve. With the understanding of the basic process, it will be more handy to implement