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