| 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 | 8 |
| 9 // Matches _WebSocketOpcode. |
| 9 class _WebSocketMessageType { | 10 class _WebSocketMessageType { |
| 10 static const int NONE = 0; | 11 static const int NONE = 0; |
| 11 static const int BINARY = 1; | 12 static const int TEXT = 1; |
| 12 static const int TEXT = 2; | 13 static const int BINARY = 2; |
| 13 } | 14 } |
| 14 | 15 |
| 15 | 16 |
| 16 class _WebSocketOpcode { | 17 class _WebSocketOpcode { |
| 17 static const int CONTINUATION = 0; | 18 static const int CONTINUATION = 0; |
| 18 static const int TEXT = 1; | 19 static const int TEXT = 1; |
| 19 static const int BINARY = 2; | 20 static const int BINARY = 2; |
| 20 static const int RESERVED_3 = 3; | 21 static const int RESERVED_3 = 3; |
| 21 static const int RESERVED_4 = 4; | 22 static const int RESERVED_4 = 4; |
| 22 static const int RESERVED_5 = 5; | 23 static const int RESERVED_5 = 5; |
| (...skipping 21 matching lines...) Expand all Loading... |
| 44 // TODO(ajohnsen): make this transformer reusable? | 45 // TODO(ajohnsen): make this transformer reusable? |
| 45 class _WebSocketProtocolTransformer implements StreamTransformer, EventSink { | 46 class _WebSocketProtocolTransformer implements StreamTransformer, EventSink { |
| 46 static const int START = 0; | 47 static const int START = 0; |
| 47 static const int LEN_FIRST = 1; | 48 static const int LEN_FIRST = 1; |
| 48 static const int LEN_REST = 2; | 49 static const int LEN_REST = 2; |
| 49 static const int MASK = 3; | 50 static const int MASK = 3; |
| 50 static const int PAYLOAD = 4; | 51 static const int PAYLOAD = 4; |
| 51 static const int CLOSED = 5; | 52 static const int CLOSED = 5; |
| 52 static const int FAILURE = 6; | 53 static const int FAILURE = 6; |
| 53 | 54 |
| 54 int _state; | 55 int _state = START; |
| 55 bool _fin; | 56 bool _fin = false; |
| 56 int _opcode; | 57 int _opcode = -1; |
| 57 int _len; | 58 int _len = -1; |
| 58 bool _masked; | 59 bool _masked = false; |
| 59 int _maskingKey; | 60 int _remainingLenBytes = -1; |
| 60 int _remainingLenBytes; | 61 int _remainingMaskingKeyBytes = 4; |
| 61 int _remainingMaskingKeyBytes; | 62 int _remainingPayloadBytes = -1; |
| 62 int _remainingPayloadBytes; | 63 int _unmaskingIndex = 0; |
| 63 int _unmaskingIndex; | 64 int _currentMessageType = _WebSocketMessageType.NONE; |
| 64 | |
| 65 int _currentMessageType; | |
| 66 List<int> _controlPayload; | |
| 67 StreamController _controller; | |
| 68 | |
| 69 int closeCode = WebSocketStatus.NO_STATUS_RECEIVED; | 65 int closeCode = WebSocketStatus.NO_STATUS_RECEIVED; |
| 70 String closeReason = ""; | 66 String closeReason = ""; |
| 71 | 67 |
| 72 bool _serverSide; | |
| 73 EventSink _eventSink; | 68 EventSink _eventSink; |
| 74 | 69 |
| 75 _WebSocketProtocolTransformer([this._serverSide = false]) { | 70 final bool _serverSide; |
| 76 _prepareForNextFrame(); | 71 final List _maskingBytes = new List(4); |
| 77 _currentMessageType = _WebSocketMessageType.NONE; | 72 final BytesBuilder _payload = new BytesBuilder(); |
| 78 } | 73 |
| 74 _WebSocketProtocolTransformer([this._serverSide = false]); |
| 79 | 75 |
| 80 Stream bind(Stream stream) { | 76 Stream bind(Stream stream) { |
| 81 return new Stream.eventTransformed( | 77 return new Stream.eventTransformed( |
| 82 stream, | 78 stream, |
| 83 (EventSink eventSink) { | 79 (EventSink eventSink) { |
| 84 if (_eventSink != null) { | 80 if (_eventSink != null) { |
| 85 throw new StateError("WebSocket transformer already used."); | 81 throw new StateError("WebSocket transformer already used."); |
| 86 } | 82 } |
| 87 _eventSink = eventSink; | 83 _eventSink = eventSink; |
| 88 return this; | 84 return this; |
| 89 }); | 85 }); |
| 90 } | 86 } |
| 91 | 87 |
| 92 void addError(Object error, [StackTrace stackTrace]) => | 88 void addError(Object error, [StackTrace stackTrace]) => |
| 93 _eventSink.addError(error, stackTrace); | 89 _eventSink.addError(error, stackTrace); |
| 94 | 90 |
| 95 void close() => _eventSink.close(); | 91 void close() => _eventSink.close(); |
| 96 | 92 |
| 97 /** | 93 /** |
| 98 * Process data received from the underlying communication channel. | 94 * Process data received from the underlying communication channel. |
| 99 */ | 95 */ |
| 100 void add(Uint8List buffer) { | 96 void add(Uint8List buffer) { |
| 101 int count = buffer.length; | 97 int count = buffer.length; |
| 102 int index = 0; | 98 int index = 0; |
| 103 int lastIndex = count; | 99 int lastIndex = count; |
| 104 try { | 100 if (_state == CLOSED) { |
| 105 if (_state == CLOSED) { | 101 throw new WebSocketException("Data on closed connection"); |
| 106 throw new WebSocketException("Data on closed connection"); | 102 } |
| 107 } | 103 if (_state == FAILURE) { |
| 108 if (_state == FAILURE) { | 104 throw new WebSocketException("Data on failed connection"); |
| 109 throw new WebSocketException("Data on failed connection"); | 105 } |
| 110 } | 106 while ((index < lastIndex) && _state != CLOSED && _state != FAILURE) { |
| 111 while ((index < lastIndex) && _state != CLOSED && _state != FAILURE) { | 107 int byte = buffer[index]; |
| 112 int byte = buffer[index]; | 108 if (_state <= LEN_REST) { |
| 113 switch (_state) { | 109 if (_state == START) { |
| 114 case START: | 110 _fin = (byte & 0x80) != 0; |
| 115 _fin = (byte & 0x80) != 0; | 111 if ((byte & 0x70) != 0) { |
| 116 if ((byte & 0x70) != 0) { | 112 // The RSV1, RSV2 bits RSV3 must be all zero. |
| 117 // The RSV1, RSV2 bits RSV3 most be all zero. | 113 throw new WebSocketException("Protocol error"); |
| 118 throw new WebSocketException("Protocol error"); | 114 } |
| 119 } | 115 _opcode = (byte & 0xF); |
| 120 _opcode = (byte & 0xF); | 116 if (_opcode <= _WebSocketOpcode.BINARY) { |
| 121 switch (_opcode) { | 117 if (_opcode == _WebSocketOpcode.CONTINUATION) { |
| 122 case _WebSocketOpcode.CONTINUATION: | |
| 123 if (_currentMessageType == _WebSocketMessageType.NONE) { | 118 if (_currentMessageType == _WebSocketMessageType.NONE) { |
| 124 throw new WebSocketException("Protocol error"); | 119 throw new WebSocketException("Protocol error"); |
| 125 } | 120 } |
| 126 break; | 121 } else { |
| 127 | 122 assert(_opcode == _WebSocketOpcode.TEXT || |
| 128 case _WebSocketOpcode.TEXT: | 123 _opcode == _WebSocketOpcode.BINARY); |
| 129 if (_currentMessageType != _WebSocketMessageType.NONE) { | 124 if (_currentMessageType != _WebSocketMessageType.NONE) { |
| 130 throw new WebSocketException("Protocol error"); | 125 throw new WebSocketException("Protocol error"); |
| 131 } | 126 } |
| 132 _currentMessageType = _WebSocketMessageType.TEXT; | 127 _currentMessageType = _opcode; |
| 133 _controller = new StreamController(sync: true); | 128 } |
| 134 _controller.stream | 129 } else if (_opcode >= _WebSocketOpcode.CLOSE && |
| 135 .transform(UTF8.decoder) | 130 _opcode <= _WebSocketOpcode.PONG) { |
| 136 .fold(new StringBuffer(), (buffer, str) => buffer..write(str)) | 131 // Control frames cannot be fragmented. |
| 137 .then((buffer) { | 132 if (!_fin) throw new WebSocketException("Protocol error"); |
| 138 _eventSink.add(buffer.toString()); | 133 } else { |
| 139 }, onError: _eventSink.addError); | 134 throw new WebSocketException("Protocol error"); |
| 140 break; | 135 } |
| 136 _state = LEN_FIRST; |
| 137 } else if (_state == LEN_FIRST) { |
| 138 _masked = (byte & 0x80) != 0; |
| 139 _len = byte & 0x7F; |
| 140 if (_isControlFrame() && _len > 125) { |
| 141 throw new WebSocketException("Protocol error"); |
| 142 } |
| 143 if (_len == 126) { |
| 144 _len = 0; |
| 145 _remainingLenBytes = 2; |
| 146 _state = LEN_REST; |
| 147 } else if (_len == 127) { |
| 148 _len = 0; |
| 149 _remainingLenBytes = 8; |
| 150 _state = LEN_REST; |
| 151 } else { |
| 152 assert(_len < 126); |
| 153 _lengthDone(); |
| 154 } |
| 155 } else { |
| 156 assert(_state == LEN_REST); |
| 157 _len = _len << 8 | byte; |
| 158 _remainingLenBytes--; |
| 159 if (_remainingLenBytes == 0) { |
| 160 _lengthDone(); |
| 161 } |
| 162 } |
| 163 } else { |
| 164 if (_state == MASK) { |
| 165 _maskingBytes[4 - _remainingMaskingKeyBytes--] = byte; |
| 166 if (_remainingMaskingKeyBytes == 0) { |
| 167 _maskDone(); |
| 168 } |
| 169 } else { |
| 170 assert(_state == PAYLOAD); |
| 171 // The payload is not handled one byte at a time but in blocks. |
| 172 int payload = min(lastIndex - index, _remainingPayloadBytes); |
| 173 _remainingPayloadBytes -= payload; |
| 174 // Unmask payload if masked. |
| 175 if (_masked) { |
| 176 for (int i = index; i < index + payload; i++) { |
| 177 buffer[i] ^= _maskingBytes[_unmaskingIndex++ & 3]; |
| 178 } |
| 179 } |
| 180 // Control frame and data frame share _payload builder. |
| 181 _payload.add(new Uint8List.view(buffer.buffer, index, payload)); |
| 182 index += payload; |
| 183 if (_isControlFrame()) { |
| 184 if (_remainingPayloadBytes == 0) _controlFrameEnd(); |
| 185 } else { |
| 186 if (_currentMessageType != _WebSocketMessageType.TEXT && |
| 187 _currentMessageType != _WebSocketMessageType.BINARY) { |
| 188 throw new WebSocketException("Protocol error"); |
| 189 } |
| 190 if (_remainingPayloadBytes == 0) _messageFrameEnd(); |
| 191 } |
| 141 | 192 |
| 142 case _WebSocketOpcode.BINARY: | 193 // Hack - as we always do index++ below. |
| 143 if (_currentMessageType != _WebSocketMessageType.NONE) { | 194 index--; |
| 144 throw new WebSocketException("Protocol error"); | 195 } |
| 145 } | 196 } |
| 146 _currentMessageType = _WebSocketMessageType.BINARY; | |
| 147 _controller = new StreamController(sync: true); | |
| 148 _controller.stream | |
| 149 .fold(new BytesBuilder(), (buffer, data) => buffer..add(data)) | |
| 150 .then((buffer) { | |
| 151 _eventSink.add(buffer.takeBytes()); | |
| 152 }, onError: _eventSink.addError); | |
| 153 break; | |
| 154 | 197 |
| 155 case _WebSocketOpcode.CLOSE: | 198 // Move to the next byte. |
| 156 case _WebSocketOpcode.PING: | 199 index++; |
| 157 case _WebSocketOpcode.PONG: | |
| 158 // Control frames cannot be fragmented. | |
| 159 if (!_fin) throw new WebSocketException("Protocol error"); | |
| 160 break; | |
| 161 | |
| 162 default: | |
| 163 throw new WebSocketException("Protocol error"); | |
| 164 } | |
| 165 _state = LEN_FIRST; | |
| 166 break; | |
| 167 | |
| 168 case LEN_FIRST: | |
| 169 _masked = (byte & 0x80) != 0; | |
| 170 _len = byte & 0x7F; | |
| 171 if (_isControlFrame() && _len > 125) { | |
| 172 throw new WebSocketException("Protocol error"); | |
| 173 } | |
| 174 if (_len < 126) { | |
| 175 _lengthDone(); | |
| 176 } else if (_len == 126) { | |
| 177 _len = 0; | |
| 178 _remainingLenBytes = 2; | |
| 179 _state = LEN_REST; | |
| 180 } else if (_len == 127) { | |
| 181 _len = 0; | |
| 182 _remainingLenBytes = 8; | |
| 183 _state = LEN_REST; | |
| 184 } | |
| 185 break; | |
| 186 | |
| 187 case LEN_REST: | |
| 188 _len = _len << 8 | byte; | |
| 189 _remainingLenBytes--; | |
| 190 if (_remainingLenBytes == 0) { | |
| 191 _lengthDone(); | |
| 192 } | |
| 193 break; | |
| 194 | |
| 195 case MASK: | |
| 196 _maskingKey = _maskingKey << 8 | byte; | |
| 197 _remainingMaskingKeyBytes--; | |
| 198 if (_remainingMaskingKeyBytes == 0) { | |
| 199 _maskDone(); | |
| 200 } | |
| 201 break; | |
| 202 | |
| 203 case PAYLOAD: | |
| 204 // The payload is not handled one byte at a time but in blocks. | |
| 205 int payload; | |
| 206 if (lastIndex - index <= _remainingPayloadBytes) { | |
| 207 payload = lastIndex - index; | |
| 208 } else { | |
| 209 payload = _remainingPayloadBytes; | |
| 210 } | |
| 211 _remainingPayloadBytes -= payload; | |
| 212 | |
| 213 // Unmask payload if masked. | |
| 214 if (_masked) { | |
| 215 for (int i = 0; i < payload; i++) { | |
| 216 int maskingByte = | |
| 217 ((_maskingKey >> ((3 - _unmaskingIndex) * 8)) & 0xFF); | |
| 218 buffer[index + i] = buffer[index + i] ^ maskingByte; | |
| 219 _unmaskingIndex = (_unmaskingIndex + 1) % 4; | |
| 220 } | |
| 221 } | |
| 222 | |
| 223 if (_isControlFrame()) { | |
| 224 if (payload > 0) { | |
| 225 // Allocate a buffer for collecting the control frame | |
| 226 // payload if any. | |
| 227 if (_controlPayload == null) { | |
| 228 _controlPayload = new List<int>(); | |
| 229 } | |
| 230 _controlPayload.addAll(buffer.sublist(index, index + payload)); | |
| 231 index += payload; | |
| 232 } | |
| 233 | |
| 234 if (_remainingPayloadBytes == 0) { | |
| 235 _controlFrameEnd(); | |
| 236 } | |
| 237 } else { | |
| 238 if (_currentMessageType != _WebSocketMessageType.TEXT && | |
| 239 _currentMessageType != _WebSocketMessageType.BINARY) { | |
| 240 throw new WebSocketException("Protocol error"); | |
| 241 } | |
| 242 _controller.add( | |
| 243 new Uint8List.view(buffer.buffer, index, payload)); | |
| 244 index += payload; | |
| 245 if (_remainingPayloadBytes == 0) { | |
| 246 _messageFrameEnd(); | |
| 247 } | |
| 248 } | |
| 249 | |
| 250 // Hack - as we always do index++ below. | |
| 251 index--; | |
| 252 break; | |
| 253 } | |
| 254 | |
| 255 // Move to the next byte. | |
| 256 index++; | |
| 257 } | |
| 258 } catch (e, stackTrace) { | |
| 259 _state = FAILURE; | |
| 260 _eventSink.addError(e, stackTrace); | |
| 261 } | 200 } |
| 262 } | 201 } |
| 263 | 202 |
| 264 void _lengthDone() { | 203 void _lengthDone() { |
| 265 if (_masked) { | 204 if (_masked) { |
| 266 if (!_serverSide) { | 205 if (!_serverSide) { |
| 267 throw new WebSocketException("Received masked frame from server"); | 206 throw new WebSocketException("Received masked frame from server"); |
| 268 } | 207 } |
| 269 _state = MASK; | 208 _state = MASK; |
| 270 _remainingMaskingKeyBytes = 4; | |
| 271 } else { | 209 } else { |
| 272 if (_serverSide) { | 210 if (_serverSide) { |
| 273 throw new WebSocketException("Received unmasked frame from client"); | 211 throw new WebSocketException("Received unmasked frame from client"); |
| 274 } | 212 } |
| 275 _remainingPayloadBytes = _len; | 213 _remainingPayloadBytes = _len; |
| 276 _startPayload(); | 214 _startPayload(); |
| 277 } | 215 } |
| 278 } | 216 } |
| 279 | 217 |
| 280 void _maskDone() { | 218 void _maskDone() { |
| (...skipping 24 matching lines...) Expand all Loading... |
| 305 } | 243 } |
| 306 } else { | 244 } else { |
| 307 _state = PAYLOAD; | 245 _state = PAYLOAD; |
| 308 } | 246 } |
| 309 } | 247 } |
| 310 | 248 |
| 311 void _messageFrameEnd() { | 249 void _messageFrameEnd() { |
| 312 if (_fin) { | 250 if (_fin) { |
| 313 switch (_currentMessageType) { | 251 switch (_currentMessageType) { |
| 314 case _WebSocketMessageType.TEXT: | 252 case _WebSocketMessageType.TEXT: |
| 315 _controller.close(); | 253 _eventSink.add(UTF8.decode(_payload.takeBytes())); |
| 316 break; | 254 break; |
| 317 case _WebSocketMessageType.BINARY: | 255 case _WebSocketMessageType.BINARY: |
| 318 _controller.close(); | 256 _eventSink.add(_payload.takeBytes()); |
| 319 break; | 257 break; |
| 320 } | 258 } |
| 321 _controller = null; | |
| 322 _currentMessageType = _WebSocketMessageType.NONE; | 259 _currentMessageType = _WebSocketMessageType.NONE; |
| 323 } | 260 } |
| 324 _prepareForNextFrame(); | 261 _prepareForNextFrame(); |
| 325 } | 262 } |
| 326 | 263 |
| 327 void _controlFrameEnd() { | 264 void _controlFrameEnd() { |
| 328 switch (_opcode) { | 265 switch (_opcode) { |
| 329 case _WebSocketOpcode.CLOSE: | 266 case _WebSocketOpcode.CLOSE: |
| 330 closeCode = WebSocketStatus.NO_STATUS_RECEIVED; | 267 closeCode = WebSocketStatus.NO_STATUS_RECEIVED; |
| 331 if (_controlPayload.length > 0) { | 268 if (_payload.length > 0) { |
| 332 if (_controlPayload.length == 1) { | 269 var bytes = _payload.takeBytes(); |
| 270 if (bytes.length == 1) { |
| 333 throw new WebSocketException("Protocol error"); | 271 throw new WebSocketException("Protocol error"); |
| 334 } | 272 } |
| 335 closeCode = _controlPayload[0] << 8 | _controlPayload[1]; | 273 closeCode = bytes[0] << 8 | bytes[1]; |
| 336 if (closeCode == WebSocketStatus.NO_STATUS_RECEIVED) { | 274 if (closeCode == WebSocketStatus.NO_STATUS_RECEIVED) { |
| 337 throw new WebSocketException("Protocol error"); | 275 throw new WebSocketException("Protocol error"); |
| 338 } | 276 } |
| 339 if (_controlPayload.length > 2) { | 277 if (bytes.length > 2) { |
| 340 closeReason = UTF8.decode(_controlPayload.sublist(2)); | 278 closeReason = UTF8.decode(bytes.sublist(2)); |
| 341 } | 279 } |
| 342 } | 280 } |
| 343 _state = CLOSED; | 281 _state = CLOSED; |
| 344 _eventSink.close(); | 282 _eventSink.close(); |
| 345 break; | 283 break; |
| 346 | 284 |
| 347 case _WebSocketOpcode.PING: | 285 case _WebSocketOpcode.PING: |
| 348 _eventSink.add(new _WebSocketPing(_controlPayload)); | 286 _eventSink.add(new _WebSocketPing(_payload.takeBytes())); |
| 349 break; | 287 break; |
| 350 | 288 |
| 351 case _WebSocketOpcode.PONG: | 289 case _WebSocketOpcode.PONG: |
| 352 _eventSink.add(new _WebSocketPong(_controlPayload)); | 290 _eventSink.add(new _WebSocketPong(_payload.takeBytes())); |
| 353 break; | 291 break; |
| 354 } | 292 } |
| 355 _prepareForNextFrame(); | 293 _prepareForNextFrame(); |
| 356 } | 294 } |
| 357 | 295 |
| 358 bool _isControlFrame() { | 296 bool _isControlFrame() { |
| 359 return _opcode == _WebSocketOpcode.CLOSE || | 297 return _opcode == _WebSocketOpcode.CLOSE || |
| 360 _opcode == _WebSocketOpcode.PING || | 298 _opcode == _WebSocketOpcode.PING || |
| 361 _opcode == _WebSocketOpcode.PONG; | 299 _opcode == _WebSocketOpcode.PONG; |
| 362 } | 300 } |
| 363 | 301 |
| 364 void _prepareForNextFrame() { | 302 void _prepareForNextFrame() { |
| 365 if (_state != CLOSED && _state != FAILURE) _state = START; | 303 if (_state != CLOSED && _state != FAILURE) _state = START; |
| 366 _fin = null; | 304 _fin = false; |
| 367 _opcode = null; | 305 _opcode = -1; |
| 368 _len = null; | 306 _len = -1; |
| 369 _masked = null; | 307 _remainingLenBytes = -1; |
| 370 _maskingKey = 0; | 308 _remainingMaskingKeyBytes = 4; |
| 371 _remainingLenBytes = null; | 309 _remainingPayloadBytes = -1; |
| 372 _remainingMaskingKeyBytes = null; | |
| 373 _remainingPayloadBytes = null; | |
| 374 _unmaskingIndex = 0; | 310 _unmaskingIndex = 0; |
| 375 _controlPayload = null; | |
| 376 } | 311 } |
| 377 } | 312 } |
| 378 | 313 |
| 379 | 314 |
| 380 class _WebSocketPing { | 315 class _WebSocketPing { |
| 381 final List<int> payload; | 316 final List<int> payload; |
| 382 _WebSocketPing([this.payload = null]); | 317 _WebSocketPing([this.payload = null]); |
| 383 } | 318 } |
| 384 | 319 |
| 385 | 320 |
| (...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 564 static Iterable createFrame(int opcode, List<int> data, bool serverSide) { | 499 static Iterable createFrame(int opcode, List<int> data, bool serverSide) { |
| 565 bool mask = !serverSide; // Masking not implemented for server. | 500 bool mask = !serverSide; // Masking not implemented for server. |
| 566 int dataLength = data == null ? 0 : data.length; | 501 int dataLength = data == null ? 0 : data.length; |
| 567 // Determine the header size. | 502 // Determine the header size. |
| 568 int headerSize = (mask) ? 6 : 2; | 503 int headerSize = (mask) ? 6 : 2; |
| 569 if (dataLength > 65535) { | 504 if (dataLength > 65535) { |
| 570 headerSize += 8; | 505 headerSize += 8; |
| 571 } else if (dataLength > 125) { | 506 } else if (dataLength > 125) { |
| 572 headerSize += 2; | 507 headerSize += 2; |
| 573 } | 508 } |
| 574 List<int> header = new List<int>(headerSize); | 509 Uint8List header = new Uint8List(headerSize); |
| 575 int index = 0; | 510 int index = 0; |
| 576 // Set FIN and opcode. | 511 // Set FIN and opcode. |
| 577 header[index++] = 0x80 | opcode; | 512 header[index++] = 0x80 | opcode; |
| 578 // Determine size and position of length field. | 513 // Determine size and position of length field. |
| 579 int lengthBytes = 1; | 514 int lengthBytes = 1; |
| 580 int firstLengthByte = 1; | 515 int firstLengthByte = 1; |
| 581 if (dataLength > 65535) { | 516 if (dataLength > 65535) { |
| 582 header[index++] = 127; | 517 header[index++] = 127; |
| 583 lengthBytes = 8; | 518 lengthBytes = 8; |
| 584 } else if (dataLength > 125) { | 519 } else if (dataLength > 125) { |
| (...skipping 13 matching lines...) Expand all Loading... |
| 598 var list; | 533 var list; |
| 599 // If this is a text message just do the masking inside the | 534 // If this is a text message just do the masking inside the |
| 600 // encoded data. | 535 // encoded data. |
| 601 if (opcode == _WebSocketOpcode.TEXT) { | 536 if (opcode == _WebSocketOpcode.TEXT) { |
| 602 list = data; | 537 list = data; |
| 603 } else { | 538 } else { |
| 604 list = new Uint8List(data.length); | 539 list = new Uint8List(data.length); |
| 605 } | 540 } |
| 606 if (data is Uint8List) { | 541 if (data is Uint8List) { |
| 607 for (int i = 0; i < data.length; i++) { | 542 for (int i = 0; i < data.length; i++) { |
| 608 list[i] = data[i] ^ maskBytes[i % 4]; | 543 list[i] = data[i] ^ maskBytes[i & 3]; |
| 609 } | 544 } |
| 610 } else { | 545 } else { |
| 611 for (int i = 0; i < data.length; i++) { | 546 for (int i = 0; i < data.length; i++) { |
| 612 if (data[i] < 0 || 255 < data[i]) { | 547 if (data[i] < 0 || 255 < data[i]) { |
| 613 throw new ArgumentError( | 548 throw new ArgumentError( |
| 614 "List element is not a byte value " | 549 "List element is not a byte value " |
| 615 "(value ${data[i]} at index $i)"); | 550 "(value ${data[i]} at index $i)"); |
| 616 } | 551 } |
| 617 list[i] = data[i] ^ maskBytes[i % 4]; | 552 list[i] = data[i] ^ maskBytes[i & 3]; |
| 618 } | 553 } |
| 619 } | 554 } |
| 620 data = list; | 555 data = list; |
| 621 } | 556 } |
| 622 } | 557 } |
| 623 assert(index == headerSize); | 558 assert(index == headerSize); |
| 624 if (data == null) { | 559 if (data == null) { |
| 625 return [header]; | 560 return [header]; |
| 626 } else { | 561 } else { |
| 627 return [header, data]; | 562 return [header, data]; |
| (...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 779 Uri uri = Uri.parse(url); | 714 Uri uri = Uri.parse(url); |
| 780 if (uri.scheme != "ws" && uri.scheme != "wss") { | 715 if (uri.scheme != "ws" && uri.scheme != "wss") { |
| 781 throw new WebSocketException("Unsupported URL scheme '${uri.scheme}'"); | 716 throw new WebSocketException("Unsupported URL scheme '${uri.scheme}'"); |
| 782 } | 717 } |
| 783 if (uri.userInfo != "") { | 718 if (uri.userInfo != "") { |
| 784 throw new WebSocketException("Unsupported user info '${uri.userInfo}'"); | 719 throw new WebSocketException("Unsupported user info '${uri.userInfo}'"); |
| 785 } | 720 } |
| 786 | 721 |
| 787 Random random = new Random(); | 722 Random random = new Random(); |
| 788 // Generate 16 random bytes. | 723 // Generate 16 random bytes. |
| 789 List<int> nonceData = new List<int>(16); | 724 Uint8List nonceData = new Uint8List(16); |
| 790 for (int i = 0; i < 16; i++) { | 725 for (int i = 0; i < 16; i++) { |
| 791 nonceData[i] = random.nextInt(256); | 726 nonceData[i] = random.nextInt(256); |
| 792 } | 727 } |
| 793 String nonce = _CryptoUtils.bytesToBase64(nonceData); | 728 String nonce = _CryptoUtils.bytesToBase64(nonceData); |
| 794 | 729 |
| 795 uri = new Uri(scheme: uri.scheme == "wss" ? "https" : "http", | 730 uri = new Uri(scheme: uri.scheme == "wss" ? "https" : "http", |
| 796 userInfo: uri.userInfo, | 731 userInfo: uri.userInfo, |
| 797 host: uri.host, | 732 host: uri.host, |
| 798 port: uri.port, | 733 port: uri.port, |
| 799 path: uri.path, | 734 path: uri.path, |
| (...skipping 165 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 965 (code < WebSocketStatus.NORMAL_CLOSURE || | 900 (code < WebSocketStatus.NORMAL_CLOSURE || |
| 966 code == WebSocketStatus.RESERVED_1004 || | 901 code == WebSocketStatus.RESERVED_1004 || |
| 967 code == WebSocketStatus.NO_STATUS_RECEIVED || | 902 code == WebSocketStatus.NO_STATUS_RECEIVED || |
| 968 code == WebSocketStatus.ABNORMAL_CLOSURE || | 903 code == WebSocketStatus.ABNORMAL_CLOSURE || |
| 969 (code > WebSocketStatus.INTERNAL_SERVER_ERROR && | 904 (code > WebSocketStatus.INTERNAL_SERVER_ERROR && |
| 970 code < WebSocketStatus.RESERVED_1015) || | 905 code < WebSocketStatus.RESERVED_1015) || |
| 971 (code >= WebSocketStatus.RESERVED_1015 && | 906 (code >= WebSocketStatus.RESERVED_1015 && |
| 972 code < 3000)); | 907 code < 3000)); |
| 973 } | 908 } |
| 974 } | 909 } |
| OLD | NEW |