Chromium Code Reviews| 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 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 != null && _remainingContent == 0) { |
|
Anders Johnsen
2012/03/15 11:24:04
== 0 should be sufficient.
Søren Gjesse
2012/03/19 11:31:48
Done.
| |
| 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 Loading... | |
| 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 } |
| OLD | NEW |