OLD | NEW |
---|---|
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 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 | 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 part of dart.io; | 5 part of dart.io; |
6 | 6 |
7 class _HttpIncoming extends Stream<List<int>> { | 7 class _HttpIncoming extends Stream<List<int>> { |
8 final int _transferLength; | 8 final int _transferLength; |
9 final Completer _dataCompleter = new Completer(); | 9 final Completer _dataCompleter = new Completer(); |
10 Stream<List<int>> _stream; | 10 Stream<List<int>> _stream; |
(...skipping 314 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
325 bool _headersWritten = false; | 325 bool _headersWritten = false; |
326 bool _chunked = false; | 326 bool _chunked = false; |
327 | 327 |
328 final IOSink _ioSink; | 328 final IOSink _ioSink; |
329 final _HttpOutgoing _outgoing; | 329 final _HttpOutgoing _outgoing; |
330 | 330 |
331 final _HttpHeaders headers; | 331 final _HttpHeaders headers; |
332 | 332 |
333 _HttpOutboundMessage(String protocolVersion, _HttpOutgoing outgoing) | 333 _HttpOutboundMessage(String protocolVersion, _HttpOutgoing outgoing) |
334 : _outgoing = outgoing, | 334 : _outgoing = outgoing, |
335 _ioSink = new IOSink(outgoing), | 335 _ioSink = new IOSink(outgoing, Encoding.ASCII), |
336 headers = new _HttpHeaders(protocolVersion); | 336 headers = new _HttpHeaders(protocolVersion); |
337 | 337 |
338 int get contentLength => headers.contentLength; | 338 int get contentLength => headers.contentLength; |
339 void set contentLength(int contentLength) { | 339 void set contentLength(int contentLength) { |
340 headers.contentLength = contentLength; | 340 headers.contentLength = contentLength; |
341 } | 341 } |
342 | 342 |
343 bool get persistentConnection => headers.persistentConnection; | 343 bool get persistentConnection => headers.persistentConnection; |
344 void set persistentConnection(bool p) { | 344 void set persistentConnection(bool p) { |
345 headers.persistentConnection = p; | 345 headers.persistentConnection = p; |
346 } | 346 } |
347 | 347 |
348 Encoding get encoding { | |
349 return headers.contentType != null ? headers.contentType.encoding | |
350 : Encoding.ISO_8859_1; | |
351 } | |
352 void set encoding(Encoding value) { | |
353 throw new StateError("IOSink encoding is not mutable"); | |
354 } | |
355 | |
356 void write(Object obj) { | |
357 _writeHeaders(); | |
358 if (_ignoreBody) return; | |
359 // This comment is copied from runtime/lib/string_buffer_patch.dart. | |
360 // TODO(srdjan): The following four lines could be replaced by | |
361 // '$obj', but apparently this is too slow on the Dart VM. | |
362 String string; | |
363 if (obj is String) { | |
364 string = obj; | |
365 } else { | |
366 string = obj.toString(); | |
367 if (string is! String) { | |
368 throw new ArgumentError('toString() did not return a string'); | |
369 } | |
370 } | |
371 if (string.isEmpty) return; | |
372 if (_chunked) { | |
373 _ChunkedTransformer._addChunk(_encodeString(string, encoding), | |
374 _ioSink.writeBytes); | |
375 } else { | |
376 _ioSink.write(string); | |
377 } | |
378 } | |
379 | |
380 void writeAll(Iterable objects) { | |
381 for (Object obj in objects) write(obj); | |
382 } | |
383 | |
384 void writeln(Object obj) { | |
385 write(obj); | |
386 write("\n"); | |
387 } | |
388 | |
389 void writeCharCode(int charCode) { | |
390 write(new String.fromCharCode(charCode)); | |
391 } | |
392 | |
393 void writeBytes(List<int> data) { | |
394 _writeHeaders(); | |
395 if (_ignoreBody || data.length == 0) return; | |
396 if (_chunked) { | |
397 _ChunkedTransformer._addChunk(data, _ioSink.writeBytes); | |
398 } else { | |
399 _ioSink.writeBytes(data); | |
400 } | |
401 } | |
402 | |
348 Future<T> consume(Stream<List<int>> stream) { | 403 Future<T> consume(Stream<List<int>> stream) { |
349 _writeHeaders(); | 404 _writeHeaders(); |
350 if (_ignoreBody) return new Future.immediate(this); | 405 if (_ignoreBody) return new Future.immediate(this); |
351 if (_chunked) { | 406 if (_chunked) { |
352 // Transform when chunked. | 407 // Transform when chunked. |
353 stream = stream.transform(new _ChunkedTransformer()); | 408 stream = stream.transform(new _ChunkedTransformer()); |
354 } | 409 } |
355 return _ioSink.consume(stream).then((_) => this); | 410 return _ioSink.consume(stream).then((_) => this); |
356 } | 411 } |
357 | 412 |
358 void add(List<int> data) { | 413 void add(List<int> data) => writeBytes(data); |
359 _writeHeaders(); | |
360 if (_ignoreBody || data.length == 0) return; | |
361 if (_chunked) { | |
362 _ChunkedTransformer._addChunk(data, _ioSink.add); | |
363 } else { | |
364 _ioSink.add(data); | |
365 } | |
366 } | |
367 | 414 |
368 void addString(String string, [Encoding encoding = Encoding.UTF_8]) { | 415 void addString(String string, [Encoding encoding = Encoding.UTF_8]) { |
369 add(_encodeString(string, encoding)); | 416 writeBytes(_encodeString(string, encoding)); |
370 } | 417 } |
371 | 418 |
372 Future<T> addStream(Stream<List<int>> stream) { | 419 Future<T> addStream(Stream<List<int>> stream) { |
373 _writeHeaders(); | 420 _writeHeaders(); |
374 if (_ignoreBody) return new Future.immediate(this); | 421 if (_ignoreBody) return new Future.immediate(this); |
375 if (_chunked) { | 422 if (_chunked) { |
376 // Transform when chunked. | 423 // Transform when chunked. |
377 stream = stream.transform(new _ChunkedTransformer(writeEnd: false)); | 424 stream = stream.transform(new _ChunkedTransformer(writeEnd: false)); |
378 } | 425 } |
379 return _ioSink.addStream(stream).then((_) => this); | 426 return _ioSink.addStream(stream).then((_) => this); |
380 } | 427 } |
381 | 428 |
382 void close() { | 429 void close() { |
383 if (!_headersWritten && !_ignoreBody && headers.chunkedTransferEncoding) { | 430 if (!_headersWritten && !_ignoreBody && headers.chunkedTransferEncoding) { |
384 // If no body was written, _ignoreBody is false (it's not a HEAD | 431 // If no body was written, _ignoreBody is false (it's not a HEAD |
385 // request) and the content-length is unspecified, set contentLength to 0. | 432 // request) and the content-length is unspecified, set contentLength to 0. |
386 headers.contentLength = 0; | 433 headers.contentLength = 0; |
387 } | 434 } |
388 _writeHeaders(); | 435 _writeHeaders(); |
389 if (!_ignoreBody) { | 436 if (!_ignoreBody) { |
390 if (_chunked) { | 437 if (_chunked) { |
391 _ChunkedTransformer._addChunk([], _ioSink.add); | 438 _ChunkedTransformer._addChunk([], _ioSink.writeBytes); |
392 } | 439 } |
393 } | 440 } |
394 _ioSink.close(); | 441 _ioSink.close(); |
395 } | 442 } |
396 | 443 |
397 Future<T> get done => _ioSink.done.then((_) => this); | 444 Future<T> get done => _ioSink.done.then((_) => this); |
398 | 445 |
399 void _writeHeaders() { | 446 void _writeHeaders() { |
400 if (_headersWritten) return; | 447 if (_headersWritten) return; |
401 bool _tmpIgnoreBody = _ignoreBody; | 448 bool _tmpIgnoreBody = _ignoreBody; |
402 _ignoreBody = false; | 449 _ignoreBody = false; |
403 _headersWritten = true; | 450 _headersWritten = true; |
451 _ioSink.encoding = Encoding.ASCII; | |
404 _writeHeader(); | 452 _writeHeader(); |
453 _ioSink.encoding = | |
454 headers.contentType != null ? headers.contentType.encoding | |
455 : Encoding.ISO_8859_1; | |
405 _ignoreBody = _tmpIgnoreBody; | 456 _ignoreBody = _tmpIgnoreBody; |
406 if (_ignoreBody) { | 457 if (_ignoreBody) { |
407 _ioSink.close(); | 458 _ioSink.close(); |
408 return; | 459 return; |
409 } | 460 } |
410 _chunked = headers.chunkedTransferEncoding; | 461 _chunked = headers.chunkedTransferEncoding; |
411 if (headers.contentLength >= 0) { | 462 if (headers.contentLength >= 0) { |
412 _outgoing.setTransferLength(headers.contentLength); | 463 _outgoing.setTransferLength(headers.contentLength); |
413 } | 464 } |
414 } | 465 } |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
448 done.catchError((_) { | 499 done.catchError((_) { |
449 // Catch any error on done, as they automatically will be propegated to | 500 // Catch any error on done, as they automatically will be propegated to |
450 // the websocket. | 501 // the websocket. |
451 }); | 502 }); |
452 return future; | 503 return future; |
453 } | 504 } |
454 | 505 |
455 HttpConnectionInfo get connectionInfo => _httpRequest.connectionInfo; | 506 HttpConnectionInfo get connectionInfo => _httpRequest.connectionInfo; |
456 | 507 |
457 void _writeHeader() { | 508 void _writeHeader() { |
458 writeSP() => add([_CharCode.SP]); | 509 writeSP() => writeBytes([_CharCode.SP]); |
459 writeCRLF() => add([_CharCode.CR, _CharCode.LF]); | 510 writeCRLF() => writeBytes([_CharCode.CR, _CharCode.LF]); |
460 | 511 |
461 // Write status line. | 512 // Write status line. |
462 if (headers.protocolVersion == "1.1") { | 513 if (headers.protocolVersion == "1.1") { |
463 add(_Const.HTTP11); | 514 writeBytes(_Const.HTTP11); |
464 } else { | 515 } else { |
465 add(_Const.HTTP10); | 516 writeBytes(_Const.HTTP10); |
466 } | 517 } |
467 writeSP(); | 518 writeSP(); |
468 addString(statusCode.toString()); | 519 write(statusCode.toString()); |
469 writeSP(); | 520 writeSP(); |
470 addString(reasonPhrase); | 521 write(reasonPhrase); |
471 writeCRLF(); | 522 writeCRLF(); |
472 | 523 |
473 var session = _httpRequest._session; | 524 var session = _httpRequest._session; |
474 if (session != null && !session._destroyed) { | 525 if (session != null && !session._destroyed) { |
475 // Mark as not new. | 526 // Mark as not new. |
476 session._isNew = false; | 527 session._isNew = false; |
477 // Make sure we only send the current session id. | 528 // Make sure we only send the current session id. |
478 bool found = false; | 529 bool found = false; |
479 for (int i = 0; i < cookies.length; i++) { | 530 for (int i = 0; i < cookies.length; i++) { |
480 if (cookies[i].name.toUpperCase() == _DART_SESSION_ID) { | 531 if (cookies[i].name.toUpperCase() == _DART_SESSION_ID) { |
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
641 onError: (e) { | 692 onError: (e) { |
642 _responseCompleter.completeError(e); | 693 _responseCompleter.completeError(e); |
643 }); | 694 }); |
644 } | 695 } |
645 | 696 |
646 void _onError(AsyncError error) { | 697 void _onError(AsyncError error) { |
647 _responseCompleter.completeError(error); | 698 _responseCompleter.completeError(error); |
648 } | 699 } |
649 | 700 |
650 void _writeHeader() { | 701 void _writeHeader() { |
651 writeSP() => add([_CharCode.SP]); | 702 writeSP() => writeBytes([_CharCode.SP]); |
652 writeCRLF() => add([_CharCode.CR, _CharCode.LF]); | 703 writeCRLF() => writeBytes([_CharCode.CR, _CharCode.LF]); |
653 | 704 |
654 addString(method); | 705 write(method); |
655 writeSP(); | 706 writeSP(); |
656 // Send the path for direct connections and the whole URL for | 707 // Send the path for direct connections and the whole URL for |
657 // proxy connections. | 708 // proxy connections. |
658 if (!_usingProxy) { | 709 if (!_usingProxy) { |
659 String path = uri.path; | 710 String path = uri.path; |
660 if (path.length == 0) path = "/"; | 711 if (path.length == 0) path = "/"; |
661 if (uri.query != "") { | 712 if (uri.query != "") { |
662 if (uri.fragment != "") { | 713 if (uri.fragment != "") { |
663 path = "${path}?${uri.query}#${uri.fragment}"; | 714 path = "${path}?${uri.query}#${uri.fragment}"; |
664 } else { | 715 } else { |
665 path = "${path}?${uri.query}"; | 716 path = "${path}?${uri.query}"; |
666 } | 717 } |
667 } | 718 } |
668 addString(path); | 719 write(path); |
669 } else { | 720 } else { |
670 addString(uri.toString()); | 721 write(uri.toString()); |
671 } | 722 } |
672 writeSP(); | 723 writeSP(); |
673 add(_Const.HTTP11); | 724 writeBytes(_Const.HTTP11); |
674 writeCRLF(); | 725 writeCRLF(); |
675 | 726 |
676 // Add the cookies to the headers. | 727 // Add the cookies to the headers. |
677 if (!cookies.isEmpty) { | 728 if (!cookies.isEmpty) { |
678 StringBuffer sb = new StringBuffer(); | 729 StringBuffer sb = new StringBuffer(); |
679 for (int i = 0; i < cookies.length; i++) { | 730 for (int i = 0; i < cookies.length; i++) { |
680 if (i > 0) sb.write("; "); | 731 if (i > 0) sb.write("; "); |
681 sb.write(cookies[i].name); | 732 sb.write(cookies[i].name); |
682 sb.write("="); | 733 sb.write("="); |
683 sb.write(cookies[i].value); | 734 sb.write(cookies[i].value); |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
771 var subscription; | 822 var subscription; |
772 subscription = stream.listen( | 823 subscription = stream.listen( |
773 (data) { | 824 (data) { |
774 if (expectedTransferLength != null) { | 825 if (expectedTransferLength != null) { |
775 _bytesWritten += data.length; | 826 _bytesWritten += data.length; |
776 if (_bytesWritten > expectedTransferLength) { | 827 if (_bytesWritten > expectedTransferLength) { |
777 subscription.cancel(); | 828 subscription.cancel(); |
778 _controller.signalError(new HttpException( | 829 _controller.signalError(new HttpException( |
779 "Content size exceeds specified contentLength. " | 830 "Content size exceeds specified contentLength. " |
780 "$_bytesWritten bytes written while expected " | 831 "$_bytesWritten bytes written while expected " |
781 "$expectedTransferLength.")); | 832 "$expectedTransferLength. " |
833 "[${new String.fromCharCodes(data)}]")); | |
782 _controller.close(); | 834 _controller.close(); |
783 return; | 835 return; |
784 } | 836 } |
785 } | 837 } |
786 _controller.add(data); | 838 _controller.add(data); |
787 }, | 839 }, |
788 onError: (error) { | 840 onError: (error) { |
789 _controller.signalError(error); | 841 _controller.signalError(error); |
790 _controller.close(); | 842 _controller.close(); |
791 }, | 843 }, |
(...skipping 713 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1505 StreamSubscription<List<int>> listen(void onData(List<int> event), | 1557 StreamSubscription<List<int>> listen(void onData(List<int> event), |
1506 {void onError(AsyncError error), | 1558 {void onError(AsyncError error), |
1507 void onDone(), | 1559 void onDone(), |
1508 bool unsubscribeOnError}) { | 1560 bool unsubscribeOnError}) { |
1509 return _incoming.listen(onData, | 1561 return _incoming.listen(onData, |
1510 onError: onError, | 1562 onError: onError, |
1511 onDone: onDone, | 1563 onDone: onDone, |
1512 unsubscribeOnError: unsubscribeOnError); | 1564 unsubscribeOnError: unsubscribeOnError); |
1513 } | 1565 } |
1514 | 1566 |
1567 Encoding get encoding => _socket.encoding; | |
1568 void set encoding(Encoding value) => _socket.encoding = value; | |
1569 | |
1570 void write(Object obj) => _socket.write(obj); | |
1571 void writeln(Object obj) => _socket.writeln(obj); | |
1572 void writeCharCode(int charCode) => _socket.writeCharCode(charCode); | |
1573 void writeAll(Iterable objects) => _socket.writeAll(objects); | |
1574 | |
Mads Ager (google)
2013/03/07 07:57:36
Nit: consistently have blank line between write me
Søren Gjesse
2013/03/07 16:28:47
Done.
| |
1575 void writeBytes(List<int> bytes) => _socket.writeBytes(bytes); | |
1576 | |
1515 Future<Socket> consume(Stream<List<int>> stream) { | 1577 Future<Socket> consume(Stream<List<int>> stream) { |
1516 return _socket.consume(stream); | 1578 return _socket.consume(stream); |
1517 } | 1579 } |
1518 | 1580 |
1519 Future<Socket> addStream(Stream<List<int>> stream) { | 1581 Future<Socket> addStream(Stream<List<int>> stream) { |
1520 return _socket.addStream(stream); | 1582 return _socket.addStream(stream); |
1521 } | 1583 } |
1522 | 1584 |
1523 void addString(String string, [Encoding encoding = Encoding.UTF_8]) { | 1585 void addString(String string, [Encoding encoding = Encoding.UTF_8]) { |
1524 return _socket.addString(string, encoding); | 1586 return _socket.addString(string, encoding); |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1639 | 1701 |
1640 | 1702 |
1641 class _RedirectInfo implements RedirectInfo { | 1703 class _RedirectInfo implements RedirectInfo { |
1642 const _RedirectInfo(int this.statusCode, | 1704 const _RedirectInfo(int this.statusCode, |
1643 String this.method, | 1705 String this.method, |
1644 Uri this.location); | 1706 Uri this.location); |
1645 final int statusCode; | 1707 final int statusCode; |
1646 final String method; | 1708 final String method; |
1647 final Uri location; | 1709 final Uri location; |
1648 } | 1710 } |
OLD | NEW |