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