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

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 additional 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() {
457 if (_state < _State.FIRST_BODY_STATE) {
458 _state = _State.FAILURE;
459 // Report the error through the error callback if any. Otherwise
460 // throw the error.
461 var e = new HttpParserException(
462 "Connection closed before full header was received");
463 if (error != null) {
464 error(e);
465 return;
466 }
467 throw e;
468 }
469
448 if (!_chunked && _contentLength == -1) { 470 if (!_chunked && _contentLength == -1) {
449 if (dataEnd != null) dataEnd(); 471 if (_state != _State.START) {
472 if (dataEnd != null) dataEnd(true);
473 }
474 _state = _State.CLOSED;
450 } else { 475 } else {
476 _state = _State.FAILURE;
451 // Report the error through the error callback if any. Otherwise 477 // Report the error through the error callback if any. Otherwise
452 // throw the error. 478 // throw the error.
453 var e = new HttpParserException( 479 var e = new HttpParserException(
454 "Connection closed before full body was received"); 480 "Connection closed before full body was received");
455 if (error != null) { 481 if (error != null) {
456 error(e); 482 error(e);
457 } else { 483 return;
458 throw e;
459 } 484 }
485 throw e;
460 } 486 }
461 } 487 }
462 488
463 int get messageType() => _messageType; 489 int get messageType() => _messageType;
464 int get contentLength() => _contentLength; 490 int get contentLength() => _contentLength;
465 bool get keepAlive() => _keepAlive; 491 bool get keepAlive() => _keepAlive;
466 492
467 void set responseToMethod(String method) => _responseToMethod = method; 493 void set responseToMethod(String method) => _responseToMethod = method;
468 494
495 bool get isIdle() => _state == _State.START;
496
497 void _bodyEnd() {
498 if (dataEnd != null) {
499 dataEnd(_messageType == _MessageType.RESPONSE && _connectionClose);
500 }
501 }
502
469 _reset() { 503 _reset() {
470 _state = _State.START; 504 _state = _State.START;
471 _messageType = _MessageType.UNDETERMINED; 505 _messageType = _MessageType.UNDETERMINED;
472 _headerField = new StringBuffer(); 506 _headerField = new StringBuffer();
473 _headerValue = new StringBuffer(); 507 _headerValue = new StringBuffer();
474 _method_or_status_code = new StringBuffer(); 508 _method_or_status_code = new StringBuffer();
475 _uri_or_reason_phrase = new StringBuffer(); 509 _uri_or_reason_phrase = new StringBuffer();
476 510
477 _contentLength = -1; 511 _contentLength = -1;
478 _keepAlive = false; 512 _keepAlive = false;
513 _connectionClose = false;
479 _chunked = false; 514 _chunked = false;
480 515
481 _noMessageBody = false; 516 _noMessageBody = false;
482 _responseToMethod = null; 517 _responseToMethod = null;
483 _remainingContent = null; 518 _remainingContent = null;
484 } 519 }
485 520
486 int _toLowerCase(int byte) { 521 int _toLowerCase(int byte) {
487 final int aCode = "A".charCodeAt(0); 522 final int aCode = "A".charCodeAt(0);
488 final int zCode = "Z".charCodeAt(0); 523 final int zCode = "Z".charCodeAt(0);
(...skipping 22 matching lines...) Expand all
511 int _state; 546 int _state;
512 int _httpVersionIndex; 547 int _httpVersionIndex;
513 int _messageType; 548 int _messageType;
514 StringBuffer _method_or_status_code; 549 StringBuffer _method_or_status_code;
515 StringBuffer _uri_or_reason_phrase; 550 StringBuffer _uri_or_reason_phrase;
516 StringBuffer _headerField; 551 StringBuffer _headerField;
517 StringBuffer _headerValue; 552 StringBuffer _headerValue;
518 553
519 int _contentLength; 554 int _contentLength;
520 bool _keepAlive; 555 bool _keepAlive;
556 bool _connectionClose;
521 bool _chunked; 557 bool _chunked;
522 558
523 bool _noMessageBody; 559 bool _noMessageBody;
524 String _responseToMethod; // Indicates the method used for the request. 560 String _responseToMethod; // Indicates the method used for the request.
525 int _remainingContent; 561 int _remainingContent;
526 562
527 // Callbacks. 563 // Callbacks.
528 Function requestStart; 564 Function requestStart;
529 Function responseStart; 565 Function responseStart;
530 Function headerReceived; 566 Function headerReceived;
531 Function headersComplete; 567 Function headersComplete;
532 Function dataReceived; 568 Function dataReceived;
533 Function dataEnd; 569 Function dataEnd;
534 Function error; 570 Function error;
535 } 571 }
536 572
537 573
538 class HttpParserException implements Exception { 574 class HttpParserException implements Exception {
539 const HttpParserException([String this.message = ""]); 575 const HttpParserException([String this.message = ""]);
540 String toString() => "HttpParserException: $message"; 576 String toString() => "HttpParserException: $message";
541 final String message; 577 final String message;
542 } 578 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698