diff options
Diffstat (limited to 'libgo/go/net/http/server.go')
-rw-r--r-- | libgo/go/net/http/server.go | 70 |
1 files changed, 52 insertions, 18 deletions
diff --git a/libgo/go/net/http/server.go b/libgo/go/net/http/server.go index 77329b2..6f7a259 100644 --- a/libgo/go/net/http/server.go +++ b/libgo/go/net/http/server.go @@ -425,6 +425,16 @@ type response struct { wants10KeepAlive bool // HTTP/1.0 w/ Connection "keep-alive" wantsClose bool // HTTP request has Connection "close" + // canWriteContinue is a boolean value accessed as an atomic int32 + // that says whether or not a 100 Continue header can be written + // to the connection. + // writeContinueMu must be held while writing the header. + // These two fields together synchronize the body reader + // (the expectContinueReader, which wants to write 100 Continue) + // against the main writer. + canWriteContinue atomicBool + writeContinueMu sync.Mutex + w *bufio.Writer // buffers output in chunks to chunkWriter cw chunkWriter @@ -515,6 +525,7 @@ type atomicBool int32 func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 } func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) } +func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) } // declareTrailer is called for each Trailer header when the // response header is written. It notes that a header will need to be @@ -629,6 +640,7 @@ func (srv *Server) newConn(rwc net.Conn) *conn { } type readResult struct { + _ incomparable n int err error b byte // byte read, if n == 1 @@ -877,21 +889,27 @@ type expectContinueReader struct { resp *response readCloser io.ReadCloser closed bool - sawEOF bool + sawEOF atomicBool } func (ecr *expectContinueReader) Read(p []byte) (n int, err error) { if ecr.closed { return 0, ErrBodyReadAfterClose } - if !ecr.resp.wroteContinue && !ecr.resp.conn.hijacked() { - ecr.resp.wroteContinue = true - ecr.resp.conn.bufw.WriteString("HTTP/1.1 100 Continue\r\n\r\n") - ecr.resp.conn.bufw.Flush() + w := ecr.resp + if !w.wroteContinue && w.canWriteContinue.isSet() && !w.conn.hijacked() { + w.wroteContinue = true + w.writeContinueMu.Lock() + if w.canWriteContinue.isSet() { + w.conn.bufw.WriteString("HTTP/1.1 100 Continue\r\n\r\n") + w.conn.bufw.Flush() + w.canWriteContinue.setFalse() + } + w.writeContinueMu.Unlock() } n, err = ecr.readCloser.Read(p) if err == io.EOF { - ecr.sawEOF = true + ecr.sawEOF.setTrue() } return } @@ -1315,7 +1333,7 @@ func (cw *chunkWriter) writeHeader(p []byte) { // because we don't know if the next bytes on the wire will be // the body-following-the-timer or the subsequent request. // See Issue 11549. - if ecr, ok := w.req.Body.(*expectContinueReader); ok && !ecr.sawEOF { + if ecr, ok := w.req.Body.(*expectContinueReader); ok && !ecr.sawEOF.isSet() { w.closeAfterReply = true } @@ -1565,6 +1583,17 @@ func (w *response) write(lenData int, dataB []byte, dataS string) (n int, err er } return 0, ErrHijacked } + + if w.canWriteContinue.isSet() { + // Body reader wants to write 100 Continue but hasn't yet. + // Tell it not to. The store must be done while holding the lock + // because the lock makes sure that there is not an active write + // this very moment. + w.writeContinueMu.Lock() + w.canWriteContinue.setFalse() + w.writeContinueMu.Unlock() + } + if !w.wroteHeader { w.WriteHeader(StatusOK) } @@ -1702,9 +1731,9 @@ func (c *conn) closeWriteAndWait() { time.Sleep(rstAvoidanceDelay) } -// validNextProto reports whether the proto is not a blacklisted ALPN -// protocol name. Empty and built-in protocol types are blacklisted -// and can't be overridden with alternate implementations. +// validNextProto reports whether the proto is a valid ALPN protocol name. +// Everything is valid except the empty string and built-in protocol types, +// so that those can't be overridden with alternate implementations. func validNextProto(proto string) bool { switch proto { case "", "http/1.1", "http/1.0": @@ -1876,6 +1905,7 @@ func (c *conn) serve(ctx context.Context) { 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} + w.canWriteContinue.setTrue() } } else if req.Header.get("Expect") != "" { w.sendExpectationFailed() @@ -2582,8 +2612,9 @@ type Server struct { // value. ConnContext func(ctx context.Context, c net.Conn) context.Context + inShutdown atomicBool // true when when server is in shutdown + disableKeepAlives int32 // accessed atomically. - inShutdown int32 // accessed atomically (non-zero means we're in Shutdown) nextProtoOnce sync.Once // guards setupHTTP2_* init nextProtoErr error // result of http2.ConfigureServer if used @@ -2629,7 +2660,7 @@ func (s *Server) closeDoneChanLocked() { // Close returns any error returned from closing the Server's // underlying Listener(s). func (srv *Server) Close() error { - atomic.StoreInt32(&srv.inShutdown, 1) + srv.inShutdown.setTrue() srv.mu.Lock() defer srv.mu.Unlock() srv.closeDoneChanLocked() @@ -2671,7 +2702,7 @@ var shutdownPollInterval = 500 * time.Millisecond // Once Shutdown has been called on a server, it may not be reused; // future calls to methods such as Serve will return ErrServerClosed. func (srv *Server) Shutdown(ctx context.Context) error { - atomic.StoreInt32(&srv.inShutdown, 1) + srv.inShutdown.setTrue() srv.mu.Lock() lnerr := srv.closeListenersLocked() @@ -2684,7 +2715,7 @@ func (srv *Server) Shutdown(ctx context.Context) error { ticker := time.NewTicker(shutdownPollInterval) defer ticker.Stop() for { - if srv.closeIdleConns() { + if srv.closeIdleConns() && srv.numListeners() == 0 { return lnerr } select { @@ -2706,6 +2737,12 @@ func (srv *Server) RegisterOnShutdown(f func()) { srv.mu.Unlock() } +func (s *Server) numListeners() int { + s.mu.Lock() + defer s.mu.Unlock() + return len(s.listeners) +} + // closeIdleConns closes all idle connections and reports whether the // server is quiescent. func (s *Server) closeIdleConns() bool { @@ -2738,7 +2775,6 @@ func (s *Server) closeListenersLocked() error { if cerr := (*ln).Close(); cerr != nil && err == nil { err = cerr } - delete(s.listeners, ln) } return err } @@ -3037,9 +3073,7 @@ func (s *Server) doKeepAlives() bool { } func (s *Server) shuttingDown() bool { - // TODO: replace inShutdown with the existing atomicBool type; - // see https://github.com/golang/go/issues/20239#issuecomment-381434582 - return atomic.LoadInt32(&s.inShutdown) != 0 + return s.inShutdown.isSet() } // SetKeepAlivesEnabled controls whether HTTP keep-alives are enabled. |