OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 #source("../../../../runtime/bin/http_parser.dart"); | |
6 | |
7 class HttpParserTest { | |
8 static void runAllTests() { | |
9 testParseRequest(); | |
10 testParseResponse(); | |
11 testParseInvalidRequest(); | |
12 testParseInvalidResponse(); | |
13 } | |
14 | |
15 static void _testParseRequest(String request, | |
16 String expectedMethod, | |
17 String expectedUri, | |
18 [int expectedContentLength = -1, | |
19 int expectedBytesReceived = 0, | |
20 Map expectedHeaders = null, | |
21 bool chunked = false, | |
22 bool upgrade = false, | |
23 int unparsedLength = 0, | |
24 bool connectionClose = false, | |
25 String expectedVersion = "1.1"]) { | |
26 _HttpParser httpParser; | |
27 bool headersCompleteCalled; | |
28 bool dataEndCalled; | |
29 String method; | |
30 String uri; | |
31 String version; | |
32 Map headers; | |
33 int contentLength; | |
34 int bytesReceived; | |
35 | |
36 void reset() { | |
37 httpParser = new _HttpParser(); | |
38 httpParser.requestStart = (m, u, v) { method = m; uri = u; version = v; }; | |
39 httpParser.responseStart = (s, r, v) { Expect.fail("Expected request"); }; | |
40 httpParser.headerReceived = (f, v) { | |
41 Expect.isFalse(headersCompleteCalled); | |
42 headers[f] = v; | |
43 }; | |
44 httpParser.headersComplete = () { | |
45 Expect.isFalse(headersCompleteCalled); | |
46 if (!chunked) { | |
47 Expect.equals(expectedContentLength, httpParser.contentLength); | |
48 } else { | |
49 Expect.equals(-1, httpParser.contentLength); | |
50 } | |
51 if (expectedHeaders != null) { | |
52 expectedHeaders.forEach( | |
53 (String name, String value) => | |
54 Expect.equals(value, headers[name])); | |
55 } | |
56 Expect.equals(upgrade, httpParser.upgrade); | |
57 Expect.equals(connectionClose, !httpParser.persistentConnection); | |
58 headersCompleteCalled = true; | |
59 }; | |
60 httpParser.dataReceived = (List<int> data) { | |
61 Expect.isTrue(headersCompleteCalled); | |
62 bytesReceived += data.length; | |
63 }; | |
64 httpParser.dataEnd = (close) { | |
65 Expect.isFalse(close); | |
66 dataEndCalled = true; | |
67 }; | |
68 | |
69 headersCompleteCalled = false; | |
70 dataEndCalled = false; | |
71 method = null; | |
72 uri = null; | |
73 headers = new Map(); | |
74 bytesReceived = 0; | |
75 } | |
76 | |
77 void testWrite(List<int> requestData, [int chunkSize = -1]) { | |
78 if (chunkSize == -1) chunkSize = requestData.length; | |
79 reset(); | |
80 int written = 0; | |
81 int unparsed; | |
82 for (int pos = 0; pos < requestData.length; pos += chunkSize) { | |
83 int remaining = requestData.length - pos; | |
84 int writeLength = Math.min(chunkSize, remaining); | |
85 written += writeLength; | |
86 int parsed = httpParser.writeList(requestData, pos, writeLength); | |
87 unparsed = writeLength - parsed; | |
88 if (httpParser.upgrade) { | |
89 unparsed += requestData.length - written; | |
90 break; | |
91 } else { | |
92 Expect.equals(0, unparsed); | |
93 } | |
94 } | |
95 Expect.equals(expectedMethod, method); | |
96 Expect.equals(expectedUri, uri); | |
97 Expect.equals(expectedVersion, version); | |
98 Expect.isTrue(headersCompleteCalled); | |
99 Expect.equals(expectedBytesReceived, bytesReceived); | |
100 if (!upgrade) Expect.isTrue(dataEndCalled); | |
101 if (unparsedLength == 0) { | |
102 Expect.equals(0, unparsed); | |
103 } else { | |
104 Expect.equals(unparsedLength, unparsed); | |
105 } | |
106 } | |
107 | |
108 // Test parsing the request three times delivering the data in | |
109 // different chunks. | |
110 List<int> requestData = request.charCodes(); | |
111 testWrite(requestData); | |
112 testWrite(requestData, 10); | |
113 testWrite(requestData, 1); | |
114 } | |
115 | |
116 static void _testParseInvalidRequest(String request) { | |
117 _HttpParser httpParser; | |
118 bool errorCalled; | |
119 | |
120 void reset() { | |
121 httpParser = new _HttpParser(); | |
122 httpParser.responseStart = (s, r) { Expect.fail("Expected request"); }; | |
123 httpParser.error = (e) { | |
124 errorCalled = true; | |
125 }; | |
126 | |
127 errorCalled = false; | |
128 } | |
129 | |
130 void testWrite(List<int> requestData, [int chunkSize = -1]) { | |
131 if (chunkSize == -1) chunkSize = requestData.length; | |
132 reset(); | |
133 for (int pos = 0; pos < requestData.length; pos += chunkSize) { | |
134 int remaining = requestData.length - pos; | |
135 int writeLength = Math.min(chunkSize, remaining); | |
136 httpParser.writeList(requestData, pos, writeLength); | |
137 } | |
138 Expect.isTrue(errorCalled); | |
139 } | |
140 | |
141 // Test parsing the request three times delivering the data in | |
142 // different chunks. | |
143 List<int> requestData = request.charCodes(); | |
144 testWrite(requestData); | |
145 testWrite(requestData, 10); | |
146 testWrite(requestData, 1); | |
147 } | |
148 | |
149 static void _testParseResponse(String response, | |
150 int expectedStatusCode, | |
151 String expectedReasonPhrase, | |
152 [int expectedContentLength = -1, | |
153 int expectedBytesReceived = 0, | |
154 Map expectedHeaders = null, | |
155 bool chunked = false, | |
156 bool close = false, | |
157 String responseToMethod = null, | |
158 bool connectionClose = false, | |
159 bool upgrade = false, | |
160 int unparsedLength = 0, | |
161 String expectedVersion = "1.1"]) { | |
162 _HttpParser httpParser; | |
163 bool headersCompleteCalled; | |
164 bool dataEndCalled; | |
165 bool dataEndClose; | |
166 int statusCode; | |
167 String reasonPhrase; | |
168 String version; | |
169 Map headers; | |
170 int contentLength; | |
171 int bytesReceived; | |
172 | |
173 void reset() { | |
174 httpParser = new _HttpParser(); | |
175 if (responseToMethod != null) { | |
176 httpParser.responseToMethod = responseToMethod; | |
177 } | |
178 httpParser.requestStart = (m, u, v) => Expect.fail("Expected response"); | |
179 httpParser.responseStart = (s, r, v) { | |
180 statusCode = s; | |
181 reasonPhrase = r; | |
182 version = v; | |
183 }; | |
184 httpParser.headerReceived = (f, v) { | |
185 Expect.isFalse(headersCompleteCalled); | |
186 headers[f] = v; | |
187 }; | |
188 httpParser.headersComplete = () { | |
189 Expect.isFalse(headersCompleteCalled); | |
190 if (!chunked && !close) { | |
191 Expect.equals(expectedContentLength, httpParser.contentLength); | |
192 } else { | |
193 Expect.equals(-1, httpParser.contentLength); | |
194 } | |
195 if (expectedHeaders != null) { | |
196 expectedHeaders.forEach((String name, String value) { | |
197 Expect.equals(value, headers[name]); | |
198 }); | |
199 } | |
200 Expect.equals(upgrade, httpParser.upgrade); | |
201 headersCompleteCalled = true; | |
202 }; | |
203 httpParser.dataReceived = (List<int> data) { | |
204 Expect.isTrue(headersCompleteCalled); | |
205 bytesReceived += data.length; | |
206 }; | |
207 httpParser.dataEnd = (close) { | |
208 dataEndCalled = true; | |
209 dataEndClose = close; | |
210 }; | |
211 | |
212 headersCompleteCalled = false; | |
213 dataEndCalled = false; | |
214 dataEndClose = null; | |
215 statusCode = -1; | |
216 reasonPhrase = null; | |
217 headers = new Map(); | |
218 bytesReceived = 0; | |
219 } | |
220 | |
221 void testWrite(List<int> requestData, [int chunkSize = -1]) { | |
222 if (chunkSize == -1) chunkSize = requestData.length; | |
223 reset(); | |
224 int written = 0; | |
225 int unparsed; | |
226 for (int pos = 0; pos < requestData.length; pos += chunkSize) { | |
227 int remaining = requestData.length - pos; | |
228 int writeLength = Math.min(chunkSize, remaining); | |
229 written += writeLength; | |
230 int parsed = httpParser.writeList(requestData, pos, writeLength); | |
231 unparsed = writeLength - parsed; | |
232 if (httpParser.upgrade) { | |
233 unparsed += requestData.length - written; | |
234 break; | |
235 } else { | |
236 Expect.equals(0, unparsed); | |
237 } | |
238 } | |
239 if (close) httpParser.connectionClosed(); | |
240 Expect.equals(expectedVersion, version); | |
241 Expect.equals(expectedStatusCode, statusCode); | |
242 Expect.equals(expectedReasonPhrase, reasonPhrase); | |
243 Expect.isTrue(headersCompleteCalled); | |
244 Expect.equals(expectedBytesReceived, bytesReceived); | |
245 if (!upgrade) { | |
246 Expect.isTrue(dataEndCalled); | |
247 if (close) Expect.isTrue(dataEndClose); | |
248 Expect.equals(dataEndClose, connectionClose); | |
249 } | |
250 if (unparsedLength == 0) { | |
251 Expect.equals(0, unparsed); | |
252 } else { | |
253 Expect.equals(unparsedLength, unparsed); | |
254 } | |
255 } | |
256 | |
257 // Test parsing the request three times delivering the data in | |
258 // different chunks. | |
259 List<int> responseData = response.charCodes(); | |
260 testWrite(responseData); | |
261 testWrite(responseData, 10); | |
262 testWrite(responseData, 1); | |
263 } | |
264 | |
265 static void _testParseInvalidResponse(String response, [bool close = false]) { | |
266 _HttpParser httpParser; | |
267 bool errorCalled; | |
268 | |
269 void reset() { | |
270 httpParser = new _HttpParser(); | |
271 httpParser.requestStart = (m, u) => Expect.fail("Expected response"); | |
272 httpParser.error = (e) => errorCalled = true; | |
273 | |
274 errorCalled = false; | |
275 } | |
276 | |
277 void testWrite(List<int> requestData, [int chunkSize = -1]) { | |
278 if (chunkSize == -1) chunkSize = requestData.length; | |
279 reset(); | |
280 for (int pos = 0; pos < requestData.length; pos += chunkSize) { | |
281 int remaining = requestData.length - pos; | |
282 int writeLength = Math.min(chunkSize, remaining); | |
283 httpParser.writeList(requestData, pos, writeLength); | |
284 } | |
285 if (close) httpParser.connectionClosed(); | |
286 Expect.isTrue(errorCalled); | |
287 } | |
288 | |
289 // Test parsing the request three times delivering the data in | |
290 // different chunks. | |
291 List<int> responseData = response.charCodes(); | |
292 testWrite(responseData); | |
293 testWrite(responseData, 10); | |
294 testWrite(responseData, 1); | |
295 } | |
296 | |
297 static void testParseRequest() { | |
298 String request; | |
299 Map headers; | |
300 request = "GET / HTTP/1.1\r\n\r\n"; | |
301 _testParseRequest(request, "GET", "/"); | |
302 | |
303 request = "POST / HTTP/1.1\r\n\r\n"; | |
304 _testParseRequest(request, "POST", "/"); | |
305 | |
306 request = "GET /index.html HTTP/1.1\r\n\r\n"; | |
307 _testParseRequest(request, "GET", "/index.html"); | |
308 | |
309 request = "POST /index.html HTTP/1.1\r\n\r\n"; | |
310 _testParseRequest(request, "POST", "/index.html"); | |
311 | |
312 request = "H /index.html HTTP/1.1\r\n\r\n"; | |
313 _testParseRequest(request, "H", "/index.html"); | |
314 | |
315 request = "HT /index.html HTTP/1.1\r\n\r\n"; | |
316 _testParseRequest(request, "HT", "/index.html"); | |
317 | |
318 request = "HTT /index.html HTTP/1.1\r\n\r\n"; | |
319 _testParseRequest(request, "HTT", "/index.html"); | |
320 | |
321 request = "HTTP /index.html HTTP/1.1\r\n\r\n"; | |
322 _testParseRequest(request, "HTTP", "/index.html"); | |
323 | |
324 request = "GET / HTTP/1.0\r\n\r\n"; | |
325 _testParseRequest(request, "GET", "/", expectedVersion: "1.0", connectionClo
se: true); | |
326 | |
327 request = "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n"; | |
328 _testParseRequest(request, "GET", "/", expectedVersion: "1.0"); | |
329 | |
330 request = """ | |
331 POST /test HTTP/1.1\r | |
332 AAA: AAA\r | |
333 \r | |
334 """; | |
335 _testParseRequest(request, "POST", "/test"); | |
336 | |
337 request = """ | |
338 POST /test HTTP/1.1\r | |
339 \r | |
340 """; | |
341 _testParseRequest(request, "POST", "/test"); | |
342 | |
343 request = """ | |
344 POST /test HTTP/1.1\r | |
345 Header-A: AAA\r | |
346 X-Header-B: bbb\r | |
347 \r | |
348 """; | |
349 headers = new Map(); | |
350 headers["header-a"] = "AAA"; | |
351 headers["x-header-b"] = "bbb"; | |
352 _testParseRequest(request, "POST", "/test", expectedHeaders: headers); | |
353 | |
354 request = """ | |
355 POST /test HTTP/1.1\r | |
356 Empty-Header-1:\r | |
357 Empty-Header-2:\r | |
358 \r | |
359 \r | |
360 """; | |
361 headers = new Map(); | |
362 headers["empty-header-1"] = ""; | |
363 headers["empty-header-2"] = ""; | |
364 _testParseRequest(request, "POST", "/test", expectedHeaders: headers); | |
365 | |
366 request = """ | |
367 POST /test HTTP/1.1\r | |
368 Header-A: AAA\r | |
369 X-Header-B:\t \t bbb\r | |
370 \r | |
371 """; | |
372 headers = new Map(); | |
373 headers["header-a"] = "AAA"; | |
374 headers["x-header-b"] = "bbb"; | |
375 _testParseRequest(request, "POST", "/test", expectedHeaders: headers); | |
376 | |
377 request = """ | |
378 POST /test HTTP/1.1\r | |
379 Header-A: AA\r | |
380 A\r | |
381 X-Header-B: b\r | |
382 b\r | |
383 \t b\r | |
384 \r | |
385 """; | |
386 headers = new Map(); | |
387 headers["header-a"] = "AAA"; | |
388 headers["x-header-b"] = "bbb"; | |
389 _testParseRequest(request, "POST", "/test", expectedHeaders: headers); | |
390 | |
391 request = """ | |
392 POST /test HTTP/1.1\r | |
393 Content-Length: 10\r | |
394 \r | |
395 0123456789"""; | |
396 _testParseRequest(request, | |
397 "POST", | |
398 "/test", | |
399 expectedContentLength: 10, | |
400 expectedBytesReceived: 10); | |
401 | |
402 // Test connection close header. | |
403 request = "GET /test HTTP/1.1\r\nConnection: close\r\n\r\n"; | |
404 _testParseRequest(request, "GET", "/test", connectionClose: true); | |
405 | |
406 // Test chunked encoding. | |
407 request = """ | |
408 POST /test HTTP/1.1\r | |
409 Transfer-Encoding: chunked\r | |
410 \r | |
411 5\r | |
412 01234\r | |
413 5\r | |
414 56789\r | |
415 0\r\n\r\n"""; | |
416 _testParseRequest(request, | |
417 "POST", | |
418 "/test", | |
419 expectedContentLength: -1, | |
420 expectedBytesReceived: 10, | |
421 chunked: true); | |
422 | |
423 // Test mixing chunked encoding and content length (content length | |
424 // is ignored). | |
425 request = """ | |
426 POST /test HTTP/1.1\r | |
427 Content-Length: 7\r | |
428 Transfer-Encoding: chunked\r | |
429 \r | |
430 5\r | |
431 01234\r | |
432 5\r | |
433 56789\r | |
434 0\r\n\r\n"""; | |
435 _testParseRequest(request, | |
436 "POST", | |
437 "/test", | |
438 expectedContentLength: -1, | |
439 expectedBytesReceived: 10, | |
440 chunked: true); | |
441 | |
442 // Test mixing chunked encoding and content length (content length | |
443 // is ignored). | |
444 request = """ | |
445 POST /test HTTP/1.1\r | |
446 Transfer-Encoding: chunked\r | |
447 Content-Length: 3\r | |
448 \r | |
449 5\r | |
450 01234\r | |
451 5\r | |
452 56789\r | |
453 0\r\n\r\n"""; | |
454 _testParseRequest(request, | |
455 "POST", | |
456 "/test", | |
457 expectedContentLength: -1, | |
458 expectedBytesReceived: 10, | |
459 chunked: true); | |
460 | |
461 // Test upper and lower case hex digits in chunked encoding. | |
462 request = """ | |
463 POST /test HTTP/1.1\r | |
464 Transfer-Encoding: chunked\r | |
465 \r | |
466 1E\r | |
467 012345678901234567890123456789\r | |
468 1e\r | |
469 012345678901234567890123456789\r | |
470 0\r\n\r\n"""; | |
471 _testParseRequest(request, | |
472 "POST", | |
473 "/test", | |
474 expectedContentLength: -1, | |
475 expectedBytesReceived: 60, | |
476 chunked: true); | |
477 | |
478 // Test chunk extensions in chunked encoding. | |
479 request = """ | |
480 POST /test HTTP/1.1\r | |
481 Transfer-Encoding: chunked\r | |
482 \r | |
483 1E;xxx\r | |
484 012345678901234567890123456789\r | |
485 1E;yyy=zzz\r | |
486 012345678901234567890123456789\r | |
487 0\r\n\r\n"""; | |
488 _testParseRequest(request, | |
489 "POST", | |
490 "/test", | |
491 expectedContentLength: -1, | |
492 expectedBytesReceived: 60, | |
493 chunked: true); | |
494 | |
495 // Test HTTP upgrade. | |
496 request = """ | |
497 GET /irc HTTP/1.1\r | |
498 Upgrade: irc/1.2\r | |
499 Connection: Upgrade\r | |
500 \r\n\x01\x01\x01\x01\x01\x02\x02\x02\x02\xFF"""; | |
501 headers = new Map(); | |
502 headers["upgrade"] = "irc/1.2"; | |
503 _testParseRequest(request, | |
504 "GET", | |
505 "/irc", | |
506 expectedHeaders: headers, | |
507 upgrade: true, | |
508 unparsedLength: 10); | |
509 | |
510 // Test HTTP upgrade with protocol data. | |
511 request = """ | |
512 GET /irc HTTP/1.1\r | |
513 Upgrade: irc/1.2\r | |
514 Connection: Upgrade\r | |
515 \r\n"""; | |
516 headers = new Map(); | |
517 headers["upgrade"] = "irc/1.2"; | |
518 _testParseRequest(request, | |
519 "GET", | |
520 "/irc", | |
521 expectedHeaders: headers, | |
522 upgrade: true); | |
523 | |
524 // Test websocket upgrade. | |
525 request = """ | |
526 GET /chat HTTP/1.1\r | |
527 Host: server.example.com\r | |
528 Upgrade: websocket\r | |
529 Connection: Upgrade\r | |
530 Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r | |
531 Origin: http://example.com\r | |
532 Sec-WebSocket-Version: 13\r | |
533 \r\n"""; | |
534 headers = new Map(); | |
535 headers["host"] = "server.example.com"; | |
536 headers["upgrade"] = "websocket"; | |
537 headers["sec-websocket-key"] = "dGhlIHNhbXBsZSBub25jZQ=="; | |
538 headers["origin"] = "http://example.com"; | |
539 headers["sec-websocket-version"] = "13"; | |
540 _testParseRequest(request, | |
541 "GET", | |
542 "/chat", | |
543 expectedHeaders: headers, | |
544 upgrade: true); | |
545 | |
546 | |
547 // Test websocket upgrade with protocol data. NOTE: When using the | |
548 // WebSocket protocol this should never happen as the client | |
549 // should not send protocol data before processing the request | |
550 // part of the opening handshake. However the HTTP parser should | |
551 // still handle this. | |
552 request = """ | |
553 GET /chat HTTP/1.1\r | |
554 Host: server.example.com\r | |
555 Upgrade: websocket\r | |
556 Connection: Upgrade\r | |
557 Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r | |
558 Origin: http://example.com\r | |
559 Sec-WebSocket-Version: 13\r | |
560 \r\n0123456"""; | |
561 headers = new Map(); | |
562 headers["host"] = "server.example.com"; | |
563 headers["upgrade"] = "websocket"; | |
564 headers["sec-websocket-key"] = "dGhlIHNhbXBsZSBub25jZQ=="; | |
565 headers["origin"] = "http://example.com"; | |
566 headers["sec-websocket-version"] = "13"; | |
567 _testParseRequest(request, | |
568 "GET", | |
569 "/chat", | |
570 expectedHeaders: headers, | |
571 upgrade: true, | |
572 unparsedLength: 7); | |
573 } | |
574 | |
575 static void testParseResponse() { | |
576 String response; | |
577 Map headers; | |
578 response = "HTTP/1.1 100 Continue\r\nContent-Length: 0\r\n\r\n"; | |
579 _testParseResponse(response, 100, "Continue", expectedContentLength: 0); | |
580 | |
581 response = "HTTP/1.1 100 Continue\r\nContent-Length: 10\r\n\r\n"; | |
582 _testParseResponse(response, | |
583 100, | |
584 "Continue", | |
585 expectedContentLength: 10, | |
586 expectedBytesReceived: 0); | |
587 | |
588 response = "HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: Close\r\n\r\
n"; | |
589 _testParseResponse(response, | |
590 200, | |
591 "OK", | |
592 expectedContentLength: 0, | |
593 connectionClose: true); | |
594 | |
595 response = "HTTP/1.0 200 OK\r\nContent-Length: 0\r\n\r\n"; | |
596 _testParseResponse(response, | |
597 200, | |
598 "OK", | |
599 expectedContentLength: 0, | |
600 expectedVersion: "1.0", | |
601 connectionClose: true); | |
602 | |
603 response = "HTTP/1.0 200 OK\r\nContent-Length: 0\r\nConnection: Keep-Alive\r
\n\r\n"; | |
604 _testParseResponse(response, 200, "OK", expectedContentLength: 0, expectedVe
rsion: "1.0"); | |
605 | |
606 response = "HTTP/1.1 204 No Content\r\nContent-Length: 11\r\n\r\n"; | |
607 _testParseResponse(response, | |
608 204, | |
609 "No Content", | |
610 expectedContentLength: 11, | |
611 expectedBytesReceived: 0); | |
612 | |
613 response = "HTTP/1.1 304 Not Modified\r\nContent-Length: 12\r\n\r\n"; | |
614 _testParseResponse(response, | |
615 304, | |
616 "Not Modified", | |
617 expectedContentLength: 12, | |
618 expectedBytesReceived: 0); | |
619 | |
620 response = "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"; | |
621 _testParseResponse(response, 200, "OK", expectedContentLength: 0); | |
622 | |
623 response = "HTTP/1.1 404 Not found\r\nContent-Length: 0\r\n\r\n"; | |
624 _testParseResponse(response, 404, "Not found", expectedContentLength: 0); | |
625 | |
626 response = "HTTP/1.1 500 Server error\r\nContent-Length: 0\r\n\r\n"; | |
627 _testParseResponse(response, 500, "Server error", expectedContentLength: 0); | |
628 | |
629 // Test response to HEAD request. | |
630 response = """ | |
631 HTTP/1.1 200 OK\r | |
632 Content-Length: 20\r | |
633 Content-Type: text/html\r | |
634 \r\n"""; | |
635 headers = new Map(); | |
636 headers["content-length"] = "20"; | |
637 headers["content-type"] = "text/html"; | |
638 _testParseResponse(response, | |
639 200, | |
640 "OK", | |
641 responseToMethod: "HEAD", | |
642 expectedContentLength: 20, | |
643 expectedBytesReceived: 0, | |
644 expectedHeaders: headers); | |
645 | |
646 // Test content. | |
647 response = """ | |
648 HTTP/1.1 200 OK\r | |
649 Content-Length: 20\r | |
650 \r | |
651 01234567890123456789"""; | |
652 _testParseResponse(response, | |
653 200, | |
654 "OK", | |
655 expectedContentLength: 20, | |
656 expectedBytesReceived: 20); | |
657 | |
658 // Test upper and lower case hex digits in chunked encoding. | |
659 response = """ | |
660 HTTP/1.1 200 OK\r | |
661 Transfer-Encoding: chunked\r | |
662 \r | |
663 1A\r | |
664 01234567890123456789012345\r | |
665 1f\r | |
666 0123456789012345678901234567890\r | |
667 0\r\n\r\n"""; | |
668 _testParseResponse(response, | |
669 200, | |
670 "OK", | |
671 expectedContentLength: -1, | |
672 expectedBytesReceived: 57, | |
673 chunked: true); | |
674 | |
675 // Test connection close header. | |
676 response = """ | |
677 HTTP/1.1 200 OK\r | |
678 Content-Length: 0\r | |
679 Connection: close\r | |
680 \r\n"""; | |
681 _testParseResponse(response, | |
682 200, | |
683 "OK", | |
684 expectedContentLength: 0, | |
685 connectionClose: true); | |
686 | |
687 // Test HTTP response without any transfer length indications | |
688 // where closing the connections indicates end of body. | |
689 response = """ | |
690 HTTP/1.1 200 OK\r | |
691 \r | |
692 01234567890123456789012345 | |
693 0123456789012345678901234567890 | |
694 """; | |
695 _testParseResponse(response, | |
696 200, | |
697 "OK", | |
698 expectedContentLength: -1, | |
699 expectedBytesReceived: 59, | |
700 close: true, | |
701 connectionClose: true); | |
702 | |
703 // Test HTTP upgrade. | |
704 response = """ | |
705 HTTP/1.1 101 Switching Protocols\r | |
706 Upgrade: irc/1.2\r | |
707 Connection: Upgrade\r | |
708 \r\n"""; | |
709 headers = new Map(); | |
710 headers["upgrade"] = "irc/1.2"; | |
711 _testParseResponse(response, | |
712 101, | |
713 "Switching Protocols", | |
714 expectedHeaders: headers, | |
715 upgrade: true); | |
716 | |
717 // Test HTTP upgrade with protocol data. | |
718 response = """ | |
719 HTTP/1.1 101 Switching Protocols\r | |
720 Upgrade: irc/1.2\r | |
721 Connection: Upgrade\r | |
722 \r\n\x00\x10\x20\x30\x40\x50\x60\x70\x80\x90\xA0\xB0\xC0\xD0\xE0\xF0"""; | |
723 headers = new Map(); | |
724 headers["upgrade"] = "irc/1.2"; | |
725 _testParseResponse(response, | |
726 101, | |
727 "Switching Protocols", | |
728 expectedHeaders: headers, | |
729 upgrade: true, | |
730 unparsedLength: 16); | |
731 | |
732 // Test websocket upgrade. | |
733 response = """ | |
734 HTTP/1.1 101 Switching Protocols\r | |
735 Upgrade: websocket\r | |
736 Connection: Upgrade\r | |
737 Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r | |
738 \r\n"""; | |
739 headers = new Map(); | |
740 headers["upgrade"] = "websocket"; | |
741 headers["sec-websocket-accept"] = "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="; | |
742 _testParseResponse(response, | |
743 101, | |
744 "Switching Protocols", | |
745 expectedHeaders: headers, | |
746 upgrade: true); | |
747 | |
748 // Test websocket upgrade with protocol data. | |
749 response = """ | |
750 HTTP/1.1 101 Switching Protocols\r | |
751 Upgrade: websocket\r | |
752 Connection: Upgrade\r | |
753 Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r | |
754 \r\nABCD"""; | |
755 headers = new Map(); | |
756 headers["upgrade"] = "websocket"; | |
757 headers["sec-websocket-accept"] = "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="; | |
758 _testParseResponse(response, | |
759 101, | |
760 "Switching Protocols", | |
761 expectedHeaders: headers, | |
762 upgrade: true, | |
763 unparsedLength: 4); | |
764 } | |
765 | |
766 static void testParseInvalidRequest() { | |
767 String request; | |
768 request = "GET /\r\n\r\n"; | |
769 _testParseInvalidRequest(request); | |
770 | |
771 request = "GET / \r\n\r\n"; | |
772 _testParseInvalidRequest(request); | |
773 | |
774 request = "/ HTTP/1.1\r\n\r\n"; | |
775 _testParseInvalidRequest(request); | |
776 | |
777 request = "GET HTTP/1.1\r\n\r\n"; | |
778 _testParseInvalidRequest(request); | |
779 | |
780 request = " / HTTP/1.1\r\n\r\n"; | |
781 _testParseInvalidRequest(request); | |
782 | |
783 request = "@ / HTTP/1.1\r\n\r\n"; | |
784 _testParseInvalidRequest(request); | |
785 | |
786 request = "GET / TTP/1.1\r\n\r\n"; | |
787 _testParseInvalidRequest(request); | |
788 | |
789 request = "GET / HTTP/1.\r\n\r\n"; | |
790 _testParseInvalidRequest(request); | |
791 | |
792 request = "GET / HTTP/1.1\r\nKeep-Alive: False\r\nbadheader\r\n\r\n"; | |
793 _testParseInvalidRequest(request); | |
794 } | |
795 | |
796 static void testParseInvalidResponse() { | |
797 String response; | |
798 | |
799 response = "HTTP/1.1\r\nContent-Length: 0\r\n\r\n"; | |
800 _testParseInvalidResponse(response); | |
801 | |
802 response = "HTTP/1.1 \r\nContent-Length: 0\r\n\r\n"; | |
803 _testParseInvalidResponse(response); | |
804 | |
805 response = "HTTP/1.1 200\r\nContent-Length: 0\r\n\r\n"; | |
806 _testParseInvalidResponse(response); | |
807 | |
808 response = "HTTP/1.1 200 \r\nContent-Length: 0\r\n\r\n"; | |
809 _testParseInvalidResponse(response); | |
810 | |
811 response = "HTTP/1.1 OK\r\nContent-Length: 0\r\n\r\n"; | |
812 _testParseInvalidResponse(response); | |
813 | |
814 response = "200 OK\r\nContent-Length: 0\r\n\r\n"; | |
815 _testParseInvalidResponse(response); | |
816 | |
817 response = "HTTP/1. 200 OK\r\nContent-Length: 0\r\n\r\n"; | |
818 _testParseInvalidResponse(response); | |
819 | |
820 response = "HTTP/1.1 200 O\rK\r\nContent-Length: 0\r\n\r\n"; | |
821 _testParseInvalidResponse(response); | |
822 | |
823 response = "HTTP/1.1 000 OK\r\nContent-Length: 0\r\n\r\n"; | |
824 _testParseInvalidResponse(response); | |
825 | |
826 response = "HTTP/1.1 999 Server Error\r\nContent-Length: 0\r\n\r\n"; | |
827 _testParseInvalidResponse(response); | |
828 | |
829 response = "HTTP/1.1 200 OK\r\nContent-Length: x\r\n\r\n"; | |
830 _testParseInvalidResponse(response); | |
831 | |
832 response = "HTTP/1.1 200 OK\r\nbadheader\r\n\r\n"; | |
833 _testParseInvalidResponse(response); | |
834 | |
835 response = """ | |
836 HTTP/1.1 200 OK\r | |
837 Transfer-Encoding: chunked\r | |
838 \r | |
839 1A\r | |
840 01234567890123456789012345\r | |
841 1g\r | |
842 0123456789012345678901234567890\r | |
843 0\r\n\r\n"""; | |
844 _testParseInvalidResponse(response); | |
845 } | |
846 } | |
847 | |
848 | |
849 void main() { | |
850 HttpParserTest.runAllTests(); | |
851 } | |
OLD | NEW |