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