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

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

Issue 9956062: Refactor the close and error handling of HTTP connections (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Addressed review comments Created 8 years, 8 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
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 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
44 static final int RESPONSE_LINE_STATUS_CODE = 6; 44 static final int RESPONSE_LINE_STATUS_CODE = 6;
45 static final int RESPONSE_LINE_REASON_PHRASE = 7; 45 static final int RESPONSE_LINE_REASON_PHRASE = 7;
46 static final int RESPONSE_LINE_ENDING = 8; 46 static final int RESPONSE_LINE_ENDING = 8;
47 static final int HEADER_START = 9; 47 static final int HEADER_START = 9;
48 static final int HEADER_FIELD = 10; 48 static final int HEADER_FIELD = 10;
49 static final int HEADER_VALUE_START = 11; 49 static final int HEADER_VALUE_START = 11;
50 static final int HEADER_VALUE = 12; 50 static final int HEADER_VALUE = 12;
51 static final int HEADER_VALUE_FOLDING_OR_ENDING = 13; 51 static final int HEADER_VALUE_FOLDING_OR_ENDING = 13;
52 static final int HEADER_VALUE_FOLD_OR_END = 14; 52 static final int HEADER_VALUE_FOLD_OR_END = 14;
53 static final int HEADER_ENDING = 15; 53 static final int HEADER_ENDING = 15;
54
54 static final int CHUNK_SIZE_STARTING_CR = 16; 55 static final int CHUNK_SIZE_STARTING_CR = 16;
55 static final int CHUNK_SIZE_STARTING_LF = 17; 56 static final int CHUNK_SIZE_STARTING_LF = 17;
56 static final int CHUNK_SIZE = 18; 57 static final int CHUNK_SIZE = 18;
57 static final int CHUNK_SIZE_EXTENSION = 19; 58 static final int CHUNK_SIZE_EXTENSION = 19;
58 static final int CHUNK_SIZE_ENDING = 20; 59 static final int CHUNK_SIZE_ENDING = 20;
59 static final int CHUNKED_BODY_DONE_CR = 21; 60 static final int CHUNKED_BODY_DONE_CR = 21;
60 static final int CHUNKED_BODY_DONE_LF = 22; 61 static final int CHUNKED_BODY_DONE_LF = 22;
61 static final int BODY = 23; 62 static final int BODY = 23;
62 static final int FAILURE = 24; 63 static final int CLOSED = 24;
64 static final int FAILURE = 25;
65
66 static final int FIRST_BODY_STATE = CHUNK_SIZE_STARTING_CR;
63 } 67 }
64 68
65 69
66 // States of the HTTP parser state machine. 70 // States of the HTTP parser state machine.
67 class _MessageType { 71 class _MessageType {
68 static final int UNDETERMINED = 0; 72 static final int UNDETERMINED = 0;
69 static final int REQUEST = 1; 73 static final int REQUEST = 1;
70 static final int RESPONSE = 0; 74 static final int RESPONSE = 0;
71 } 75 }
72 76
(...skipping 29 matching lines...) Expand all
102 // CRLF 106 // CRLF
103 // [ message-body ] 107 // [ message-body ]
104 // start-line = Request-Line | Status-Line 108 // start-line = Request-Line | Status-Line
105 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF 109 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
106 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF 110 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
107 // message-header = field-name ":" [ field-value ] 111 // message-header = field-name ":" [ field-value ]
108 int writeList(List<int> buffer, int offset, int count) { 112 int writeList(List<int> buffer, int offset, int count) {
109 int index = offset; 113 int index = offset;
110 int lastIndex = offset + count; 114 int lastIndex = offset + count;
111 try { 115 try {
116 if (_state == _State.CLOSED) {
117 throw new HttpParserException("Data on closed connection");
118 }
112 while ((index < lastIndex) && _state != _State.FAILURE) { 119 while ((index < lastIndex) && _state != _State.FAILURE) {
113 int byte = buffer[index]; 120 int byte = buffer[index];
114 switch (_state) { 121 switch (_state) {
115 case _State.START: 122 case _State.START:
116 if (byte == _Const.HTTP11[0]) { 123 if (byte == _Const.HTTP11[0]) {
117 // Start parsing HTTP method. 124 // Start parsing HTTP method.
118 _httpVersionIndex = 1; 125 _httpVersionIndex = 1;
119 _state = _State.METHOD_OR_HTTP_VERSION; 126 _state = _State.METHOD_OR_HTTP_VERSION;
120 } else { 127 } else {
121 // Start parsing method. 128 // Start parsing method.
(...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after
280 case _State.HEADER_VALUE_FOLD_OR_END: 287 case _State.HEADER_VALUE_FOLD_OR_END:
281 if (byte == _CharCode.SP || byte == _CharCode.HT) { 288 if (byte == _CharCode.SP || byte == _CharCode.HT) {
282 _state = _State.HEADER_VALUE_START; 289 _state = _State.HEADER_VALUE_START;
283 } else { 290 } else {
284 String headerField = _headerField.toString(); 291 String headerField = _headerField.toString();
285 String headerValue =_headerValue.toString(); 292 String headerValue =_headerValue.toString();
286 // Ignore the Content-Length header if Transfer-Encoding 293 // Ignore the Content-Length header if Transfer-Encoding
287 // is chunked (RFC 2616 section 4.4) 294 // is chunked (RFC 2616 section 4.4)
288 if (headerField == "content-length" && !_chunked) { 295 if (headerField == "content-length" && !_chunked) {
289 _contentLength = Math.parseInt(headerValue); 296 _contentLength = Math.parseInt(headerValue);
290 } else if (headerField == "connection" && 297 } else if (headerField == "connection") {
291 headerValue == "keep-alive") { 298 if (headerValue == "keep-alive") {
292 _keepAlive = true; 299 _keepAlive = true;
300 } else if (headerValue == "close") {
301 _connectionClose = true;
302 }
293 } else if (headerField == "transfer-encoding" && 303 } else if (headerField == "transfer-encoding" &&
294 headerValue == "chunked") { 304 headerValue == "chunked") {
295 _chunked = true; 305 _chunked = true;
296 _contentLength = -1; 306 _contentLength = -1;
297 } 307 }
298 if (headerReceived != null) { 308 if (headerReceived != null) {
299 headerReceived(headerField, headerValue); 309 headerReceived(headerField, headerValue);
300 } 310 }
301 _headerField.clear(); 311 _headerField.clear();
302 _headerValue.clear(); 312 _headerValue.clear();
303 313
304 if (byte == _CharCode.CR) { 314 if (byte == _CharCode.CR) {
305 _state = _State.HEADER_ENDING; 315 _state = _State.HEADER_ENDING;
306 } else { 316 } else {
307 // Start of new header field. 317 // Start of new header field.
308 _headerField.addCharCode(_toLowerCase(byte)); 318 _headerField.addCharCode(_toLowerCase(byte));
309 _state = _State.HEADER_FIELD; 319 _state = _State.HEADER_FIELD;
310 } 320 }
311 } 321 }
312 break; 322 break;
313 323
314 case _State.HEADER_ENDING: 324 case _State.HEADER_ENDING:
315 _expect(byte, _CharCode.LF); 325 _expect(byte, _CharCode.LF);
316 if (headersComplete != null) headersComplete(); 326 if (headersComplete != null) headersComplete();
317
318 if (_chunked) { 327 if (_chunked) {
319 _state = _State.CHUNK_SIZE; 328 _state = _State.CHUNK_SIZE;
320 _remainingContent = 0; 329 _remainingContent = 0;
321 } else if (_contentLength == 0 || 330 } else if (_contentLength == 0 ||
322 (_messageType == _MessageType.REQUEST && 331 (_messageType == _MessageType.REQUEST &&
323 _contentLength == -1) || 332 _contentLength == -1) ||
324 (_messageType == _MessageType.RESPONSE && 333 (_messageType == _MessageType.RESPONSE &&
325 (_noMessageBody || _responseToMethod == "HEAD"))) { 334 (_noMessageBody || _responseToMethod == "HEAD"))) {
326 // If there is no message body get ready to process the 335 // If there is no message body get ready to process the
327 // next request. 336 // next request.
328 if (dataEnd != null) dataEnd(); 337 _bodyEnd();
329 _state = _State.START; 338 _reset();
330 } else if (_contentLength > 0) { 339 } else if (_contentLength > 0) {
331 _remainingContent = _contentLength; 340 _remainingContent = _contentLength;
332 _state = _State.BODY; 341 _state = _State.BODY;
333 } else { 342 } else {
334 // Neither chunked nor content length. End of body 343 // Neither chunked nor content length. End of body
335 // indicated by close. 344 // indicated by close.
336 _state = _State.BODY; 345 _state = _State.BODY;
337 } 346 }
338 break; 347 break;
339 348
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
373 } 382 }
374 break; 383 break;
375 384
376 case _State.CHUNKED_BODY_DONE_CR: 385 case _State.CHUNKED_BODY_DONE_CR:
377 _expect(byte, _CharCode.CR); 386 _expect(byte, _CharCode.CR);
378 _state = _State.CHUNKED_BODY_DONE_LF; 387 _state = _State.CHUNKED_BODY_DONE_LF;
379 break; 388 break;
380 389
381 case _State.CHUNKED_BODY_DONE_LF: 390 case _State.CHUNKED_BODY_DONE_LF:
382 _expect(byte, _CharCode.LF); 391 _expect(byte, _CharCode.LF);
383 if (dataEnd != null) dataEnd(); 392 _bodyEnd();
384 _reset(); 393 _reset();
385 break; 394 break;
386 395
387 case _State.BODY: 396 case _State.BODY:
388 // The body is not handled one byte at the time but in blocks. 397 // The body is not handled one byte at the time but in blocks.
389 int dataAvailable = lastIndex - index; 398 int dataAvailable = lastIndex - index;
390 ByteArray data; 399 ByteArray data;
391 if (_remainingContent == null || 400 if (_remainingContent == null ||
392 dataAvailable <= _remainingContent) { 401 dataAvailable <= _remainingContent) {
393 data = new ByteArray(dataAvailable); 402 data = new ByteArray(dataAvailable);
394 data.setRange(0, dataAvailable, buffer, index); 403 data.setRange(0, dataAvailable, buffer, index);
395 } else { 404 } else {
396 data = new ByteArray(_remainingContent); 405 data = new ByteArray(_remainingContent);
397 data.setRange(0, _remainingContent, buffer, index); 406 data.setRange(0, _remainingContent, buffer, index);
398 } 407 }
399 408
400 if (dataReceived != null) dataReceived(data); 409 if (dataReceived != null) dataReceived(data);
401 if (_remainingContent != null) { 410 if (_remainingContent != null) {
402 _remainingContent -= data.length; 411 _remainingContent -= data.length;
403 } 412 }
404 index += data.length; 413 index += data.length;
405 if (_remainingContent == 0) { 414 if (_remainingContent == 0) {
406 if (!_chunked) { 415 if (!_chunked) {
407 if (dataEnd != null) dataEnd(); 416 _bodyEnd();
408 _reset(); 417 _reset();
409 } else { 418 } else {
410 _state = _State.CHUNK_SIZE_STARTING_CR; 419 _state = _State.CHUNK_SIZE_STARTING_CR;
411 } 420 }
412 } 421 }
413 422
414 // Hack - as we always do index++ below. 423 // Hack - as we always do index++ below.
415 index--; 424 index--;
416 break; 425 break;
417 426
(...skipping 20 matching lines...) Expand all
438 } else { 447 } else {
439 throw e; 448 throw e;
440 } 449 }
441 } 450 }
442 451
443 // Return the number of bytes parsed. 452 // Return the number of bytes parsed.
444 return index - offset; 453 return index - offset;
445 } 454 }
446 455
447 int connectionClosed() { 456 int connectionClosed() {
448 if (!_chunked && _contentLength == -1) { 457 if (_state < _State.FIRST_BODY_STATE) {
449 if (dataEnd != null) dataEnd();
450 } else {
451 // Report the error through the error callback if any. Otherwise 458 // Report the error through the error callback if any. Otherwise
452 // throw the error. 459 // throw the error.
453 var e = new HttpParserException( 460 var e = new HttpParserException(
461 "Connection closed before full header was received");
462 if (error != null) {
463 error(e);
Anders Johnsen 2012/04/03 07:20:16 We should return after 'error(e)' so we don't hit
Søren Gjesse 2012/04/10 13:10:08 Done.
464 } else {
465 throw e;
466 }
467 }
468
469 if (!_chunked && _contentLength == -1) {
470 if (_state != _State.START) {
471 if (dataEnd != null) dataEnd(true);
472 }
473 _state = _State.CLOSED;
474 } else {
475 _state = _State.FAILURE;
476 // Report the error through the error callback if any. Otherwise
477 // throw the error.
478 var e = new HttpParserException(
454 "Connection closed before full body was received"); 479 "Connection closed before full body was received");
455 if (error != null) { 480 if (error != null) {
456 error(e); 481 error(e);
Anders Johnsen 2012/04/03 07:20:16 Maybe return here as well, just for good measure.
Søren Gjesse 2012/04/10 13:10:08 Done.
457 } else { 482 } else {
458 throw e; 483 throw e;
459 } 484 }
460 } 485 }
461 } 486 }
462 487
463 int get messageType() => _messageType; 488 int get messageType() => _messageType;
464 int get contentLength() => _contentLength; 489 int get contentLength() => _contentLength;
465 bool get keepAlive() => _keepAlive; 490 bool get keepAlive() => _keepAlive;
466 491
467 void set responseToMethod(String method) => _responseToMethod = method; 492 void set responseToMethod(String method) => _responseToMethod = method;
468 493
494 bool get isIdle() => _state == _State.START;
495
496 void _bodyEnd() {
497 if (dataEnd != null) {
498 dataEnd(_messageType == _MessageType.RESPONSE && _connectionClose);
499 }
500 }
501
469 _reset() { 502 _reset() {
470 _state = _State.START; 503 _state = _State.START;
471 _messageType = _MessageType.UNDETERMINED; 504 _messageType = _MessageType.UNDETERMINED;
472 _headerField = new StringBuffer(); 505 _headerField = new StringBuffer();
473 _headerValue = new StringBuffer(); 506 _headerValue = new StringBuffer();
474 _method_or_status_code = new StringBuffer(); 507 _method_or_status_code = new StringBuffer();
475 _uri_or_reason_phrase = new StringBuffer(); 508 _uri_or_reason_phrase = new StringBuffer();
476 509
477 _contentLength = -1; 510 _contentLength = -1;
478 _keepAlive = false; 511 _keepAlive = false;
512 _connectionClose = false;
479 _chunked = false; 513 _chunked = false;
480 514
481 _noMessageBody = false; 515 _noMessageBody = false;
482 _responseToMethod = null; 516 _responseToMethod = null;
483 _remainingContent = null; 517 _remainingContent = null;
484 } 518 }
485 519
486 int _toLowerCase(int byte) { 520 int _toLowerCase(int byte) {
487 final int aCode = "A".charCodeAt(0); 521 final int aCode = "A".charCodeAt(0);
488 final int zCode = "Z".charCodeAt(0); 522 final int zCode = "Z".charCodeAt(0);
(...skipping 22 matching lines...) Expand all
511 int _state; 545 int _state;
512 int _httpVersionIndex; 546 int _httpVersionIndex;
513 int _messageType; 547 int _messageType;
514 StringBuffer _method_or_status_code; 548 StringBuffer _method_or_status_code;
515 StringBuffer _uri_or_reason_phrase; 549 StringBuffer _uri_or_reason_phrase;
516 StringBuffer _headerField; 550 StringBuffer _headerField;
517 StringBuffer _headerValue; 551 StringBuffer _headerValue;
518 552
519 int _contentLength; 553 int _contentLength;
520 bool _keepAlive; 554 bool _keepAlive;
555 bool _connectionClose;
521 bool _chunked; 556 bool _chunked;
522 557
523 bool _noMessageBody; 558 bool _noMessageBody;
524 String _responseToMethod; // Indicates the method used for the request. 559 String _responseToMethod; // Indicates the method used for the request.
525 int _remainingContent; 560 int _remainingContent;
526 561
527 // Callbacks. 562 // Callbacks.
528 Function requestStart; 563 Function requestStart;
529 Function responseStart; 564 Function responseStart;
530 Function headerReceived; 565 Function headerReceived;
531 Function headersComplete; 566 Function headersComplete;
532 Function dataReceived; 567 Function dataReceived;
533 Function dataEnd; 568 Function dataEnd;
534 Function error; 569 Function error;
535 } 570 }
536 571
537 572
538 class HttpParserException implements Exception { 573 class HttpParserException implements Exception {
539 const HttpParserException([String this.message = ""]); 574 const HttpParserException([String this.message = ""]);
540 String toString() => "HttpParserException: $message"; 575 String toString() => "HttpParserException: $message";
541 final String message; 576 final String message;
542 } 577 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698