aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/net/http/server.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/net/http/server.go')
-rw-r--r--libgo/go/net/http/server.go70
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.