Commit 84ded8ba authored by Kenny Grant's avatar Kenny Grant Committed by Brad Fitzpatrick

net/http: make Server log on bad requests from clients

Fixes #12745

Change-Id: Iebb7c97cb5b68dc080644d796a6ca1c120d41b26
Reviewed-on: https://go-review.googlesource.com/27950Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 238247eb
...@@ -1017,6 +1017,7 @@ func TestResponseSetsTLSConnectionState(t *testing.T) { ...@@ -1017,6 +1017,7 @@ func TestResponseSetsTLSConnectionState(t *testing.T) {
func TestHTTPSClientDetectsHTTPServer(t *testing.T) { func TestHTTPSClientDetectsHTTPServer(t *testing.T) {
defer afterTest(t) defer afterTest(t)
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {})) ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {}))
ts.Config.ErrorLog = quietLog
defer ts.Close() defer ts.Close()
_, err := Get(strings.Replace(ts.URL, "http", "https", 1)) _, err := Get(strings.Replace(ts.URL, "http", "https", 1))
......
...@@ -56,6 +56,10 @@ const ( ...@@ -56,6 +56,10 @@ const (
h2Mode = true h2Mode = true
) )
var optQuietLog = func(ts *httptest.Server) {
ts.Config.ErrorLog = quietLog
}
func newClientServerTest(t *testing.T, h2 bool, h Handler, opts ...interface{}) *clientServerTest { func newClientServerTest(t *testing.T, h2 bool, h Handler, opts ...interface{}) *clientServerTest {
cst := &clientServerTest{ cst := &clientServerTest{
t: t, t: t,
...@@ -1077,7 +1081,7 @@ func testTransportRejectsInvalidHeaders(t *testing.T, h2 bool) { ...@@ -1077,7 +1081,7 @@ func testTransportRejectsInvalidHeaders(t *testing.T, h2 bool) {
defer afterTest(t) defer afterTest(t)
cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
fmt.Fprintf(w, "Handler saw headers: %q", r.Header) fmt.Fprintf(w, "Handler saw headers: %q", r.Header)
})) }), optQuietLog)
defer cst.close() defer cst.close()
cst.tr.DisableKeepAlives = true cst.tr.DisableKeepAlives = true
......
...@@ -6,6 +6,8 @@ package http_test ...@@ -6,6 +6,8 @@ package http_test
import ( import (
"fmt" "fmt"
"io/ioutil"
"log"
"net/http" "net/http"
"os" "os"
"runtime" "runtime"
...@@ -15,6 +17,8 @@ import ( ...@@ -15,6 +17,8 @@ import (
"time" "time"
) )
var quietLog = log.New(ioutil.Discard, "", 0)
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
v := m.Run() v := m.Run()
if v == 0 && goroutineLeaked() { if v == 0 && goroutineLeaked() {
......
...@@ -759,7 +759,7 @@ func TestServeHTTP10Close(t *testing.T) { ...@@ -759,7 +759,7 @@ func TestServeHTTP10Close(t *testing.T) {
// TestClientCanClose verifies that clients can also force a connection to close. // TestClientCanClose verifies that clients can also force a connection to close.
func TestClientCanClose(t *testing.T) { func TestClientCanClose(t *testing.T) {
testTCPConnectionCloses(t, "GET / HTTP/1.1\r\nConnection: close\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) { testTCPConnectionCloses(t, "GET / HTTP/1.1\r\nHost: foo\r\nConnection: close\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
// Nothing. // Nothing.
})) }))
} }
...@@ -767,7 +767,7 @@ func TestClientCanClose(t *testing.T) { ...@@ -767,7 +767,7 @@ func TestClientCanClose(t *testing.T) {
// TestHandlersCanSetConnectionClose verifies that handlers can force a connection to close, // TestHandlersCanSetConnectionClose verifies that handlers can force a connection to close,
// even for HTTP/1.1 requests. // even for HTTP/1.1 requests.
func TestHandlersCanSetConnectionClose11(t *testing.T) { func TestHandlersCanSetConnectionClose11(t *testing.T) {
testTCPConnectionCloses(t, "GET / HTTP/1.1\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) { testTCPConnectionCloses(t, "GET / HTTP/1.1\r\nHost: foo\r\n\r\n\r\n", HandlerFunc(func(w ResponseWriter, r *Request) {
w.Header().Set("Connection", "close") w.Header().Set("Connection", "close")
})) }))
} }
...@@ -2377,7 +2377,7 @@ func testRequestLimit(t *testing.T, h2 bool) { ...@@ -2377,7 +2377,7 @@ func testRequestLimit(t *testing.T, h2 bool) {
defer afterTest(t) defer afterTest(t)
cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) { cst := newClientServerTest(t, h2, HandlerFunc(func(w ResponseWriter, r *Request) {
t.Fatalf("didn't expect to get request in Handler") t.Fatalf("didn't expect to get request in Handler")
})) }), optQuietLog)
defer cst.close() defer cst.close()
req, _ := NewRequest("GET", cst.ts.URL, nil) req, _ := NewRequest("GET", cst.ts.URL, nil)
var bytesPerHeader = len("header12345: val12345\r\n") var bytesPerHeader = len("header12345: val12345\r\n")
...@@ -4119,7 +4119,11 @@ func TestServerValidatesHostHeader(t *testing.T) { ...@@ -4119,7 +4119,11 @@ func TestServerValidatesHostHeader(t *testing.T) {
io.WriteString(&conn.readBuf, methodTarget+tt.proto+"\r\n"+tt.host+"\r\n") io.WriteString(&conn.readBuf, methodTarget+tt.proto+"\r\n"+tt.host+"\r\n")
ln := &oneConnListener{conn} ln := &oneConnListener{conn}
go Serve(ln, HandlerFunc(func(ResponseWriter, *Request) {})) srv := Server{
ErrorLog: quietLog,
Handler: HandlerFunc(func(ResponseWriter, *Request) {}),
}
go srv.Serve(ln)
<-conn.closec <-conn.closec
res, err := ReadResponse(bufio.NewReader(&conn.writeBuf), nil) res, err := ReadResponse(bufio.NewReader(&conn.writeBuf), nil)
if err != nil { if err != nil {
...@@ -4184,9 +4188,10 @@ func TestServerValidatesHeaders(t *testing.T) { ...@@ -4184,9 +4188,10 @@ func TestServerValidatesHeaders(t *testing.T) {
{"X-Foo: bar\r\n", 200}, {"X-Foo: bar\r\n", 200},
{"Foo: a space\r\n", 200}, {"Foo: a space\r\n", 200},
{"A space: foo\r\n", 400}, // space in header {"A space: foo\r\n", 400}, // space in header
{"foo\xffbar: foo\r\n", 400}, // binary in header {"foo\xffbar: foo\r\n", 400}, // binary in header
{"foo\x00bar: foo\r\n", 400}, // binary in header {"foo\x00bar: foo\r\n", 400}, // binary in header
{"Foo: " + strings.Repeat("x", 1<<21) + "\r\n", 431}, // header too large
{"foo: foo foo\r\n", 200}, // LWS space is okay {"foo: foo foo\r\n", 200}, // LWS space is okay
{"foo: foo\tfoo\r\n", 200}, // LWS tab is okay {"foo: foo\tfoo\r\n", 200}, // LWS tab is okay
...@@ -4199,7 +4204,11 @@ func TestServerValidatesHeaders(t *testing.T) { ...@@ -4199,7 +4204,11 @@ func TestServerValidatesHeaders(t *testing.T) {
io.WriteString(&conn.readBuf, "GET / HTTP/1.1\r\nHost: foo\r\n"+tt.header+"\r\n") io.WriteString(&conn.readBuf, "GET / HTTP/1.1\r\nHost: foo\r\n"+tt.header+"\r\n")
ln := &oneConnListener{conn} ln := &oneConnListener{conn}
go Serve(ln, HandlerFunc(func(ResponseWriter, *Request) {})) srv := Server{
ErrorLog: quietLog,
Handler: HandlerFunc(func(ResponseWriter, *Request) {}),
}
go srv.Serve(ln)
<-conn.closec <-conn.closec
res, err := ReadResponse(bufio.NewReader(&conn.writeBuf), nil) res, err := ReadResponse(bufio.NewReader(&conn.writeBuf), nil)
if err != nil { if err != nil {
......
...@@ -1682,6 +1682,23 @@ func (e badRequestError) Error() string { return "Bad Request: " + string(e) } ...@@ -1682,6 +1682,23 @@ func (e badRequestError) Error() string { return "Bad Request: " + string(e) }
// trace to the server's error log. // trace to the server's error log.
var ErrAbortHandler = errors.New("net/http: abort Handler") var ErrAbortHandler = errors.New("net/http: abort Handler")
// isCommonNetReadError reports whether err is a common error
// encountered during reading a request off the network when the
// client has gone away or had its read fail somehow. This is used to
// determine which logs are interesting enough to log about.
func isCommonNetReadError(err error) bool {
if err == io.EOF {
return true
}
if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
return true
}
if oe, ok := err.(*net.OpError); ok && oe.Op == "read" {
return true
}
return false
}
// Serve a new connection. // Serve a new connection.
func (c *conn) serve(ctx context.Context) { func (c *conn) serve(ctx context.Context) {
c.remoteAddr = c.rwc.RemoteAddr().String() c.remoteAddr = c.rwc.RemoteAddr().String()
...@@ -1737,27 +1754,31 @@ func (c *conn) serve(ctx context.Context) { ...@@ -1737,27 +1754,31 @@ func (c *conn) serve(ctx context.Context) {
c.setState(c.rwc, StateActive) c.setState(c.rwc, StateActive)
} }
if err != nil { if err != nil {
const errorHeaders = "\r\nContent-Type: text/plain; charset=utf-8\r\nConnection: close\r\n\r\n"
if err == errTooLarge { if err == errTooLarge {
// Their HTTP client may or may not be // Their HTTP client may or may not be
// able to read this if we're // able to read this if we're
// responding to them and hanging up // responding to them and hanging up
// while they're still writing their // while they're still writing their
// request. Undefined behavior. // request. Undefined behavior.
io.WriteString(c.rwc, "HTTP/1.1 431 Request Header Fields Too Large\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n431 Request Header Fields Too Large") const publicErr = "431 Request Header Fields Too Large"
c.server.logf("http: %s", publicErr)
fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr)
c.closeWriteAndWait() c.closeWriteAndWait()
return return
} }
if err == io.EOF { if isCommonNetReadError(err) {
return // don't reply return // don't reply
} }
if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
return // don't reply publicErr := "400 Bad Request"
}
var publicErr string
if v, ok := err.(badRequestError); ok { if v, ok := err.(badRequestError); ok {
publicErr = ": " + string(v) publicErr = publicErr + ": " + string(v)
} }
io.WriteString(c.rwc, "HTTP/1.1 400 Bad Request\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\n400 Bad Request"+publicErr)
c.server.logf("http: %s", publicErr)
fmt.Fprintf(c.rwc, "HTTP/1.1 "+publicErr+errorHeaders+publicErr)
return return
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment