| 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 15 matching lines...) Expand all Loading... |
| 26 static const int PING = 9; | 26 static const int PING = 9; |
| 27 static const int PONG = 10; | 27 static const int PONG = 10; |
| 28 static const int RESERVED_B = 11; | 28 static const int RESERVED_B = 11; |
| 29 static const int RESERVED_C = 12; | 29 static const int RESERVED_C = 12; |
| 30 static const int RESERVED_D = 13; | 30 static const int RESERVED_D = 13; |
| 31 static const int RESERVED_E = 14; | 31 static const int RESERVED_E = 14; |
| 32 static const int RESERVED_F = 15; | 32 static const int RESERVED_F = 15; |
| 33 } | 33 } |
| 34 | 34 |
| 35 /** | 35 /** |
| 36 * The web socket protocol processor handles the protocol byte stream | 36 * The web socket protocol transformer handles the protocol byte stream |
| 37 * which is supplied through the [:update:] and [:closed:] | 37 * which is supplied through the [:handleData:]. As the protocol is processed, |
| 38 * methods. As the protocol is processed the following callbacks are | 38 * it'll output frame data as either a List<int> or String. |
| 39 * called: | |
| 40 * | 39 * |
| 41 * [:onMessageStart:] | 40 * Important infomation about usage: Be sure you use unsubscribeOnError, so the |
| 42 * [:onMessageData:] | 41 * socket will be closed when the processer encounter an error. Not using it |
| 43 * [:onMessageEnd:] | 42 * will lead to undefined behaviour. |
| 44 * [:onClosed:] | |
| 45 * | |
| 46 */ | 43 */ |
| 47 class _WebSocketProtocolProcessor { | 44 class _WebSocketProtocolTransformer extends StreamEventTransformer { |
| 48 static const int START = 0; | 45 static const int START = 0; |
| 49 static const int LEN_FIRST = 1; | 46 static const int LEN_FIRST = 1; |
| 50 static const int LEN_REST = 2; | 47 static const int LEN_REST = 2; |
| 51 static const int MASK = 3; | 48 static const int MASK = 3; |
| 52 static const int PAYLOAD = 4; | 49 static const int PAYLOAD = 4; |
| 53 static const int CLOSED = 5; | 50 static const int CLOSED = 5; |
| 54 static const int FAILURE = 6; | 51 static const int FAILURE = 6; |
| 55 | 52 |
| 56 _WebSocketProtocolProcessor() { | 53 _WebSocketProtocolTransformer() { |
| 57 _prepareForNextFrame(); | 54 _prepareForNextFrame(); |
| 58 _currentMessageType = _WebSocketMessageType.NONE; | 55 _currentMessageType = _WebSocketMessageType.NONE; |
| 59 } | 56 } |
| 60 | 57 |
| 61 /** | 58 /** |
| 62 * Process data received from the underlying communication channel. | 59 * Process data received from the underlying communication channel. |
| 63 */ | 60 */ |
| 64 void update(List<int> buffer, int offset, int count) { | 61 void handleData(List<int> buffer, StreamSink sink) { |
| 65 int index = offset; | 62 int count = buffer.length; |
| 66 int lastIndex = offset + count; | 63 int index = 0; |
| 64 int lastIndex = count; |
| 67 try { | 65 try { |
| 68 if (_state == CLOSED) { | 66 if (_state == CLOSED) { |
| 69 throw new WebSocketException("Data on closed connection"); | 67 throw new WebSocketException("Data on closed connection"); |
| 70 } | 68 } |
| 71 if (_state == FAILURE) { | 69 if (_state == FAILURE) { |
| 72 throw new WebSocketException("Data on failed connection"); | 70 throw new WebSocketException("Data on failed connection"); |
| 73 } | 71 } |
| 74 while ((index < lastIndex) && _state != CLOSED && _state != FAILURE) { | 72 while ((index < lastIndex) && _state != CLOSED && _state != FAILURE) { |
| 75 int byte = buffer[index]; | 73 int byte = buffer[index]; |
| 76 switch (_state) { | 74 switch (_state) { |
| 77 case START: | 75 case START: |
| 78 _fin = (byte & 0x80) != 0; | 76 _fin = (byte & 0x80) != 0; |
| 79 _opcode = (byte & 0xF); | 77 _opcode = (byte & 0xF); |
| 80 switch (_opcode) { | 78 switch (_opcode) { |
| 81 case _WebSocketOpcode.CONTINUATION: | 79 case _WebSocketOpcode.CONTINUATION: |
| 82 if (_currentMessageType == _WebSocketMessageType.NONE) { | 80 if (_currentMessageType == _WebSocketMessageType.NONE) { |
| 83 throw new WebSocketException("Protocol error"); | 81 throw new WebSocketException("Protocol error"); |
| 84 } | 82 } |
| 85 break; | 83 break; |
| 86 | 84 |
| 87 case _WebSocketOpcode.TEXT: | 85 case _WebSocketOpcode.TEXT: |
| 88 if (_currentMessageType != _WebSocketMessageType.NONE) { | 86 if (_currentMessageType != _WebSocketMessageType.NONE) { |
| 89 throw new WebSocketException("Protocol error"); | 87 throw new WebSocketException("Protocol error"); |
| 90 } | 88 } |
| 91 _currentMessageType = _WebSocketMessageType.TEXT; | 89 _currentMessageType = _WebSocketMessageType.TEXT; |
| 92 if (onMessageStart != null) { | 90 _buffer = new StringBuffer(); |
| 93 onMessageStart(_WebSocketMessageType.TEXT); | |
| 94 } | |
| 95 break; | 91 break; |
| 96 | 92 |
| 97 case _WebSocketOpcode.BINARY: | 93 case _WebSocketOpcode.BINARY: |
| 98 if (_currentMessageType != _WebSocketMessageType.NONE) { | 94 if (_currentMessageType != _WebSocketMessageType.NONE) { |
| 99 throw new WebSocketException("Protocol error"); | 95 throw new WebSocketException("Protocol error"); |
| 100 } | 96 } |
| 101 _currentMessageType = _WebSocketMessageType.BINARY; | 97 _currentMessageType = _WebSocketMessageType.BINARY; |
| 102 if (onMessageStart != null) { | 98 _buffer = new _BufferList(); |
| 103 onMessageStart(_WebSocketMessageType.BINARY); | |
| 104 } | |
| 105 break; | 99 break; |
| 106 | 100 |
| 107 case _WebSocketOpcode.CLOSE: | 101 case _WebSocketOpcode.CLOSE: |
| 108 case _WebSocketOpcode.PING: | 102 case _WebSocketOpcode.PING: |
| 109 case _WebSocketOpcode.PONG: | 103 case _WebSocketOpcode.PONG: |
| 110 // Control frames cannot be fragmented. | 104 // Control frames cannot be fragmented. |
| 111 if (!_fin) throw new WebSocketException("Protocol error"); | 105 if (!_fin) throw new WebSocketException("Protocol error"); |
| 112 break; | 106 break; |
| 113 | 107 |
| 114 default: | 108 default: |
| 115 throw new WebSocketException("Protocol error"); | 109 throw new WebSocketException("Protocol error"); |
| 116 } | 110 } |
| 117 _state = LEN_FIRST; | 111 _state = LEN_FIRST; |
| 118 break; | 112 break; |
| 119 | 113 |
| 120 case LEN_FIRST: | 114 case LEN_FIRST: |
| 121 _masked = (byte & 0x80) != 0; | 115 _masked = (byte & 0x80) != 0; |
| 122 _len = byte & 0x7F; | 116 _len = byte & 0x7F; |
| 123 if (_isControlFrame() && _len > 126) { | 117 if (_isControlFrame() && _len > 126) { |
| 124 throw new WebSocketException("Protocol error"); | 118 throw new WebSocketException("Protocol error"); |
| 125 } | 119 } |
| 126 if (_len < 126) { | 120 if (_len < 126) { |
| 127 _lengthDone(); | 121 _lengthDone(sink); |
| 128 } else if (_len == 126) { | 122 } else if (_len == 126) { |
| 129 _len = 0; | 123 _len = 0; |
| 130 _remainingLenBytes = 2; | 124 _remainingLenBytes = 2; |
| 131 _state = LEN_REST; | 125 _state = LEN_REST; |
| 132 } else if (_len == 127) { | 126 } else if (_len == 127) { |
| 133 _len = 0; | 127 _len = 0; |
| 134 _remainingLenBytes = 8; | 128 _remainingLenBytes = 8; |
| 135 _state = LEN_REST; | 129 _state = LEN_REST; |
| 136 } | 130 } |
| 137 break; | 131 break; |
| 138 | 132 |
| 139 case LEN_REST: | 133 case LEN_REST: |
| 140 _len = _len << 8 | byte; | 134 _len = _len << 8 | byte; |
| 141 _remainingLenBytes--; | 135 _remainingLenBytes--; |
| 142 if (_remainingLenBytes == 0) { | 136 if (_remainingLenBytes == 0) { |
| 143 _lengthDone(); | 137 _lengthDone(sink); |
| 144 } | 138 } |
| 145 break; | 139 break; |
| 146 | 140 |
| 147 case MASK: | 141 case MASK: |
| 148 _maskingKey = _maskingKey << 8 | byte; | 142 _maskingKey = _maskingKey << 8 | byte; |
| 149 _remainingMaskingKeyBytes--; | 143 _remainingMaskingKeyBytes--; |
| 150 if (_remainingMaskingKeyBytes == 0) { | 144 if (_remainingMaskingKeyBytes == 0) { |
| 151 _maskDone(); | 145 _maskDone(sink); |
| 152 } | 146 } |
| 153 break; | 147 break; |
| 154 | 148 |
| 155 case PAYLOAD: | 149 case PAYLOAD: |
| 156 // The payload is not handled one byte at a time but in blocks. | 150 // The payload is not handled one byte at a time but in blocks. |
| 157 int payload; | 151 int payload; |
| 158 if (lastIndex - index <= _remainingPayloadBytes) { | 152 if (lastIndex - index <= _remainingPayloadBytes) { |
| 159 payload = lastIndex - index; | 153 payload = lastIndex - index; |
| 160 } else { | 154 } else { |
| 161 payload = _remainingPayloadBytes; | 155 payload = _remainingPayloadBytes; |
| (...skipping 15 matching lines...) Expand all Loading... |
| 177 // Allocate a buffer for collecting the control frame | 171 // Allocate a buffer for collecting the control frame |
| 178 // payload if any. | 172 // payload if any. |
| 179 if (_controlPayload == null) { | 173 if (_controlPayload == null) { |
| 180 _controlPayload = new List<int>(); | 174 _controlPayload = new List<int>(); |
| 181 } | 175 } |
| 182 _controlPayload.addAll(buffer.getRange(index, payload)); | 176 _controlPayload.addAll(buffer.getRange(index, payload)); |
| 183 index += payload; | 177 index += payload; |
| 184 } | 178 } |
| 185 | 179 |
| 186 if (_remainingPayloadBytes == 0) { | 180 if (_remainingPayloadBytes == 0) { |
| 187 _controlFrameEnd(); | 181 _controlFrameEnd(sink); |
| 188 } | 182 } |
| 189 } else { | 183 } else { |
| 190 switch (_currentMessageType) { | 184 switch (_currentMessageType) { |
| 191 case _WebSocketMessageType.NONE: | 185 case _WebSocketMessageType.NONE: |
| 192 throw new WebSocketException("Protocol error"); | 186 throw new WebSocketException("Protocol error"); |
| 193 | 187 |
| 194 case _WebSocketMessageType.TEXT: | 188 case _WebSocketMessageType.TEXT: |
| 195 case _WebSocketMessageType.BINARY: | 189 _buffer.add(_decodeString(buffer.getRange(index, payload))); |
| 196 if (onMessageData != null) { | |
| 197 onMessageData(buffer, index, payload); | |
| 198 } | |
| 199 index += payload; | 190 index += payload; |
| 200 if (_remainingPayloadBytes == 0) { | 191 if (_remainingPayloadBytes == 0) { |
| 201 _messageFrameEnd(); | 192 _messageFrameEnd(sink); |
| 202 } | 193 } |
| 203 break; | 194 break; |
| 204 | 195 |
| 196 case _WebSocketMessageType.BINARY: |
| 197 _buffer.add(buffer.getRange(index, payload)); |
| 198 index += payload; |
| 199 if (_remainingPayloadBytes == 0) { |
| 200 _messageFrameEnd(sink); |
| 201 } |
| 202 break; |
| 203 |
| 205 default: | 204 default: |
| 206 throw new WebSocketException("Protocol error"); | 205 throw new WebSocketException("Protocol error"); |
| 207 } | 206 } |
| 208 } | 207 } |
| 209 | 208 |
| 210 // Hack - as we always do index++ below. | 209 // Hack - as we always do index++ below. |
| 211 index--; | 210 index--; |
| 212 break; | 211 break; |
| 213 } | 212 } |
| 214 | 213 |
| 215 // Move to the next byte. | 214 // Move to the next byte. |
| 216 index++; | 215 index++; |
| 217 } | 216 } |
| 218 } catch (e) { | 217 } catch (e) { |
| 219 if (onClosed != null) onClosed(WebSocketStatus.PROTOCOL_ERROR, | |
| 220 "Protocol error"); | |
| 221 _state = FAILURE; | 218 _state = FAILURE; |
| 219 sink.signalError(e); |
| 222 } | 220 } |
| 223 } | 221 } |
| 224 | 222 |
| 225 /** | 223 void _lengthDone(StreamSink sink) { |
| 226 * Indicate that the underlying communication channel has been closed. | |
| 227 */ | |
| 228 void closed() { | |
| 229 if (_state == START || _state == CLOSED || _state == FAILURE) return; | |
| 230 if (onClosed != null) onClosed(WebSocketStatus.ABNORMAL_CLOSURE, | |
| 231 "Connection closed unexpectedly"); | |
| 232 _state = CLOSED; | |
| 233 } | |
| 234 | |
| 235 void _lengthDone() { | |
| 236 if (_masked) { | 224 if (_masked) { |
| 237 _state = MASK; | 225 _state = MASK; |
| 238 _remainingMaskingKeyBytes = 4; | 226 _remainingMaskingKeyBytes = 4; |
| 239 } else { | 227 } else { |
| 240 _remainingPayloadBytes = _len; | 228 _remainingPayloadBytes = _len; |
| 241 _startPayload(); | 229 _startPayload(sink); |
| 242 } | 230 } |
| 243 } | 231 } |
| 244 | 232 |
| 245 void _maskDone() { | 233 void _maskDone(StreamSink sink) { |
| 246 _remainingPayloadBytes = _len; | 234 _remainingPayloadBytes = _len; |
| 247 _startPayload(); | 235 _startPayload(sink); |
| 248 } | 236 } |
| 249 | 237 |
| 250 void _startPayload() { | 238 void _startPayload(StreamSink sink) { |
| 251 // If there is no actual payload perform perform callbacks without | 239 // If there is no actual payload perform perform callbacks without |
| 252 // going through the PAYLOAD state. | 240 // going through the PAYLOAD state. |
| 253 if (_remainingPayloadBytes == 0) { | 241 if (_remainingPayloadBytes == 0) { |
| 254 if (_isControlFrame()) { | 242 if (_isControlFrame()) { |
| 255 switch (_opcode) { | 243 switch (_opcode) { |
| 256 case _WebSocketOpcode.CLOSE: | 244 case _WebSocketOpcode.CLOSE: |
| 257 if (onClosed != null) onClosed(1005, ""); | |
| 258 _state = CLOSED; | 245 _state = CLOSED; |
| 246 sink.close(); |
| 259 break; | 247 break; |
| 260 case _WebSocketOpcode.PING: | 248 case _WebSocketOpcode.PING: |
| 261 if (onPing != null) onPing(null); | 249 // TODO(ajohnsen): Handle ping. |
| 262 break; | 250 break; |
| 263 case _WebSocketOpcode.PONG: | 251 case _WebSocketOpcode.PONG: |
| 264 if (onPong != null) onPong(null); | 252 // TODO(ajohnsen): Handle pong. |
| 265 break; | 253 break; |
| 266 } | 254 } |
| 267 _prepareForNextFrame(); | 255 _prepareForNextFrame(); |
| 268 } else { | 256 } else { |
| 269 _messageFrameEnd(); | 257 _messageFrameEnd(sink); |
| 270 } | 258 } |
| 271 } else { | 259 } else { |
| 272 _state = PAYLOAD; | 260 _state = PAYLOAD; |
| 273 } | 261 } |
| 274 } | 262 } |
| 275 | 263 |
| 276 void _messageFrameEnd() { | 264 void _messageFrameEnd(StreamSink sink) { |
| 277 if (_fin) { | 265 if (_fin) { |
| 278 if (onMessageEnd != null) onMessageEnd(); | 266 switch (_currentMessageType) { |
| 267 case _WebSocketMessageType.TEXT: |
| 268 sink.add(_buffer.toString()); |
| 269 break; |
| 270 case _WebSocketMessageType.BINARY: |
| 271 if (_buffer.length == 0) { |
| 272 sink.add(const []); |
| 273 } else { |
| 274 sink.add(_buffer.readBytes(_buffer.length)); |
| 275 } |
| 276 break; |
| 277 } |
| 278 _buffer = null; |
| 279 _currentMessageType = _WebSocketMessageType.NONE; | 279 _currentMessageType = _WebSocketMessageType.NONE; |
| 280 } | 280 } |
| 281 _prepareForNextFrame(); | 281 _prepareForNextFrame(); |
| 282 } | 282 } |
| 283 | 283 |
| 284 void _controlFrameEnd() { | 284 void _controlFrameEnd(StreamSink sink) { |
| 285 switch (_opcode) { | 285 switch (_opcode) { |
| 286 case _WebSocketOpcode.CLOSE: | 286 case _WebSocketOpcode.CLOSE: |
| 287 int status = WebSocketStatus.NO_STATUS_RECEIVED; | 287 closeCode = WebSocketStatus.NO_STATUS_RECEIVED; |
| 288 String reason = ""; | |
| 289 if (_controlPayload.length > 0) { | 288 if (_controlPayload.length > 0) { |
| 290 if (_controlPayload.length == 1) { | 289 if (_controlPayload.length == 1) { |
| 291 throw new WebSocketException("Protocol error"); | 290 throw new WebSocketException("Protocol error"); |
| 292 } | 291 } |
| 293 status = _controlPayload[0] << 8 | _controlPayload[1]; | 292 closeCode = _controlPayload[0] << 8 | _controlPayload[1]; |
| 294 if (status == WebSocketStatus.NO_STATUS_RECEIVED) { | 293 if (closeCode == WebSocketStatus.NO_STATUS_RECEIVED) { |
| 295 throw new WebSocketException("Protocol error"); | 294 throw new WebSocketException("Protocol error"); |
| 296 } | 295 } |
| 297 if (_controlPayload.length > 2) { | 296 if (_controlPayload.length > 2) { |
| 298 reason = _decodeString( | 297 closeReason = _decodeString( |
| 299 _controlPayload.getRange(2, _controlPayload.length - 2)); | 298 _controlPayload.getRange(2, _controlPayload.length - 2)); |
| 300 } | 299 } |
| 301 } | 300 } |
| 302 if (onClosed != null) onClosed(status, reason); | |
| 303 _state = CLOSED; | 301 _state = CLOSED; |
| 302 sink.close(); |
| 304 break; | 303 break; |
| 305 | 304 |
| 306 case _WebSocketOpcode.PING: | 305 case _WebSocketOpcode.PING: |
| 307 if (onPing != null) onPing(_controlPayload); | 306 // TODO(ajohnsen): Handle ping. |
| 308 break; | 307 break; |
| 309 | 308 |
| 310 case _WebSocketOpcode.PONG: | 309 case _WebSocketOpcode.PONG: |
| 311 if (onPong != null) onPong(_controlPayload); | 310 // TODO(ajohnsen): Handle pong. |
| 312 break; | 311 break; |
| 313 } | 312 } |
| 314 _prepareForNextFrame(); | 313 _prepareForNextFrame(); |
| 315 } | 314 } |
| 316 | 315 |
| 317 bool _isControlFrame() { | 316 bool _isControlFrame() { |
| 318 return _opcode == _WebSocketOpcode.CLOSE || | 317 return _opcode == _WebSocketOpcode.CLOSE || |
| 319 _opcode == _WebSocketOpcode.PING || | 318 _opcode == _WebSocketOpcode.PING || |
| 320 _opcode == _WebSocketOpcode.PONG; | 319 _opcode == _WebSocketOpcode.PONG; |
| 321 } | 320 } |
| (...skipping 18 matching lines...) Expand all Loading... |
| 340 int _len; | 339 int _len; |
| 341 bool _masked; | 340 bool _masked; |
| 342 int _maskingKey; | 341 int _maskingKey; |
| 343 int _remainingLenBytes; | 342 int _remainingLenBytes; |
| 344 int _remainingMaskingKeyBytes; | 343 int _remainingMaskingKeyBytes; |
| 345 int _remainingPayloadBytes; | 344 int _remainingPayloadBytes; |
| 346 int _unmaskingIndex; | 345 int _unmaskingIndex; |
| 347 | 346 |
| 348 int _currentMessageType; | 347 int _currentMessageType; |
| 349 List<int> _controlPayload; | 348 List<int> _controlPayload; |
| 349 var _buffer; // Either StringBuffer or _BufferList. |
| 350 | 350 |
| 351 Function onMessageStart; | 351 int closeCode = WebSocketStatus.NO_STATUS_RECEIVED; |
| 352 Function onMessageData; | 352 String closeReason = ""; |
| 353 Function onMessageEnd; | |
| 354 Function onPing; | |
| 355 Function onPong; | |
| 356 Function onClosed; | |
| 357 } | 353 } |
| 358 | 354 |
| 359 | 355 |
| 360 class _WebSocketTransformerImpl implements WebSocketTransformer { | 356 class _WebSocketTransformerImpl implements WebSocketTransformer { |
| 361 final StreamController<WebSocket> _controller = | 357 final StreamController<WebSocket> _controller = |
| 362 new StreamController<WebSocket>(); | 358 new StreamController<WebSocket>(); |
| 363 | 359 |
| 364 Stream<WebSocket> bind(Stream<HttpRequest> stream) { | 360 Stream<WebSocket> bind(Stream<HttpRequest> stream) { |
| 365 stream.listen((request) { | 361 stream.listen((request) { |
| 366 _upgrade(request) | 362 _upgrade(request) |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 423 return false; | 419 return false; |
| 424 } | 420 } |
| 425 return true; | 421 return true; |
| 426 } | 422 } |
| 427 } | 423 } |
| 428 | 424 |
| 429 | 425 |
| 430 class _WebSocketImpl extends Stream implements WebSocket { | 426 class _WebSocketImpl extends Stream implements WebSocket { |
| 431 final StreamController _controller = new StreamController(); | 427 final StreamController _controller = new StreamController(); |
| 432 | 428 |
| 433 final _WebSocketProtocolProcessor _processor = | |
| 434 new _WebSocketProtocolProcessor(); | |
| 435 | |
| 436 final Socket _socket; | 429 final Socket _socket; |
| 437 int _readyState = WebSocket.CONNECTING; | 430 int _readyState = WebSocket.CONNECTING; |
| 438 bool _writeClosed = false; | 431 bool _writeClosed = false; |
| 439 int _closeCode; | 432 int _closeCode; |
| 440 String _closeReason; | 433 String _closeReason; |
| 441 | 434 |
| 442 static final HttpClient _httpClient = new HttpClient(); | 435 static final HttpClient _httpClient = new HttpClient(); |
| 443 | 436 |
| 444 static Future<WebSocket> connect(String url, [protocols]) { | 437 static Future<WebSocket> connect(String url, [protocols]) { |
| 445 Uri uri = Uri.parse(url); | 438 Uri uri = Uri.parse(url); |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 507 } | 500 } |
| 508 } | 501 } |
| 509 return response.detachSocket() | 502 return response.detachSocket() |
| 510 .then((socket) => new _WebSocketImpl._fromSocket(socket)); | 503 .then((socket) => new _WebSocketImpl._fromSocket(socket)); |
| 511 }); | 504 }); |
| 512 } | 505 } |
| 513 | 506 |
| 514 _WebSocketImpl._fromSocket(Socket this._socket) { | 507 _WebSocketImpl._fromSocket(Socket this._socket) { |
| 515 _readyState = WebSocket.OPEN; | 508 _readyState = WebSocket.OPEN; |
| 516 | 509 |
| 517 int type; | 510 bool closed = false; |
| 518 var data; | 511 var transformer = new _WebSocketProtocolTransformer(); |
| 519 _processor.onMessageStart = (int t) { | 512 _socket.transform(transformer).listen( |
| 520 type = t; | 513 (data) { |
| 521 if (type == _WebSocketMessageType.TEXT) { | 514 _controller.add(data); |
| 522 data = new StringBuffer(); | 515 }, |
| 523 } else { | 516 onError: (error) { |
| 524 data = []; | 517 if (closed) return; |
| 525 } | 518 closed = true; |
| 526 }; | 519 _controller.signalError(error); |
| 527 _processor.onMessageData = (buffer, offset, count) { | 520 _controller.close(); |
| 528 if (type == _WebSocketMessageType.TEXT) { | 521 }, |
| 529 data.add(_decodeString(buffer.getRange(offset, count))); | 522 onDone: () { |
| 530 } else { | 523 if (closed) return; |
| 531 data.addAll(buffer.getRange(offset, count)); | 524 closed = true; |
| 532 } | 525 if (_readyState == WebSocket.OPEN) { |
| 533 }; | 526 _readyState = WebSocket.CLOSING; |
| 534 _processor.onMessageEnd = () { | 527 if (transformer.closeCode != WebSocketStatus.NO_STATUS_RECEIVED) { |
| 535 if (type == _WebSocketMessageType.TEXT) { | 528 _close(transformer.closeCode); |
| 536 _controller.add(data.toString()); | 529 } else { |
| 537 } else { | 530 _close(); |
| 538 _controller.add(data); | 531 } |
| 539 } | 532 _readyState = WebSocket.CLOSED; |
| 540 }; | 533 } |
| 541 _processor.onClosed = (code, reason) { | 534 _closeCode = transformer.closeCode; |
| 542 bool clean = true; | 535 _closeReason = transformer.closeReason; |
| 543 if (_readyState == WebSocket.OPEN) { | 536 _controller.close(); |
| 544 _readyState = WebSocket.CLOSING; | 537 if (_writeClosed) _socket.destroy(); |
| 545 if (code != WebSocketStatus.NO_STATUS_RECEIVED) { | 538 }, |
| 546 _close(code); | 539 unsubscribeOnError: true); |
| 547 } else { | |
| 548 _close(); | |
| 549 clean = false; | |
| 550 } | |
| 551 _readyState = WebSocket.CLOSED; | |
| 552 } | |
| 553 if (_readyState == WebSocket.CLOSED) return; | |
| 554 _closeCode = code; | |
| 555 _closeReason = reason; | |
| 556 _controller.close(); | |
| 557 }; | |
| 558 | |
| 559 _socket.listen( | |
| 560 (data) => _processor.update(data, 0, data.length), | |
| 561 onDone: () => _processor.closed(), | |
| 562 onError: (error) => _controller.signalError(error)); | |
| 563 | 540 |
| 564 _socket.done | 541 _socket.done |
| 565 .catchError((error) { | 542 .catchError((error) { |
| 566 if (_readyState == WebSocket.CLOSED) return; | 543 if (closed) return; |
| 544 closed = true; |
| 567 _readyState = WebSocket.CLOSED; | 545 _readyState = WebSocket.CLOSED; |
| 568 _closeCode = ABNORMAL_CLOSURE; | 546 _closeCode = WebSocketStatus.ABNORMAL_CLOSURE; |
| 569 _controller.signalError(error); | 547 _controller.signalError(error); |
| 570 _controller.close(); | 548 _controller.close(); |
| 571 _socket.destroy(); | |
| 572 }) | 549 }) |
| 573 .whenComplete(() { | 550 .whenComplete(() { |
| 574 _writeClosed = true; | 551 _writeClosed = true; |
| 575 }); | 552 }); |
| 576 } | 553 } |
| 577 | 554 |
| 578 StreamSubscription listen(void onData(message), | 555 StreamSubscription listen(void onData(message), |
| 579 {void onError(AsyncError error), | 556 {void onError(AsyncError error), |
| 580 void onDone(), | 557 void onDone(), |
| 581 bool unsubscribeOnError}) { | 558 bool unsubscribeOnError}) { |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 641 } else { | 618 } else { |
| 642 if (message is !List<int>) { | 619 if (message is !List<int>) { |
| 643 throw new ArgumentError(message); | 620 throw new ArgumentError(message); |
| 644 } | 621 } |
| 645 opcode = _WebSocketOpcode.BINARY; | 622 opcode = _WebSocketOpcode.BINARY; |
| 646 data = message; | 623 data = message; |
| 647 } | 624 } |
| 648 } else { | 625 } else { |
| 649 opcode = _WebSocketOpcode.TEXT; | 626 opcode = _WebSocketOpcode.TEXT; |
| 650 } | 627 } |
| 651 try { | 628 _sendFrame(opcode, data); |
| 652 _sendFrame(opcode, data); | |
| 653 } catch (_) { | |
| 654 // The socket can be closed before _socket.done have a chance | |
| 655 // to complete. | |
| 656 } | |
| 657 } | 629 } |
| 658 | 630 |
| 659 void _sendFrame(int opcode, [List<int> data]) { | 631 void _sendFrame(int opcode, [List<int> data]) { |
| 660 if (_writeClosed) return; | 632 if (_writeClosed) return; |
| 661 bool mask = false; // Masking not implemented for server. | 633 bool mask = false; // Masking not implemented for server. |
| 662 int dataLength = data == null ? 0 : data.length; | 634 int dataLength = data == null ? 0 : data.length; |
| 663 // Determine the header size. | 635 // Determine the header size. |
| 664 int headerSize = (mask) ? 6 : 2; | 636 int headerSize = (mask) ? 6 : 2; |
| 665 if (dataLength > 65535) { | 637 if (dataLength > 65535) { |
| 666 headerSize += 8; | 638 headerSize += 8; |
| (...skipping 12 matching lines...) Expand all Loading... |
| 679 lengthBytes = 8; | 651 lengthBytes = 8; |
| 680 } else if (dataLength > 125) { | 652 } else if (dataLength > 125) { |
| 681 header[index++] = 126; | 653 header[index++] = 126; |
| 682 lengthBytes = 2; | 654 lengthBytes = 2; |
| 683 } | 655 } |
| 684 // Write the length in network byte order into the header. | 656 // Write the length in network byte order into the header. |
| 685 for (int i = 0; i < lengthBytes; i++) { | 657 for (int i = 0; i < lengthBytes; i++) { |
| 686 header[index++] = dataLength >> (((lengthBytes - 1) - i) * 8) & 0xFF; | 658 header[index++] = dataLength >> (((lengthBytes - 1) - i) * 8) & 0xFF; |
| 687 } | 659 } |
| 688 assert(index == headerSize); | 660 assert(index == headerSize); |
| 689 _socket.add(header); | 661 try { |
| 690 if (data != null) { | 662 _socket.add(header); |
| 691 _socket.add(data); | 663 if (data != null) { |
| 664 _socket.add(data); |
| 665 } |
| 666 } catch (_) { |
| 667 // The socket can be closed before _socket.done have a chance |
| 668 // to complete. |
| 669 _writeClosed = true; |
| 692 } | 670 } |
| 693 } | 671 } |
| 694 } | 672 } |
| OLD | NEW |