Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(168)

Side by Side Diff: runtime/bin/http_parser.dart

Issue 9704009: Support parsing HTTP responses where the body length is not known (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Addressed review comments Created 8 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | tests/standalone/src/io/HttpParserTest.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 // Global constants. 5 // Global constants.
6 class _Const { 6 class _Const {
7 // Bytes for "HTTP/1.0". 7 // Bytes for "HTTP/1.0".
8 static final HTTP10 = const [72, 84, 84, 80, 47, 49, 46, 48]; 8 static final HTTP10 = const [72, 84, 84, 80, 47, 49, 46, 48];
9 // Bytes for "HTTP/1.1". 9 // Bytes for "HTTP/1.1".
10 static final HTTP11 = const [72, 84, 84, 80, 47, 49, 46, 49]; 10 static final HTTP11 = const [72, 84, 84, 80, 47, 49, 46, 49];
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
57 * RequestStart 57 * RequestStart
58 * UriReceived 58 * UriReceived
59 * HeaderReceived 59 * HeaderReceived
60 * HeadersComplete 60 * HeadersComplete
61 * DataReceived 61 * DataReceived
62 * DataEnd 62 * DataEnd
63 * are generated. 63 * are generated.
64 * Currently only HTTP requests with Content-Length header are supported. 64 * Currently only HTTP requests with Content-Length header are supported.
65 */ 65 */
66 class _HttpParser { 66 class _HttpParser {
67 _HttpParser() 67 _HttpParser() {
68 : _state = _State.START, 68 _reset();
69 _failure = false, 69 }
70 _headerField = new StringBuffer(),
71 _headerValue = new StringBuffer(),
72 _method_or_status_code = new StringBuffer(),
73 _uri_or_reason_phrase = new StringBuffer();
74 70
75 // From RFC 2616. 71 // From RFC 2616.
76 // generic-message = start-line 72 // generic-message = start-line
77 // *(message-header CRLF) 73 // *(message-header CRLF)
78 // CRLF 74 // CRLF
79 // [ message-body ] 75 // [ message-body ]
80 // start-line = Request-Line | Status-Line 76 // start-line = Request-Line | Status-Line
81 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF 77 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
82 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF 78 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
83 // message-header = field-name ":" [ field-value ] 79 // message-header = field-name ":" [ field-value ]
84 int writeList(List<int> buffer, int offset, int count) { 80 int writeList(List<int> buffer, int offset, int count) {
85 int index = offset; 81 int index = offset;
86 int lastIndex = offset + count; 82 int lastIndex = offset + count;
87 while ((index < lastIndex) && !_failure) { 83 while ((index < lastIndex) && !_failure) {
88 int byte = buffer[index]; 84 int byte = buffer[index];
89 switch (_state) { 85 switch (_state) {
90 case _State.START: 86 case _State.START:
91 _contentLength = 0;
92 _keepAlive = false;
93 _chunked = false;
94
95 if (byte == _Const.HTTP11[0]) { 87 if (byte == _Const.HTTP11[0]) {
96 // Start parsing HTTP method. 88 // Start parsing HTTP method.
97 _httpVersionIndex = 1; 89 _httpVersionIndex = 1;
98 _state = _State.METHOD_OR_HTTP_VERSION; 90 _state = _State.METHOD_OR_HTTP_VERSION;
99 } else { 91 } else {
100 // Start parsing method. 92 // Start parsing method.
101 _method_or_status_code.addCharCode(byte); 93 _method_or_status_code.addCharCode(byte);
102 _state = _State.REQUEST_LINE_METHOD; 94 _state = _State.REQUEST_LINE_METHOD;
103 } 95 }
104 break; 96 break;
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
157 _method_or_status_code.clear(); 149 _method_or_status_code.clear();
158 _uri_or_reason_phrase.clear(); 150 _uri_or_reason_phrase.clear();
159 _state = _State.HEADER_START; 151 _state = _State.HEADER_START;
160 break; 152 break;
161 153
162 case _State.RESPONSE_LINE_STATUS_CODE: 154 case _State.RESPONSE_LINE_STATUS_CODE:
163 if (byte == _CharCode.SP) { 155 if (byte == _CharCode.SP) {
164 _state = _State.RESPONSE_LINE_REASON_PHRASE; 156 _state = _State.RESPONSE_LINE_REASON_PHRASE;
165 } else { 157 } else {
166 if (byte < 0x30 && 0x39 < byte) { 158 if (byte < 0x30 && 0x39 < byte) {
159 if (error != null) error("Failed to parse HTTP");
167 _failure = true; 160 _failure = true;
168 } else { 161 } else {
169 _method_or_status_code.addCharCode(byte); 162 _method_or_status_code.addCharCode(byte);
170 } 163 }
171 } 164 }
172 break; 165 break;
173 166
174 case _State.RESPONSE_LINE_REASON_PHRASE: 167 case _State.RESPONSE_LINE_REASON_PHRASE:
175 if (byte == _CharCode.CR) { 168 if (byte == _CharCode.CR) {
176 _state = _State.RESPONSE_LINE_ENDING; 169 _state = _State.RESPONSE_LINE_ENDING;
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
272 if (_chunked) { 265 if (_chunked) {
273 _state = _State.CHUNK_SIZE; 266 _state = _State.CHUNK_SIZE;
274 _remainingContent = 0; 267 _remainingContent = 0;
275 } else if (_contentLength == 0) { 268 } else if (_contentLength == 0) {
276 if (dataEnd != null) dataEnd(); 269 if (dataEnd != null) dataEnd();
277 _state = _State.START; 270 _state = _State.START;
278 } else if (_contentLength > 0) { 271 } else if (_contentLength > 0) {
279 _remainingContent = _contentLength; 272 _remainingContent = _contentLength;
280 _state = _State.BODY; 273 _state = _State.BODY;
281 } else { 274 } else {
282 // TODO(sgjesse): Error handling. 275 // Neither chunked nor content length. End of body
276 // indicated by close.
277 _state = _State.BODY;
283 } 278 }
284 break; 279 break;
285 280
286 case _State.CHUNK_SIZE_STARTING_CR: 281 case _State.CHUNK_SIZE_STARTING_CR:
287 _expect(byte, _CharCode.CR); 282 _expect(byte, _CharCode.CR);
288 _state = _State.CHUNK_SIZE_STARTING_LF; 283 _state = _State.CHUNK_SIZE_STARTING_LF;
289 break; 284 break;
290 285
291 case _State.CHUNK_SIZE_STARTING_LF: 286 case _State.CHUNK_SIZE_STARTING_LF:
292 _expect(byte, _CharCode.LF); 287 _expect(byte, _CharCode.LF);
(...skipping 19 matching lines...) Expand all
312 break; 307 break;
313 308
314 case _State.CHUNKED_BODY_DONE_CR: 309 case _State.CHUNKED_BODY_DONE_CR:
315 _expect(byte, _CharCode.CR); 310 _expect(byte, _CharCode.CR);
316 _state = _State.CHUNKED_BODY_DONE_LF; 311 _state = _State.CHUNKED_BODY_DONE_LF;
317 break; 312 break;
318 313
319 case _State.CHUNKED_BODY_DONE_LF: 314 case _State.CHUNKED_BODY_DONE_LF:
320 _expect(byte, _CharCode.LF); 315 _expect(byte, _CharCode.LF);
321 if (dataEnd != null) dataEnd(); 316 if (dataEnd != null) dataEnd();
322 _state = _State.START; 317 _reset();
323 break; 318 break;
324 319
325 case _State.BODY: 320 case _State.BODY:
326 // The body is not handled one byte at the time but in blocks. 321 // The body is not handled one byte at the time but in blocks.
327 int dataAvailable = lastIndex - index; 322 int dataAvailable = lastIndex - index;
328 ByteArray data; 323 ByteArray data;
329 if (dataAvailable <= _remainingContent) { 324 if (_remainingContent == null || dataAvailable <= _remainingContent) {
330 data = new ByteArray(dataAvailable); 325 data = new ByteArray(dataAvailable);
331 data.setRange(0, dataAvailable, buffer, index); 326 data.setRange(0, dataAvailable, buffer, index);
332 } else { 327 } else {
333 data = new ByteArray(_remainingContent); 328 data = new ByteArray(_remainingContent);
334 data.setRange(0, _remainingContent, buffer, index); 329 data.setRange(0, _remainingContent, buffer, index);
335 } 330 }
336 331
337 if (dataReceived != null) dataReceived(data); 332 if (dataReceived != null) dataReceived(data);
338 _remainingContent -= data.length; 333 if (_remainingContent != null) {
334 _remainingContent -= data.length;
335 }
339 index += data.length; 336 index += data.length;
340 if (_remainingContent == 0) { 337 if (_remainingContent == 0) {
341 if (!_chunked) { 338 if (!_chunked) {
342 if (dataEnd != null) dataEnd(); 339 if (dataEnd != null) dataEnd();
343 _state = _State.START; 340 _reset();
344 } else { 341 } else {
345 _state = _State.CHUNK_SIZE_STARTING_CR; 342 _state = _State.CHUNK_SIZE_STARTING_CR;
346 } 343 }
347 } 344 }
348 345
349 // Hack - as we always do index++ below. 346 // Hack - as we always do index++ below.
350 index--; 347 index--;
351 break; 348 break;
352 349
353 default: 350 default:
354 // Should be unreachable. 351 // Should be unreachable.
355 assert(false); 352 assert(false);
356 } 353 }
357 354
358 // Move to the next byte. 355 // Move to the next byte.
359 index++; 356 index++;
360 } 357 }
361 358
362 // Return the number of bytes parsed. 359 // Return the number of bytes parsed.
363 return index - offset; 360 return index - offset;
364 } 361 }
365 362
363 int connectionClosed() {
364 if (!_chunked && _contentLength == -1) {
365 if (dataEnd != null) dataEnd();
366 } else {
367 if (error != null) {
368 error("Connection closed before full body was received");
369 }
370 }
371 }
372
366 int get contentLength() => _contentLength; 373 int get contentLength() => _contentLength;
367 bool get keepAlive() => _keepAlive; 374 bool get keepAlive() => _keepAlive;
368 375
376 _reset() {
377 _state = _State.START;
378 _failure = false;
379 _headerField = new StringBuffer();
380 _headerValue = new StringBuffer();
381 _method_or_status_code = new StringBuffer();
382 _uri_or_reason_phrase = new StringBuffer();
383
384 _contentLength = -1;
385 _keepAlive = false;
386 _chunked = false;
387 _remainingContent = null;
388 }
389
369 int _toLowerCase(int byte) { 390 int _toLowerCase(int byte) {
370 final int aCode = "A".charCodeAt(0); 391 final int aCode = "A".charCodeAt(0);
371 final int zCode = "Z".charCodeAt(0); 392 final int zCode = "Z".charCodeAt(0);
372 final int delta = "a".charCodeAt(0) - aCode; 393 final int delta = "a".charCodeAt(0) - aCode;
373 return (aCode <= byte && byte <= zCode) ? byte + delta : byte; 394 return (aCode <= byte && byte <= zCode) ? byte + delta : byte;
374 } 395 }
375 396
376 int _expect(int val1, int val2) { 397 int _expect(int val1, int val2) {
377 if (val1 != val2) { 398 if (val1 != val2) {
399 if (error != null) error("Failed to parse HTTP");
378 _failure = true; 400 _failure = true;
379 } 401 }
380 } 402 }
381 403
382 int _expectHexDigit(int byte) { 404 int _expectHexDigit(int byte) {
383 if (0x30 <= byte && byte <= 0x39) { 405 if (0x30 <= byte && byte <= 0x39) {
384 return byte - 0x30; // 0 - 9 406 return byte - 0x30; // 0 - 9
385 } else if (0x41 <= byte && byte <= 0x46) { 407 } else if (0x41 <= byte && byte <= 0x46) {
386 return byte - 0x41 + 10; // A - F 408 return byte - 0x41 + 10; // A - F
387 } else if (0x61 <= byte && byte <= 0x66) { 409 } else if (0x61 <= byte && byte <= 0x66) {
(...skipping 18 matching lines...) Expand all
406 428
407 int _remainingContent; 429 int _remainingContent;
408 430
409 // Callbacks. 431 // Callbacks.
410 Function requestStart; 432 Function requestStart;
411 Function responseStart; 433 Function responseStart;
412 Function headerReceived; 434 Function headerReceived;
413 Function headersComplete; 435 Function headersComplete;
414 Function dataReceived; 436 Function dataReceived;
415 Function dataEnd; 437 Function dataEnd;
438 Function error;
416 } 439 }
OLDNEW
« no previous file with comments | « no previous file | tests/standalone/src/io/HttpParserTest.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698