mirror of
https://github.com/golang/go
synced 2024-11-24 22:47:58 -07:00
http: flesh out server Expect handling + tests
This mostly adds Expect 100-continue tests (from the perspective of server correctness) that were missing before. It also fixes a few missing cases that will probably never come up in practice, but it's nice to have handled correctly. Proper 100-continue client support remains a TODO. R=rsc, bradfitzwork CC=golang-dev https://golang.org/cl/4399044
This commit is contained in:
parent
99f069a97f
commit
c7d16cc411
@ -534,3 +534,85 @@ func TestTLSServer(t *testing.T) {
|
|||||||
t.Errorf("expected body %q; got %q", e, g)
|
t.Errorf("expected body %q; got %q", e, g)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type serverExpectTest struct {
|
||||||
|
contentLength int // of request body
|
||||||
|
expectation string // e.g. "100-continue"
|
||||||
|
readBody bool // whether handler should read the body (if false, sends StatusUnauthorized)
|
||||||
|
expectedResponse string // expected substring in first line of http response
|
||||||
|
}
|
||||||
|
|
||||||
|
var serverExpectTests = []serverExpectTest{
|
||||||
|
// Normal 100-continues, case-insensitive.
|
||||||
|
{100, "100-continue", true, "100 Continue"},
|
||||||
|
{100, "100-cOntInUE", true, "100 Continue"},
|
||||||
|
|
||||||
|
// No 100-continue.
|
||||||
|
{100, "", true, "200 OK"},
|
||||||
|
|
||||||
|
// 100-continue but requesting client to deny us,
|
||||||
|
// so it never eads the body.
|
||||||
|
{100, "100-continue", false, "401 Unauthorized"},
|
||||||
|
// Likewise without 100-continue:
|
||||||
|
{100, "", false, "401 Unauthorized"},
|
||||||
|
|
||||||
|
// Non-standard expectations are failures
|
||||||
|
{0, "a-pony", false, "417 Expectation Failed"},
|
||||||
|
|
||||||
|
// Expect-100 requested but no body
|
||||||
|
{0, "100-continue", true, "400 Bad Request"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that the server responds to the "Expect" request header
|
||||||
|
// correctly.
|
||||||
|
func TestServerExpect(t *testing.T) {
|
||||||
|
ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
|
||||||
|
// Note using r.FormValue("readbody") because for POST
|
||||||
|
// requests that would read from r.Body, which we only
|
||||||
|
// conditionally want to do.
|
||||||
|
if strings.Contains(r.URL.RawPath, "readbody=true") {
|
||||||
|
ioutil.ReadAll(r.Body)
|
||||||
|
w.Write([]byte("Hi"))
|
||||||
|
} else {
|
||||||
|
w.WriteHeader(StatusUnauthorized)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
defer ts.Close()
|
||||||
|
|
||||||
|
runTest := func(test serverExpectTest) {
|
||||||
|
conn, err := net.Dial("tcp", ts.Listener.Addr().String())
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Dial: %v", err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
sendf := func(format string, args ...interface{}) {
|
||||||
|
_, err := fmt.Fprintf(conn, format, args...)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error writing %q: %v", format, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
sendf("POST /?readbody=%v HTTP/1.1\r\n"+
|
||||||
|
"Connection: close\r\n"+
|
||||||
|
"Content-Length: %d\r\n"+
|
||||||
|
"Expect: %s\r\nHost: foo\r\n\r\n",
|
||||||
|
test.readBody, test.contentLength, test.expectation)
|
||||||
|
if test.contentLength > 0 && strings.ToLower(test.expectation) != "100-continue" {
|
||||||
|
body := strings.Repeat("A", test.contentLength)
|
||||||
|
sendf(body)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
bufr := bufio.NewReader(conn)
|
||||||
|
line, err := bufr.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ReadString: %v", err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(line, test.expectedResponse) {
|
||||||
|
t.Errorf("for test %#v got first line=%q", test, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range serverExpectTests {
|
||||||
|
runTest(test)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -180,12 +180,6 @@ func (c *conn) readRequest() (w *response, err os.Error) {
|
|||||||
w.req = req
|
w.req = req
|
||||||
w.header = make(Header)
|
w.header = make(Header)
|
||||||
w.contentLength = -1
|
w.contentLength = -1
|
||||||
|
|
||||||
// Expect 100 Continue support
|
|
||||||
if req.expectsContinue() && req.ProtoAtLeast(1, 1) {
|
|
||||||
// Wrap the Body reader with one that replies on the connection
|
|
||||||
req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
|
|
||||||
}
|
|
||||||
return w, nil
|
return w, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -446,6 +440,38 @@ func (c *conn) serve() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Expect 100 Continue support
|
||||||
|
req := w.req
|
||||||
|
if req.expectsContinue() {
|
||||||
|
if req.ProtoAtLeast(1, 1) {
|
||||||
|
// Wrap the Body reader with one that replies on the connection
|
||||||
|
req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
|
||||||
|
}
|
||||||
|
if req.ContentLength == 0 {
|
||||||
|
w.Header().Set("Connection", "close")
|
||||||
|
w.WriteHeader(StatusBadRequest)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
req.Header.Del("Expect")
|
||||||
|
} else if req.Header.Get("Expect") != "" {
|
||||||
|
// TODO(bradfitz): let ServeHTTP handlers handle
|
||||||
|
// requests with non-standard expectation[s]? Seems
|
||||||
|
// theoretical at best, and doesn't fit into the
|
||||||
|
// current ServeHTTP model anyway. We'd need to
|
||||||
|
// make the ResponseWriter an optional
|
||||||
|
// "ExpectReplier" interface or something.
|
||||||
|
//
|
||||||
|
// For now we'll just obey RFC 2616 14.20 which says
|
||||||
|
// "If a server receives a request containing an
|
||||||
|
// Expect field that includes an expectation-
|
||||||
|
// extension that it does not support, it MUST
|
||||||
|
// respond with a 417 (Expectation Failed) status."
|
||||||
|
w.Header().Set("Connection", "close")
|
||||||
|
w.WriteHeader(StatusExpectationFailed)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
// HTTP cannot have multiple simultaneous active requests.[*]
|
// HTTP cannot have multiple simultaneous active requests.[*]
|
||||||
// Until the server replies to this request, it can't read another,
|
// Until the server replies to this request, it can't read another,
|
||||||
// so we might as well run the handler in this goroutine.
|
// so we might as well run the handler in this goroutine.
|
||||||
|
Loading…
Reference in New Issue
Block a user