...

Source file src/net/http/transfer_test.go

Documentation: net/http

		 1  // Copyright 2012 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  	"crypto/rand"
		11  	"fmt"
		12  	"io"
		13  	"os"
		14  	"reflect"
		15  	"strings"
		16  	"testing"
		17  )
		18  
		19  func TestBodyReadBadTrailer(t *testing.T) {
		20  	b := &body{
		21  		src: strings.NewReader("foobar"),
		22  		hdr: true, // force reading the trailer
		23  		r:	 bufio.NewReader(strings.NewReader("")),
		24  	}
		25  	buf := make([]byte, 7)
		26  	n, err := b.Read(buf[:3])
		27  	got := string(buf[:n])
		28  	if got != "foo" || err != nil {
		29  		t.Fatalf(`first Read = %d (%q), %v; want 3 ("foo")`, n, got, err)
		30  	}
		31  
		32  	n, err = b.Read(buf[:])
		33  	got = string(buf[:n])
		34  	if got != "bar" || err != nil {
		35  		t.Fatalf(`second Read = %d (%q), %v; want 3 ("bar")`, n, got, err)
		36  	}
		37  
		38  	n, err = b.Read(buf[:])
		39  	got = string(buf[:n])
		40  	if err == nil {
		41  		t.Errorf("final Read was successful (%q), expected error from trailer read", got)
		42  	}
		43  }
		44  
		45  func TestFinalChunkedBodyReadEOF(t *testing.T) {
		46  	res, err := ReadResponse(bufio.NewReader(strings.NewReader(
		47  		"HTTP/1.1 200 OK\r\n"+
		48  			"Transfer-Encoding: chunked\r\n"+
		49  			"\r\n"+
		50  			"0a\r\n"+
		51  			"Body here\n\r\n"+
		52  			"09\r\n"+
		53  			"continued\r\n"+
		54  			"0\r\n"+
		55  			"\r\n")), nil)
		56  	if err != nil {
		57  		t.Fatal(err)
		58  	}
		59  	want := "Body here\ncontinued"
		60  	buf := make([]byte, len(want))
		61  	n, err := res.Body.Read(buf)
		62  	if n != len(want) || err != io.EOF {
		63  		t.Logf("body = %#v", res.Body)
		64  		t.Errorf("Read = %v, %v; want %d, EOF", n, err, len(want))
		65  	}
		66  	if string(buf) != want {
		67  		t.Errorf("buf = %q; want %q", buf, want)
		68  	}
		69  }
		70  
		71  func TestDetectInMemoryReaders(t *testing.T) {
		72  	pr, _ := io.Pipe()
		73  	tests := []struct {
		74  		r		io.Reader
		75  		want bool
		76  	}{
		77  		{pr, false},
		78  
		79  		{bytes.NewReader(nil), true},
		80  		{bytes.NewBuffer(nil), true},
		81  		{strings.NewReader(""), true},
		82  
		83  		{io.NopCloser(pr), false},
		84  
		85  		{io.NopCloser(bytes.NewReader(nil)), true},
		86  		{io.NopCloser(bytes.NewBuffer(nil)), true},
		87  		{io.NopCloser(strings.NewReader("")), true},
		88  	}
		89  	for i, tt := range tests {
		90  		got := isKnownInMemoryReader(tt.r)
		91  		if got != tt.want {
		92  			t.Errorf("%d: got = %v; want %v", i, got, tt.want)
		93  		}
		94  	}
		95  }
		96  
		97  type mockTransferWriter struct {
		98  	CalledReader io.Reader
		99  	WriteCalled	bool
	 100  }
	 101  
	 102  var _ io.ReaderFrom = (*mockTransferWriter)(nil)
	 103  
	 104  func (w *mockTransferWriter) ReadFrom(r io.Reader) (int64, error) {
	 105  	w.CalledReader = r
	 106  	return io.Copy(io.Discard, r)
	 107  }
	 108  
	 109  func (w *mockTransferWriter) Write(p []byte) (int, error) {
	 110  	w.WriteCalled = true
	 111  	return io.Discard.Write(p)
	 112  }
	 113  
	 114  func TestTransferWriterWriteBodyReaderTypes(t *testing.T) {
	 115  	fileType := reflect.TypeOf(&os.File{})
	 116  	bufferType := reflect.TypeOf(&bytes.Buffer{})
	 117  
	 118  	nBytes := int64(1 << 10)
	 119  	newFileFunc := func() (r io.Reader, done func(), err error) {
	 120  		f, err := os.CreateTemp("", "net-http-newfilefunc")
	 121  		if err != nil {
	 122  			return nil, nil, err
	 123  		}
	 124  
	 125  		// Write some bytes to the file to enable reading.
	 126  		if _, err := io.CopyN(f, rand.Reader, nBytes); err != nil {
	 127  			return nil, nil, fmt.Errorf("failed to write data to file: %v", err)
	 128  		}
	 129  		if _, err := f.Seek(0, 0); err != nil {
	 130  			return nil, nil, fmt.Errorf("failed to seek to front: %v", err)
	 131  		}
	 132  
	 133  		done = func() {
	 134  			f.Close()
	 135  			os.Remove(f.Name())
	 136  		}
	 137  
	 138  		return f, done, nil
	 139  	}
	 140  
	 141  	newBufferFunc := func() (io.Reader, func(), error) {
	 142  		return bytes.NewBuffer(make([]byte, nBytes)), func() {}, nil
	 143  	}
	 144  
	 145  	cases := []struct {
	 146  		name						 string
	 147  		bodyFunc				 func() (io.Reader, func(), error)
	 148  		method					 string
	 149  		contentLength		int64
	 150  		transferEncoding []string
	 151  		limitedReader		bool
	 152  		expectedReader	 reflect.Type
	 153  		expectedWrite		bool
	 154  	}{
	 155  		{
	 156  			name:					 "file, non-chunked, size set",
	 157  			bodyFunc:			 newFileFunc,
	 158  			method:				 "PUT",
	 159  			contentLength:	nBytes,
	 160  			limitedReader:	true,
	 161  			expectedReader: fileType,
	 162  		},
	 163  		{
	 164  			name:	 "file, non-chunked, size set, nopCloser wrapped",
	 165  			method: "PUT",
	 166  			bodyFunc: func() (io.Reader, func(), error) {
	 167  				r, cleanup, err := newFileFunc()
	 168  				return io.NopCloser(r), cleanup, err
	 169  			},
	 170  			contentLength:	nBytes,
	 171  			limitedReader:	true,
	 172  			expectedReader: fileType,
	 173  		},
	 174  		{
	 175  			name:					 "file, non-chunked, negative size",
	 176  			method:				 "PUT",
	 177  			bodyFunc:			 newFileFunc,
	 178  			contentLength:	-1,
	 179  			expectedReader: fileType,
	 180  		},
	 181  		{
	 182  			name:					 "file, non-chunked, CONNECT, negative size",
	 183  			method:				 "CONNECT",
	 184  			bodyFunc:			 newFileFunc,
	 185  			contentLength:	-1,
	 186  			expectedReader: fileType,
	 187  		},
	 188  		{
	 189  			name:						 "file, chunked",
	 190  			method:					 "PUT",
	 191  			bodyFunc:				 newFileFunc,
	 192  			transferEncoding: []string{"chunked"},
	 193  			expectedWrite:		true,
	 194  		},
	 195  		{
	 196  			name:					 "buffer, non-chunked, size set",
	 197  			bodyFunc:			 newBufferFunc,
	 198  			method:				 "PUT",
	 199  			contentLength:	nBytes,
	 200  			limitedReader:	true,
	 201  			expectedReader: bufferType,
	 202  		},
	 203  		{
	 204  			name:	 "buffer, non-chunked, size set, nopCloser wrapped",
	 205  			method: "PUT",
	 206  			bodyFunc: func() (io.Reader, func(), error) {
	 207  				r, cleanup, err := newBufferFunc()
	 208  				return io.NopCloser(r), cleanup, err
	 209  			},
	 210  			contentLength:	nBytes,
	 211  			limitedReader:	true,
	 212  			expectedReader: bufferType,
	 213  		},
	 214  		{
	 215  			name:					"buffer, non-chunked, negative size",
	 216  			method:				"PUT",
	 217  			bodyFunc:			newBufferFunc,
	 218  			contentLength: -1,
	 219  			expectedWrite: true,
	 220  		},
	 221  		{
	 222  			name:					"buffer, non-chunked, CONNECT, negative size",
	 223  			method:				"CONNECT",
	 224  			bodyFunc:			newBufferFunc,
	 225  			contentLength: -1,
	 226  			expectedWrite: true,
	 227  		},
	 228  		{
	 229  			name:						 "buffer, chunked",
	 230  			method:					 "PUT",
	 231  			bodyFunc:				 newBufferFunc,
	 232  			transferEncoding: []string{"chunked"},
	 233  			expectedWrite:		true,
	 234  		},
	 235  	}
	 236  
	 237  	for _, tc := range cases {
	 238  		t.Run(tc.name, func(t *testing.T) {
	 239  			body, cleanup, err := tc.bodyFunc()
	 240  			if err != nil {
	 241  				t.Fatal(err)
	 242  			}
	 243  			defer cleanup()
	 244  
	 245  			mw := &mockTransferWriter{}
	 246  			tw := &transferWriter{
	 247  				Body:						 body,
	 248  				ContentLength:		tc.contentLength,
	 249  				TransferEncoding: tc.transferEncoding,
	 250  			}
	 251  
	 252  			if err := tw.writeBody(mw); err != nil {
	 253  				t.Fatal(err)
	 254  			}
	 255  
	 256  			if tc.expectedReader != nil {
	 257  				if mw.CalledReader == nil {
	 258  					t.Fatal("did not call ReadFrom")
	 259  				}
	 260  
	 261  				var actualReader reflect.Type
	 262  				lr, ok := mw.CalledReader.(*io.LimitedReader)
	 263  				if ok && tc.limitedReader {
	 264  					actualReader = reflect.TypeOf(lr.R)
	 265  				} else {
	 266  					actualReader = reflect.TypeOf(mw.CalledReader)
	 267  				}
	 268  
	 269  				if tc.expectedReader != actualReader {
	 270  					t.Fatalf("got reader %T want %T", actualReader, tc.expectedReader)
	 271  				}
	 272  			}
	 273  
	 274  			if tc.expectedWrite && !mw.WriteCalled {
	 275  				t.Fatal("did not invoke Write")
	 276  			}
	 277  		})
	 278  	}
	 279  }
	 280  
	 281  func TestParseTransferEncoding(t *testing.T) {
	 282  	tests := []struct {
	 283  		hdr		 Header
	 284  		wantErr error
	 285  	}{
	 286  		{
	 287  			hdr:		 Header{"Transfer-Encoding": {"fugazi"}},
	 288  			wantErr: &unsupportedTEError{`unsupported transfer encoding: "fugazi"`},
	 289  		},
	 290  		{
	 291  			hdr:		 Header{"Transfer-Encoding": {"chunked, chunked", "identity", "chunked"}},
	 292  			wantErr: &unsupportedTEError{`too many transfer encodings: ["chunked, chunked" "identity" "chunked"]`},
	 293  		},
	 294  		{
	 295  			hdr:		 Header{"Transfer-Encoding": {""}},
	 296  			wantErr: &unsupportedTEError{`unsupported transfer encoding: ""`},
	 297  		},
	 298  		{
	 299  			hdr:		 Header{"Transfer-Encoding": {"chunked, identity"}},
	 300  			wantErr: &unsupportedTEError{`unsupported transfer encoding: "chunked, identity"`},
	 301  		},
	 302  		{
	 303  			hdr:		 Header{"Transfer-Encoding": {"chunked", "identity"}},
	 304  			wantErr: &unsupportedTEError{`too many transfer encodings: ["chunked" "identity"]`},
	 305  		},
	 306  		{
	 307  			hdr:		 Header{"Transfer-Encoding": {"\x0bchunked"}},
	 308  			wantErr: &unsupportedTEError{`unsupported transfer encoding: "\vchunked"`},
	 309  		},
	 310  		{
	 311  			hdr:		 Header{"Transfer-Encoding": {"chunked"}},
	 312  			wantErr: nil,
	 313  		},
	 314  	}
	 315  
	 316  	for i, tt := range tests {
	 317  		tr := &transferReader{
	 318  			Header:		 tt.hdr,
	 319  			ProtoMajor: 1,
	 320  			ProtoMinor: 1,
	 321  		}
	 322  		gotErr := tr.parseTransferEncoding()
	 323  		if !reflect.DeepEqual(gotErr, tt.wantErr) {
	 324  			t.Errorf("%d.\ngot error:\n%v\nwant error:\n%v\n\n", i, gotErr, tt.wantErr)
	 325  		}
	 326  	}
	 327  }
	 328  
	 329  // issue 39017 - disallow Content-Length values such as "+3"
	 330  func TestParseContentLength(t *testing.T) {
	 331  	tests := []struct {
	 332  		cl			string
	 333  		wantErr error
	 334  	}{
	 335  		{
	 336  			cl:			"3",
	 337  			wantErr: nil,
	 338  		},
	 339  		{
	 340  			cl:			"+3",
	 341  			wantErr: badStringError("bad Content-Length", "+3"),
	 342  		},
	 343  		{
	 344  			cl:			"-3",
	 345  			wantErr: badStringError("bad Content-Length", "-3"),
	 346  		},
	 347  		{
	 348  			// max int64, for safe conversion before returning
	 349  			cl:			"9223372036854775807",
	 350  			wantErr: nil,
	 351  		},
	 352  		{
	 353  			cl:			"9223372036854775808",
	 354  			wantErr: badStringError("bad Content-Length", "9223372036854775808"),
	 355  		},
	 356  	}
	 357  
	 358  	for _, tt := range tests {
	 359  		if _, gotErr := parseContentLength(tt.cl); !reflect.DeepEqual(gotErr, tt.wantErr) {
	 360  			t.Errorf("%q:\n\tgot=%v\n\twant=%v", tt.cl, gotErr, tt.wantErr)
	 361  		}
	 362  	}
	 363  }
	 364  

View as plain text