func (s *server) Stop() error { s.stopOnce.Do(func() { defer stats.BumpTime(s.stats, "stop.time").End() stats.BumpSum(s.stats, "stop", 1) // first disable keep-alive for new connections s.server.SetKeepAlivesEnabled(false) // then close the listener so new connections can't connect come thru closeErr := s.listener.Close() <-s.serveDone // then trigger the background goroutine to stop and wait for it stopDone := make(chan struct{}) s.stop <- stopDone // wait for stop select { case <-stopDone: case <-s.clock.After(s.stopTimeout): defer stats.BumpTime(s.stats, "kill.time").End() stats.BumpSum(s.stats, "kill", 1) // stop timed out, wait for kill killDone := make(chan struct{}) s.kill <- killDone select { case <-killDone: case <-s.clock.After(s.killTimeout): // kill timed out, give up stats.BumpSum(s.stats, "kill.timeout", 1) } } if closeErr != nil && !isUseOfClosedError(closeErr) { stats.BumpSum(s.stats, "listener.close.error", 1) s.stopErr = closeErr } }) return s.stopErr }
// ListenAndServe returns a Server for the given http.Server. It is equivalent // to ListendAndServe from the standard library, but returns immediately. // Requests will be accepted in a background goroutine. If the http.Server has // a non-nil TLSConfig, a TLS enabled listener will be setup. func (h HTTP) ListenAndServe(s *http.Server) (Server, error) { addr := s.Addr if addr == "" { if s.TLSConfig == nil { addr = ":http" } else { addr = ":https" } } l, err := net.Listen("tcp", addr) if err != nil { stats.BumpSum(h.Stats, "listen.error", 1) return nil, err } if s.TLSConfig != nil { l = tls.NewListener(l, s.TLSConfig) } return h.Serve(s, l), nil }
func (s *server) serve() { stats.BumpSum(s.stats, "serve", 1) s.serveErr <- s.server.Serve(s.listener) close(s.serveDone) close(s.serveErr) }
func (s *server) manage() { defer func() { close(s.new) close(s.active) close(s.idle) close(s.closed) close(s.stop) close(s.kill) }() var stopDone chan struct{} conns := map[net.Conn]http.ConnState{} var countNew, countActive, countIdle float64 // decConn decrements the count associated with the current state of the // given connection. decConn := func(c net.Conn) { switch conns[c] { default: panic(fmt.Errorf("unknown existing connection: %s", c)) case http.StateNew: countNew-- case http.StateActive: countActive-- case http.StateIdle: countIdle-- } } // setup a ticker to report various values every minute. if we don't have a // Stats implementation provided, we Stop it so it never ticks. statsTicker := s.clock.Ticker(time.Minute) if s.stats == nil { statsTicker.Stop() } for { select { case <-statsTicker.C: // we'll only get here when s.stats is not nil s.stats.BumpAvg("http-state.new", countNew) s.stats.BumpAvg("http-state.active", countActive) s.stats.BumpAvg("http-state.idle", countIdle) s.stats.BumpAvg("http-state.total", countNew+countActive+countIdle) case c := <-s.new: conns[c] = http.StateNew countNew++ case c := <-s.active: decConn(c) countActive++ conns[c] = http.StateActive case c := <-s.idle: decConn(c) countIdle++ conns[c] = http.StateIdle // if we're already stopping, close it if stopDone != nil { c.Close() } case c := <-s.closed: stats.BumpSum(s.stats, "conn.closed", 1) decConn(c) delete(conns, c) // if we're waiting to stop and are all empty, we just closed the last // connection and we're done. if stopDone != nil && len(conns) == 0 { close(stopDone) return } case stopDone = <-s.stop: // if we're already all empty, we're already done if len(conns) == 0 { close(stopDone) return } // close current idle connections right away for c, cs := range conns { if cs == http.StateIdle { c.Close() } } // continue the loop and wait for all the ConnState updates which will // eventually close(stopDone) and return from this goroutine. case killDone := <-s.kill: // force close all connections stats.BumpSum(s.stats, "kill.conn.count", float64(len(conns))) for c := range conns { c.Close() } // don't block the kill. close(killDone) // continue the loop and we wait for all the ConnState updates and will // return from this goroutine when we're all done. otherwise we'll try to // send those ConnState updates on closed channels. } } }