]> Cypherpunks repositories - gostls13.git/commitdiff
net/http/httptest: add NewRequest helper for ease of testing handlers
authorBrad Fitzpatrick <bradfitz@golang.org>
Wed, 23 Mar 2016 05:25:27 +0000 (16:25 +1100)
committerBrad Fitzpatrick <bradfitz@golang.org>
Wed, 23 Mar 2016 23:22:22 +0000 (23:22 +0000)
Fixes #14199

Change-Id: Ic9284023b663de3db1ca7b7b1e96eeab82ec0944
Reviewed-on: https://go-review.googlesource.com/21016
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Andrew Gerrand <adg@golang.org>
src/net/http/httptest/httptest.go [new file with mode: 0644]
src/net/http/httptest/httptest_test.go [new file with mode: 0644]
src/net/http/httptest/recorder.go

diff --git a/src/net/http/httptest/httptest.go b/src/net/http/httptest/httptest.go
new file mode 100644 (file)
index 0000000..e2148a6
--- /dev/null
@@ -0,0 +1,88 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package httptest provides utilities for HTTP testing.
+package httptest
+
+import (
+       "bufio"
+       "bytes"
+       "crypto/tls"
+       "io"
+       "io/ioutil"
+       "net/http"
+       "strings"
+)
+
+// NewRequest returns a new incoming server Request, suitable
+// for passing to an http.Handler for testing.
+//
+// The target is the RFC 7230 "request-target": it may be either a
+// path or an absolute URL. If target is an absolute URL, the host name
+// from the URL is used. Otherwise, "example.com" is used.
+//
+// The TLS field is set to a non-nil dummy value if target has scheme
+// "https".
+//
+// The Request.Proto is always HTTP/1.1.
+//
+// An empty method means "GET".
+//
+// The provided body may be nil. If the body is of type *bytes.Reader,
+// *strings.Reader, or *bytes.Buffer, the Request.ContentLength is
+// set.
+//
+// NewRequest panics on error for ease of use in testing, where a
+// panic is acceptable.
+func NewRequest(method, target string, body io.Reader) *http.Request {
+       if method == "" {
+               method = "GET"
+       }
+       req, err := http.ReadRequest(bufio.NewReader(strings.NewReader(method + " " + target + " HTTP/1.0\r\n\r\n")))
+       if err != nil {
+               panic("invalid NewRequest arguments; " + err.Error())
+       }
+
+       // HTTP/1.0 was used above to avoid needing a Host field. Change it to 1.1 here.
+       req.Proto = "HTTP/1.1"
+       req.ProtoMinor = 1
+       req.Close = false
+
+       if body != nil {
+               switch v := body.(type) {
+               case *bytes.Buffer:
+                       req.ContentLength = int64(v.Len())
+               case *bytes.Reader:
+                       req.ContentLength = int64(v.Len())
+               case *strings.Reader:
+                       req.ContentLength = int64(v.Len())
+               default:
+                       req.ContentLength = -1
+               }
+               if rc, ok := body.(io.ReadCloser); ok {
+                       req.Body = rc
+               } else {
+                       req.Body = ioutil.NopCloser(body)
+               }
+       }
+
+       // 192.0.2.0/24 is "TEST-NET" in RFC 5737 for use solely in
+       // documentation and example source code and should not be
+       // used publicly.
+       req.RemoteAddr = "192.0.2.1:1234"
+
+       if req.Host == "" {
+               req.Host = "example.com"
+       }
+
+       if strings.HasPrefix(target, "https://") {
+               req.TLS = &tls.ConnectionState{
+                       Version:           tls.VersionTLS12,
+                       HandshakeComplete: true,
+                       ServerName:        req.Host,
+               }
+       }
+
+       return req
+}
diff --git a/src/net/http/httptest/httptest_test.go b/src/net/http/httptest/httptest_test.go
new file mode 100644 (file)
index 0000000..18ba738
--- /dev/null
@@ -0,0 +1,177 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package httptest
+
+import (
+       "crypto/tls"
+       "io"
+       "io/ioutil"
+       "net/http"
+       "net/url"
+       "reflect"
+       "strings"
+       "testing"
+)
+
+func TestNewRequest(t *testing.T) {
+       tests := [...]struct {
+               method, uri string
+               body        io.Reader
+
+               want     *http.Request
+               wantBody string
+       }{
+               // Empty method means GET:
+               0: {
+                       method: "",
+                       uri:    "/",
+                       body:   nil,
+                       want: &http.Request{
+                               Method:     "GET",
+                               Host:       "example.com",
+                               URL:        &url.URL{Path: "/"},
+                               Header:     http.Header{},
+                               Proto:      "HTTP/1.1",
+                               ProtoMajor: 1,
+                               ProtoMinor: 1,
+                               RemoteAddr: "192.0.2.1:1234",
+                               RequestURI: "/",
+                       },
+                       wantBody: "",
+               },
+
+               // GET with full URL:
+               1: {
+                       method: "GET",
+                       uri:    "http://foo.com/path/%2f/bar/",
+                       body:   nil,
+                       want: &http.Request{
+                               Method: "GET",
+                               Host:   "foo.com",
+                               URL: &url.URL{
+                                       Scheme:  "http",
+                                       Path:    "/path///bar/",
+                                       RawPath: "/path/%2f/bar/",
+                                       Host:    "foo.com",
+                               },
+                               Header:     http.Header{},
+                               Proto:      "HTTP/1.1",
+                               ProtoMajor: 1,
+                               ProtoMinor: 1,
+                               RemoteAddr: "192.0.2.1:1234",
+                               RequestURI: "http://foo.com/path/%2f/bar/",
+                       },
+                       wantBody: "",
+               },
+
+               // GET with full https URL:
+               2: {
+                       method: "GET",
+                       uri:    "https://foo.com/path/",
+                       body:   nil,
+                       want: &http.Request{
+                               Method: "GET",
+                               Host:   "foo.com",
+                               URL: &url.URL{
+                                       Scheme: "https",
+                                       Path:   "/path/",
+                                       Host:   "foo.com",
+                               },
+                               Header:     http.Header{},
+                               Proto:      "HTTP/1.1",
+                               ProtoMajor: 1,
+                               ProtoMinor: 1,
+                               RemoteAddr: "192.0.2.1:1234",
+                               RequestURI: "https://foo.com/path/",
+                               TLS: &tls.ConnectionState{
+                                       Version:           tls.VersionTLS12,
+                                       HandshakeComplete: true,
+                                       ServerName:        "foo.com",
+                               },
+                       },
+                       wantBody: "",
+               },
+
+               // Post with known length
+               3: {
+                       method: "POST",
+                       uri:    "/",
+                       body:   strings.NewReader("foo"),
+                       want: &http.Request{
+                               Method:        "POST",
+                               Host:          "example.com",
+                               URL:           &url.URL{Path: "/"},
+                               Header:        http.Header{},
+                               Proto:         "HTTP/1.1",
+                               ContentLength: 3,
+                               ProtoMajor:    1,
+                               ProtoMinor:    1,
+                               RemoteAddr:    "192.0.2.1:1234",
+                               RequestURI:    "/",
+                       },
+                       wantBody: "foo",
+               },
+
+               // Post with unknown length
+               4: {
+                       method: "POST",
+                       uri:    "/",
+                       body:   struct{ io.Reader }{strings.NewReader("foo")},
+                       want: &http.Request{
+                               Method:        "POST",
+                               Host:          "example.com",
+                               URL:           &url.URL{Path: "/"},
+                               Header:        http.Header{},
+                               Proto:         "HTTP/1.1",
+                               ContentLength: -1,
+                               ProtoMajor:    1,
+                               ProtoMinor:    1,
+                               RemoteAddr:    "192.0.2.1:1234",
+                               RequestURI:    "/",
+                       },
+                       wantBody: "foo",
+               },
+
+               // OPTIONS *
+               5: {
+                       method: "OPTIONS",
+                       uri:    "*",
+                       want: &http.Request{
+                               Method:     "OPTIONS",
+                               Host:       "example.com",
+                               URL:        &url.URL{Path: "*"},
+                               Header:     http.Header{},
+                               Proto:      "HTTP/1.1",
+                               ProtoMajor: 1,
+                               ProtoMinor: 1,
+                               RemoteAddr: "192.0.2.1:1234",
+                               RequestURI: "*",
+                       },
+               },
+       }
+       for i, tt := range tests {
+               got := NewRequest(tt.method, tt.uri, tt.body)
+               slurp, err := ioutil.ReadAll(got.Body)
+               if err != nil {
+                       t.Errorf("%i. ReadAll: %v", i, err)
+               }
+               if string(slurp) != tt.wantBody {
+                       t.Errorf("%i. Body = %q; want %q", i, slurp, tt.wantBody)
+               }
+               got.Body = nil // before DeepEqual
+               if !reflect.DeepEqual(got.URL, tt.want.URL) {
+                       t.Errorf("%d. Request.URL mismatch:\n got: %#v\nwant: %#v", i, got.URL, tt.want.URL)
+               }
+               if !reflect.DeepEqual(got.Header, tt.want.Header) {
+                       t.Errorf("%d. Request.Header mismatch:\n got: %#v\nwant: %#v", i, got.Header, tt.want.Header)
+               }
+               if !reflect.DeepEqual(got.TLS, tt.want.TLS) {
+                       t.Errorf("%d. Request.TLS mismatch:\n got: %#v\nwant: %#v", i, got.TLS, tt.want.TLS)
+               }
+               if !reflect.DeepEqual(got, tt.want) {
+                       t.Errorf("%d. Request mismatch:\n got: %#v\nwant: %#v", i, got, tt.want)
+               }
+       }
+}
index 4e3948dd9175762097946137188da23612f22dfc..b1f49541d576e556dee47786b33627da77097552 100644 (file)
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-// Package httptest provides utilities for HTTP testing.
 package httptest
 
 import (