aboutsummaryrefslogtreecommitdiff
path: root/src/net/http/server.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/net/http/server.go')
-rw-r--r--src/net/http/server.go79
1 files changed, 50 insertions, 29 deletions
diff --git a/src/net/http/server.go b/src/net/http/server.go
index 32b4130c22..9786a68129 100644
--- a/src/net/http/server.go
+++ b/src/net/http/server.go
@@ -224,7 +224,7 @@ type CloseNotifier interface {
// that the channel receives a value.
//
// If the protocol is HTTP/1.1 and CloseNotify is called while
- // processing an idempotent request (such a GET) while
+ // processing an idempotent request (such as GET) while
// HTTP/1.1 pipelining is in use, the arrival of a subsequent
// pipelined request may cause a value to be sent on the
// returned channel. In practice HTTP/1.1 pipelining is not
@@ -425,7 +425,6 @@ type response struct {
reqBody io.ReadCloser
cancelCtx context.CancelFunc // when ServeHTTP exits
wroteHeader bool // a non-1xx header has been (logically) written
- wroteContinue bool // 100 Continue response was written
wants10KeepAlive bool // HTTP/1.0 w/ Connection "keep-alive"
wantsClose bool // HTTP request has Connection "close"
@@ -436,8 +435,8 @@ type response struct {
// These two fields together synchronize the body reader (the
// expectContinueReader, which wants to write 100 Continue)
// against the main writer.
- canWriteContinue atomic.Bool
writeContinueMu sync.Mutex
+ canWriteContinue atomic.Bool
w *bufio.Writer // buffers output in chunks to chunkWriter
cw chunkWriter
@@ -565,6 +564,14 @@ func (w *response) requestTooLarge() {
}
}
+// disableWriteContinue stops Request.Body.Read from sending an automatic 100-Continue.
+// If a 100-Continue is being written, it waits for it to complete before continuing.
+func (w *response) disableWriteContinue() {
+ w.writeContinueMu.Lock()
+ w.canWriteContinue.Store(false)
+ w.writeContinueMu.Unlock()
+}
+
// writerOnly hides an io.Writer value's optional ReadFrom method
// from io.Copy.
type writerOnly struct {
@@ -917,8 +924,7 @@ func (ecr *expectContinueReader) Read(p []byte) (n int, err error) {
return 0, ErrBodyReadAfterClose
}
w := ecr.resp
- if !w.wroteContinue && w.canWriteContinue.Load() && !w.conn.hijacked() {
- w.wroteContinue = true
+ if w.canWriteContinue.Load() {
w.writeContinueMu.Lock()
if w.canWriteContinue.Load() {
w.conn.bufw.WriteString("HTTP/1.1 100 Continue\r\n\r\n")
@@ -1102,9 +1108,9 @@ func (w *response) Header() Header {
// maxPostHandlerReadBytes is the max number of Request.Body bytes not
// consumed by a handler that the server will read from the client
-// in order to keep a connection alive. If there are more bytes than
-// this then the server to be paranoid instead sends a "Connection:
-// close" response.
+// in order to keep a connection alive. If there are more bytes
+// than this, the server, to be paranoid, instead sends a
+// "Connection close" response.
//
// This number is approximately what a typical machine's TCP buffer
// size is anyway. (if we have the bytes on the machine, we might as
@@ -1159,18 +1165,17 @@ func (w *response) WriteHeader(code int) {
}
checkWriteHeaderCode(code)
+ if code < 101 || code > 199 {
+ // Sending a 100 Continue or any non-1xx header disables the
+ // automatically-sent 100 Continue from Request.Body.Read.
+ w.disableWriteContinue()
+ }
+
// Handle informational headers.
//
// We shouldn't send any further headers after 101 Switching Protocols,
// so it takes the non-informational path.
if code >= 100 && code <= 199 && code != StatusSwitchingProtocols {
- // Prevent a potential race with an automatically-sent 100 Continue triggered by Request.Body.Read()
- if code == 100 && w.canWriteContinue.Load() {
- w.writeContinueMu.Lock()
- w.canWriteContinue.Store(false)
- w.writeContinueMu.Unlock()
- }
-
writeStatusLine(w.conn.bufw, w.req.ProtoAtLeast(1, 1), code, w.statusBuf[:])
// Per RFC 8297 we must not clear the current header map
@@ -1378,14 +1383,20 @@ func (cw *chunkWriter) writeHeader(p []byte) {
//
// If full duplex mode has been enabled with ResponseController.EnableFullDuplex,
// then leave the request body alone.
+ //
+ // We don't take this path when w.closeAfterReply is set.
+ // We may not need to consume the request to get ready for the next one
+ // (since we're closing the conn), but a client which sends a full request
+ // before reading a response may deadlock in this case.
+ // This behavior has been present since CL 5268043 (2011), however,
+ // so it doesn't seem to be causing problems.
if w.req.ContentLength != 0 && !w.closeAfterReply && !w.fullDuplex {
var discard, tooBig bool
switch bdy := w.req.Body.(type) {
case *expectContinueReader:
- if bdy.resp.wroteContinue {
- discard = true
- }
+ // We only get here if we have already fully consumed the request body
+ // (see above).
case *body:
bdy.mu.Lock()
switch {
@@ -1626,13 +1637,8 @@ func (w *response) write(lenData int, dataB []byte, dataS string) (n int, err er
}
if w.canWriteContinue.Load() {
- // 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.Store(false)
- w.writeContinueMu.Unlock()
+ // Body reader wants to write 100 Continue but hasn't yet. Tell it not to.
+ w.disableWriteContinue()
}
if !w.wroteHeader {
@@ -1900,6 +1906,7 @@ func (c *conn) serve(ctx context.Context) {
}
if inFlightResponse != nil {
inFlightResponse.cancelCtx()
+ inFlightResponse.disableWriteContinue()
}
if !c.hijacked() {
if inFlightResponse != nil {
@@ -2106,6 +2113,7 @@ func (w *response) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error) {
if w.handlerDone.Load() {
panic("net/http: Hijack called after ServeHTTP finished")
}
+ w.disableWriteContinue()
if w.wroteHeader {
w.cw.flush()
}
@@ -2175,10 +2183,23 @@ func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
// It does not otherwise end the request; the caller should ensure no further
// writes are done to w.
// The error message should be plain text.
+//
+// Error deletes the Content-Length and Content-Encoding headers,
+// sets Content-Type to “text/plain; charset=utf-8”,
+// and sets X-Content-Type-Options to “nosniff”.
+// This configures the header properly for the error message,
+// in case the caller had set it up expecting a successful output.
func Error(w ResponseWriter, error string, code int) {
- w.Header().Del("Content-Length")
- w.Header().Set("Content-Type", "text/plain; charset=utf-8")
- w.Header().Set("X-Content-Type-Options", "nosniff")
+ h := w.Header()
+ // We delete headers which might be valid for some other content,
+ // but not anymore for the error content.
+ h.Del("Content-Length")
+ h.Del("Content-Encoding")
+
+ // There might be content type already set, but we reset it to
+ // text/plain for the error message.
+ h.Set("Content-Type", "text/plain; charset=utf-8")
+ h.Set("X-Content-Type-Options", "nosniff")
w.WriteHeader(code)
fmt.Fprintln(w, error)
}
@@ -2682,7 +2703,7 @@ func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if use121 {
h, _ = mux.mux121.findHandler(r)
} else {
- h, _, r.pat, r.matches = mux.findHandler(r)
+ h, r.Pattern, r.pat, r.matches = mux.findHandler(r)
}
h.ServeHTTP(w, r)
}