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: 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 var charset; |
| 350 if (headers.contentType != null && headers.contentType.charset != null) { |
| 351 charset = headers.contentType.charset; |
| 352 } else { |
| 353 charset = "iso-8859-1"; |
| 354 } |
| 355 return Encoding.fromName(charset); |
| 356 } |
| 357 |
| 358 void set encoding(Encoding value) { |
| 359 throw new StateError("IOSink encoding is not mutable"); |
| 360 } |
| 361 |
| 362 void write(Object obj) { |
| 363 _writeHeaders(); |
| 364 if (_ignoreBody) return; |
| 365 // This comment is copied from runtime/lib/string_buffer_patch.dart. |
| 366 // TODO(srdjan): The following four lines could be replaced by |
| 367 // '$obj', but apparently this is too slow on the Dart VM. |
| 368 String string; |
| 369 if (obj is String) { |
| 370 string = obj; |
| 371 } else { |
| 372 string = obj.toString(); |
| 373 if (string is! String) { |
| 374 throw new ArgumentError('toString() did not return a string'); |
| 375 } |
| 376 } |
| 377 if (string.isEmpty) return; |
| 378 if (_chunked) { |
| 379 _ChunkedTransformer._addChunk(_encodeString(string, encoding), |
| 380 _ioSink.writeBytes); |
| 381 } else { |
| 382 _ioSink.write(string); |
| 383 } |
| 384 } |
| 385 |
| 386 void writeAll(Iterable objects) { |
| 387 for (Object obj in objects) write(obj); |
| 388 } |
| 389 |
| 390 void writeln(Object obj) { |
| 391 write(obj); |
| 392 write("\n"); |
| 393 } |
| 394 |
| 395 void writeCharCode(int charCode) { |
| 396 write(new String.fromCharCode(charCode)); |
| 397 } |
| 398 |
| 399 void writeBytes(List<int> data) { |
| 400 _writeHeaders(); |
| 401 if (_ignoreBody || data.length == 0) return; |
| 402 if (_chunked) { |
| 403 _ChunkedTransformer._addChunk(data, _ioSink.writeBytes); |
| 404 } else { |
| 405 _ioSink.writeBytes(data); |
| 406 } |
| 407 } |
| 408 |
348 Future<T> consume(Stream<List<int>> stream) { | 409 Future<T> consume(Stream<List<int>> stream) { |
349 _writeHeaders(); | 410 _writeHeaders(); |
350 if (_ignoreBody) return new Future.immediate(this); | 411 if (_ignoreBody) return new Future.immediate(this); |
351 if (_chunked) { | 412 if (_chunked) { |
352 // Transform when chunked. | 413 // Transform when chunked. |
353 stream = stream.transform(new _ChunkedTransformer()); | 414 stream = stream.transform(new _ChunkedTransformer()); |
354 } | 415 } |
355 return _ioSink.consume(stream).then((_) => this); | 416 return _ioSink.consume(stream).then((_) => this); |
356 } | 417 } |
357 | 418 |
358 void add(List<int> data) { | 419 Future<T> writeStream(Stream<List<int>> stream) { |
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 | |
368 void addString(String string, [Encoding encoding = Encoding.UTF_8]) { | |
369 add(_encodeString(string, encoding)); | |
370 } | |
371 | |
372 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.writeStream(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 _headersWritten = true; | 448 _headersWritten = true; |
| 449 _ioSink.encoding = Encoding.ASCII; |
402 _writeHeader(); | 450 _writeHeader(); |
| 451 _ioSink.encoding = encoding; |
403 if (_ignoreBody) { | 452 if (_ignoreBody) { |
404 _ioSink.close(); | 453 _ioSink.close(); |
405 return; | 454 return; |
406 } | 455 } |
407 _chunked = headers.chunkedTransferEncoding; | 456 _chunked = headers.chunkedTransferEncoding; |
408 if (headers.contentLength >= 0) { | 457 if (headers.contentLength >= 0) { |
409 _outgoing.setTransferLength(headers.contentLength); | 458 _outgoing.setTransferLength(headers.contentLength); |
410 } | 459 } |
411 } | 460 } |
412 | 461 |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
445 done.catchError((_) { | 494 done.catchError((_) { |
446 // Catch any error on done, as they automatically will be propegated to | 495 // Catch any error on done, as they automatically will be propegated to |
447 // the websocket. | 496 // the websocket. |
448 }); | 497 }); |
449 return future; | 498 return future; |
450 } | 499 } |
451 | 500 |
452 HttpConnectionInfo get connectionInfo => _httpRequest.connectionInfo; | 501 HttpConnectionInfo get connectionInfo => _httpRequest.connectionInfo; |
453 | 502 |
454 void _writeHeader() { | 503 void _writeHeader() { |
455 writeSP() => _ioSink.add([_CharCode.SP]); | 504 writeSP() => _ioSink.writeBytes([_CharCode.SP]); |
456 writeCRLF() => _ioSink.add([_CharCode.CR, _CharCode.LF]); | 505 writeCRLF() => _ioSink.writeBytes([_CharCode.CR, _CharCode.LF]); |
457 | 506 |
458 // Write status line. | 507 // Write status line. |
459 if (headers.protocolVersion == "1.1") { | 508 if (headers.protocolVersion == "1.1") { |
460 _ioSink.add(_Const.HTTP11); | 509 _ioSink.writeBytes(_Const.HTTP11); |
461 } else { | 510 } else { |
462 _ioSink.add(_Const.HTTP10); | 511 _ioSink.writeBytes(_Const.HTTP10); |
463 } | 512 } |
464 writeSP(); | 513 writeSP(); |
465 _ioSink.addString(statusCode.toString()); | 514 _ioSink.write(statusCode.toString()); |
466 writeSP(); | 515 writeSP(); |
467 _ioSink.addString(reasonPhrase); | 516 _ioSink.write(reasonPhrase); |
468 writeCRLF(); | 517 writeCRLF(); |
469 | 518 |
470 var session = _httpRequest._session; | 519 var session = _httpRequest._session; |
471 if (session != null && !session._destroyed) { | 520 if (session != null && !session._destroyed) { |
472 // Mark as not new. | 521 // Mark as not new. |
473 session._isNew = false; | 522 session._isNew = false; |
474 // Make sure we only send the current session id. | 523 // Make sure we only send the current session id. |
475 bool found = false; | 524 bool found = false; |
476 for (int i = 0; i < cookies.length; i++) { | 525 for (int i = 0; i < cookies.length; i++) { |
477 if (cookies[i].name.toUpperCase() == _DART_SESSION_ID) { | 526 if (cookies[i].name.toUpperCase() == _DART_SESSION_ID) { |
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
638 onError: (e) { | 687 onError: (e) { |
639 _responseCompleter.completeError(e); | 688 _responseCompleter.completeError(e); |
640 }); | 689 }); |
641 } | 690 } |
642 | 691 |
643 void _onError(AsyncError error) { | 692 void _onError(AsyncError error) { |
644 _responseCompleter.completeError(error); | 693 _responseCompleter.completeError(error); |
645 } | 694 } |
646 | 695 |
647 void _writeHeader() { | 696 void _writeHeader() { |
648 writeSP() => _ioSink.add([_CharCode.SP]); | 697 writeSP() => _ioSink.writeBytes([_CharCode.SP]); |
649 writeCRLF() => _ioSink.add([_CharCode.CR, _CharCode.LF]); | 698 writeCRLF() => _ioSink.writeBytes([_CharCode.CR, _CharCode.LF]); |
650 | 699 |
651 _ioSink.addString(method); | 700 _ioSink.write(method); |
652 writeSP(); | 701 writeSP(); |
653 // Send the path for direct connections and the whole URL for | 702 // Send the path for direct connections and the whole URL for |
654 // proxy connections. | 703 // proxy connections. |
655 if (!_usingProxy) { | 704 if (!_usingProxy) { |
656 String path = uri.path; | 705 String path = uri.path; |
657 if (path.length == 0) path = "/"; | 706 if (path.length == 0) path = "/"; |
658 if (uri.query != "") { | 707 if (uri.query != "") { |
659 if (uri.fragment != "") { | 708 if (uri.fragment != "") { |
660 path = "${path}?${uri.query}#${uri.fragment}"; | 709 path = "${path}?${uri.query}#${uri.fragment}"; |
661 } else { | 710 } else { |
662 path = "${path}?${uri.query}"; | 711 path = "${path}?${uri.query}"; |
663 } | 712 } |
664 } | 713 } |
665 _ioSink.addString(path); | 714 _ioSink.write(path); |
666 } else { | 715 } else { |
667 _ioSink.addString(uri.toString()); | 716 _ioSink.write(uri.toString()); |
668 } | 717 } |
669 writeSP(); | 718 writeSP(); |
670 _ioSink.add(_Const.HTTP11); | 719 _ioSink.writeBytes(_Const.HTTP11); |
671 writeCRLF(); | 720 writeCRLF(); |
672 | 721 |
673 // Add the cookies to the headers. | 722 // Add the cookies to the headers. |
674 if (!cookies.isEmpty) { | 723 if (!cookies.isEmpty) { |
675 StringBuffer sb = new StringBuffer(); | 724 StringBuffer sb = new StringBuffer(); |
676 for (int i = 0; i < cookies.length; i++) { | 725 for (int i = 0; i < cookies.length; i++) { |
677 if (i > 0) sb.write("; "); | 726 if (i > 0) sb.write("; "); |
678 sb.write(cookies[i].name); | 727 sb.write(cookies[i].name); |
679 sb.write("="); | 728 sb.write("="); |
680 sb.write(cookies[i].value); | 729 sb.write(cookies[i].value); |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
768 var subscription; | 817 var subscription; |
769 subscription = stream.listen( | 818 subscription = stream.listen( |
770 (data) { | 819 (data) { |
771 if (expectedTransferLength != null) { | 820 if (expectedTransferLength != null) { |
772 _bytesWritten += data.length; | 821 _bytesWritten += data.length; |
773 if (_bytesWritten > expectedTransferLength) { | 822 if (_bytesWritten > expectedTransferLength) { |
774 subscription.cancel(); | 823 subscription.cancel(); |
775 _controller.signalError(new HttpException( | 824 _controller.signalError(new HttpException( |
776 "Content size exceeds specified contentLength. " | 825 "Content size exceeds specified contentLength. " |
777 "$_bytesWritten bytes written while expected " | 826 "$_bytesWritten bytes written while expected " |
778 "$expectedTransferLength.")); | 827 "$expectedTransferLength. " |
| 828 "[${new String.fromCharCodes(data)}]")); |
779 _controller.close(); | 829 _controller.close(); |
780 return; | 830 return; |
781 } | 831 } |
782 } | 832 } |
783 _controller.add(data); | 833 _controller.add(data); |
784 }, | 834 }, |
785 onError: (error) { | 835 onError: (error) { |
786 _controller.signalError(error); | 836 _controller.signalError(error); |
787 _controller.close(); | 837 _controller.close(); |
788 }, | 838 }, |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
894 cr.authorize(request); | 944 cr.authorize(request); |
895 } | 945 } |
896 } | 946 } |
897 // Start sending the request (lazy, delayed until the user provides | 947 // Start sending the request (lazy, delayed until the user provides |
898 // data). | 948 // data). |
899 _httpParser.responseToMethod = method; | 949 _httpParser.responseToMethod = method; |
900 _streamFuture = outgoing.onStream((stream) { | 950 _streamFuture = outgoing.onStream((stream) { |
901 // Sending request, set up response completer. | 951 // Sending request, set up response completer. |
902 _nextResponseCompleter = new Completer(); | 952 _nextResponseCompleter = new Completer(); |
903 | 953 |
904 var requestFuture = _socket.addStream(stream) | 954 var requestFuture = _socket.writeStream(stream) |
905 .catchError((e) { | 955 .catchError((e) { |
906 destroy(); | 956 destroy(); |
907 throw e; | 957 throw e; |
908 }); | 958 }); |
909 | 959 |
910 // Listen for response. | 960 // Listen for response. |
911 _nextResponseCompleter.future | 961 _nextResponseCompleter.future |
912 .then((incoming) { | 962 .then((incoming) { |
913 incoming.dataDone.then((_) { | 963 incoming.dataDone.then((_) { |
914 if (incoming.headers.persistentConnection && | 964 if (incoming.headers.persistentConnection && |
(...skipping 306 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1221 (incoming) { | 1271 (incoming) { |
1222 // Only handle one incoming request at the time. Keep the | 1272 // Only handle one incoming request at the time. Keep the |
1223 // stream paused until the request has been send. | 1273 // stream paused until the request has been send. |
1224 _subscription.pause(); | 1274 _subscription.pause(); |
1225 _state = _ACTIVE; | 1275 _state = _ACTIVE; |
1226 var outgoing = new _HttpOutgoing(); | 1276 var outgoing = new _HttpOutgoing(); |
1227 var response = new _HttpResponse(incoming.headers.protocolVersion, | 1277 var response = new _HttpResponse(incoming.headers.protocolVersion, |
1228 outgoing); | 1278 outgoing); |
1229 var request = new _HttpRequest(response, incoming, _httpServer, this); | 1279 var request = new _HttpRequest(response, incoming, _httpServer, this); |
1230 outgoing.onStream((stream) { | 1280 outgoing.onStream((stream) { |
1231 return _streamFuture = _socket.addStream(stream) | 1281 return _streamFuture = _socket.writeStream(stream) |
1232 .then((_) { | 1282 .then((_) { |
1233 if (_state == _DETACHED) return; | 1283 if (_state == _DETACHED) return; |
1234 if (response.persistentConnection && | 1284 if (response.persistentConnection && |
1235 request.persistentConnection && | 1285 request.persistentConnection && |
1236 incoming.fullBodyRead) { | 1286 incoming.fullBodyRead) { |
1237 _state = _IDLE; | 1287 _state = _IDLE; |
1238 // Resume the subscription for incoming requests as the | 1288 // Resume the subscription for incoming requests as the |
1239 // request is now processed. | 1289 // request is now processed. |
1240 _subscription.resume(); | 1290 _subscription.resume(); |
1241 } else { | 1291 } else { |
(...skipping 260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1502 StreamSubscription<List<int>> listen(void onData(List<int> event), | 1552 StreamSubscription<List<int>> listen(void onData(List<int> event), |
1503 {void onError(AsyncError error), | 1553 {void onError(AsyncError error), |
1504 void onDone(), | 1554 void onDone(), |
1505 bool unsubscribeOnError}) { | 1555 bool unsubscribeOnError}) { |
1506 return _incoming.listen(onData, | 1556 return _incoming.listen(onData, |
1507 onError: onError, | 1557 onError: onError, |
1508 onDone: onDone, | 1558 onDone: onDone, |
1509 unsubscribeOnError: unsubscribeOnError); | 1559 unsubscribeOnError: unsubscribeOnError); |
1510 } | 1560 } |
1511 | 1561 |
| 1562 Encoding get encoding => _socket.encoding; |
| 1563 |
| 1564 void set encoding(Encoding value) { |
| 1565 _socket.encoding = value; |
| 1566 } |
| 1567 |
| 1568 void write(Object obj) => _socket.write(obj); |
| 1569 |
| 1570 void writeln(Object obj) => _socket.writeln(obj); |
| 1571 |
| 1572 void writeCharCode(int charCode) => _socket.writeCharCode(charCode); |
| 1573 |
| 1574 void writeAll(Iterable objects) => _socket.writeAll(objects); |
| 1575 |
| 1576 void writeBytes(List<int> bytes) => _socket.writeBytes(bytes); |
| 1577 |
1512 Future<Socket> consume(Stream<List<int>> stream) { | 1578 Future<Socket> consume(Stream<List<int>> stream) { |
1513 return _socket.consume(stream); | 1579 return _socket.consume(stream); |
1514 } | 1580 } |
1515 | 1581 |
1516 Future<Socket> addStream(Stream<List<int>> stream) { | 1582 Future<Socket> writeStream(Stream<List<int>> stream) { |
1517 return _socket.addStream(stream); | 1583 return _socket.writeStream(stream); |
1518 } | |
1519 | |
1520 void addString(String string, [Encoding encoding = Encoding.UTF_8]) { | |
1521 return _socket.addString(string, encoding); | |
1522 } | 1584 } |
1523 | 1585 |
1524 void destroy() => _socket.destroy(); | 1586 void destroy() => _socket.destroy(); |
1525 void add(List<int> data) => _socket.add(data); | 1587 |
1526 void close() => _socket.close(); | 1588 void close() => _socket.close(); |
| 1589 |
1527 Future<Socket> get done => _socket.done; | 1590 Future<Socket> get done => _socket.done; |
| 1591 |
1528 int get port => _socket.port; | 1592 int get port => _socket.port; |
| 1593 |
1529 String get remoteHost => _socket.remoteHost; | 1594 String get remoteHost => _socket.remoteHost; |
| 1595 |
1530 int get remotePort => _socket.remotePort; | 1596 int get remotePort => _socket.remotePort; |
1531 } | 1597 } |
1532 | 1598 |
1533 | 1599 |
1534 class _AuthenticationScheme { | 1600 class _AuthenticationScheme { |
1535 static const UNKNOWN = const _AuthenticationScheme(-1); | 1601 static const UNKNOWN = const _AuthenticationScheme(-1); |
1536 static const BASIC = const _AuthenticationScheme(0); | 1602 static const BASIC = const _AuthenticationScheme(0); |
1537 static const DIGEST = const _AuthenticationScheme(1); | 1603 static const DIGEST = const _AuthenticationScheme(1); |
1538 | 1604 |
1539 const _AuthenticationScheme(this._scheme); | 1605 const _AuthenticationScheme(this._scheme); |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1636 | 1702 |
1637 | 1703 |
1638 class _RedirectInfo implements RedirectInfo { | 1704 class _RedirectInfo implements RedirectInfo { |
1639 const _RedirectInfo(int this.statusCode, | 1705 const _RedirectInfo(int this.statusCode, |
1640 String this.method, | 1706 String this.method, |
1641 Uri this.location); | 1707 Uri this.location); |
1642 final int statusCode; | 1708 final int statusCode; |
1643 final String method; | 1709 final String method; |
1644 final Uri location; | 1710 final Uri location; |
1645 } | 1711 } |
OLD | NEW |