aboutsummaryrefslogtreecommitdiff
path: root/src/net/http/serve_test.go
diff options
context:
space:
mode:
authorEmmanuel T Odeke <emmanuel@orijtech.com>2019-10-10 19:11:54 -0400
committerEmmanuel Odeke <emm.odeke@gmail.com>2019-10-21 18:38:15 +0000
commitff4e0e42d8f662529c3e6ac5af6591d31805ae2f (patch)
treec88af9cc68f632316759a9e1d3a9a965c3d07f38 /src/net/http/serve_test.go
parent03978a97e5336c0ef46cec9b4f0733e9d897d083 (diff)
downloadgo-ff4e0e42d8f662529c3e6ac5af6591d31805ae2f.tar.gz
go-ff4e0e42d8f662529c3e6ac5af6591d31805ae2f.zip
net/http: make TimeoutHandler log spurious WriteHeader calls
Makes TimeoutHandler consistent with other handlers, by logging any spurious WriteHeader calls. Fixes #30803 Change-Id: I693fbdf8378f31bca13d579eece8e8e00eb175bf Reviewed-on: https://go-review.googlesource.com/c/go/+/200518 Run-TryBot: Emmanuel Odeke <emm.odeke@gmail.com> Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org>
Diffstat (limited to 'src/net/http/serve_test.go')
-rw-r--r--src/net/http/serve_test.go93
1 files changed, 93 insertions, 0 deletions
diff --git a/src/net/http/serve_test.go b/src/net/http/serve_test.go
index d060aa4732..4c53c95eda 100644
--- a/src/net/http/serve_test.go
+++ b/src/net/http/serve_test.go
@@ -29,7 +29,9 @@ import (
"net/url"
"os"
"os/exec"
+ "path/filepath"
"reflect"
+ "regexp"
"runtime"
"runtime/debug"
"sort"
@@ -6266,6 +6268,97 @@ func testContentEncodingNoSniffing(t *testing.T, h2 bool) {
}
}
+// Issue 30803: ensure that TimeoutHandler logs spurious
+// WriteHeader calls, for consistency with other Handlers.
+func TestTimeoutHandlerSuperfluousLogs(t *testing.T) {
+ setParallel(t)
+ defer afterTest(t)
+
+ pc, curFile, _, _ := runtime.Caller(0)
+ curFileBaseName := filepath.Base(curFile)
+ testFuncName := runtime.FuncForPC(pc).Name()
+
+ timeoutMsg := "timed out here!"
+ maxTimeout := 200 * time.Millisecond
+
+ tests := []struct {
+ name string
+ sleepTime time.Duration
+ wantResp string
+ }{
+ {
+ name: "return before timeout",
+ sleepTime: 0,
+ wantResp: "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n",
+ },
+ {
+ name: "return after timeout",
+ sleepTime: maxTimeout * 2,
+ wantResp: fmt.Sprintf("HTTP/1.1 503 Service Unavailable\r\nContent-Length: %d\r\n\r\n%s",
+ len(timeoutMsg), timeoutMsg),
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ var lastSpuriousLine int32
+
+ sh := HandlerFunc(func(w ResponseWriter, r *Request) {
+ w.WriteHeader(404)
+ w.WriteHeader(404)
+ w.WriteHeader(404)
+ w.WriteHeader(404)
+ _, _, line, _ := runtime.Caller(0)
+ atomic.StoreInt32(&lastSpuriousLine, int32(line))
+
+ <-time.After(tt.sleepTime)
+ })
+
+ logBuf := new(bytes.Buffer)
+ srvLog := log.New(logBuf, "", 0)
+ th := TimeoutHandler(sh, maxTimeout, timeoutMsg)
+ cst := newClientServerTest(t, h1Mode /* the test is protocol-agnostic */, th, optWithServerLog(srvLog))
+ defer cst.close()
+
+ res, err := cst.c.Get(cst.ts.URL)
+ if err != nil {
+ t.Fatalf("Unexpected error: %v", err)
+ }
+
+ // Deliberately removing the "Date" header since it is highly ephemeral
+ // and will cause failure if we try to match it exactly.
+ res.Header.Del("Date")
+ res.Header.Del("Content-Type")
+
+ // Match the response.
+ blob, _ := httputil.DumpResponse(res, true)
+ if g, w := string(blob), tt.wantResp; g != w {
+ t.Errorf("Response mismatch\nGot\n%q\n\nWant\n%q", g, w)
+ }
+
+ // Given 4 w.WriteHeader calls, only the first one is valid
+ // and the rest should be reported as the 3 spurious logs.
+ logEntries := strings.Split(strings.TrimSpace(logBuf.String()), "\n")
+ if g, w := len(logEntries), 3; g != w {
+ blob, _ := json.MarshalIndent(logEntries, "", " ")
+ t.Fatalf("Server logs count mismatch\ngot %d, want %d\n\nGot\n%s\n", g, w, blob)
+ }
+
+ // Now ensure that the regexes match exactly.
+ // "http: superfluous response.WriteHeader call from <fn>.func\d.\d (<curFile>:lastSpuriousLine-[1, 3]"
+ for i, logEntry := range logEntries {
+ wantLine := atomic.LoadInt32(&lastSpuriousLine) - 3 + int32(i)
+ pat := fmt.Sprintf("^http: superfluous response.WriteHeader call from %s.func\\d+.\\d+ \\(%s:%d\\)$",
+ testFuncName, curFileBaseName, wantLine)
+ re := regexp.MustCompile(pat)
+ if !re.MatchString(logEntry) {
+ t.Errorf("Log entry mismatch\n\t%s\ndoes not match\n\t%s", logEntry, pat)
+ }
+ }
+ })
+ }
+}
+
// fetchWireResponse is a helper for dialing to host,
// sending http1ReqBody as the payload and retrieving
// the response as it was sent on the wire.