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

Side by Side Diff: pkg/dev_compiler/tool/input_sdk/lib/io/http_parser.dart

Issue 2698353003: unfork DDC's copy of most SDK libraries (Closed)
Patch Set: revert core_patch Created 3 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
OLDNEW
(Empty)
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file
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.
4
5 part of dart.io;
6
7 // Global constants.
8 class _Const {
9 // Bytes for "HTTP".
10 static const HTTP = const [72, 84, 84, 80];
11 // Bytes for "HTTP/1.".
12 static const HTTP1DOT = const [72, 84, 84, 80, 47, 49, 46];
13 // Bytes for "HTTP/1.0".
14 static const HTTP10 = const [72, 84, 84, 80, 47, 49, 46, 48];
15 // Bytes for "HTTP/1.1".
16 static const HTTP11 = const [72, 84, 84, 80, 47, 49, 46, 49];
17
18 static const bool T = true;
19 static const bool F = false;
20 // Loopup-map for the following characters: '()<>@,;:\\"/[]?={} \t'.
21 static const SEPARATOR_MAP = const [
22 F,F,F,F,F,F,F,F,F,T,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,T,F,T,F,F,
23 F,F,F,T,T,F,F,T,F,F,T,F,F,F,F,F,F,F,F,F,F,T,T,T,T,T,T,T,F,F,F,F,F,F,F,F,F,
24 F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,T,T,T,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,
25 F,F,F,F,F,F,F,F,F,F,F,F,T,F,T,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,
26 F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,
27 F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,
28 F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F,F];
29 }
30
31
32 // Frequently used character codes.
33 class _CharCode {
34 static const int HT = 9;
35 static const int LF = 10;
36 static const int CR = 13;
37 static const int SP = 32;
38 static const int AMPERSAND = 38;
39 static const int COMMA = 44;
40 static const int DASH = 45;
41 static const int SLASH = 47;
42 static const int ZERO = 48;
43 static const int ONE = 49;
44 static const int COLON = 58;
45 static const int SEMI_COLON = 59;
46 static const int EQUAL = 61;
47 }
48
49
50 // States of the HTTP parser state machine.
51 class _State {
52 static const int START = 0;
53 static const int METHOD_OR_RESPONSE_HTTP_VERSION = 1;
54 static const int RESPONSE_HTTP_VERSION = 2;
55 static const int REQUEST_LINE_METHOD = 3;
56 static const int REQUEST_LINE_URI = 4;
57 static const int REQUEST_LINE_HTTP_VERSION = 5;
58 static const int REQUEST_LINE_ENDING = 6;
59 static const int RESPONSE_LINE_STATUS_CODE = 7;
60 static const int RESPONSE_LINE_REASON_PHRASE = 8;
61 static const int RESPONSE_LINE_ENDING = 9;
62 static const int HEADER_START = 10;
63 static const int HEADER_FIELD = 11;
64 static const int HEADER_VALUE_START = 12;
65 static const int HEADER_VALUE = 13;
66 static const int HEADER_VALUE_FOLDING_OR_ENDING = 14;
67 static const int HEADER_VALUE_FOLD_OR_END = 15;
68 static const int HEADER_ENDING = 16;
69
70 static const int CHUNK_SIZE_STARTING_CR = 17;
71 static const int CHUNK_SIZE_STARTING_LF = 18;
72 static const int CHUNK_SIZE = 19;
73 static const int CHUNK_SIZE_EXTENSION = 20;
74 static const int CHUNK_SIZE_ENDING = 21;
75 static const int CHUNKED_BODY_DONE_CR = 22;
76 static const int CHUNKED_BODY_DONE_LF = 23;
77 static const int BODY = 24;
78 static const int CLOSED = 25;
79 static const int UPGRADED = 26;
80 static const int FAILURE = 27;
81
82 static const int FIRST_BODY_STATE = CHUNK_SIZE_STARTING_CR;
83 }
84
85 // HTTP version of the request or response being parsed.
86 class _HttpVersion {
87 static const int UNDETERMINED = 0;
88 static const int HTTP10 = 1;
89 static const int HTTP11 = 2;
90 }
91
92 // States of the HTTP parser state machine.
93 class _MessageType {
94 static const int UNDETERMINED = 0;
95 static const int REQUEST = 1;
96 static const int RESPONSE = 0;
97 }
98
99
100 /**
101 * The _HttpDetachedStreamSubscription takes a subscription and some extra data,
102 * and makes it possible to "inject" the data in from of other data events
103 * from the subscription.
104 *
105 * It does so by overriding pause/resume, so that once the
106 * _HttpDetachedStreamSubscription is resumed, it'll deliver the data before
107 * resuming the underlaying subscription.
108 */
109 class _HttpDetachedStreamSubscription implements StreamSubscription<List<int>> {
110 StreamSubscription<List<int>> _subscription;
111 List<int> _injectData;
112 bool _isCanceled = false;
113 int _pauseCount = 1;
114 Function _userOnData;
115 bool _scheduled = false;
116
117 _HttpDetachedStreamSubscription(this._subscription,
118 this._injectData,
119 this._userOnData);
120
121 bool get isPaused => _subscription.isPaused;
122
123 Future/*<T>*/ asFuture/*<T>*/([/*=T*/ futureValue]) =>
124 _subscription.asFuture/*<T>*/(futureValue);
125
126 Future cancel() {
127 _isCanceled = true;
128 _injectData = null;
129 return _subscription.cancel();
130 }
131
132 void onData(void handleData(List<int> data)) {
133 _userOnData = handleData;
134 _subscription.onData(handleData);
135 }
136
137 void onDone(void handleDone()) {
138 _subscription.onDone(handleDone);
139 }
140
141 void onError(Function handleError) {
142 _subscription.onError(handleError);
143 }
144
145 void pause([Future resumeSignal]) {
146 if (_injectData == null) {
147 _subscription.pause(resumeSignal);
148 } else {
149 _pauseCount++;
150 if (resumeSignal != null) {
151 resumeSignal.whenComplete(resume);
152 }
153 }
154 }
155
156 void resume() {
157 if (_injectData == null) {
158 _subscription.resume();
159 } else {
160 _pauseCount--;
161 _maybeScheduleData();
162 }
163 }
164
165 void _maybeScheduleData() {
166 if (_scheduled) return;
167 if (_pauseCount != 0) return;
168 _scheduled = true;
169 scheduleMicrotask(() {
170 _scheduled = false;
171 if (_pauseCount > 0 || _isCanceled) return;
172 var data = _injectData;
173 _injectData = null;
174 // To ensure that 'subscription.isPaused' is false, we resume the
175 // subscription here. This is fine as potential events are delayed.
176 _subscription.resume();
177 if (_userOnData != null) {
178 _userOnData(data);
179 }
180 });
181 }
182 }
183
184
185 class _HttpDetachedIncoming extends Stream<List<int>> {
186 final StreamSubscription subscription;
187 final List<int> bufferedData;
188
189 _HttpDetachedIncoming(this.subscription, this.bufferedData);
190
191 StreamSubscription<List<int>> listen(void onData(List<int> event),
192 {Function onError,
193 void onDone(),
194 bool cancelOnError}) {
195 if (subscription != null) {
196 subscription
197 ..onData(onData)
198 ..onError(onError)
199 ..onDone(onDone);
200 if (bufferedData == null) {
201 return subscription..resume();
202 }
203 return new _HttpDetachedStreamSubscription(subscription,
204 bufferedData,
205 onData)
206 ..resume();
207 } else {
208 return new Stream.fromIterable(bufferedData)
209 .listen(onData,
210 onError: onError,
211 onDone: onDone,
212 cancelOnError: cancelOnError);
213 }
214 }
215 }
216
217
218 /**
219 * HTTP parser which parses the data stream given to [consume].
220 *
221 * If an HTTP parser error occours, the parser will signal an error to either
222 * the current _HttpIncoming or the _parser itself.
223 *
224 * The connection upgrades (e.g. switching from HTTP/1.1 to the
225 * WebSocket protocol) is handled in a special way. If connection
226 * upgrade is specified in the headers, then on the callback to
227 * [:responseStart:] the [:upgrade:] property on the [:HttpParser:]
228 * object will be [:true:] indicating that from now on the protocol is
229 * not HTTP anymore and no more callbacks will happen, that is
230 * [:dataReceived:] and [:dataEnd:] are not called in this case as
231 * there is no more HTTP data. After the upgrade the method
232 * [:readUnparsedData:] can be used to read any remaining bytes in the
233 * HTTP parser which are part of the protocol the connection is
234 * upgrading to. These bytes cannot be processed by the HTTP parser
235 * and should be handled according to whatever protocol is being
236 * upgraded to.
237 */
238 class _HttpParser extends Stream<_HttpIncoming> {
239 // State.
240 bool _parserCalled = false;
241
242 // The data that is currently being parsed.
243 Uint8List _buffer;
244 int _index;
245
246 final bool _requestParser;
247 int _state;
248 int _httpVersionIndex;
249 int _messageType;
250 int _statusCode = 0;
251 int _statusCodeLength = 0;
252 final List<int> _method = [];
253 final List<int> _uri_or_reason_phrase = [];
254 final List<int> _headerField = [];
255 final List<int> _headerValue = [];
256
257 int _httpVersion;
258 int _transferLength = -1;
259 bool _persistentConnection;
260 bool _connectionUpgrade;
261 bool _chunked;
262
263 bool _noMessageBody = false;
264 int _remainingContent = -1;
265
266 _HttpHeaders _headers;
267
268 // The current incoming connection.
269 _HttpIncoming _incoming;
270 StreamSubscription _socketSubscription;
271 bool _paused = true;
272 bool _bodyPaused = false;
273 StreamController<_HttpIncoming> _controller;
274 StreamController<List<int>> _bodyController;
275
276 factory _HttpParser.requestParser() {
277 return new _HttpParser._(true);
278 }
279
280 factory _HttpParser.responseParser() {
281 return new _HttpParser._(false);
282 }
283
284 _HttpParser._(this._requestParser) {
285 _controller = new StreamController<_HttpIncoming>(
286 sync: true,
287 onListen: () {
288 _paused = false;
289 },
290 onPause: () {
291 _paused = true;
292 _pauseStateChanged();
293 },
294 onResume: () {
295 _paused = false;
296 _pauseStateChanged();
297 },
298 onCancel: () {
299 if (_socketSubscription != null) {
300 _socketSubscription.cancel();
301 }
302 });
303 _reset();
304 }
305
306
307 StreamSubscription<_HttpIncoming> listen(void onData(_HttpIncoming event),
308 {Function onError,
309 void onDone(),
310 bool cancelOnError}) {
311 return _controller.stream.listen(onData,
312 onError: onError,
313 onDone: onDone,
314 cancelOnError: cancelOnError);
315 }
316
317 void listenToStream(Stream<List<int>> stream) {
318 // Listen to the stream and handle data accordingly. When a
319 // _HttpIncoming is created, _dataPause, _dataResume, _dataDone is
320 // given to provide a way of controlling the parser.
321 // TODO(ajohnsen): Remove _dataPause, _dataResume and _dataDone and clean up
322 // how the _HttpIncoming signals the parser.
323 _socketSubscription = stream.listen(
324 _onData,
325 onError: _controller.addError,
326 onDone: _onDone);
327 }
328
329 void _parse() {
330 try {
331 _doParse();
332 } catch (e, s) {
333 _state = _State.FAILURE;
334 _reportError(e, s);
335 }
336 }
337
338 // Process end of headers. Returns true if the parser should stop
339 // parsing and return. This will be in case of either an upgrade
340 // request or a request or response with an empty body.
341 bool _headersEnd() {
342 _headers._mutable = false;
343
344 _transferLength = _headers.contentLength;
345 // Ignore the Content-Length header if Transfer-Encoding
346 // is chunked (RFC 2616 section 4.4)
347 if (_chunked) _transferLength = -1;
348
349 // If a request message has neither Content-Length nor
350 // Transfer-Encoding the message must not have a body (RFC
351 // 2616 section 4.3).
352 if (_messageType == _MessageType.REQUEST &&
353 _transferLength < 0 &&
354 _chunked == false) {
355 _transferLength = 0;
356 }
357 if (_connectionUpgrade) {
358 _state = _State.UPGRADED;
359 _transferLength = 0;
360 }
361 _createIncoming(_transferLength);
362 if (_requestParser) {
363 _incoming.method =
364 new String.fromCharCodes(_method);
365 _incoming.uri =
366 Uri.parse(
367 new String.fromCharCodes(_uri_or_reason_phrase));
368 } else {
369 _incoming.statusCode = _statusCode;
370 _incoming.reasonPhrase =
371 new String.fromCharCodes(_uri_or_reason_phrase);
372 }
373 _method.clear();
374 _uri_or_reason_phrase.clear();
375 if (_connectionUpgrade) {
376 _incoming.upgraded = true;
377 _parserCalled = false;
378 var tmp = _incoming;
379 _closeIncoming();
380 _controller.add(tmp);
381 return true;
382 }
383 if (_transferLength == 0 ||
384 (_messageType == _MessageType.RESPONSE && _noMessageBody)) {
385 _reset();
386 var tmp = _incoming;
387 _closeIncoming();
388 _controller.add(tmp);
389 return false;
390 } else if (_chunked) {
391 _state = _State.CHUNK_SIZE;
392 _remainingContent = 0;
393 } else if (_transferLength > 0) {
394 _remainingContent = _transferLength;
395 _state = _State.BODY;
396 } else {
397 // Neither chunked nor content length. End of body
398 // indicated by close.
399 _state = _State.BODY;
400 }
401 _parserCalled = false;
402 _controller.add(_incoming);
403 return true;
404 }
405
406 // From RFC 2616.
407 // generic-message = start-line
408 // *(message-header CRLF)
409 // CRLF
410 // [ message-body ]
411 // start-line = Request-Line | Status-Line
412 // Request-Line = Method SP Request-URI SP HTTP-Version CRLF
413 // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
414 // message-header = field-name ":" [ field-value ]
415 void _doParse() {
416 assert(!_parserCalled);
417 _parserCalled = true;
418 if (_state == _State.CLOSED) {
419 throw new HttpException("Data on closed connection");
420 }
421 if (_state == _State.FAILURE) {
422 throw new HttpException("Data on failed connection");
423 }
424 while (_buffer != null &&
425 _index < _buffer.length &&
426 _state != _State.FAILURE &&
427 _state != _State.UPGRADED) {
428 // Depending on _incoming, we either break on _bodyPaused or _paused.
429 if ((_incoming != null && _bodyPaused) ||
430 (_incoming == null && _paused)) {
431 _parserCalled = false;
432 return;
433 }
434 int byte = _buffer[_index++];
435 switch (_state) {
436 case _State.START:
437 if (byte == _Const.HTTP[0]) {
438 // Start parsing method or HTTP version.
439 _httpVersionIndex = 1;
440 _state = _State.METHOD_OR_RESPONSE_HTTP_VERSION;
441 } else {
442 // Start parsing method.
443 if (!_isTokenChar(byte)) {
444 throw new HttpException("Invalid request method");
445 }
446 _method.add(byte);
447 if (!_requestParser) {
448 throw new HttpException("Invalid response line");
449 }
450 _state = _State.REQUEST_LINE_METHOD;
451 }
452 break;
453
454 case _State.METHOD_OR_RESPONSE_HTTP_VERSION:
455 if (_httpVersionIndex < _Const.HTTP.length &&
456 byte == _Const.HTTP[_httpVersionIndex]) {
457 // Continue parsing HTTP version.
458 _httpVersionIndex++;
459 } else if (_httpVersionIndex == _Const.HTTP.length &&
460 byte == _CharCode.SLASH) {
461 // HTTP/ parsed. As method is a token this cannot be a
462 // method anymore.
463 _httpVersionIndex++;
464 if (_requestParser) {
465 throw new HttpException("Invalid request line");
466 }
467 _state = _State.RESPONSE_HTTP_VERSION;
468 } else {
469 // Did not parse HTTP version. Expect method instead.
470 for (int i = 0; i < _httpVersionIndex; i++) {
471 _method.add(_Const.HTTP[i]);
472 }
473 if (byte == _CharCode.SP) {
474 _state = _State.REQUEST_LINE_URI;
475 } else {
476 _method.add(byte);
477 _httpVersion = _HttpVersion.UNDETERMINED;
478 if (!_requestParser) {
479 throw new HttpException("Invalid response line");
480 }
481 _state = _State.REQUEST_LINE_METHOD;
482 }
483 }
484 break;
485
486 case _State.RESPONSE_HTTP_VERSION:
487 if (_httpVersionIndex < _Const.HTTP1DOT.length) {
488 // Continue parsing HTTP version.
489 _expect(byte, _Const.HTTP1DOT[_httpVersionIndex]);
490 _httpVersionIndex++;
491 } else if (_httpVersionIndex == _Const.HTTP1DOT.length &&
492 byte == _CharCode.ONE) {
493 // HTTP/1.1 parsed.
494 _httpVersion = _HttpVersion.HTTP11;
495 _persistentConnection = true;
496 _httpVersionIndex++;
497 } else if (_httpVersionIndex == _Const.HTTP1DOT.length &&
498 byte == _CharCode.ZERO) {
499 // HTTP/1.0 parsed.
500 _httpVersion = _HttpVersion.HTTP10;
501 _persistentConnection = false;
502 _httpVersionIndex++;
503 } else if (_httpVersionIndex == _Const.HTTP1DOT.length + 1) {
504 _expect(byte, _CharCode.SP);
505 // HTTP version parsed.
506 _state = _State.RESPONSE_LINE_STATUS_CODE;
507 } else {
508 throw new HttpException("Invalid response line");
509 }
510 break;
511
512 case _State.REQUEST_LINE_METHOD:
513 if (byte == _CharCode.SP) {
514 _state = _State.REQUEST_LINE_URI;
515 } else {
516 if (_Const.SEPARATOR_MAP[byte] ||
517 byte == _CharCode.CR ||
518 byte == _CharCode.LF) {
519 throw new HttpException("Invalid request method");
520 }
521 _method.add(byte);
522 }
523 break;
524
525 case _State.REQUEST_LINE_URI:
526 if (byte == _CharCode.SP) {
527 if (_uri_or_reason_phrase.length == 0) {
528 throw new HttpException("Invalid request URI");
529 }
530 _state = _State.REQUEST_LINE_HTTP_VERSION;
531 _httpVersionIndex = 0;
532 } else {
533 if (byte == _CharCode.CR || byte == _CharCode.LF) {
534 throw new HttpException("Invalid request URI");
535 }
536 _uri_or_reason_phrase.add(byte);
537 }
538 break;
539
540 case _State.REQUEST_LINE_HTTP_VERSION:
541 if (_httpVersionIndex < _Const.HTTP1DOT.length) {
542 _expect(byte, _Const.HTTP11[_httpVersionIndex]);
543 _httpVersionIndex++;
544 } else if (_httpVersionIndex == _Const.HTTP1DOT.length) {
545 if (byte == _CharCode.ONE) {
546 // HTTP/1.1 parsed.
547 _httpVersion = _HttpVersion.HTTP11;
548 _persistentConnection = true;
549 _httpVersionIndex++;
550 } else if (byte == _CharCode.ZERO) {
551 // HTTP/1.0 parsed.
552 _httpVersion = _HttpVersion.HTTP10;
553 _persistentConnection = false;
554 _httpVersionIndex++;
555 } else {
556 throw new HttpException("Invalid response line");
557 }
558 } else {
559 if (byte == _CharCode.CR) {
560 _state = _State.REQUEST_LINE_ENDING;
561 } else {
562 _expect(byte, _CharCode.LF);
563 _messageType = _MessageType.REQUEST;
564 _state = _State.HEADER_START;
565 }
566 }
567 break;
568
569 case _State.REQUEST_LINE_ENDING:
570 _expect(byte, _CharCode.LF);
571 _messageType = _MessageType.REQUEST;
572 _state = _State.HEADER_START;
573 break;
574
575 case _State.RESPONSE_LINE_STATUS_CODE:
576 if (byte == _CharCode.SP) {
577 _state = _State.RESPONSE_LINE_REASON_PHRASE;
578 } else if (byte == _CharCode.CR) {
579 // Some HTTP servers does not follow the spec. and send
580 // \r\n right after the status code.
581 _state = _State.RESPONSE_LINE_ENDING;
582 } else {
583 _statusCodeLength++;
584 if ((byte < 0x30 && 0x39 < byte) || _statusCodeLength > 3) {
585 throw new HttpException("Invalid response status code");
586 } else {
587 _statusCode = _statusCode * 10 + byte - 0x30;
588 }
589 }
590 break;
591
592 case _State.RESPONSE_LINE_REASON_PHRASE:
593 if (byte == _CharCode.CR) {
594 _state = _State.RESPONSE_LINE_ENDING;
595 } else {
596 if (byte == _CharCode.CR || byte == _CharCode.LF) {
597 throw new HttpException("Invalid response reason phrase");
598 }
599 _uri_or_reason_phrase.add(byte);
600 }
601 break;
602
603 case _State.RESPONSE_LINE_ENDING:
604 _expect(byte, _CharCode.LF);
605 _messageType == _MessageType.RESPONSE;
606 if (_statusCode < 100 || _statusCode > 599) {
607 throw new HttpException("Invalid response status code");
608 } else {
609 // Check whether this response will never have a body.
610 if (_statusCode <= 199 || _statusCode == 204 ||
611 _statusCode == 304) {
612 _noMessageBody = true;
613 }
614 }
615 _state = _State.HEADER_START;
616 break;
617
618 case _State.HEADER_START:
619 _headers = new _HttpHeaders(version);
620 if (byte == _CharCode.CR) {
621 _state = _State.HEADER_ENDING;
622 } else if (byte == _CharCode.LF) {
623 _state = _State.HEADER_ENDING;
624 _index--; // Make the new state see the LF again.
625 } else {
626 // Start of new header field.
627 _headerField.add(_toLowerCaseByte(byte));
628 _state = _State.HEADER_FIELD;
629 }
630 break;
631
632 case _State.HEADER_FIELD:
633 if (byte == _CharCode.COLON) {
634 _state = _State.HEADER_VALUE_START;
635 } else {
636 if (!_isTokenChar(byte)) {
637 throw new HttpException("Invalid header field name");
638 }
639 _headerField.add(_toLowerCaseByte(byte));
640 }
641 break;
642
643 case _State.HEADER_VALUE_START:
644 if (byte == _CharCode.CR) {
645 _state = _State.HEADER_VALUE_FOLDING_OR_ENDING;
646 } else if (byte == _CharCode.LF) {
647 _state = _State.HEADER_VALUE_FOLD_OR_END;
648 } else if (byte != _CharCode.SP && byte != _CharCode.HT) {
649 // Start of new header value.
650 _headerValue.add(byte);
651 _state = _State.HEADER_VALUE;
652 }
653 break;
654
655 case _State.HEADER_VALUE:
656 if (byte == _CharCode.CR) {
657 _state = _State.HEADER_VALUE_FOLDING_OR_ENDING;
658 } else if (byte == _CharCode.LF) {
659 _state = _State.HEADER_VALUE_FOLD_OR_END;
660 } else {
661 _headerValue.add(byte);
662 }
663 break;
664
665 case _State.HEADER_VALUE_FOLDING_OR_ENDING:
666 _expect(byte, _CharCode.LF);
667 _state = _State.HEADER_VALUE_FOLD_OR_END;
668 break;
669
670 case _State.HEADER_VALUE_FOLD_OR_END:
671 if (byte == _CharCode.SP || byte == _CharCode.HT) {
672 _state = _State.HEADER_VALUE_START;
673 } else {
674 String headerField = new String.fromCharCodes(_headerField);
675 String headerValue = new String.fromCharCodes(_headerValue);
676 if (headerField == "transfer-encoding" &&
677 _caseInsensitiveCompare("chunked".codeUnits, _headerValue)) {
678 _chunked = true;
679 }
680 if (headerField == "connection") {
681 List<String> tokens = _tokenizeFieldValue(headerValue);
682 for (int i = 0; i < tokens.length; i++) {
683 if (_caseInsensitiveCompare("upgrade".codeUnits,
684 tokens[i].codeUnits)) {
685 _connectionUpgrade = true;
686 }
687 _headers._add(headerField, tokens[i]);
688 }
689 } else {
690 _headers._add(headerField, headerValue);
691 }
692 _headerField.clear();
693 _headerValue.clear();
694
695 if (byte == _CharCode.CR) {
696 _state = _State.HEADER_ENDING;
697 } else if (byte == _CharCode.LF) {
698 _state = _State.HEADER_ENDING;
699 _index--; // Make the new state see the LF again.
700 } else {
701 // Start of new header field.
702 _headerField.add(_toLowerCaseByte(byte));
703 _state = _State.HEADER_FIELD;
704 }
705 }
706 break;
707
708 case _State.HEADER_ENDING:
709 _expect(byte, _CharCode.LF);
710 if (_headersEnd()) {
711 return;
712 } else {
713 break;
714 }
715 return;
716
717 case _State.CHUNK_SIZE_STARTING_CR:
718 _expect(byte, _CharCode.CR);
719 _state = _State.CHUNK_SIZE_STARTING_LF;
720 break;
721
722 case _State.CHUNK_SIZE_STARTING_LF:
723 _expect(byte, _CharCode.LF);
724 _state = _State.CHUNK_SIZE;
725 break;
726
727 case _State.CHUNK_SIZE:
728 if (byte == _CharCode.CR) {
729 _state = _State.CHUNK_SIZE_ENDING;
730 } else if (byte == _CharCode.SEMI_COLON) {
731 _state = _State.CHUNK_SIZE_EXTENSION;
732 } else {
733 int value = _expectHexDigit(byte);
734 _remainingContent = _remainingContent * 16 + value;
735 }
736 break;
737
738 case _State.CHUNK_SIZE_EXTENSION:
739 if (byte == _CharCode.CR) {
740 _state = _State.CHUNK_SIZE_ENDING;
741 }
742 break;
743
744 case _State.CHUNK_SIZE_ENDING:
745 _expect(byte, _CharCode.LF);
746 if (_remainingContent > 0) {
747 _state = _State.BODY;
748 } else {
749 _state = _State.CHUNKED_BODY_DONE_CR;
750 }
751 break;
752
753 case _State.CHUNKED_BODY_DONE_CR:
754 _expect(byte, _CharCode.CR);
755 _state = _State.CHUNKED_BODY_DONE_LF;
756 break;
757
758 case _State.CHUNKED_BODY_DONE_LF:
759 _expect(byte, _CharCode.LF);
760 _reset();
761 _closeIncoming();
762 break;
763
764 case _State.BODY:
765 // The body is not handled one byte at a time but in blocks.
766 _index--;
767 int dataAvailable = _buffer.length - _index;
768 if (_remainingContent >= 0 && dataAvailable > _remainingContent) {
769 dataAvailable = _remainingContent;
770 }
771 // Always present the data as a view. This way we can handle all
772 // cases like this, and the user will not experince different data
773 // typed (which could lead to polymorphic user code).
774 List<int> data = new Uint8List.view(_buffer.buffer,
775 _buffer.offsetInBytes + _index,
776 dataAvailable);
777 _bodyController.add(data);
778 if (_remainingContent != -1) {
779 _remainingContent -= data.length;
780 }
781 _index += data.length;
782 if (_remainingContent == 0) {
783 if (!_chunked) {
784 _reset();
785 _closeIncoming();
786 } else {
787 _state = _State.CHUNK_SIZE_STARTING_CR;
788 }
789 }
790 break;
791
792 case _State.FAILURE:
793 // Should be unreachable.
794 assert(false);
795 break;
796
797 default:
798 // Should be unreachable.
799 assert(false);
800 break;
801 }
802 }
803
804 _parserCalled = false;
805 if (_buffer != null && _index == _buffer.length) {
806 // If all data is parsed release the buffer and resume receiving
807 // data.
808 _releaseBuffer();
809 if (_state != _State.UPGRADED && _state != _State.FAILURE) {
810 _socketSubscription.resume();
811 }
812 }
813 }
814
815 void _onData(List<int> buffer) {
816 _socketSubscription.pause();
817 assert(_buffer == null);
818 _buffer = buffer;
819 _index = 0;
820 _parse();
821 }
822
823 void _onDone() {
824 // onDone cancles the subscription.
825 _socketSubscription = null;
826 if (_state == _State.CLOSED || _state == _State.FAILURE) return;
827
828 if (_incoming != null) {
829 if (_state != _State.UPGRADED &&
830 !(_state == _State.START && !_requestParser) &&
831 !(_state == _State.BODY && !_chunked && _transferLength == -1)) {
832 _bodyController.addError(
833 new HttpException("Connection closed while receiving data"));
834 }
835 _closeIncoming(true);
836 _controller.close();
837 return;
838 }
839 // If the connection is idle the HTTP stream is closed.
840 if (_state == _State.START) {
841 if (!_requestParser) {
842 _reportError(new HttpException(
843 "Connection closed before full header was received"));
844 }
845 _controller.close();
846 return;
847 }
848
849 if (_state == _State.UPGRADED) {
850 _controller.close();
851 return;
852 }
853
854 if (_state < _State.FIRST_BODY_STATE) {
855 _state = _State.FAILURE;
856 // Report the error through the error callback if any. Otherwise
857 // throw the error.
858 _reportError(new HttpException(
859 "Connection closed before full header was received"));
860 _controller.close();
861 return;
862 }
863
864 if (!_chunked && _transferLength == -1) {
865 _state = _State.CLOSED;
866 } else {
867 _state = _State.FAILURE;
868 // Report the error through the error callback if any. Otherwise
869 // throw the error.
870 _reportError(new HttpException(
871 "Connection closed before full body was received"));
872 }
873 _controller.close();
874 }
875
876 String get version {
877 switch (_httpVersion) {
878 case _HttpVersion.HTTP10:
879 return "1.0";
880 case _HttpVersion.HTTP11:
881 return "1.1";
882 }
883 return null;
884 }
885
886 int get messageType => _messageType;
887 int get transferLength => _transferLength;
888 bool get upgrade => _connectionUpgrade && _state == _State.UPGRADED;
889 bool get persistentConnection => _persistentConnection;
890
891 void set isHead(bool value) {
892 if (value) _noMessageBody = true;
893 }
894
895 _HttpDetachedIncoming detachIncoming() {
896 // Simulate detached by marking as upgraded.
897 _state = _State.UPGRADED;
898 return new _HttpDetachedIncoming(_socketSubscription,
899 readUnparsedData());
900 }
901
902 List<int> readUnparsedData() {
903 if (_buffer == null) return null;
904 if (_index == _buffer.length) return null;
905 var result = _buffer.sublist(_index);
906 _releaseBuffer();
907 return result;
908 }
909
910 void _reset() {
911 if (_state == _State.UPGRADED) return;
912 _state = _State.START;
913 _messageType = _MessageType.UNDETERMINED;
914 _headerField.clear();
915 _headerValue.clear();
916 _method.clear();
917 _uri_or_reason_phrase.clear();
918
919 _statusCode = 0;
920 _statusCodeLength = 0;
921
922 _httpVersion = _HttpVersion.UNDETERMINED;
923 _transferLength = -1;
924 _persistentConnection = false;
925 _connectionUpgrade = false;
926 _chunked = false;
927
928 _noMessageBody = false;
929 _remainingContent = -1;
930
931 _headers = null;
932 }
933
934 void _releaseBuffer() {
935 _buffer = null;
936 _index = null;
937 }
938
939 static bool _isTokenChar(int byte) {
940 return byte > 31 && byte < 128 && !_Const.SEPARATOR_MAP[byte];
941 }
942
943 static bool _isValueChar(int byte) {
944 return (byte > 31 && byte < 128) || (byte == _CharCode.SP) ||
945 (byte == _CharCode.HT);
946 }
947
948 static List<String> _tokenizeFieldValue(String headerValue) {
949 List<String> tokens = new List<String>();
950 int start = 0;
951 int index = 0;
952 while (index < headerValue.length) {
953 if (headerValue[index] == ",") {
954 tokens.add(headerValue.substring(start, index));
955 start = index + 1;
956 } else if (headerValue[index] == " " || headerValue[index] == "\t") {
957 start++;
958 }
959 index++;
960 }
961 tokens.add(headerValue.substring(start, index));
962 return tokens;
963 }
964
965 static int _toLowerCaseByte(int x) {
966 // Optimized version:
967 // - 0x41 is 'A'
968 // - 0x7f is ASCII mask
969 // - 26 is the number of alpha characters.
970 // - 0x20 is the delta between lower and upper chars.
971 return (((x - 0x41) & 0x7f) < 26) ? (x | 0x20) : x;
972 }
973
974 // expected should already be lowercase.
975 bool _caseInsensitiveCompare(List<int> expected, List<int> value) {
976 if (expected.length != value.length) return false;
977 for (int i = 0; i < expected.length; i++) {
978 if (expected[i] != _toLowerCaseByte(value[i])) return false;
979 }
980 return true;
981 }
982
983 int _expect(int val1, int val2) {
984 if (val1 != val2) {
985 throw new HttpException("Failed to parse HTTP");
986 }
987 }
988
989 int _expectHexDigit(int byte) {
990 if (0x30 <= byte && byte <= 0x39) {
991 return byte - 0x30; // 0 - 9
992 } else if (0x41 <= byte && byte <= 0x46) {
993 return byte - 0x41 + 10; // A - F
994 } else if (0x61 <= byte && byte <= 0x66) {
995 return byte - 0x61 + 10; // a - f
996 } else {
997 throw new HttpException("Failed to parse HTTP");
998 }
999 }
1000
1001 void _createIncoming(int transferLength) {
1002 assert(_incoming == null);
1003 assert(_bodyController == null);
1004 assert(!_bodyPaused);
1005 var incoming;
1006 _bodyController = new StreamController<List<int>>(
1007 sync: true,
1008 onListen: () {
1009 if (incoming != _incoming) return;
1010 assert(_bodyPaused);
1011 _bodyPaused = false;
1012 _pauseStateChanged();
1013 },
1014 onPause: () {
1015 if (incoming != _incoming) return;
1016 assert(!_bodyPaused);
1017 _bodyPaused = true;
1018 _pauseStateChanged();
1019 },
1020 onResume: () {
1021 if (incoming != _incoming) return;
1022 assert(_bodyPaused);
1023 _bodyPaused = false;
1024 _pauseStateChanged();
1025 },
1026 onCancel: () {
1027 if (incoming != _incoming) return;
1028 if (_socketSubscription != null) {
1029 _socketSubscription.cancel();
1030 }
1031 _closeIncoming(true);
1032 _controller.close();
1033 });
1034 incoming = _incoming = new _HttpIncoming(
1035 _headers, transferLength, _bodyController.stream);
1036 _bodyPaused = true;
1037 _pauseStateChanged();
1038 }
1039
1040 void _closeIncoming([bool closing = false]) {
1041 // Ignore multiple close (can happen in re-entrance).
1042 if (_incoming == null) return;
1043 var tmp = _incoming;
1044 tmp.close(closing);
1045 _incoming = null;
1046 if (_bodyController != null) {
1047 _bodyController.close();
1048 _bodyController = null;
1049 }
1050 _bodyPaused = false;
1051 _pauseStateChanged();
1052 }
1053
1054 void _pauseStateChanged() {
1055 if (_incoming != null) {
1056 if (!_bodyPaused && !_parserCalled) {
1057 _parse();
1058 }
1059 } else {
1060 if (!_paused && !_parserCalled) {
1061 _parse();
1062 }
1063 }
1064 }
1065
1066 void _reportError(error, [stackTrace]) {
1067 if (_socketSubscription != null) _socketSubscription.cancel();
1068 _state = _State.FAILURE;
1069 _controller.addError(error, stackTrace);
1070 _controller.close();
1071 }
1072 }
OLDNEW
« no previous file with comments | « pkg/dev_compiler/tool/input_sdk/lib/io/http_impl.dart ('k') | pkg/dev_compiler/tool/input_sdk/lib/io/http_session.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698