github.com/sean-/go@v0.0.0-20151219100004-97f854cd7bb6/src/net/http/httputil/dump_test.go (about)

     1  // Copyright 2011 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package httputil
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"fmt"
    11  	"io"
    12  	"io/ioutil"
    13  	"net/http"
    14  	"net/url"
    15  	"runtime"
    16  	"strings"
    17  	"testing"
    18  )
    19  
    20  type dumpTest struct {
    21  	Req  http.Request
    22  	Body interface{} // optional []byte or func() io.ReadCloser to populate Req.Body
    23  
    24  	WantDump    string
    25  	WantDumpOut string
    26  	NoBody      bool // if true, set DumpRequest{,Out} body to false
    27  }
    28  
    29  var dumpTests = []dumpTest{
    30  
    31  	// HTTP/1.1 => chunked coding; body; empty trailer
    32  	{
    33  		Req: http.Request{
    34  			Method: "GET",
    35  			URL: &url.URL{
    36  				Scheme: "http",
    37  				Host:   "www.google.com",
    38  				Path:   "/search",
    39  			},
    40  			ProtoMajor:       1,
    41  			ProtoMinor:       1,
    42  			TransferEncoding: []string{"chunked"},
    43  		},
    44  
    45  		Body: []byte("abcdef"),
    46  
    47  		WantDump: "GET /search HTTP/1.1\r\n" +
    48  			"Host: www.google.com\r\n" +
    49  			"Transfer-Encoding: chunked\r\n\r\n" +
    50  			chunk("abcdef") + chunk(""),
    51  	},
    52  
    53  	// Verify that DumpRequest preserves the HTTP version number, doesn't add a Host,
    54  	// and doesn't add a User-Agent.
    55  	{
    56  		Req: http.Request{
    57  			Method:     "GET",
    58  			URL:        mustParseURL("/foo"),
    59  			ProtoMajor: 1,
    60  			ProtoMinor: 0,
    61  			Header: http.Header{
    62  				"X-Foo": []string{"X-Bar"},
    63  			},
    64  		},
    65  
    66  		WantDump: "GET /foo HTTP/1.0\r\n" +
    67  			"X-Foo: X-Bar\r\n\r\n",
    68  	},
    69  
    70  	{
    71  		Req: *mustNewRequest("GET", "http://example.com/foo", nil),
    72  
    73  		WantDumpOut: "GET /foo HTTP/1.1\r\n" +
    74  			"Host: example.com\r\n" +
    75  			"User-Agent: Go-http-client/1.1\r\n" +
    76  			"Accept-Encoding: gzip\r\n\r\n",
    77  	},
    78  
    79  	// Test that an https URL doesn't try to do an SSL negotiation
    80  	// with a bytes.Buffer and hang with all goroutines not
    81  	// runnable.
    82  	{
    83  		Req: *mustNewRequest("GET", "https://example.com/foo", nil),
    84  
    85  		WantDumpOut: "GET /foo HTTP/1.1\r\n" +
    86  			"Host: example.com\r\n" +
    87  			"User-Agent: Go-http-client/1.1\r\n" +
    88  			"Accept-Encoding: gzip\r\n\r\n",
    89  	},
    90  
    91  	// Request with Body, but Dump requested without it.
    92  	{
    93  		Req: http.Request{
    94  			Method: "POST",
    95  			URL: &url.URL{
    96  				Scheme: "http",
    97  				Host:   "post.tld",
    98  				Path:   "/",
    99  			},
   100  			ContentLength: 6,
   101  			ProtoMajor:    1,
   102  			ProtoMinor:    1,
   103  		},
   104  
   105  		Body: []byte("abcdef"),
   106  
   107  		WantDumpOut: "POST / HTTP/1.1\r\n" +
   108  			"Host: post.tld\r\n" +
   109  			"User-Agent: Go-http-client/1.1\r\n" +
   110  			"Content-Length: 6\r\n" +
   111  			"Accept-Encoding: gzip\r\n\r\n",
   112  
   113  		NoBody: true,
   114  	},
   115  
   116  	// Request with Body > 8196 (default buffer size)
   117  	{
   118  		Req: http.Request{
   119  			Method: "POST",
   120  			URL: &url.URL{
   121  				Scheme: "http",
   122  				Host:   "post.tld",
   123  				Path:   "/",
   124  			},
   125  			ContentLength: 8193,
   126  			ProtoMajor:    1,
   127  			ProtoMinor:    1,
   128  		},
   129  
   130  		Body: bytes.Repeat([]byte("a"), 8193),
   131  
   132  		WantDumpOut: "POST / HTTP/1.1\r\n" +
   133  			"Host: post.tld\r\n" +
   134  			"User-Agent: Go-http-client/1.1\r\n" +
   135  			"Content-Length: 8193\r\n" +
   136  			"Accept-Encoding: gzip\r\n\r\n" +
   137  			strings.Repeat("a", 8193),
   138  	},
   139  
   140  	{
   141  		Req: *mustReadRequest("GET http://foo.com/ HTTP/1.1\r\n" +
   142  			"User-Agent: blah\r\n\r\n"),
   143  		NoBody: true,
   144  		WantDump: "GET http://foo.com/ HTTP/1.1\r\n" +
   145  			"User-Agent: blah\r\n\r\n",
   146  	},
   147  }
   148  
   149  func TestDumpRequest(t *testing.T) {
   150  	numg0 := runtime.NumGoroutine()
   151  	for i, tt := range dumpTests {
   152  		setBody := func() {
   153  			if tt.Body == nil {
   154  				return
   155  			}
   156  			switch b := tt.Body.(type) {
   157  			case []byte:
   158  				tt.Req.Body = ioutil.NopCloser(bytes.NewReader(b))
   159  			case func() io.ReadCloser:
   160  				tt.Req.Body = b()
   161  			default:
   162  				t.Fatalf("Test %d: unsupported Body of %T", i, tt.Body)
   163  			}
   164  		}
   165  		setBody()
   166  		if tt.Req.Header == nil {
   167  			tt.Req.Header = make(http.Header)
   168  		}
   169  
   170  		if tt.WantDump != "" {
   171  			setBody()
   172  			dump, err := DumpRequest(&tt.Req, !tt.NoBody)
   173  			if err != nil {
   174  				t.Errorf("DumpRequest #%d: %s", i, err)
   175  				continue
   176  			}
   177  			if string(dump) != tt.WantDump {
   178  				t.Errorf("DumpRequest %d, expecting:\n%s\nGot:\n%s\n", i, tt.WantDump, string(dump))
   179  				continue
   180  			}
   181  		}
   182  
   183  		if tt.WantDumpOut != "" {
   184  			setBody()
   185  			dump, err := DumpRequestOut(&tt.Req, !tt.NoBody)
   186  			if err != nil {
   187  				t.Errorf("DumpRequestOut #%d: %s", i, err)
   188  				continue
   189  			}
   190  			if string(dump) != tt.WantDumpOut {
   191  				t.Errorf("DumpRequestOut %d, expecting:\n%s\nGot:\n%s\n", i, tt.WantDumpOut, string(dump))
   192  				continue
   193  			}
   194  		}
   195  	}
   196  	if dg := runtime.NumGoroutine() - numg0; dg > 4 {
   197  		buf := make([]byte, 4096)
   198  		buf = buf[:runtime.Stack(buf, true)]
   199  		t.Errorf("Unexpectedly large number of new goroutines: %d new: %s", dg, buf)
   200  	}
   201  }
   202  
   203  func chunk(s string) string {
   204  	return fmt.Sprintf("%x\r\n%s\r\n", len(s), s)
   205  }
   206  
   207  func mustParseURL(s string) *url.URL {
   208  	u, err := url.Parse(s)
   209  	if err != nil {
   210  		panic(fmt.Sprintf("Error parsing URL %q: %v", s, err))
   211  	}
   212  	return u
   213  }
   214  
   215  func mustNewRequest(method, url string, body io.Reader) *http.Request {
   216  	req, err := http.NewRequest(method, url, body)
   217  	if err != nil {
   218  		panic(fmt.Sprintf("NewRequest(%q, %q, %p) err = %v", method, url, body, err))
   219  	}
   220  	return req
   221  }
   222  
   223  func mustReadRequest(s string) *http.Request {
   224  	req, err := http.ReadRequest(bufio.NewReader(strings.NewReader(s)))
   225  	if err != nil {
   226  		panic(err)
   227  	}
   228  	return req
   229  }
   230  
   231  var dumpResTests = []struct {
   232  	res  *http.Response
   233  	body bool
   234  	want string
   235  }{
   236  	{
   237  		res: &http.Response{
   238  			Status:        "200 OK",
   239  			StatusCode:    200,
   240  			Proto:         "HTTP/1.1",
   241  			ProtoMajor:    1,
   242  			ProtoMinor:    1,
   243  			ContentLength: 50,
   244  			Header: http.Header{
   245  				"Foo": []string{"Bar"},
   246  			},
   247  			Body: ioutil.NopCloser(strings.NewReader("foo")), // shouldn't be used
   248  		},
   249  		body: false, // to verify we see 50, not empty or 3.
   250  		want: `HTTP/1.1 200 OK
   251  Content-Length: 50
   252  Foo: Bar`,
   253  	},
   254  
   255  	{
   256  		res: &http.Response{
   257  			Status:        "200 OK",
   258  			StatusCode:    200,
   259  			Proto:         "HTTP/1.1",
   260  			ProtoMajor:    1,
   261  			ProtoMinor:    1,
   262  			ContentLength: 3,
   263  			Body:          ioutil.NopCloser(strings.NewReader("foo")),
   264  		},
   265  		body: true,
   266  		want: `HTTP/1.1 200 OK
   267  Content-Length: 3
   268  
   269  foo`,
   270  	},
   271  
   272  	{
   273  		res: &http.Response{
   274  			Status:           "200 OK",
   275  			StatusCode:       200,
   276  			Proto:            "HTTP/1.1",
   277  			ProtoMajor:       1,
   278  			ProtoMinor:       1,
   279  			ContentLength:    -1,
   280  			Body:             ioutil.NopCloser(strings.NewReader("foo")),
   281  			TransferEncoding: []string{"chunked"},
   282  		},
   283  		body: true,
   284  		want: `HTTP/1.1 200 OK
   285  Transfer-Encoding: chunked
   286  
   287  3
   288  foo
   289  0`,
   290  	},
   291  }
   292  
   293  func TestDumpResponse(t *testing.T) {
   294  	for i, tt := range dumpResTests {
   295  		gotb, err := DumpResponse(tt.res, tt.body)
   296  		if err != nil {
   297  			t.Errorf("%d. DumpResponse = %v", i, err)
   298  			continue
   299  		}
   300  		got := string(gotb)
   301  		got = strings.TrimSpace(got)
   302  		got = strings.Replace(got, "\r", "", -1)
   303  
   304  		if got != tt.want {
   305  			t.Errorf("%d.\nDumpResponse got:\n%s\n\nWant:\n%s\n", i, got, tt.want)
   306  		}
   307  	}
   308  }