Source file src/net/http/responsewrite_test.go

     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  	"io"
     9  	"strings"
    10  	"testing"
    11  )
    12  
    13  type respWriteTest struct {
    14  	Resp Response
    15  	Raw  string
    16  }
    17  
    18  func TestResponseWrite(t *testing.T) {
    19  	respWriteTests := []respWriteTest{
    20  		// HTTP/1.0, identity coding; no trailer
    21  		{
    22  			Response{
    23  				StatusCode:    503,
    24  				ProtoMajor:    1,
    25  				ProtoMinor:    0,
    26  				Request:       dummyReq("GET"),
    27  				Header:        Header{},
    28  				Body:          io.NopCloser(strings.NewReader("abcdef")),
    29  				ContentLength: 6,
    30  			},
    31  
    32  			"HTTP/1.0 503 Service Unavailable\r\n" +
    33  				"Content-Length: 6\r\n\r\n" +
    34  				"abcdef",
    35  		},
    36  		// Unchunked response without Content-Length.
    37  		{
    38  			Response{
    39  				StatusCode:    200,
    40  				ProtoMajor:    1,
    41  				ProtoMinor:    0,
    42  				Request:       dummyReq("GET"),
    43  				Header:        Header{},
    44  				Body:          io.NopCloser(strings.NewReader("abcdef")),
    45  				ContentLength: -1,
    46  			},
    47  			"HTTP/1.0 200 OK\r\n" +
    48  				"\r\n" +
    49  				"abcdef",
    50  		},
    51  		// HTTP/1.1 response with unknown length and Connection: close
    52  		{
    53  			Response{
    54  				StatusCode:    200,
    55  				ProtoMajor:    1,
    56  				ProtoMinor:    1,
    57  				Request:       dummyReq("GET"),
    58  				Header:        Header{},
    59  				Body:          io.NopCloser(strings.NewReader("abcdef")),
    60  				ContentLength: -1,
    61  				Close:         true,
    62  			},
    63  			"HTTP/1.1 200 OK\r\n" +
    64  				"Connection: close\r\n" +
    65  				"\r\n" +
    66  				"abcdef",
    67  		},
    68  		// HTTP/1.1 response with unknown length and not setting connection: close
    69  		{
    70  			Response{
    71  				StatusCode:    200,
    72  				ProtoMajor:    1,
    73  				ProtoMinor:    1,
    74  				Request:       dummyReq11("GET"),
    75  				Header:        Header{},
    76  				Body:          io.NopCloser(strings.NewReader("abcdef")),
    77  				ContentLength: -1,
    78  				Close:         false,
    79  			},
    80  			"HTTP/1.1 200 OK\r\n" +
    81  				"Connection: close\r\n" +
    82  				"\r\n" +
    83  				"abcdef",
    84  		},
    85  		// HTTP/1.1 response with unknown length and not setting connection: close, but
    86  		// setting chunked.
    87  		{
    88  			Response{
    89  				StatusCode:       200,
    90  				ProtoMajor:       1,
    91  				ProtoMinor:       1,
    92  				Request:          dummyReq11("GET"),
    93  				Header:           Header{},
    94  				Body:             io.NopCloser(strings.NewReader("abcdef")),
    95  				ContentLength:    -1,
    96  				TransferEncoding: []string{"chunked"},
    97  				Close:            false,
    98  			},
    99  			"HTTP/1.1 200 OK\r\n" +
   100  				"Transfer-Encoding: chunked\r\n\r\n" +
   101  				"6\r\nabcdef\r\n0\r\n\r\n",
   102  		},
   103  		// HTTP/1.1 response 0 content-length, and nil body
   104  		{
   105  			Response{
   106  				StatusCode:    200,
   107  				ProtoMajor:    1,
   108  				ProtoMinor:    1,
   109  				Request:       dummyReq11("GET"),
   110  				Header:        Header{},
   111  				Body:          nil,
   112  				ContentLength: 0,
   113  				Close:         false,
   114  			},
   115  			"HTTP/1.1 200 OK\r\n" +
   116  				"Content-Length: 0\r\n" +
   117  				"\r\n",
   118  		},
   119  		// HTTP/1.1 response 0 content-length, and non-nil empty body
   120  		{
   121  			Response{
   122  				StatusCode:    200,
   123  				ProtoMajor:    1,
   124  				ProtoMinor:    1,
   125  				Request:       dummyReq11("GET"),
   126  				Header:        Header{},
   127  				Body:          io.NopCloser(strings.NewReader("")),
   128  				ContentLength: 0,
   129  				Close:         false,
   130  			},
   131  			"HTTP/1.1 200 OK\r\n" +
   132  				"Content-Length: 0\r\n" +
   133  				"\r\n",
   134  		},
   135  		// HTTP/1.1 response 0 content-length, and non-nil non-empty body
   136  		{
   137  			Response{
   138  				StatusCode:    200,
   139  				ProtoMajor:    1,
   140  				ProtoMinor:    1,
   141  				Request:       dummyReq11("GET"),
   142  				Header:        Header{},
   143  				Body:          io.NopCloser(strings.NewReader("foo")),
   144  				ContentLength: 0,
   145  				Close:         false,
   146  			},
   147  			"HTTP/1.1 200 OK\r\n" +
   148  				"Connection: close\r\n" +
   149  				"\r\nfoo",
   150  		},
   151  		// HTTP/1.1, chunked coding; empty trailer; close
   152  		{
   153  			Response{
   154  				StatusCode:       200,
   155  				ProtoMajor:       1,
   156  				ProtoMinor:       1,
   157  				Request:          dummyReq("GET"),
   158  				Header:           Header{},
   159  				Body:             io.NopCloser(strings.NewReader("abcdef")),
   160  				ContentLength:    6,
   161  				TransferEncoding: []string{"chunked"},
   162  				Close:            true,
   163  			},
   164  
   165  			"HTTP/1.1 200 OK\r\n" +
   166  				"Connection: close\r\n" +
   167  				"Transfer-Encoding: chunked\r\n\r\n" +
   168  				"6\r\nabcdef\r\n0\r\n\r\n",
   169  		},
   170  
   171  		// Header value with a newline character (Issue 914).
   172  		// Also tests removal of leading and trailing whitespace.
   173  		{
   174  			Response{
   175  				StatusCode: 204,
   176  				ProtoMajor: 1,
   177  				ProtoMinor: 1,
   178  				Request:    dummyReq("GET"),
   179  				Header: Header{
   180  					"Foo": []string{" Bar\nBaz "},
   181  				},
   182  				Body:             nil,
   183  				ContentLength:    0,
   184  				TransferEncoding: []string{"chunked"},
   185  				Close:            true,
   186  			},
   187  
   188  			"HTTP/1.1 204 No Content\r\n" +
   189  				"Connection: close\r\n" +
   190  				"Foo: Bar Baz\r\n" +
   191  				"\r\n",
   192  		},
   193  
   194  		// Want a single Content-Length header. Fixing issue 8180 where
   195  		// there were two.
   196  		{
   197  			Response{
   198  				StatusCode:       StatusOK,
   199  				ProtoMajor:       1,
   200  				ProtoMinor:       1,
   201  				Request:          &Request{Method: "POST"},
   202  				Header:           Header{},
   203  				ContentLength:    0,
   204  				TransferEncoding: nil,
   205  				Body:             nil,
   206  			},
   207  			"HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n",
   208  		},
   209  
   210  		// When a response to a POST has Content-Length: -1, make sure we don't
   211  		// write the Content-Length as -1.
   212  		{
   213  			Response{
   214  				StatusCode:    StatusOK,
   215  				ProtoMajor:    1,
   216  				ProtoMinor:    1,
   217  				Request:       &Request{Method: "POST"},
   218  				Header:        Header{},
   219  				ContentLength: -1,
   220  				Body:          io.NopCloser(strings.NewReader("abcdef")),
   221  			},
   222  			"HTTP/1.1 200 OK\r\nConnection: close\r\n\r\nabcdef",
   223  		},
   224  
   225  		// Status code under 100 should be zero-padded to
   226  		// three digits.  Still bogus, but less bogus. (be
   227  		// consistent with generating three digits, since the
   228  		// Transport requires it)
   229  		{
   230  			Response{
   231  				StatusCode: 7,
   232  				Status:     "license to violate specs",
   233  				ProtoMajor: 1,
   234  				ProtoMinor: 0,
   235  				Request:    dummyReq("GET"),
   236  				Header:     Header{},
   237  				Body:       nil,
   238  			},
   239  
   240  			"HTTP/1.0 007 license to violate specs\r\nContent-Length: 0\r\n\r\n",
   241  		},
   242  
   243  		// No stutter.  Status code in 1xx range response should
   244  		// not include a Content-Length header.  See issue #16942.
   245  		{
   246  			Response{
   247  				StatusCode: 123,
   248  				Status:     "123 Sesame Street",
   249  				ProtoMajor: 1,
   250  				ProtoMinor: 0,
   251  				Request:    dummyReq("GET"),
   252  				Header:     Header{},
   253  				Body:       nil,
   254  			},
   255  
   256  			"HTTP/1.0 123 Sesame Street\r\n\r\n",
   257  		},
   258  
   259  		// Status code 204 (No content) response should not include a
   260  		// Content-Length header.  See issue #16942.
   261  		{
   262  			Response{
   263  				StatusCode: 204,
   264  				Status:     "No Content",
   265  				ProtoMajor: 1,
   266  				ProtoMinor: 0,
   267  				Request:    dummyReq("GET"),
   268  				Header:     Header{},
   269  				Body:       nil,
   270  			},
   271  
   272  			"HTTP/1.0 204 No Content\r\n\r\n",
   273  		},
   274  	}
   275  
   276  	for i := range respWriteTests {
   277  		tt := &respWriteTests[i]
   278  		var braw strings.Builder
   279  		err := tt.Resp.Write(&braw)
   280  		if err != nil {
   281  			t.Errorf("error writing #%d: %s", i, err)
   282  			continue
   283  		}
   284  		sraw := braw.String()
   285  		if sraw != tt.Raw {
   286  			t.Errorf("Test %d, expecting:\n%q\nGot:\n%q\n", i, tt.Raw, sraw)
   287  			continue
   288  		}
   289  	}
   290  }
   291  

View as plain text