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

Side by Side Diff: sdk/lib/io/websocket_impl.dart

Issue 1208473005: WebSocket Compression (Closed) Base URL: https://github.com/dart-lang/sdk.git
Patch Set: Take Suggestions Created 5 years, 5 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
« sdk/lib/io/websocket.dart ('K') | « sdk/lib/io/websocket.dart ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 const String _webSocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; 7 const String _webSocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
8 8
9 // Matches _WebSocketOpcode. 9 // Matches _WebSocketOpcode.
10 class _WebSocketMessageType { 10 class _WebSocketMessageType {
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
44 */ 44 */
45 // TODO(ajohnsen): make this transformer reusable? 45 // TODO(ajohnsen): make this transformer reusable?
46 class _WebSocketProtocolTransformer implements StreamTransformer, EventSink { 46 class _WebSocketProtocolTransformer implements StreamTransformer, EventSink {
47 static const int START = 0; 47 static const int START = 0;
48 static const int LEN_FIRST = 1; 48 static const int LEN_FIRST = 1;
49 static const int LEN_REST = 2; 49 static const int LEN_REST = 2;
50 static const int MASK = 3; 50 static const int MASK = 3;
51 static const int PAYLOAD = 4; 51 static const int PAYLOAD = 4;
52 static const int CLOSED = 5; 52 static const int CLOSED = 5;
53 static const int FAILURE = 6; 53 static const int FAILURE = 6;
54 static const int FIN = 0x80;
55 static const int RSV1 = 0x40;
56 static const int RSV2 = 0x20;
57 static const int RSV3 = 0x10;
58 static const int OPCODE = 0xF;
54 59
55 int _state = START; 60 int _state = START;
56 bool _fin = false; 61 bool _fin = false;
57 bool _compressed = false; 62 bool _compressed = false;
58 int _opcode = -1; 63 int _opcode = -1;
59 int _len = -1; 64 int _len = -1;
60 bool _masked = false; 65 bool _masked = false;
61 int _remainingLenBytes = -1; 66 int _remainingLenBytes = -1;
62 int _remainingMaskingKeyBytes = 4; 67 int _remainingMaskingKeyBytes = 4;
63 int _remainingPayloadBytes = -1; 68 int _remainingPayloadBytes = -1;
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
102 if (_state == CLOSED) { 107 if (_state == CLOSED) {
103 throw new WebSocketException("Data on closed connection"); 108 throw new WebSocketException("Data on closed connection");
104 } 109 }
105 if (_state == FAILURE) { 110 if (_state == FAILURE) {
106 throw new WebSocketException("Data on failed connection"); 111 throw new WebSocketException("Data on failed connection");
107 } 112 }
108 while ((index < lastIndex) && _state != CLOSED && _state != FAILURE) { 113 while ((index < lastIndex) && _state != CLOSED && _state != FAILURE) {
109 int byte = buffer[index]; 114 int byte = buffer[index];
110 if (_state <= LEN_REST) { 115 if (_state <= LEN_REST) {
111 if (_state == START) { 116 if (_state == START) {
112 _fin = (byte & 0x80) != 0; 117 _fin = (byte & FIN) != 0;
113 118
114 if ((byte & 0x40) != 0) { 119 if ((byte & RSV1) != 0) {
115 _compressed = true; 120 _compressed = true;
116 } 121 }
117 122
118 if ((byte & 0x20) != 0 || (byte & 0x10) != 0) { 123 if ((byte & RSV2) != 0 || (byte & RSV3) != 0) {
119 // The RSV2 and RSV3 bits must be all zero. 124 // The RSV2 and RSV3 bits must be all zero.
120 throw new WebSocketException("Protocol error"); 125 throw new WebSocketException("Protocol error");
121 } 126 }
122 127
123 _opcode = (byte & 0xF); 128 _opcode = (byte & OPCODE);
124 129
125 if (_opcode <= _WebSocketOpcode.BINARY) { 130 if (_opcode <= _WebSocketOpcode.BINARY) {
126 if (_opcode == _WebSocketOpcode.CONTINUATION) { 131 if (_opcode == _WebSocketOpcode.CONTINUATION) {
127 if (_currentMessageType == _WebSocketMessageType.NONE) { 132 if (_currentMessageType == _WebSocketMessageType.NONE) {
128 throw new WebSocketException("Protocol error"); 133 throw new WebSocketException("Protocol error");
129 } 134 }
130 } else { 135 } else {
131 assert(_opcode == _WebSocketOpcode.TEXT || 136 assert(_opcode == _WebSocketOpcode.TEXT ||
132 _opcode == _WebSocketOpcode.BINARY); 137 _opcode == _WebSocketOpcode.BINARY);
133 if (_currentMessageType != _WebSocketMessageType.NONE) { 138 if (_currentMessageType != _WebSocketMessageType.NONE) {
(...skipping 280 matching lines...) Expand 10 before | Expand all | Expand 10 after
414 ..headers.add(HttpHeaders.UPGRADE, "websocket"); 419 ..headers.add(HttpHeaders.UPGRADE, "websocket");
415 String key = request.headers.value("Sec-WebSocket-Key"); 420 String key = request.headers.value("Sec-WebSocket-Key");
416 _SHA1 sha1 = new _SHA1(); 421 _SHA1 sha1 = new _SHA1();
417 sha1.add("$key$_webSocketGUID".codeUnits); 422 sha1.add("$key$_webSocketGUID".codeUnits);
418 String accept = _CryptoUtils.bytesToBase64(sha1.close()); 423 String accept = _CryptoUtils.bytesToBase64(sha1.close());
419 response.headers.add("Sec-WebSocket-Accept", accept); 424 response.headers.add("Sec-WebSocket-Accept", accept);
420 if (protocol != null) { 425 if (protocol != null) {
421 response.headers.add("Sec-WebSocket-Protocol", protocol); 426 response.headers.add("Sec-WebSocket-Protocol", protocol);
422 } 427 }
423 428
424 var extensionHeader = request.headers.value("Sec-WebSocket-Extensions"); 429 var deflate = _negotiateCompression(request, response, compression);
425
426 if (extensionHeader == null) {
427 extensionHeader = "";
428 }
429
430 Iterable<List<String>> extensions = extensionHeader.split(",").map((it) => it.split("; "));
431
432 if (compression.enabled && extensions.any((x) => x[0] == "permessage-defla te")) {
433 var opts = extensions.firstWhere((x) => x[0] == "permessage-deflate");
434 response.headers.add("Sec-WebSocket-Extensions", compression._createHead er(opts));
435 }
436 430
437 response.headers.contentLength = 0; 431 response.headers.contentLength = 0;
438 return response.detachSocket() 432 return response.detachSocket()
439 .then((socket) => new _WebSocketImpl._fromSocket( 433 .then((socket) => new _WebSocketImpl._fromSocket(
440 socket, protocol, compression, true)); 434 socket, protocol, compression, true, deflate));
441 } 435 }
442 436
443 var protocols = request.headers['Sec-WebSocket-Protocol']; 437 var protocols = request.headers['Sec-WebSocket-Protocol'];
444 if (protocols != null && _protocolSelector != null) { 438 if (protocols != null && _protocolSelector != null) {
445 // The suggested protocols can be spread over multiple lines, each 439 // The suggested protocols can be spread over multiple lines, each
446 // consisting of multiple protocols. To unify all of them, first join 440 // consisting of multiple protocols. To unify all of them, first join
447 // the lists with ', ' and then tokenize. 441 // the lists with ', ' and then tokenize.
448 protocols = _HttpParser._tokenizeFieldValue(protocols.join(', ')); 442 protocols = _HttpParser._tokenizeFieldValue(protocols.join(', '));
449 return new Future(() => _protocolSelector(protocols)) 443 return new Future(() => _protocolSelector(protocols))
450 .then((protocol) { 444 .then((protocol) {
451 if (protocols.indexOf(protocol) < 0) { 445 if (protocols.indexOf(protocol) < 0) {
452 throw new WebSocketException( 446 throw new WebSocketException(
453 "Selected protocol is not in the list of available protocols"); 447 "Selected protocol is not in the list of available protocols");
454 } 448 }
455 return protocol; 449 return protocol;
456 }) 450 })
457 .catchError((error) { 451 .catchError((error) {
458 response 452 response
459 ..statusCode = HttpStatus.INTERNAL_SERVER_ERROR 453 ..statusCode = HttpStatus.INTERNAL_SERVER_ERROR
460 ..close(); 454 ..close();
461 throw error; 455 throw error;
462 }) 456 })
463 .then(upgrade); 457 .then(upgrade);
464 } else { 458 } else {
465 return upgrade(null); 459 return upgrade(null);
466 } 460 }
467 } 461 }
468 462
463 static _WebSocketPerMessageDeflate _negotiateCompression(HttpRequest request,
464 HttpResponse response,
465 CompressionOptions compression) {
kustermann 2015/07/20 09:34:12 Consider using dart_style for automatic indentatio
466 var extensionHeader = request.headers.value("Sec-WebSocket-Extensions");
467
468 if (extensionHeader == null) {
469 extensionHeader = "";
470 }
471
472 Iterable<List<String>> extensions = extensionHeader
473 .split(",")
474 .map((it) => it.split("; "));
475
476 if (compression.enabled && extensions.any((x) => x[0] == _WebSocketImpl.PER_ MESSAGE_DEFLATE)) {
477 var opts = extensions.firstWhere((x) => x[0] == _WebSocketImpl.PER_MESSAGE _DEFLATE);
kustermann 2015/07/20 09:34:12 Seems redundant to do two traversals of [extension
kustermann 2015/07/20 09:34:12 Normally we don't abbreviate, meaning opts -> opti
478 response.headers.add("Sec-WebSocket-Extensions", compression._createHeader (opts));
479 var noContextTakeover = opts.contains("server_no_context_takeover");
480 var deflate = new _WebSocketPerMessageDeflate(
481 noContextTakeover: noContextTakeover,
482 serverSide: true);
483
484 return deflate;
485 }
486
487 return null;
488 }
489
469 static bool _isUpgradeRequest(HttpRequest request) { 490 static bool _isUpgradeRequest(HttpRequest request) {
470 if (request.method != "GET") { 491 if (request.method != "GET") {
471 return false; 492 return false;
472 } 493 }
473 if (request.headers[HttpHeaders.CONNECTION] == null) { 494 if (request.headers[HttpHeaders.CONNECTION] == null) {
474 return false; 495 return false;
475 } 496 }
476 bool isUpgrade = false; 497 bool isUpgrade = false;
477 request.headers[HttpHeaders.CONNECTION].forEach((String value) { 498 request.headers[HttpHeaders.CONNECTION].forEach((String value) {
478 if (value.toLowerCase() == "upgrade") isUpgrade = true; 499 if (value.toLowerCase() == "upgrade") isUpgrade = true;
(...skipping 18 matching lines...) Expand all
497 class _WebSocketPerMessageDeflate { 518 class _WebSocketPerMessageDeflate {
498 bool noContextTakeover; 519 bool noContextTakeover;
499 int clientMaxWindowBits; 520 int clientMaxWindowBits;
500 int serverMaxWindowBits; 521 int serverMaxWindowBits;
501 bool serverSide; 522 bool serverSide;
502 523
503 ZLibDecoder decoder; 524 ZLibDecoder decoder;
504 ZLibEncoder encoder; 525 ZLibEncoder encoder;
505 526
506 _WebSocketPerMessageDeflate({this.clientMaxWindowBits, 527 _WebSocketPerMessageDeflate({this.clientMaxWindowBits,
507 this.serverMaxWindowBits, this.noContextTakeover, 528 this.serverMaxWindowBits,
529 this.noContextTakeover,
508 this.serverSide: false}) { 530 this.serverSide: false}) {
509 if (clientMaxWindowBits == null) { 531 if (clientMaxWindowBits == null) {
510 clientMaxWindowBits = 15; 532 clientMaxWindowBits = _WebSocketImpl.DEFAULT_WINDOW_BITS;
511 } 533 }
512 534
513 if (serverMaxWindowBits == null) { 535 if (serverMaxWindowBits == null) {
514 serverMaxWindowBits = 15; 536 serverMaxWindowBits = _WebSocketImpl.DEFAULT_WINDOW_BITS;
515 } 537 }
516 } 538 }
517 539
518 void _ensureDecoder() { 540 void _ensureDecoder() {
519 if (noContextTakeover || decoder == null) { 541 if (noContextTakeover || decoder == null) {
520 decoder = new ZLibDecoder(windowBits: serverSide ? 542 decoder = new ZLibDecoder(windowBits: serverSide ?
521 clientMaxWindowBits : serverMaxWindowBits); 543 clientMaxWindowBits : serverMaxWindowBits);
522 } 544 }
523 } 545 }
524 546
525 void _ensureEncoder() { 547 void _ensureEncoder() {
526 if (noContextTakeover || encoder == null) { 548 if (noContextTakeover || encoder == null) {
527 encoder = new ZLibEncoder(windowBits: serverSide ? 549 encoder = new ZLibEncoder(windowBits: serverSide ?
528 serverMaxWindowBits : clientMaxWindowBits); 550 serverMaxWindowBits : clientMaxWindowBits);
529 } 551 }
530 } 552 }
531 553
532 List<int> processIncomingMessage(List<int> msg) { 554 List<int> processIncomingMessage(List<int> msg) {
533 _ensureDecoder(); 555 _ensureDecoder();
534 var builder = new BytesBuilder(); 556 var builder = new BytesBuilder();
535 builder.add(msg); 557 builder.add(msg);
536 builder.add(const [0x00, 0x00, 0xff, 0xff]); 558 builder.add(const [0x00, 0x00, 0xff, 0xff]);
537 return decoder.convert(builder.takeBytes()); 559 var result = decoder.convert(builder.takeBytes());
560 if (noContextTakeover) {
561 decoder = null;
562 }
563 return result;
538 } 564 }
539 565
540 List<int> processOutgoingMessage(List<int> msg) { 566 List<int> processOutgoingMessage(List<int> msg) {
541 _ensureEncoder(); 567 _ensureEncoder();
542 var c = encoder.convert(msg); 568 var c = encoder.convert(msg);
543 c = c.sublist(0, c.length - 4); 569 c = c.sublist(0, c.length - 4);
570 if (noContextTakeover) {
571 encoder = null;
572 }
544 return c; 573 return c;
545 } 574 }
546 } 575 }
547 576
548 // TODO(ajohnsen): Make this transformer reusable. 577 // TODO(ajohnsen): Make this transformer reusable.
549 class _WebSocketOutgoingTransformer implements StreamTransformer, EventSink { 578 class _WebSocketOutgoingTransformer implements StreamTransformer, EventSink {
550 final _WebSocketImpl webSocket; 579 final _WebSocketImpl webSocket;
551 EventSink _eventSink; 580 EventSink _eventSink;
552 581
553 _WebSocketPerMessageDeflate _deflateHelper; 582 _WebSocketPerMessageDeflate _deflateHelper;
(...skipping 20 matching lines...) Expand all
574 if (message is _WebSocketPing) { 603 if (message is _WebSocketPing) {
575 addFrame(_WebSocketOpcode.PING, message.payload); 604 addFrame(_WebSocketOpcode.PING, message.payload);
576 return; 605 return;
577 } 606 }
578 List<int> data; 607 List<int> data;
579 int opcode; 608 int opcode;
580 if (message != null) { 609 if (message != null) {
581 if (message is String) { 610 if (message is String) {
582 opcode = _WebSocketOpcode.TEXT; 611 opcode = _WebSocketOpcode.TEXT;
583 data = UTF8.encode(message); 612 data = UTF8.encode(message);
584
585 if (_deflateHelper != null) {
586 data = _deflateHelper.processOutgoingMessage(data);
587 }
588 } else { 613 } else {
589 if (message is !List<int>) { 614 if (message is !List<int>) {
590 throw new ArgumentError(message); 615 throw new ArgumentError(message);
591 } 616 }
592 opcode = _WebSocketOpcode.BINARY; 617 opcode = _WebSocketOpcode.BINARY;
593 data = message; 618 data = message;
594
595 if (_deflateHelper != null) {
596 data = _deflateHelper.processOutgoingMessage(data);
597 }
598 } 619 }
599 620
600 if (_deflateHelper != null) { 621 if (_deflateHelper != null) {
601 data = _deflateHelper.processOutgoingMessage(data); 622 data = _deflateHelper.processOutgoingMessage(data);
602 } 623 }
603 } else { 624 } else {
604 opcode = _WebSocketOpcode.TEXT; 625 opcode = _WebSocketOpcode.TEXT;
605 } 626 }
606 addFrame(opcode, data); 627 addFrame(opcode, data);
607 } 628 }
(...skipping 11 matching lines...) Expand all
619 data.add(code & 0xFF); 640 data.add(code & 0xFF);
620 if (reason != null) { 641 if (reason != null) {
621 data.addAll(UTF8.encode(reason)); 642 data.addAll(UTF8.encode(reason));
622 } 643 }
623 } 644 }
624 addFrame(_WebSocketOpcode.CLOSE, data); 645 addFrame(_WebSocketOpcode.CLOSE, data);
625 _eventSink.close(); 646 _eventSink.close();
626 } 647 }
627 648
628 void addFrame(int opcode, List<int> data) => 649 void addFrame(int opcode, List<int> data) =>
629 createFrame(opcode, data, webSocket._serverSide, webSocket._deflate != null) 650 createFrame(opcode, data, webSocket._serverSide, _deflateHelper != null)
630 .forEach((e) { 651 .forEach((e) {
631 _eventSink.add(e); 652 _eventSink.add(e);
632 }); 653 });
633 654
634 static Iterable createFrame(int opcode, List<int> data, bool serverSide, bool compressed) { 655 static Iterable createFrame(int opcode, List<int> data, bool serverSide, bool compressed) {
635 bool mask = !serverSide; // Masking not implemented for server. 656 bool mask = !serverSide; // Masking not implemented for server.
636 int dataLength = data == null ? 0 : data.length; 657 int dataLength = data == null ? 0 : data.length;
637 // Determine the header size. 658 // Determine the header size.
638 int headerSize = (mask) ? 6 : 2; 659 int headerSize = (mask) ? 6 : 2;
639 if (dataLength > 65535) { 660 if (dataLength > 65535) {
(...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after
845 _closed = true; 866 _closed = true;
846 _cancel(); 867 _cancel();
847 close(); 868 close();
848 } 869 }
849 } 870 }
850 871
851 872
852 class _WebSocketImpl extends Stream with _ServiceObject implements WebSocket { 873 class _WebSocketImpl extends Stream with _ServiceObject implements WebSocket {
853 // Use default Map so we keep order. 874 // Use default Map so we keep order.
854 static Map<int, _WebSocketImpl> _webSockets = new Map<int, _WebSocketImpl>(); 875 static Map<int, _WebSocketImpl> _webSockets = new Map<int, _WebSocketImpl>();
876 static const int DEFAULT_WINDOW_BITS = 15;
877 static const String PER_MESSAGE_DEFLATE = "permessage-deflate";
855 878
856 final String protocol; 879 final String protocol;
857 880
858 StreamController _controller; 881 StreamController _controller;
859 StreamSubscription _subscription; 882 StreamSubscription _subscription;
860 StreamSink _sink; 883 StreamSink _sink;
861 884
862 final _socket; 885 final _socket;
863 final bool _serverSide; 886 final bool _serverSide;
864 int _readyState = WebSocket.CONNECTING; 887 int _readyState = WebSocket.CONNECTING;
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
910 } 933 }
911 if (headers != null) { 934 if (headers != null) {
912 headers.forEach((field, value) => request.headers.add(field, value)); 935 headers.forEach((field, value) => request.headers.add(field, value));
913 } 936 }
914 // Setup the initial handshake. 937 // Setup the initial handshake.
915 request.headers 938 request.headers
916 ..set(HttpHeaders.CONNECTION, "Upgrade") 939 ..set(HttpHeaders.CONNECTION, "Upgrade")
917 ..set(HttpHeaders.UPGRADE, "websocket") 940 ..set(HttpHeaders.UPGRADE, "websocket")
918 ..set("Sec-WebSocket-Key", nonce) 941 ..set("Sec-WebSocket-Key", nonce)
919 ..set("Cache-Control", "no-cache") 942 ..set("Cache-Control", "no-cache")
920 ..set("Sec-WebSocket-Version", "13") 943 ..set("Sec-WebSocket-Version", "13");
921 ..set("Sec-WebSocket-Extensions", "permessage-deflate");
922 if (protocols != null) { 944 if (protocols != null) {
923 request.headers.add("Sec-WebSocket-Protocol", protocols.toList()); 945 request.headers.add("Sec-WebSocket-Protocol", protocols.toList());
924 } 946 }
925 947
926 request.headers.add("Sec-WebSocket-Extensions", compression._createHeade r()); 948 request.headers.add("Sec-WebSocket-Extensions", compression._createHeade r());
927 949
928 return request.close(); 950 return request.close();
929 }) 951 })
930 .then((response) { 952 .then((response) {
931 void error(String message) { 953 void error(String message) {
(...skipping 22 matching lines...) Expand all
954 if (expectedAccept.length != receivedAccept.length) { 976 if (expectedAccept.length != receivedAccept.length) {
955 error("Reasponse header 'Sec-WebSocket-Accept' is the wrong length"); 977 error("Reasponse header 'Sec-WebSocket-Accept' is the wrong length");
956 } 978 }
957 for (int i = 0; i < expectedAccept.length; i++) { 979 for (int i = 0; i < expectedAccept.length; i++) {
958 if (expectedAccept[i] != receivedAccept[i]) { 980 if (expectedAccept[i] != receivedAccept[i]) {
959 error("Bad response 'Sec-WebSocket-Accept' header"); 981 error("Bad response 'Sec-WebSocket-Accept' header");
960 } 982 }
961 } 983 }
962 var protocol = response.headers.value('Sec-WebSocket-Protocol'); 984 var protocol = response.headers.value('Sec-WebSocket-Protocol');
963 985
964 String extensionHeader = response.headers.value('Sec-WebSocket-Extension s'); 986 _WebSocketPerMessageDeflate deflate =
965 987 negotiateClientCompression(response, compression);
966 if (extensionHeader == null) {
967 extensionHeader = "";
968 }
969
970 Iterable<List<String>> extensions = extensionHeader
971 .split(", ")
972 .map((it) => it.split("; "));
973
974 _WebSocketPerMessageDeflate deflate;
975
976 if (compression.enabled && extensions.any((x) => x[0] == "permessage-def late")) {
977 var opts = extensions.firstWhere((x) => x[0] == "permessage-deflate");
978 var noContextTakeover = opts.contains("client_no_context_takeover");
979
980 int getWindowBits(String type) {
981 var o = opts.firstWhere((x) =>
982 x.startsWith("${type}_max_window_bits="), orElse: () => null);
983
984 if (o == null) {
985 return 15;
986 }
987
988 try {
989 o = o.substring("client_max_window_bits=".length);
990 o = int.parse(o);
991 } catch (e) {
992 return 15;
993 }
994
995 return o;
996 }
997
998 deflate = new _WebSocketPerMessageDeflate(
999 clientMaxWindowBits: getWindowBits("client"),
1000 serverMaxWindowBits: getWindowBits("server"),
1001 noContextTakeover: noContextTakeover);
1002 }
1003 988
1004 return response.detachSocket() 989 return response.detachSocket()
1005 .then((socket) => new _WebSocketImpl._fromSocket(socket, protocol, 990 .then((socket) => new _WebSocketImpl._fromSocket(socket, protocol,
1006 compression, false, deflate)); 991 compression, false, deflate));
1007 }); 992 });
1008 } 993 }
1009 994
995 static _WebSocketPerMessageDeflate negotiateClientCompression(
996 HttpClientResponse response,
997 CompressionOptions compression) {
998 String extensionHeader = response.headers.value('Sec-WebSocket-Extensions');
999
1000 if (extensionHeader == null) {
1001 extensionHeader = "";
1002 }
1003
1004 Iterable<List<String>> extensions = extensionHeader
1005 .split(", ")
1006 .map((it) => it.split("; "));
1007
1008 if (compression.enabled && extensions.any((x) => x[0] == PER_MESSAGE_DEFLATE )) {
1009 var opts = extensions.firstWhere((x) => x[0] == PER_MESSAGE_DEFLATE);
1010 var noContextTakeover = opts.contains("client_no_context_takeover");
1011
1012 int getWindowBits(String type) {
1013 var o = opts.firstWhere((x) =>
1014 x.startsWith("${type}_max_window_bits="), orElse: () => null);
1015
1016 if (o == null) {
1017 return DEFAULT_WINDOW_BITS;
1018 }
1019
1020 try {
1021 o = o.substring("${type}_max_window_bits=".length);
1022 o = int.parse(o);
1023 } catch (e) {
1024 return DEFAULT_WINDOW_BITS;
1025 }
1026
1027 return o;
1028 }
1029
1030 return new _WebSocketPerMessageDeflate(
1031 clientMaxWindowBits: getWindowBits("client"),
1032 serverMaxWindowBits: getWindowBits("server"),
1033 noContextTakeover: noContextTakeover);
1034 }
1035
1036 return null;
1037 }
1038
1010 _WebSocketImpl._fromSocket(this._socket, this.protocol, 1039 _WebSocketImpl._fromSocket(this._socket, this.protocol,
1011 CompressionOptions compression, [this._serverSide = false, 1040 CompressionOptions compression, [this._serverSide = false,
1012 _WebSocketPerMessageDeflate deflate]) { 1041 _WebSocketPerMessageDeflate deflate]) {
1013 _consumer = new _WebSocketConsumer(this, _socket); 1042 _consumer = new _WebSocketConsumer(this, _socket);
1014 _sink = new _StreamSinkImpl(_consumer); 1043 _sink = new _StreamSinkImpl(_consumer);
1015 _readyState = WebSocket.OPEN; 1044 _readyState = WebSocket.OPEN;
1016 _deflate = deflate; 1045 _deflate = deflate;
1017 1046
1018 var transformer = new _WebSocketProtocolTransformer(_serverSide, _deflate); 1047 var transformer = new _WebSocketProtocolTransformer(_serverSide, _deflate);
1019 _subscription = _socket.transform(transformer).listen( 1048 _subscription = _socket.transform(transformer).listen(
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after
1182 (code < WebSocketStatus.NORMAL_CLOSURE || 1211 (code < WebSocketStatus.NORMAL_CLOSURE ||
1183 code == WebSocketStatus.RESERVED_1004 || 1212 code == WebSocketStatus.RESERVED_1004 ||
1184 code == WebSocketStatus.NO_STATUS_RECEIVED || 1213 code == WebSocketStatus.NO_STATUS_RECEIVED ||
1185 code == WebSocketStatus.ABNORMAL_CLOSURE || 1214 code == WebSocketStatus.ABNORMAL_CLOSURE ||
1186 (code > WebSocketStatus.INTERNAL_SERVER_ERROR && 1215 (code > WebSocketStatus.INTERNAL_SERVER_ERROR &&
1187 code < WebSocketStatus.RESERVED_1015) || 1216 code < WebSocketStatus.RESERVED_1015) ||
1188 (code >= WebSocketStatus.RESERVED_1015 && 1217 (code >= WebSocketStatus.RESERVED_1015 &&
1189 code < 3000)); 1218 code < 3000));
1190 } 1219 }
1191 } 1220 }
OLDNEW
« sdk/lib/io/websocket.dart ('K') | « sdk/lib/io/websocket.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698