| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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); |
| 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); |
| 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 Loading... |
| 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 } |
| OLD | NEW |