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