| 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 const String _webSocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; | 7 const String _webSocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; |
| 8 const String _clientNoContextTakeover = "client_no_context_takeover"; | 8 const String _clientNoContextTakeover = "client_no_context_takeover"; |
| 9 const String _serverNoContextTakeover = "server_no_context_takeover"; | 9 const String _serverNoContextTakeover = "server_no_context_takeover"; |
| 10 const String _clientMaxWindowBits = "client_max_window_bits"; | 10 const String _clientMaxWindowBits = "client_max_window_bits"; |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 52 * The web socket protocol transformer handles the protocol byte stream | 52 * The web socket protocol transformer handles the protocol byte stream |
| 53 * which is supplied through the [:handleData:]. As the protocol is processed, | 53 * which is supplied through the [:handleData:]. As the protocol is processed, |
| 54 * it'll output frame data as either a List<int> or String. | 54 * it'll output frame data as either a List<int> or String. |
| 55 * | 55 * |
| 56 * Important information about usage: Be sure you use cancelOnError, so the | 56 * Important information about usage: Be sure you use cancelOnError, so the |
| 57 * socket will be closed when the processor encounter an error. Not using it | 57 * socket will be closed when the processor encounter an error. Not using it |
| 58 * will lead to undefined behaviour. | 58 * will lead to undefined behaviour. |
| 59 */ | 59 */ |
| 60 // TODO(ajohnsen): make this transformer reusable? | 60 // TODO(ajohnsen): make this transformer reusable? |
| 61 class _WebSocketProtocolTransformer | 61 class _WebSocketProtocolTransformer |
| 62 implements StreamTransformer<List<int>, dynamic>, EventSink<Uint8List> { | 62 implements StreamTransformer<List<int>, dynamic>, EventSink<List<int>> { |
| 63 static const int START = 0; | 63 static const int START = 0; |
| 64 static const int LEN_FIRST = 1; | 64 static const int LEN_FIRST = 1; |
| 65 static const int LEN_REST = 2; | 65 static const int LEN_REST = 2; |
| 66 static const int MASK = 3; | 66 static const int MASK = 3; |
| 67 static const int PAYLOAD = 4; | 67 static const int PAYLOAD = 4; |
| 68 static const int CLOSED = 5; | 68 static const int CLOSED = 5; |
| 69 static const int FAILURE = 6; | 69 static const int FAILURE = 6; |
| 70 static const int FIN = 0x80; | 70 static const int FIN = 0x80; |
| 71 static const int RSV1 = 0x40; | 71 static const int RSV1 = 0x40; |
| 72 static const int RSV2 = 0x20; | 72 static const int RSV2 = 0x20; |
| (...skipping 920 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 993 request.headers.add("Sec-WebSocket-Protocol", protocols.toList()); | 993 request.headers.add("Sec-WebSocket-Protocol", protocols.toList()); |
| 994 } | 994 } |
| 995 | 995 |
| 996 if (compression.enabled) { | 996 if (compression.enabled) { |
| 997 request.headers | 997 request.headers |
| 998 .add("Sec-WebSocket-Extensions", compression._createHeader()); | 998 .add("Sec-WebSocket-Extensions", compression._createHeader()); |
| 999 } | 999 } |
| 1000 | 1000 |
| 1001 return request.close(); | 1001 return request.close(); |
| 1002 }).then((response) { | 1002 }).then((response) { |
| 1003 | |
| 1004 void error(String message) { | 1003 void error(String message) { |
| 1005 // Flush data. | 1004 // Flush data. |
| 1006 response.detachSocket().then((socket) { | 1005 response.detachSocket().then((socket) { |
| 1007 socket.destroy(); | 1006 socket.destroy(); |
| 1008 }); | 1007 }); |
| 1009 throw new WebSocketException(message); | 1008 throw new WebSocketException(message); |
| 1010 } | 1009 } |
| 1011 | |
| 1012 if (response.statusCode != HttpStatus.SWITCHING_PROTOCOLS || | 1010 if (response.statusCode != HttpStatus.SWITCHING_PROTOCOLS || |
| 1013 response.headers[HttpHeaders.CONNECTION] == null || | 1011 response.headers[HttpHeaders.CONNECTION] == null || |
| 1014 !response.headers[HttpHeaders.CONNECTION] | 1012 !response.headers[HttpHeaders.CONNECTION] |
| 1015 .any((value) => value.toLowerCase() == "upgrade") || | 1013 .any((value) => value.toLowerCase() == "upgrade") || |
| 1016 response.headers.value(HttpHeaders.UPGRADE).toLowerCase() != | 1014 response.headers.value(HttpHeaders.UPGRADE).toLowerCase() != |
| 1017 "websocket") { | 1015 "websocket") { |
| 1018 error("Connection to '$uri' was not upgraded to websocket"); | 1016 error("Connection to '$uri' was not upgraded to websocket"); |
| 1019 } | 1017 } |
| 1020 String accept = response.headers.value("Sec-WebSocket-Accept"); | 1018 String accept = response.headers.value("Sec-WebSocket-Accept"); |
| 1021 if (accept == null) { | 1019 if (accept == null) { |
| 1022 error("Response did not contain a 'Sec-WebSocket-Accept' header"); | 1020 error("Response did not contain a 'Sec-WebSocket-Accept' header"); |
| 1023 } | 1021 } |
| 1024 _SHA1 sha1 = new _SHA1(); | 1022 _SHA1 sha1 = new _SHA1(); |
| 1025 sha1.add("$nonce$_webSocketGUID".codeUnits); | 1023 sha1.add("$nonce$_webSocketGUID".codeUnits); |
| 1026 List<int> expectedAccept = sha1.close(); | 1024 List<int> expectedAccept = sha1.close(); |
| 1027 List<int> receivedAccept = _CryptoUtils.base64StringToBytes(accept); | 1025 List<int> receivedAccept = _CryptoUtils.base64StringToBytes(accept); |
| 1028 if (expectedAccept.length != receivedAccept.length) { | 1026 if (expectedAccept.length != receivedAccept.length) { |
| 1029 error("Reasponse header 'Sec-WebSocket-Accept' is the wrong length"); | 1027 error("Reasponse header 'Sec-WebSocket-Accept' is the wrong length"); |
| 1030 } | 1028 } |
| 1031 for (int i = 0; i < expectedAccept.length; i++) { | 1029 for (int i = 0; i < expectedAccept.length; i++) { |
| 1032 if (expectedAccept[i] != receivedAccept[i]) { | 1030 if (expectedAccept[i] != receivedAccept[i]) { |
| 1033 error("Bad response 'Sec-WebSocket-Accept' header"); | 1031 error("Bad response 'Sec-WebSocket-Accept' header"); |
| 1034 } | 1032 } |
| 1035 } | 1033 } |
| 1036 var protocol = response.headers.value('Sec-WebSocket-Protocol'); | 1034 var protocol = response.headers.value('Sec-WebSocket-Protocol'); |
| 1037 | 1035 |
| 1038 _WebSocketPerMessageDeflate deflate = | 1036 _WebSocketPerMessageDeflate deflate = |
| 1039 negotiateClientCompression(response, compression); | 1037 negotiateClientCompression(response, compression); |
| 1040 | 1038 |
| 1041 return response.detachSocket().then/*<WebSocket>*/((socket) => | 1039 return response.detachSocket().then((socket) => |
| 1042 new _WebSocketImpl._fromSocket( | 1040 new _WebSocketImpl._fromSocket( |
| 1043 socket, protocol, compression, false, deflate)); | 1041 socket, protocol, compression, false, deflate)); |
| 1044 }); | 1042 }); |
| 1045 } | 1043 } |
| 1046 | 1044 |
| 1047 static _WebSocketPerMessageDeflate negotiateClientCompression( | 1045 static _WebSocketPerMessageDeflate negotiateClientCompression( |
| 1048 HttpClientResponse response, CompressionOptions compression) { | 1046 HttpClientResponse response, CompressionOptions compression) { |
| 1049 String extensionHeader = response.headers.value('Sec-WebSocket-Extensions'); | 1047 String extensionHeader = response.headers.value('Sec-WebSocket-Extensions'); |
| 1050 | 1048 |
| 1051 if (extensionHeader == null) { | 1049 if (extensionHeader == null) { |
| 1052 extensionHeader = ""; | 1050 extensionHeader = ""; |
| 1053 } | 1051 } |
| 1054 | 1052 |
| 1055 var hv = HeaderValue.parse(extensionHeader, valueSeparator: ','); | 1053 var hv = HeaderValue.parse(extensionHeader, valueSeparator: ','); |
| 1056 | 1054 |
| 1057 if (compression.enabled && hv.value == PER_MESSAGE_DEFLATE) { | 1055 if (compression.enabled && hv.value == PER_MESSAGE_DEFLATE) { |
| 1058 var serverNoContextTakeover = | 1056 var serverNoContextTakeover = |
| 1059 hv.parameters.containsKey(_serverNoContextTakeover); | 1057 hv.parameters.containsKey(_serverNoContextTakeover); |
| 1060 var clientNoContextTakeover = | 1058 var clientNoContextTakeover = |
| 1061 hv.parameters.containsKey(_clientNoContextTakeover); | 1059 hv.parameters.containsKey(_clientNoContextTakeover); |
| 1062 | 1060 |
| 1063 int getWindowBits(String type) { | 1061 int getWindowBits(String type) { |
| 1064 var o = hv.parameters[type]; | 1062 var o = hv.parameters[type]; |
| 1065 if (o == null) { | 1063 if (o == null) { |
| 1066 return DEFAULT_WINDOW_BITS; | 1064 return DEFAULT_WINDOW_BITS; |
| 1067 } | 1065 } |
| 1068 | 1066 |
| 1069 return int.parse(o, onError: (s) => DEFAULT_WINDOW_BITS); | 1067 o = int.parse(o, onError: (s) => DEFAULT_WINDOW_BITS); |
| 1068 return o; |
| 1070 } | 1069 } |
| 1071 | 1070 |
| 1072 return new _WebSocketPerMessageDeflate( | 1071 return new _WebSocketPerMessageDeflate( |
| 1073 clientMaxWindowBits: getWindowBits(_clientMaxWindowBits), | 1072 clientMaxWindowBits: getWindowBits(_clientMaxWindowBits), |
| 1074 serverMaxWindowBits: getWindowBits(_serverMaxWindowBits), | 1073 serverMaxWindowBits: getWindowBits(_serverMaxWindowBits), |
| 1075 clientNoContextTakeover: clientNoContextTakeover, | 1074 clientNoContextTakeover: clientNoContextTakeover, |
| 1076 serverNoContextTakeover: serverNoContextTakeover); | 1075 serverNoContextTakeover: serverNoContextTakeover); |
| 1077 } | 1076 } |
| 1078 | 1077 |
| 1079 return null; | 1078 return null; |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1214 _outCloseReason = reason; | 1213 _outCloseReason = reason; |
| 1215 } | 1214 } |
| 1216 _writeClosed = true; | 1215 _writeClosed = true; |
| 1217 _consumer.closeSocket(); | 1216 _consumer.closeSocket(); |
| 1218 _webSockets.remove(_serviceId); | 1217 _webSockets.remove(_serviceId); |
| 1219 } | 1218 } |
| 1220 | 1219 |
| 1221 String get _serviceTypePath => 'io/websockets'; | 1220 String get _serviceTypePath => 'io/websockets'; |
| 1222 String get _serviceTypeName => 'WebSocket'; | 1221 String get _serviceTypeName => 'WebSocket'; |
| 1223 | 1222 |
| 1224 Map<String, dynamic> _toJSON(bool ref) { | 1223 Map _toJSON(bool ref) { |
| 1225 var name = '${_socket.address.host}:${_socket.port}'; | 1224 var name = '${_socket.address.host}:${_socket.port}'; |
| 1226 var r = <String, dynamic>{ | 1225 var r = <String, dynamic>{ |
| 1227 'id': _servicePath, | 1226 'id': _servicePath, |
| 1228 'type': _serviceType(ref), | 1227 'type': _serviceType(ref), |
| 1229 'name': name, | 1228 'name': name, |
| 1230 'user_name': name, | 1229 'user_name': name, |
| 1231 }; | 1230 }; |
| 1232 if (ref) { | 1231 if (ref) { |
| 1233 return r; | 1232 return r; |
| 1234 } | 1233 } |
| (...skipping 14 matching lines...) Expand all Loading... |
| 1249 return code != null && | 1248 return code != null && |
| 1250 (code < WebSocketStatus.NORMAL_CLOSURE || | 1249 (code < WebSocketStatus.NORMAL_CLOSURE || |
| 1251 code == WebSocketStatus.RESERVED_1004 || | 1250 code == WebSocketStatus.RESERVED_1004 || |
| 1252 code == WebSocketStatus.NO_STATUS_RECEIVED || | 1251 code == WebSocketStatus.NO_STATUS_RECEIVED || |
| 1253 code == WebSocketStatus.ABNORMAL_CLOSURE || | 1252 code == WebSocketStatus.ABNORMAL_CLOSURE || |
| 1254 (code > WebSocketStatus.INTERNAL_SERVER_ERROR && | 1253 (code > WebSocketStatus.INTERNAL_SERVER_ERROR && |
| 1255 code < WebSocketStatus.RESERVED_1015) || | 1254 code < WebSocketStatus.RESERVED_1015) || |
| 1256 (code >= WebSocketStatus.RESERVED_1015 && code < 3000)); | 1255 (code >= WebSocketStatus.RESERVED_1015 && code < 3000)); |
| 1257 } | 1256 } |
| 1258 } | 1257 } |
| OLD | NEW |