github.com/ader1990/go@v0.0.0-20140630135419-8c24447fa791/src/pkg/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  
   116  func TestDumpRequest(t *testing.T) {
   117  	numg0 := runtime.NumGoroutine()
   118  	for i, tt := range dumpTests {
   119  		setBody := func() {
   120  			if tt.Body == nil {
   121  				return
   122  			}
   123  			switch b := tt.Body.(type) {
   124  			case []byte:
   125  				tt.Req.Body = ioutil.NopCloser(bytes.NewReader(b))
   126  			case func() io.ReadCloser:
   127  				tt.Req.Body = b()
   128  			}
   129  		}
   130  		setBody()
   131  		if tt.Req.Header == nil {
   132  			tt.Req.Header = make(http.Header)
   133  		}
   134  
   135  		if tt.WantDump != "" {
   136  			setBody()
   137  			dump, err := DumpRequest(&tt.Req, !tt.NoBody)
   138  			if err != nil {
   139  				t.Errorf("DumpRequest #%d: %s", i, err)
   140  				continue
   141  			}
   142  			if string(dump) != tt.WantDump {
   143  				t.Errorf("DumpRequest %d, expecting:\n%s\nGot:\n%s\n", i, tt.WantDump, string(dump))
   144  				continue
   145  			}
   146  		}
   147  
   148  		if tt.WantDumpOut != "" {
   149  			setBody()
   150  			dump, err := DumpRequestOut(&tt.Req, !tt.NoBody)
   151  			if err != nil {
   152  				t.Errorf("DumpRequestOut #%d: %s", i, err)
   153  				continue
   154  			}
   155  			if string(dump) != tt.WantDumpOut {
   156  				t.Errorf("DumpRequestOut %d, expecting:\n%s\nGot:\n%s\n", i, tt.WantDumpOut, string(dump))
   157  				continue
   158  			}
   159  		}
   160  	}
   161  	if dg := runtime.NumGoroutine() - numg0; dg > 4 {
   162  		t.Errorf("Unexpectedly large number of new goroutines: %d new", dg)
   163  	}
   164  }
   165  
   166  func chunk(s string) string {
   167  	return fmt.Sprintf("%x\r\n%s\r\n", len(s), s)
   168  }
   169  
   170  func mustParseURL(s string) *url.URL {
   171  	u, err := url.Parse(s)
   172  	if err != nil {
   173  		panic(fmt.Sprintf("Error parsing URL %q: %v", s, err))
   174  	}
   175  	return u
   176  }
   177  
   178  func mustNewRequest(method, url string, body io.Reader) *http.Request {
   179  	req, err := http.NewRequest(method, url, body)
   180  	if err != nil {
   181  		panic(fmt.Sprintf("NewRequest(%q, %q, %p) err = %v", method, url, body, err))
   182  	}
   183  	return req
   184  }
   185  
   186  var dumpResTests = []struct {
   187  	res  *http.Response
   188  	body bool
   189  	want string
   190  }{
   191  	{
   192  		res: &http.Response{
   193  			Status:        "200 OK",
   194  			StatusCode:    200,
   195  			Proto:         "HTTP/1.1",
   196  			ProtoMajor:    1,
   197  			ProtoMinor:    1,
   198  			ContentLength: 50,
   199  			Header: http.Header{
   200  				"Foo": []string{"Bar"},
   201  			},
   202  			Body: ioutil.NopCloser(strings.NewReader("foo")), // shouldn't be used
   203  		},
   204  		body: false, // to verify we see 50, not empty or 3.
   205  		want: `HTTP/1.1 200 OK
   206  Content-Length: 50
   207  Foo: Bar`,
   208  	},
   209  
   210  	{
   211  		res: &http.Response{
   212  			Status:        "200 OK",
   213  			StatusCode:    200,
   214  			Proto:         "HTTP/1.1",
   215  			ProtoMajor:    1,
   216  			ProtoMinor:    1,
   217  			ContentLength: 3,
   218  			Body:          ioutil.NopCloser(strings.NewReader("foo")),
   219  		},
   220  		body: true,
   221  		want: `HTTP/1.1 200 OK
   222  Content-Length: 3
   223  
   224  foo`,
   225  	},
   226  
   227  	{
   228  		res: &http.Response{
   229  			Status:           "200 OK",
   230  			StatusCode:       200,
   231  			Proto:            "HTTP/1.1",
   232  			ProtoMajor:       1,
   233  			ProtoMinor:       1,
   234  			ContentLength:    -1,
   235  			Body:             ioutil.NopCloser(strings.NewReader("foo")),
   236  			TransferEncoding: []string{"chunked"},
   237  		},
   238  		body: true,
   239  		want: `HTTP/1.1 200 OK
   240  Transfer-Encoding: chunked
   241  
   242  3
   243  foo
   244  0`,
   245  	},
   246  }
   247  
   248  func TestDumpResponse(t *testing.T) {
   249  	for i, tt := range dumpResTests {
   250  		gotb, err := DumpResponse(tt.res, tt.body)
   251  		if err != nil {
   252  			t.Errorf("%d. DumpResponse = %v", i, err)
   253  			continue
   254  		}
   255  		got := string(gotb)
   256  		got = strings.TrimSpace(got)
   257  		got = strings.Replace(got, "\r", "", -1)
   258  
   259  		if got != tt.want {
   260  			t.Errorf("%d.\nDumpResponse got:\n%s\n\nWant:\n%s\n", i, got, tt.want)
   261  		}
   262  	}
   263  }