github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/src/net/http/response_test.go (about)

     1  // Copyright 2010 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 http
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"compress/gzip"
    11  	"crypto/rand"
    12  	"fmt"
    13  	"go/ast"
    14  	"io"
    15  	"io/ioutil"
    16  	"net/http/internal"
    17  	"net/url"
    18  	"reflect"
    19  	"regexp"
    20  	"strings"
    21  	"testing"
    22  )
    23  
    24  type respTest struct {
    25  	Raw  string
    26  	Resp Response
    27  	Body string
    28  }
    29  
    30  func dummyReq(method string) *Request {
    31  	return &Request{Method: method}
    32  }
    33  
    34  func dummyReq11(method string) *Request {
    35  	return &Request{Method: method, Proto: "HTTP/1.1", ProtoMajor: 1, ProtoMinor: 1}
    36  }
    37  
    38  var respTests = []respTest{
    39  	// Unchunked response without Content-Length.
    40  	{
    41  		"HTTP/1.0 200 OK\r\n" +
    42  			"Connection: close\r\n" +
    43  			"\r\n" +
    44  			"Body here\n",
    45  
    46  		Response{
    47  			Status:     "200 OK",
    48  			StatusCode: 200,
    49  			Proto:      "HTTP/1.0",
    50  			ProtoMajor: 1,
    51  			ProtoMinor: 0,
    52  			Request:    dummyReq("GET"),
    53  			Header: Header{
    54  				"Connection": {"close"}, // TODO(rsc): Delete?
    55  			},
    56  			Close:         true,
    57  			ContentLength: -1,
    58  		},
    59  
    60  		"Body here\n",
    61  	},
    62  
    63  	// Unchunked HTTP/1.1 response without Content-Length or
    64  	// Connection headers.
    65  	{
    66  		"HTTP/1.1 200 OK\r\n" +
    67  			"\r\n" +
    68  			"Body here\n",
    69  
    70  		Response{
    71  			Status:        "200 OK",
    72  			StatusCode:    200,
    73  			Proto:         "HTTP/1.1",
    74  			ProtoMajor:    1,
    75  			ProtoMinor:    1,
    76  			Header:        Header{},
    77  			Request:       dummyReq("GET"),
    78  			Close:         true,
    79  			ContentLength: -1,
    80  		},
    81  
    82  		"Body here\n",
    83  	},
    84  
    85  	// Unchunked HTTP/1.1 204 response without Content-Length.
    86  	{
    87  		"HTTP/1.1 204 No Content\r\n" +
    88  			"\r\n" +
    89  			"Body should not be read!\n",
    90  
    91  		Response{
    92  			Status:        "204 No Content",
    93  			StatusCode:    204,
    94  			Proto:         "HTTP/1.1",
    95  			ProtoMajor:    1,
    96  			ProtoMinor:    1,
    97  			Header:        Header{},
    98  			Request:       dummyReq("GET"),
    99  			Close:         false,
   100  			ContentLength: 0,
   101  		},
   102  
   103  		"",
   104  	},
   105  
   106  	// Unchunked response with Content-Length.
   107  	{
   108  		"HTTP/1.0 200 OK\r\n" +
   109  			"Content-Length: 10\r\n" +
   110  			"Connection: close\r\n" +
   111  			"\r\n" +
   112  			"Body here\n",
   113  
   114  		Response{
   115  			Status:     "200 OK",
   116  			StatusCode: 200,
   117  			Proto:      "HTTP/1.0",
   118  			ProtoMajor: 1,
   119  			ProtoMinor: 0,
   120  			Request:    dummyReq("GET"),
   121  			Header: Header{
   122  				"Connection":     {"close"},
   123  				"Content-Length": {"10"},
   124  			},
   125  			Close:         true,
   126  			ContentLength: 10,
   127  		},
   128  
   129  		"Body here\n",
   130  	},
   131  
   132  	// Chunked response without Content-Length.
   133  	{
   134  		"HTTP/1.1 200 OK\r\n" +
   135  			"Transfer-Encoding: chunked\r\n" +
   136  			"\r\n" +
   137  			"0a\r\n" +
   138  			"Body here\n\r\n" +
   139  			"09\r\n" +
   140  			"continued\r\n" +
   141  			"0\r\n" +
   142  			"\r\n",
   143  
   144  		Response{
   145  			Status:           "200 OK",
   146  			StatusCode:       200,
   147  			Proto:            "HTTP/1.1",
   148  			ProtoMajor:       1,
   149  			ProtoMinor:       1,
   150  			Request:          dummyReq("GET"),
   151  			Header:           Header{},
   152  			Close:            false,
   153  			ContentLength:    -1,
   154  			TransferEncoding: []string{"chunked"},
   155  		},
   156  
   157  		"Body here\ncontinued",
   158  	},
   159  
   160  	// Chunked response with Content-Length.
   161  	{
   162  		"HTTP/1.1 200 OK\r\n" +
   163  			"Transfer-Encoding: chunked\r\n" +
   164  			"Content-Length: 10\r\n" +
   165  			"\r\n" +
   166  			"0a\r\n" +
   167  			"Body here\n\r\n" +
   168  			"0\r\n" +
   169  			"\r\n",
   170  
   171  		Response{
   172  			Status:           "200 OK",
   173  			StatusCode:       200,
   174  			Proto:            "HTTP/1.1",
   175  			ProtoMajor:       1,
   176  			ProtoMinor:       1,
   177  			Request:          dummyReq("GET"),
   178  			Header:           Header{},
   179  			Close:            false,
   180  			ContentLength:    -1,
   181  			TransferEncoding: []string{"chunked"},
   182  		},
   183  
   184  		"Body here\n",
   185  	},
   186  
   187  	// Chunked response in response to a HEAD request
   188  	{
   189  		"HTTP/1.1 200 OK\r\n" +
   190  			"Transfer-Encoding: chunked\r\n" +
   191  			"\r\n",
   192  
   193  		Response{
   194  			Status:           "200 OK",
   195  			StatusCode:       200,
   196  			Proto:            "HTTP/1.1",
   197  			ProtoMajor:       1,
   198  			ProtoMinor:       1,
   199  			Request:          dummyReq("HEAD"),
   200  			Header:           Header{},
   201  			TransferEncoding: []string{"chunked"},
   202  			Close:            false,
   203  			ContentLength:    -1,
   204  		},
   205  
   206  		"",
   207  	},
   208  
   209  	// Content-Length in response to a HEAD request
   210  	{
   211  		"HTTP/1.0 200 OK\r\n" +
   212  			"Content-Length: 256\r\n" +
   213  			"\r\n",
   214  
   215  		Response{
   216  			Status:           "200 OK",
   217  			StatusCode:       200,
   218  			Proto:            "HTTP/1.0",
   219  			ProtoMajor:       1,
   220  			ProtoMinor:       0,
   221  			Request:          dummyReq("HEAD"),
   222  			Header:           Header{"Content-Length": {"256"}},
   223  			TransferEncoding: nil,
   224  			Close:            true,
   225  			ContentLength:    256,
   226  		},
   227  
   228  		"",
   229  	},
   230  
   231  	// Content-Length in response to a HEAD request with HTTP/1.1
   232  	{
   233  		"HTTP/1.1 200 OK\r\n" +
   234  			"Content-Length: 256\r\n" +
   235  			"\r\n",
   236  
   237  		Response{
   238  			Status:           "200 OK",
   239  			StatusCode:       200,
   240  			Proto:            "HTTP/1.1",
   241  			ProtoMajor:       1,
   242  			ProtoMinor:       1,
   243  			Request:          dummyReq("HEAD"),
   244  			Header:           Header{"Content-Length": {"256"}},
   245  			TransferEncoding: nil,
   246  			Close:            false,
   247  			ContentLength:    256,
   248  		},
   249  
   250  		"",
   251  	},
   252  
   253  	// No Content-Length or Chunked in response to a HEAD request
   254  	{
   255  		"HTTP/1.0 200 OK\r\n" +
   256  			"\r\n",
   257  
   258  		Response{
   259  			Status:           "200 OK",
   260  			StatusCode:       200,
   261  			Proto:            "HTTP/1.0",
   262  			ProtoMajor:       1,
   263  			ProtoMinor:       0,
   264  			Request:          dummyReq("HEAD"),
   265  			Header:           Header{},
   266  			TransferEncoding: nil,
   267  			Close:            true,
   268  			ContentLength:    -1,
   269  		},
   270  
   271  		"",
   272  	},
   273  
   274  	// explicit Content-Length of 0.
   275  	{
   276  		"HTTP/1.1 200 OK\r\n" +
   277  			"Content-Length: 0\r\n" +
   278  			"\r\n",
   279  
   280  		Response{
   281  			Status:     "200 OK",
   282  			StatusCode: 200,
   283  			Proto:      "HTTP/1.1",
   284  			ProtoMajor: 1,
   285  			ProtoMinor: 1,
   286  			Request:    dummyReq("GET"),
   287  			Header: Header{
   288  				"Content-Length": {"0"},
   289  			},
   290  			Close:         false,
   291  			ContentLength: 0,
   292  		},
   293  
   294  		"",
   295  	},
   296  
   297  	// Status line without a Reason-Phrase, but trailing space.
   298  	// (permitted by RFC 2616)
   299  	{
   300  		"HTTP/1.0 303 \r\n\r\n",
   301  		Response{
   302  			Status:        "303 ",
   303  			StatusCode:    303,
   304  			Proto:         "HTTP/1.0",
   305  			ProtoMajor:    1,
   306  			ProtoMinor:    0,
   307  			Request:       dummyReq("GET"),
   308  			Header:        Header{},
   309  			Close:         true,
   310  			ContentLength: -1,
   311  		},
   312  
   313  		"",
   314  	},
   315  
   316  	// Status line without a Reason-Phrase, and no trailing space.
   317  	// (not permitted by RFC 2616, but we'll accept it anyway)
   318  	{
   319  		"HTTP/1.0 303\r\n\r\n",
   320  		Response{
   321  			Status:        "303",
   322  			StatusCode:    303,
   323  			Proto:         "HTTP/1.0",
   324  			ProtoMajor:    1,
   325  			ProtoMinor:    0,
   326  			Request:       dummyReq("GET"),
   327  			Header:        Header{},
   328  			Close:         true,
   329  			ContentLength: -1,
   330  		},
   331  
   332  		"",
   333  	},
   334  
   335  	// golang.org/issue/4767: don't special-case multipart/byteranges responses
   336  	{
   337  		`HTTP/1.1 206 Partial Content
   338  Connection: close
   339  Content-Type: multipart/byteranges; boundary=18a75608c8f47cef
   340  
   341  some body`,
   342  		Response{
   343  			Status:     "206 Partial Content",
   344  			StatusCode: 206,
   345  			Proto:      "HTTP/1.1",
   346  			ProtoMajor: 1,
   347  			ProtoMinor: 1,
   348  			Request:    dummyReq("GET"),
   349  			Header: Header{
   350  				"Content-Type": []string{"multipart/byteranges; boundary=18a75608c8f47cef"},
   351  			},
   352  			Close:         true,
   353  			ContentLength: -1,
   354  		},
   355  
   356  		"some body",
   357  	},
   358  
   359  	// Unchunked response without Content-Length, Request is nil
   360  	{
   361  		"HTTP/1.0 200 OK\r\n" +
   362  			"Connection: close\r\n" +
   363  			"\r\n" +
   364  			"Body here\n",
   365  
   366  		Response{
   367  			Status:     "200 OK",
   368  			StatusCode: 200,
   369  			Proto:      "HTTP/1.0",
   370  			ProtoMajor: 1,
   371  			ProtoMinor: 0,
   372  			Header: Header{
   373  				"Connection": {"close"}, // TODO(rsc): Delete?
   374  			},
   375  			Close:         true,
   376  			ContentLength: -1,
   377  		},
   378  
   379  		"Body here\n",
   380  	},
   381  
   382  	// 206 Partial Content. golang.org/issue/8923
   383  	{
   384  		"HTTP/1.1 206 Partial Content\r\n" +
   385  			"Content-Type: text/plain; charset=utf-8\r\n" +
   386  			"Accept-Ranges: bytes\r\n" +
   387  			"Content-Range: bytes 0-5/1862\r\n" +
   388  			"Content-Length: 6\r\n\r\n" +
   389  			"foobar",
   390  
   391  		Response{
   392  			Status:     "206 Partial Content",
   393  			StatusCode: 206,
   394  			Proto:      "HTTP/1.1",
   395  			ProtoMajor: 1,
   396  			ProtoMinor: 1,
   397  			Request:    dummyReq("GET"),
   398  			Header: Header{
   399  				"Accept-Ranges":  []string{"bytes"},
   400  				"Content-Length": []string{"6"},
   401  				"Content-Type":   []string{"text/plain; charset=utf-8"},
   402  				"Content-Range":  []string{"bytes 0-5/1862"},
   403  			},
   404  			ContentLength: 6,
   405  		},
   406  
   407  		"foobar",
   408  	},
   409  
   410  	// Both keep-alive and close, on the same Connection line. (Issue 8840)
   411  	{
   412  		"HTTP/1.1 200 OK\r\n" +
   413  			"Content-Length: 256\r\n" +
   414  			"Connection: keep-alive, close\r\n" +
   415  			"\r\n",
   416  
   417  		Response{
   418  			Status:     "200 OK",
   419  			StatusCode: 200,
   420  			Proto:      "HTTP/1.1",
   421  			ProtoMajor: 1,
   422  			ProtoMinor: 1,
   423  			Request:    dummyReq("HEAD"),
   424  			Header: Header{
   425  				"Content-Length": {"256"},
   426  			},
   427  			TransferEncoding: nil,
   428  			Close:            true,
   429  			ContentLength:    256,
   430  		},
   431  
   432  		"",
   433  	},
   434  
   435  	// Both keep-alive and close, on different Connection lines. (Issue 8840)
   436  	{
   437  		"HTTP/1.1 200 OK\r\n" +
   438  			"Content-Length: 256\r\n" +
   439  			"Connection: keep-alive\r\n" +
   440  			"Connection: close\r\n" +
   441  			"\r\n",
   442  
   443  		Response{
   444  			Status:     "200 OK",
   445  			StatusCode: 200,
   446  			Proto:      "HTTP/1.1",
   447  			ProtoMajor: 1,
   448  			ProtoMinor: 1,
   449  			Request:    dummyReq("HEAD"),
   450  			Header: Header{
   451  				"Content-Length": {"256"},
   452  			},
   453  			TransferEncoding: nil,
   454  			Close:            true,
   455  			ContentLength:    256,
   456  		},
   457  
   458  		"",
   459  	},
   460  
   461  	// Issue 12785: HTTP/1.0 response with bogus (to be ignored) Transfer-Encoding.
   462  	// Without a Content-Length.
   463  	{
   464  		"HTTP/1.0 200 OK\r\n" +
   465  			"Transfer-Encoding: bogus\r\n" +
   466  			"\r\n" +
   467  			"Body here\n",
   468  
   469  		Response{
   470  			Status:        "200 OK",
   471  			StatusCode:    200,
   472  			Proto:         "HTTP/1.0",
   473  			ProtoMajor:    1,
   474  			ProtoMinor:    0,
   475  			Request:       dummyReq("GET"),
   476  			Header:        Header{},
   477  			Close:         true,
   478  			ContentLength: -1,
   479  		},
   480  
   481  		"Body here\n",
   482  	},
   483  
   484  	// Issue 12785: HTTP/1.0 response with bogus (to be ignored) Transfer-Encoding.
   485  	// With a Content-Length.
   486  	{
   487  		"HTTP/1.0 200 OK\r\n" +
   488  			"Transfer-Encoding: bogus\r\n" +
   489  			"Content-Length: 10\r\n" +
   490  			"\r\n" +
   491  			"Body here\n",
   492  
   493  		Response{
   494  			Status:     "200 OK",
   495  			StatusCode: 200,
   496  			Proto:      "HTTP/1.0",
   497  			ProtoMajor: 1,
   498  			ProtoMinor: 0,
   499  			Request:    dummyReq("GET"),
   500  			Header: Header{
   501  				"Content-Length": {"10"},
   502  			},
   503  			Close:         true,
   504  			ContentLength: 10,
   505  		},
   506  
   507  		"Body here\n",
   508  	},
   509  
   510  	{
   511  		"HTTP/1.1 200 OK\r\n" +
   512  			"Content-Encoding: gzip\r\n" +
   513  			"Content-Length: 23\r\n" +
   514  			"Connection: keep-alive\r\n" +
   515  			"Keep-Alive: timeout=7200\r\n\r\n" +
   516  			"\x1f\x8b\b\x00\x00\x00\x00\x00\x00\x00s\xf3\xf7\a\x00\xab'\xd4\x1a\x03\x00\x00\x00",
   517  		Response{
   518  			Status:     "200 OK",
   519  			StatusCode: 200,
   520  			Proto:      "HTTP/1.1",
   521  			ProtoMajor: 1,
   522  			ProtoMinor: 1,
   523  			Request:    dummyReq("GET"),
   524  			Header: Header{
   525  				"Content-Length":   {"23"},
   526  				"Content-Encoding": {"gzip"},
   527  				"Connection":       {"keep-alive"},
   528  				"Keep-Alive":       {"timeout=7200"},
   529  			},
   530  			Close:         false,
   531  			ContentLength: 23,
   532  		},
   533  		"\x1f\x8b\b\x00\x00\x00\x00\x00\x00\x00s\xf3\xf7\a\x00\xab'\xd4\x1a\x03\x00\x00\x00",
   534  	},
   535  
   536  	// Issue 19989: two spaces between HTTP version and status.
   537  	{
   538  		"HTTP/1.0  401 Unauthorized\r\n" +
   539  			"Content-type: text/html\r\n" +
   540  			"WWW-Authenticate: Basic realm=\"\"\r\n\r\n" +
   541  			"Your Authentication failed.\r\n",
   542  		Response{
   543  			Status:     "401 Unauthorized",
   544  			StatusCode: 401,
   545  			Proto:      "HTTP/1.0",
   546  			ProtoMajor: 1,
   547  			ProtoMinor: 0,
   548  			Request:    dummyReq("GET"),
   549  			Header: Header{
   550  				"Content-Type":     {"text/html"},
   551  				"Www-Authenticate": {`Basic realm=""`},
   552  			},
   553  			Close:         true,
   554  			ContentLength: -1,
   555  		},
   556  		"Your Authentication failed.\r\n",
   557  	},
   558  
   559  	// leading whitespace in the first header. golang.org/issue/22464
   560  	{
   561  		"HTTP/1.1 200 OK\r\n" +
   562  			" Content-type: text/html\r\n" +
   563  			"\tIgnore: foobar\r\n" +
   564  			"Foo: bar\r\n\r\n",
   565  		Response{
   566  			Status:     "200 OK",
   567  			StatusCode: 200,
   568  			Proto:      "HTTP/1.1",
   569  			ProtoMajor: 1,
   570  			ProtoMinor: 1,
   571  			Request:    dummyReq("GET"),
   572  			Header: Header{
   573  				"Foo": {"bar"},
   574  			},
   575  			Close:         true,
   576  			ContentLength: -1,
   577  		},
   578  		"",
   579  	},
   580  }
   581  
   582  // tests successful calls to ReadResponse, and inspects the returned Response.
   583  // For error cases, see TestReadResponseErrors below.
   584  func TestReadResponse(t *testing.T) {
   585  	for i, tt := range respTests {
   586  		resp, err := ReadResponse(bufio.NewReader(strings.NewReader(tt.Raw)), tt.Resp.Request)
   587  		if err != nil {
   588  			t.Errorf("#%d: %v", i, err)
   589  			continue
   590  		}
   591  		rbody := resp.Body
   592  		resp.Body = nil
   593  		diff(t, fmt.Sprintf("#%d Response", i), resp, &tt.Resp)
   594  		var bout bytes.Buffer
   595  		if rbody != nil {
   596  			_, err = io.Copy(&bout, rbody)
   597  			if err != nil {
   598  				t.Errorf("#%d: %v", i, err)
   599  				continue
   600  			}
   601  			rbody.Close()
   602  		}
   603  		body := bout.String()
   604  		if body != tt.Body {
   605  			t.Errorf("#%d: Body = %q want %q", i, body, tt.Body)
   606  		}
   607  	}
   608  }
   609  
   610  func TestWriteResponse(t *testing.T) {
   611  	for i, tt := range respTests {
   612  		resp, err := ReadResponse(bufio.NewReader(strings.NewReader(tt.Raw)), tt.Resp.Request)
   613  		if err != nil {
   614  			t.Errorf("#%d: %v", i, err)
   615  			continue
   616  		}
   617  		err = resp.Write(ioutil.Discard)
   618  		if err != nil {
   619  			t.Errorf("#%d: %v", i, err)
   620  			continue
   621  		}
   622  	}
   623  }
   624  
   625  var readResponseCloseInMiddleTests = []struct {
   626  	chunked, compressed bool
   627  }{
   628  	{false, false},
   629  	{true, false},
   630  	{true, true},
   631  }
   632  
   633  // TestReadResponseCloseInMiddle tests that closing a body after
   634  // reading only part of its contents advances the read to the end of
   635  // the request, right up until the next request.
   636  func TestReadResponseCloseInMiddle(t *testing.T) {
   637  	t.Parallel()
   638  	for _, test := range readResponseCloseInMiddleTests {
   639  		fatalf := func(format string, args ...interface{}) {
   640  			args = append([]interface{}{test.chunked, test.compressed}, args...)
   641  			t.Fatalf("on test chunked=%v, compressed=%v: "+format, args...)
   642  		}
   643  		checkErr := func(err error, msg string) {
   644  			if err == nil {
   645  				return
   646  			}
   647  			fatalf(msg+": %v", err)
   648  		}
   649  		var buf bytes.Buffer
   650  		buf.WriteString("HTTP/1.1 200 OK\r\n")
   651  		if test.chunked {
   652  			buf.WriteString("Transfer-Encoding: chunked\r\n")
   653  		} else {
   654  			buf.WriteString("Content-Length: 1000000\r\n")
   655  		}
   656  		var wr io.Writer = &buf
   657  		if test.chunked {
   658  			wr = internal.NewChunkedWriter(wr)
   659  		}
   660  		if test.compressed {
   661  			buf.WriteString("Content-Encoding: gzip\r\n")
   662  			wr = gzip.NewWriter(wr)
   663  		}
   664  		buf.WriteString("\r\n")
   665  
   666  		chunk := bytes.Repeat([]byte{'x'}, 1000)
   667  		for i := 0; i < 1000; i++ {
   668  			if test.compressed {
   669  				// Otherwise this compresses too well.
   670  				_, err := io.ReadFull(rand.Reader, chunk)
   671  				checkErr(err, "rand.Reader ReadFull")
   672  			}
   673  			wr.Write(chunk)
   674  		}
   675  		if test.compressed {
   676  			err := wr.(*gzip.Writer).Close()
   677  			checkErr(err, "compressor close")
   678  		}
   679  		if test.chunked {
   680  			buf.WriteString("0\r\n\r\n")
   681  		}
   682  		buf.WriteString("Next Request Here")
   683  
   684  		bufr := bufio.NewReader(&buf)
   685  		resp, err := ReadResponse(bufr, dummyReq("GET"))
   686  		checkErr(err, "ReadResponse")
   687  		expectedLength := int64(-1)
   688  		if !test.chunked {
   689  			expectedLength = 1000000
   690  		}
   691  		if resp.ContentLength != expectedLength {
   692  			fatalf("expected response length %d, got %d", expectedLength, resp.ContentLength)
   693  		}
   694  		if resp.Body == nil {
   695  			fatalf("nil body")
   696  		}
   697  		if test.compressed {
   698  			gzReader, err := gzip.NewReader(resp.Body)
   699  			checkErr(err, "gzip.NewReader")
   700  			resp.Body = &readerAndCloser{gzReader, resp.Body}
   701  		}
   702  
   703  		rbuf := make([]byte, 2500)
   704  		n, err := io.ReadFull(resp.Body, rbuf)
   705  		checkErr(err, "2500 byte ReadFull")
   706  		if n != 2500 {
   707  			fatalf("ReadFull only read %d bytes", n)
   708  		}
   709  		if test.compressed == false && !bytes.Equal(bytes.Repeat([]byte{'x'}, 2500), rbuf) {
   710  			fatalf("ReadFull didn't read 2500 'x'; got %q", string(rbuf))
   711  		}
   712  		resp.Body.Close()
   713  
   714  		rest, err := ioutil.ReadAll(bufr)
   715  		checkErr(err, "ReadAll on remainder")
   716  		if e, g := "Next Request Here", string(rest); e != g {
   717  			g = regexp.MustCompile(`(xx+)`).ReplaceAllStringFunc(g, func(match string) string {
   718  				return fmt.Sprintf("x(repeated x%d)", len(match))
   719  			})
   720  			fatalf("remainder = %q, expected %q", g, e)
   721  		}
   722  	}
   723  }
   724  
   725  func diff(t *testing.T, prefix string, have, want interface{}) {
   726  	hv := reflect.ValueOf(have).Elem()
   727  	wv := reflect.ValueOf(want).Elem()
   728  	if hv.Type() != wv.Type() {
   729  		t.Errorf("%s: type mismatch %v want %v", prefix, hv.Type(), wv.Type())
   730  	}
   731  	for i := 0; i < hv.NumField(); i++ {
   732  		name := hv.Type().Field(i).Name
   733  		if !ast.IsExported(name) {
   734  			continue
   735  		}
   736  		hf := hv.Field(i).Interface()
   737  		wf := wv.Field(i).Interface()
   738  		if !reflect.DeepEqual(hf, wf) {
   739  			t.Errorf("%s: %s = %v want %v", prefix, name, hf, wf)
   740  		}
   741  	}
   742  }
   743  
   744  type responseLocationTest struct {
   745  	location string // Response's Location header or ""
   746  	requrl   string // Response.Request.URL or ""
   747  	want     string
   748  	wantErr  error
   749  }
   750  
   751  var responseLocationTests = []responseLocationTest{
   752  	{"/foo", "http://bar.com/baz", "http://bar.com/foo", nil},
   753  	{"http://foo.com/", "http://bar.com/baz", "http://foo.com/", nil},
   754  	{"", "http://bar.com/baz", "", ErrNoLocation},
   755  	{"/bar", "", "/bar", nil},
   756  }
   757  
   758  func TestLocationResponse(t *testing.T) {
   759  	for i, tt := range responseLocationTests {
   760  		res := new(Response)
   761  		res.Header = make(Header)
   762  		res.Header.Set("Location", tt.location)
   763  		if tt.requrl != "" {
   764  			res.Request = &Request{}
   765  			var err error
   766  			res.Request.URL, err = url.Parse(tt.requrl)
   767  			if err != nil {
   768  				t.Fatalf("bad test URL %q: %v", tt.requrl, err)
   769  			}
   770  		}
   771  
   772  		got, err := res.Location()
   773  		if tt.wantErr != nil {
   774  			if err == nil {
   775  				t.Errorf("%d. err=nil; want %q", i, tt.wantErr)
   776  				continue
   777  			}
   778  			if g, e := err.Error(), tt.wantErr.Error(); g != e {
   779  				t.Errorf("%d. err=%q; want %q", i, g, e)
   780  				continue
   781  			}
   782  			continue
   783  		}
   784  		if err != nil {
   785  			t.Errorf("%d. err=%q", i, err)
   786  			continue
   787  		}
   788  		if g, e := got.String(), tt.want; g != e {
   789  			t.Errorf("%d. Location=%q; want %q", i, g, e)
   790  		}
   791  	}
   792  }
   793  
   794  func TestResponseStatusStutter(t *testing.T) {
   795  	r := &Response{
   796  		Status:     "123 some status",
   797  		StatusCode: 123,
   798  		ProtoMajor: 1,
   799  		ProtoMinor: 3,
   800  	}
   801  	var buf bytes.Buffer
   802  	r.Write(&buf)
   803  	if strings.Contains(buf.String(), "123 123") {
   804  		t.Errorf("stutter in status: %s", buf.String())
   805  	}
   806  }
   807  
   808  func TestResponseContentLengthShortBody(t *testing.T) {
   809  	const shortBody = "Short body, not 123 bytes."
   810  	br := bufio.NewReader(strings.NewReader("HTTP/1.1 200 OK\r\n" +
   811  		"Content-Length: 123\r\n" +
   812  		"\r\n" +
   813  		shortBody))
   814  	res, err := ReadResponse(br, &Request{Method: "GET"})
   815  	if err != nil {
   816  		t.Fatal(err)
   817  	}
   818  	if res.ContentLength != 123 {
   819  		t.Fatalf("Content-Length = %d; want 123", res.ContentLength)
   820  	}
   821  	var buf bytes.Buffer
   822  	n, err := io.Copy(&buf, res.Body)
   823  	if n != int64(len(shortBody)) {
   824  		t.Errorf("Copied %d bytes; want %d, len(%q)", n, len(shortBody), shortBody)
   825  	}
   826  	if buf.String() != shortBody {
   827  		t.Errorf("Read body %q; want %q", buf.String(), shortBody)
   828  	}
   829  	if err != io.ErrUnexpectedEOF {
   830  		t.Errorf("io.Copy error = %#v; want io.ErrUnexpectedEOF", err)
   831  	}
   832  }
   833  
   834  // Test various ReadResponse error cases. (also tests success cases, but mostly
   835  // it's about errors).  This does not test anything involving the bodies. Only
   836  // the return value from ReadResponse itself.
   837  func TestReadResponseErrors(t *testing.T) {
   838  	type testCase struct {
   839  		name    string // optional, defaults to in
   840  		in      string
   841  		header  Header
   842  		wantErr interface{} // nil, err value, or string substring
   843  	}
   844  
   845  	status := func(s string, wantErr interface{}) testCase {
   846  		if wantErr == true {
   847  			wantErr = "malformed HTTP status code"
   848  		}
   849  		return testCase{
   850  			name:    fmt.Sprintf("status %q", s),
   851  			in:      "HTTP/1.1 " + s + "\r\nFoo: bar\r\n\r\n",
   852  			wantErr: wantErr,
   853  		}
   854  	}
   855  
   856  	version := func(s string, wantErr interface{}) testCase {
   857  		if wantErr == true {
   858  			wantErr = "malformed HTTP version"
   859  		}
   860  		return testCase{
   861  			name:    fmt.Sprintf("version %q", s),
   862  			in:      s + " 200 OK\r\n\r\n",
   863  			wantErr: wantErr,
   864  		}
   865  	}
   866  
   867  	contentLength := func(status, body string, wantErr interface{}, header Header) testCase {
   868  		return testCase{
   869  			name:    fmt.Sprintf("status %q %q", status, body),
   870  			in:      fmt.Sprintf("HTTP/1.1 %s\r\n%s", status, body),
   871  			wantErr: wantErr,
   872  			header:  header,
   873  		}
   874  	}
   875  
   876  	errMultiCL := "message cannot contain multiple Content-Length headers"
   877  
   878  	tests := []testCase{
   879  		{"", "", nil, io.ErrUnexpectedEOF},
   880  		{"", "HTTP/1.1 301 Moved Permanently\r\nFoo: bar", nil, io.ErrUnexpectedEOF},
   881  		{"", "HTTP/1.1", nil, "malformed HTTP response"},
   882  		{"", "HTTP/2.0", nil, "malformed HTTP response"},
   883  		status("20X Unknown", true),
   884  		status("abcd Unknown", true),
   885  		status("二百/两百 OK", true),
   886  		status(" Unknown", true),
   887  		status("c8 OK", true),
   888  		status("0x12d Moved Permanently", true),
   889  		status("200 OK", nil),
   890  		status("000 OK", nil),
   891  		status("001 OK", nil),
   892  		status("404 NOTFOUND", nil),
   893  		status("20 OK", true),
   894  		status("00 OK", true),
   895  		status("-10 OK", true),
   896  		status("1000 OK", true),
   897  		status("999 Done", nil),
   898  		status("-1 OK", true),
   899  		status("-200 OK", true),
   900  		version("HTTP/1.2", nil),
   901  		version("HTTP/2.0", nil),
   902  		version("HTTP/1.100000000002", true),
   903  		version("HTTP/1.-1", true),
   904  		version("HTTP/A.B", true),
   905  		version("HTTP/1", true),
   906  		version("http/1.1", true),
   907  
   908  		contentLength("200 OK", "Content-Length: 10\r\nContent-Length: 7\r\n\r\nGopher hey\r\n", errMultiCL, nil),
   909  		contentLength("200 OK", "Content-Length: 7\r\nContent-Length: 7\r\n\r\nGophers\r\n", nil, Header{"Content-Length": {"7"}}),
   910  		contentLength("201 OK", "Content-Length: 0\r\nContent-Length: 7\r\n\r\nGophers\r\n", errMultiCL, nil),
   911  		contentLength("300 OK", "Content-Length: 0\r\nContent-Length: 0 \r\n\r\nGophers\r\n", nil, Header{"Content-Length": {"0"}}),
   912  		contentLength("200 OK", "Content-Length:\r\nContent-Length:\r\n\r\nGophers\r\n", nil, nil),
   913  		contentLength("206 OK", "Content-Length:\r\nContent-Length: 0 \r\nConnection: close\r\n\r\nGophers\r\n", errMultiCL, nil),
   914  
   915  		// multiple content-length headers for 204 and 304 should still be checked
   916  		contentLength("204 OK", "Content-Length: 7\r\nContent-Length: 8\r\n\r\n", errMultiCL, nil),
   917  		contentLength("204 OK", "Content-Length: 3\r\nContent-Length: 3\r\n\r\n", nil, nil),
   918  		contentLength("304 OK", "Content-Length: 880\r\nContent-Length: 1\r\n\r\n", errMultiCL, nil),
   919  		contentLength("304 OK", "Content-Length: 961\r\nContent-Length: 961\r\n\r\n", nil, nil),
   920  	}
   921  
   922  	for i, tt := range tests {
   923  		br := bufio.NewReader(strings.NewReader(tt.in))
   924  		_, rerr := ReadResponse(br, nil)
   925  		if err := matchErr(rerr, tt.wantErr); err != nil {
   926  			name := tt.name
   927  			if name == "" {
   928  				name = fmt.Sprintf("%d. input %q", i, tt.in)
   929  			}
   930  			t.Errorf("%s: %v", name, err)
   931  		}
   932  	}
   933  }
   934  
   935  // wantErr can be nil, an error value to match exactly, or type string to
   936  // match a substring.
   937  func matchErr(err error, wantErr interface{}) error {
   938  	if err == nil {
   939  		if wantErr == nil {
   940  			return nil
   941  		}
   942  		if sub, ok := wantErr.(string); ok {
   943  			return fmt.Errorf("unexpected success; want error with substring %q", sub)
   944  		}
   945  		return fmt.Errorf("unexpected success; want error %v", wantErr)
   946  	}
   947  	if wantErr == nil {
   948  		return fmt.Errorf("%v; want success", err)
   949  	}
   950  	if sub, ok := wantErr.(string); ok {
   951  		if strings.Contains(err.Error(), sub) {
   952  			return nil
   953  		}
   954  		return fmt.Errorf("error = %v; want an error with substring %q", err, sub)
   955  	}
   956  	if err == wantErr {
   957  		return nil
   958  	}
   959  	return fmt.Errorf("%v; want %v", err, wantErr)
   960  }
   961  
   962  func TestNeedsSniff(t *testing.T) {
   963  	// needsSniff returns true with an empty response.
   964  	r := &response{}
   965  	if got, want := r.needsSniff(), true; got != want {
   966  		t.Errorf("needsSniff = %t; want %t", got, want)
   967  	}
   968  	// needsSniff returns false when Content-Type = nil.
   969  	r.handlerHeader = Header{"Content-Type": nil}
   970  	if got, want := r.needsSniff(), false; got != want {
   971  		t.Errorf("needsSniff empty Content-Type = %t; want %t", got, want)
   972  	}
   973  }
   974  
   975  // A response should only write out single Connection: close header. Tests #19499.
   976  func TestResponseWritesOnlySingleConnectionClose(t *testing.T) {
   977  	const connectionCloseHeader = "Connection: close"
   978  
   979  	res, err := ReadResponse(bufio.NewReader(strings.NewReader("HTTP/1.0 200 OK\r\n\r\nAAAA")), nil)
   980  	if err != nil {
   981  		t.Fatalf("ReadResponse failed %v", err)
   982  	}
   983  
   984  	var buf1 bytes.Buffer
   985  	if err = res.Write(&buf1); err != nil {
   986  		t.Fatalf("Write failed %v", err)
   987  	}
   988  	if res, err = ReadResponse(bufio.NewReader(&buf1), nil); err != nil {
   989  		t.Fatalf("ReadResponse failed %v", err)
   990  	}
   991  
   992  	var buf2 bytes.Buffer
   993  	if err = res.Write(&buf2); err != nil {
   994  		t.Fatalf("Write failed %v", err)
   995  	}
   996  	if count := strings.Count(buf2.String(), connectionCloseHeader); count != 1 {
   997  		t.Errorf("Found %d %q header", count, connectionCloseHeader)
   998  	}
   999  }