netramesh

Форк
0
/
readrequest_test.go 
475 строк · 9.7 Кб
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
	"fmt"
11
	"io"
12
	"io/ioutil"
13
	"net/url"
14
	"reflect"
15
	"strings"
16
	"testing"
17
)
18

19
type reqTest struct {
20
	Raw     string
21
	Req     *Request
22
	Body    string
23
	Trailer Header
24
	Error   string
25
}
26

27
var noError = ""
28
var noBodyStr = ""
29
var noTrailer Header = nil
30

31
var reqTests = []reqTest{
32
	// Baseline test; All Request fields included for template use
33
	{
34
		"GET http://www.techcrunch.com/ HTTP/1.1\r\n" +
35
			"Host: www.techcrunch.com\r\n" +
36
			"User-Agent: Fake\r\n" +
37
			"Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n" +
38
			"Accept-Language: en-us,en;q=0.5\r\n" +
39
			"Accept-Encoding: gzip,deflate\r\n" +
40
			"Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7\r\n" +
41
			"Keep-Alive: 300\r\n" +
42
			"Content-Length: 7\r\n" +
43
			"Proxy-Connection: keep-alive\r\n\r\n" +
44
			"abcdef\n???",
45

46
		&Request{
47
			Method: "GET",
48
			URL: &url.URL{
49
				Scheme: "http",
50
				Host:   "www.techcrunch.com",
51
				Path:   "/",
52
			},
53
			Proto:      "HTTP/1.1",
54
			ProtoMajor: 1,
55
			ProtoMinor: 1,
56
			Header: Header{
57
				"Accept":           {"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"},
58
				"Accept-Language":  {"en-us,en;q=0.5"},
59
				"Accept-Encoding":  {"gzip,deflate"},
60
				"Accept-Charset":   {"ISO-8859-1,utf-8;q=0.7,*;q=0.7"},
61
				"Keep-Alive":       {"300"},
62
				"Proxy-Connection": {"keep-alive"},
63
				"Content-Length":   {"7"},
64
				"User-Agent":       {"Fake"},
65
			},
66
			Close:         false,
67
			ContentLength: 7,
68
			Host:          "www.techcrunch.com",
69
			RequestURI:    "http://www.techcrunch.com/",
70
		},
71

72
		"abcdef\n",
73

74
		noTrailer,
75
		noError,
76
	},
77

78
	// GET request with no body (the normal case)
79
	{
80
		"GET / HTTP/1.1\r\n" +
81
			"Host: foo.com\r\n\r\n",
82

83
		&Request{
84
			Method: "GET",
85
			URL: &url.URL{
86
				Path: "/",
87
			},
88
			Proto:         "HTTP/1.1",
89
			ProtoMajor:    1,
90
			ProtoMinor:    1,
91
			Header:        Header{},
92
			Close:         false,
93
			ContentLength: 0,
94
			Host:          "foo.com",
95
			RequestURI:    "/",
96
		},
97

98
		noBodyStr,
99
		noTrailer,
100
		noError,
101
	},
102

103
	// Tests that we don't parse a path that looks like a
104
	// scheme-relative URI as a scheme-relative URI.
105
	{
106
		"GET //user@host/is/actually/a/path/ HTTP/1.1\r\n" +
107
			"Host: test\r\n\r\n",
108

109
		&Request{
110
			Method: "GET",
111
			URL: &url.URL{
112
				Path: "//user@host/is/actually/a/path/",
113
			},
114
			Proto:         "HTTP/1.1",
115
			ProtoMajor:    1,
116
			ProtoMinor:    1,
117
			Header:        Header{},
118
			Close:         false,
119
			ContentLength: 0,
120
			Host:          "test",
121
			RequestURI:    "//user@host/is/actually/a/path/",
122
		},
123

124
		noBodyStr,
125
		noTrailer,
126
		noError,
127
	},
128

129
	// Tests a bogus absolute-path on the Request-Line (RFC 7230 section 5.3.1)
130
	{
131
		"GET ../../../../etc/passwd HTTP/1.1\r\n" +
132
			"Host: test\r\n\r\n",
133
		nil,
134
		noBodyStr,
135
		noTrailer,
136
		"parse ../../../../etc/passwd: invalid URI for request",
137
	},
138

139
	// Tests missing URL:
140
	{
141
		"GET  HTTP/1.1\r\n" +
142
			"Host: test\r\n\r\n",
143
		nil,
144
		noBodyStr,
145
		noTrailer,
146
		"parse : empty url",
147
	},
148

149
	// Tests chunked body with trailer:
150
	{
151
		"POST / HTTP/1.1\r\n" +
152
			"Host: foo.com\r\n" +
153
			"Transfer-Encoding: chunked\r\n\r\n" +
154
			"3\r\nfoo\r\n" +
155
			"3\r\nbar\r\n" +
156
			"0\r\n" +
157
			"Trailer-Key: Trailer-Value\r\n" +
158
			"\r\n",
159
		&Request{
160
			Method: "POST",
161
			URL: &url.URL{
162
				Path: "/",
163
			},
164
			TransferEncoding: []string{"chunked"},
165
			Proto:            "HTTP/1.1",
166
			ProtoMajor:       1,
167
			ProtoMinor:       1,
168
			Header:           Header{},
169
			ContentLength:    -1,
170
			Host:             "foo.com",
171
			RequestURI:       "/",
172
		},
173

174
		"foobar",
175
		Header{
176
			"Trailer-Key": {"Trailer-Value"},
177
		},
178
		noError,
179
	},
180

181
	// Tests chunked body and a bogus Content-Length which should be deleted.
182
	{
183
		"POST / HTTP/1.1\r\n" +
184
			"Host: foo.com\r\n" +
185
			"Transfer-Encoding: chunked\r\n" +
186
			"Content-Length: 9999\r\n\r\n" + // to be removed.
187
			"3\r\nfoo\r\n" +
188
			"3\r\nbar\r\n" +
189
			"0\r\n" +
190
			"\r\n",
191
		&Request{
192
			Method: "POST",
193
			URL: &url.URL{
194
				Path: "/",
195
			},
196
			TransferEncoding: []string{"chunked"},
197
			Proto:            "HTTP/1.1",
198
			ProtoMajor:       1,
199
			ProtoMinor:       1,
200
			Header:           Header{},
201
			ContentLength:    -1,
202
			Host:             "foo.com",
203
			RequestURI:       "/",
204
		},
205

206
		"foobar",
207
		noTrailer,
208
		noError,
209
	},
210

211
	// CONNECT request with domain name:
212
	{
213
		"CONNECT www.google.com:443 HTTP/1.1\r\n\r\n",
214

215
		&Request{
216
			Method: "CONNECT",
217
			URL: &url.URL{
218
				Host: "www.google.com:443",
219
			},
220
			Proto:         "HTTP/1.1",
221
			ProtoMajor:    1,
222
			ProtoMinor:    1,
223
			Header:        Header{},
224
			Close:         false,
225
			ContentLength: 0,
226
			Host:          "www.google.com:443",
227
			RequestURI:    "www.google.com:443",
228
		},
229

230
		noBodyStr,
231
		noTrailer,
232
		noError,
233
	},
234

235
	// CONNECT request with IP address:
236
	{
237
		"CONNECT 127.0.0.1:6060 HTTP/1.1\r\n\r\n",
238

239
		&Request{
240
			Method: "CONNECT",
241
			URL: &url.URL{
242
				Host: "127.0.0.1:6060",
243
			},
244
			Proto:         "HTTP/1.1",
245
			ProtoMajor:    1,
246
			ProtoMinor:    1,
247
			Header:        Header{},
248
			Close:         false,
249
			ContentLength: 0,
250
			Host:          "127.0.0.1:6060",
251
			RequestURI:    "127.0.0.1:6060",
252
		},
253

254
		noBodyStr,
255
		noTrailer,
256
		noError,
257
	},
258

259
	// CONNECT request for RPC:
260
	{
261
		"CONNECT /_goRPC_ HTTP/1.1\r\n\r\n",
262

263
		&Request{
264
			Method: "CONNECT",
265
			URL: &url.URL{
266
				Path: "/_goRPC_",
267
			},
268
			Proto:         "HTTP/1.1",
269
			ProtoMajor:    1,
270
			ProtoMinor:    1,
271
			Header:        Header{},
272
			Close:         false,
273
			ContentLength: 0,
274
			Host:          "",
275
			RequestURI:    "/_goRPC_",
276
		},
277

278
		noBodyStr,
279
		noTrailer,
280
		noError,
281
	},
282

283
	// SSDP Notify request. golang.org/issue/3692
284
	{
285
		"NOTIFY * HTTP/1.1\r\nServer: foo\r\n\r\n",
286
		&Request{
287
			Method: "NOTIFY",
288
			URL: &url.URL{
289
				Path: "*",
290
			},
291
			Proto:      "HTTP/1.1",
292
			ProtoMajor: 1,
293
			ProtoMinor: 1,
294
			Header: Header{
295
				"Server": []string{"foo"},
296
			},
297
			Close:         false,
298
			ContentLength: 0,
299
			RequestURI:    "*",
300
		},
301

302
		noBodyStr,
303
		noTrailer,
304
		noError,
305
	},
306

307
	// OPTIONS request. Similar to golang.org/issue/3692
308
	{
309
		"OPTIONS * HTTP/1.1\r\nServer: foo\r\n\r\n",
310
		&Request{
311
			Method: "OPTIONS",
312
			URL: &url.URL{
313
				Path: "*",
314
			},
315
			Proto:      "HTTP/1.1",
316
			ProtoMajor: 1,
317
			ProtoMinor: 1,
318
			Header: Header{
319
				"Server": []string{"foo"},
320
			},
321
			Close:         false,
322
			ContentLength: 0,
323
			RequestURI:    "*",
324
		},
325

326
		noBodyStr,
327
		noTrailer,
328
		noError,
329
	},
330

331
	// Connection: close. golang.org/issue/8261
332
	{
333
		"GET / HTTP/1.1\r\nHost: issue8261.com\r\nConnection: close\r\n\r\n",
334
		&Request{
335
			Method: "GET",
336
			URL: &url.URL{
337
				Path: "/",
338
			},
339
			Header: Header{
340
				// This wasn't removed from Go 1.0 to
341
				// Go 1.3, so locking it in that we
342
				// keep this:
343
				"Connection": []string{"close"},
344
			},
345
			Host:       "issue8261.com",
346
			Proto:      "HTTP/1.1",
347
			ProtoMajor: 1,
348
			ProtoMinor: 1,
349
			Close:      true,
350
			RequestURI: "/",
351
		},
352

353
		noBodyStr,
354
		noTrailer,
355
		noError,
356
	},
357

358
	// HEAD with Content-Length 0. Make sure this is permitted,
359
	// since I think we used to send it.
360
	{
361
		"HEAD / HTTP/1.1\r\nHost: issue8261.com\r\nConnection: close\r\nContent-Length: 0\r\n\r\n",
362
		&Request{
363
			Method: "HEAD",
364
			URL: &url.URL{
365
				Path: "/",
366
			},
367
			Header: Header{
368
				"Connection":     []string{"close"},
369
				"Content-Length": []string{"0"},
370
			},
371
			Host:       "issue8261.com",
372
			Proto:      "HTTP/1.1",
373
			ProtoMajor: 1,
374
			ProtoMinor: 1,
375
			Close:      true,
376
			RequestURI: "/",
377
		},
378

379
		noBodyStr,
380
		noTrailer,
381
		noError,
382
	},
383

384
	// http2 client preface:
385
	{
386
		"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n",
387
		&Request{
388
			Method: "PRI",
389
			URL: &url.URL{
390
				Path: "*",
391
			},
392
			Header:        Header{},
393
			Proto:         "HTTP/2.0",
394
			ProtoMajor:    2,
395
			ProtoMinor:    0,
396
			RequestURI:    "*",
397
			ContentLength: -1,
398
			Close:         true,
399
		},
400
		noBodyStr,
401
		noTrailer,
402
		noError,
403
	},
404
}
405

406
func TestReadRequest(t *testing.T) {
407
	for i := range reqTests {
408
		tt := &reqTests[i]
409
		req, err := ReadRequest(bufio.NewReader(strings.NewReader(tt.Raw)))
410
		if err != nil {
411
			if err.Error() != tt.Error {
412
				t.Errorf("#%d: error %q, want error %q", i, err.Error(), tt.Error)
413
			}
414
			continue
415
		}
416
		rbody := req.Body
417
		req.Body = nil
418
		testName := fmt.Sprintf("Test %d (%q)", i, tt.Raw)
419
		diff(t, testName, req, tt.Req)
420
		var bout bytes.Buffer
421
		if rbody != nil {
422
			_, err := io.Copy(&bout, rbody)
423
			if err != nil {
424
				t.Fatalf("%s: copying body: %v", testName, err)
425
			}
426
			rbody.Close()
427
		}
428
		body := bout.String()
429
		if body != tt.Body {
430
			t.Errorf("%s: Body = %q want %q", testName, body, tt.Body)
431
		}
432
		if !reflect.DeepEqual(tt.Trailer, req.Trailer) {
433
			t.Errorf("%s: Trailers differ.\n got: %v\nwant: %v", testName, req.Trailer, tt.Trailer)
434
		}
435
	}
436
}
437

438
// reqBytes treats req as a request (with \n delimiters) and returns it with \r\n delimiters,
439
// ending in \r\n\r\n
440
func reqBytes(req string) []byte {
441
	return []byte(strings.Replace(strings.TrimSpace(req), "\n", "\r\n", -1) + "\r\n\r\n")
442
}
443

444
var badRequestTests = []struct {
445
	name string
446
	req  []byte
447
}{
448
	{"bad_connect_host", reqBytes("CONNECT []%20%48%54%54%50%2f%31%2e%31%0a%4d%79%48%65%61%64%65%72%3a%20%31%32%33%0a%0a HTTP/1.0")},
449
	{"smuggle_two_contentlen", reqBytes(`POST / HTTP/1.1
450
Content-Length: 3
451
Content-Length: 4
452

453
abc`)},
454
	{"smuggle_content_len_head", reqBytes(`HEAD / HTTP/1.1
455
Host: foo
456
Content-Length: 5`)},
457

458
	// golang.org/issue/22464
459
	{"leading_space_in_header", reqBytes(`HEAD / HTTP/1.1
460
 Host: foo
461
Content-Length: 5`)},
462
	{"leading_tab_in_header", reqBytes(`HEAD / HTTP/1.1
463
\tHost: foo
464
Content-Length: 5`)},
465
}
466

467
func TestReadRequest_Bad(t *testing.T) {
468
	for _, tt := range badRequestTests {
469
		got, err := ReadRequest(bufio.NewReader(bytes.NewReader(tt.req)))
470
		if err == nil {
471
			all, err := ioutil.ReadAll(got.Body)
472
			t.Errorf("%s: got unexpected request = %#v\n  Body = %q, %v", tt.name, got, all, err)
473
		}
474
	}
475
}
476

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.