diff options
author | Emmanuel T Odeke <emmanuel@orijtech.com> | 2019-10-10 19:11:54 -0400 |
---|---|---|
committer | Emmanuel Odeke <emm.odeke@gmail.com> | 2019-10-21 18:38:15 +0000 |
commit | ff4e0e42d8f662529c3e6ac5af6591d31805ae2f (patch) | |
tree | c88af9cc68f632316759a9e1d3a9a965c3d07f38 /src/net/http/serve_test.go | |
parent | 03978a97e5336c0ef46cec9b4f0733e9d897d083 (diff) | |
download | go-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.go | 93 |
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. |