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 26 matching lines...) Expand all Loading... | |
| 37 * which is supplied through the [:update:] and [:closed:] | 37 * which is supplied through the [:update:] and [:closed:] |
| 38 * methods. As the protocol is processed the following callbacks are | 38 * methods. As the protocol is processed the following callbacks are |
| 39 * called: | 39 * called: |
| 40 * | 40 * |
| 41 * [:onMessageStart:] | 41 * [:onMessageStart:] |
| 42 * [:onMessageData:] | 42 * [:onMessageData:] |
| 43 * [:onMessageEnd:] | 43 * [:onMessageEnd:] |
| 44 * [:onClosed:] | 44 * [:onClosed:] |
| 45 * | 45 * |
| 46 */ | 46 */ |
| 47 class _WebSocketProtocolProcessor { | 47 class _WebSocketProtocolTransformer extends StreamEventTransformer { |
| 48 static const int START = 0; | 48 static const int START = 0; |
| 49 static const int LEN_FIRST = 1; | 49 static const int LEN_FIRST = 1; |
| 50 static const int LEN_REST = 2; | 50 static const int LEN_REST = 2; |
| 51 static const int MASK = 3; | 51 static const int MASK = 3; |
| 52 static const int PAYLOAD = 4; | 52 static const int PAYLOAD = 4; |
| 53 static const int CLOSED = 5; | 53 static const int CLOSED = 5; |
| 54 static const int FAILURE = 6; | 54 static const int FAILURE = 6; |
| 55 | 55 |
| 56 _WebSocketProtocolProcessor() { | 56 _WebSocketProtocolTransformer() { |
| 57 _prepareForNextFrame(); | 57 _prepareForNextFrame(); |
| 58 _currentMessageType = _WebSocketMessageType.NONE; | 58 _currentMessageType = _WebSocketMessageType.NONE; |
| 59 } | 59 } |
| 60 | 60 |
| 61 /** | 61 /** |
| 62 * Process data received from the underlying communication channel. | 62 * Process data received from the underlying communication channel. |
| 63 */ | 63 */ |
| 64 void update(List<int> buffer, int offset, int count) { | 64 void handleData(List<int> buffer, StreamSink sink) { |
| 65 int index = offset; | 65 int count = buffer.length; |
| 66 int lastIndex = offset + count; | 66 int index = 0; |
| 67 int lastIndex = count; | |
| 67 try { | 68 try { |
| 68 if (_state == CLOSED) { | 69 if (_state == CLOSED) { |
| 69 throw new WebSocketException("Data on closed connection"); | 70 throw new WebSocketException("Data on closed connection"); |
| 70 } | 71 } |
| 71 if (_state == FAILURE) { | 72 if (_state == FAILURE) { |
| 72 throw new WebSocketException("Data on failed connection"); | 73 throw new WebSocketException("Data on failed connection"); |
| 73 } | 74 } |
| 74 while ((index < lastIndex) && _state != CLOSED && _state != FAILURE) { | 75 while ((index < lastIndex) && _state != CLOSED && _state != FAILURE) { |
| 75 int byte = buffer[index]; | 76 int byte = buffer[index]; |
| 76 switch (_state) { | 77 switch (_state) { |
| 77 case START: | 78 case START: |
| 78 _fin = (byte & 0x80) != 0; | 79 _fin = (byte & 0x80) != 0; |
| 79 _opcode = (byte & 0xF); | 80 _opcode = (byte & 0xF); |
| 80 switch (_opcode) { | 81 switch (_opcode) { |
| 81 case _WebSocketOpcode.CONTINUATION: | 82 case _WebSocketOpcode.CONTINUATION: |
| 82 if (_currentMessageType == _WebSocketMessageType.NONE) { | 83 if (_currentMessageType == _WebSocketMessageType.NONE) { |
| 83 throw new WebSocketException("Protocol error"); | 84 throw new WebSocketException("Protocol error"); |
| 84 } | 85 } |
| 85 break; | 86 break; |
| 86 | 87 |
| 87 case _WebSocketOpcode.TEXT: | 88 case _WebSocketOpcode.TEXT: |
| 88 if (_currentMessageType != _WebSocketMessageType.NONE) { | 89 if (_currentMessageType != _WebSocketMessageType.NONE) { |
| 89 throw new WebSocketException("Protocol error"); | 90 throw new WebSocketException("Protocol error"); |
| 90 } | 91 } |
| 91 _currentMessageType = _WebSocketMessageType.TEXT; | 92 _currentMessageType = _WebSocketMessageType.TEXT; |
| 92 if (onMessageStart != null) { | 93 _buffer = new StringBuffer(); |
| 93 onMessageStart(_WebSocketMessageType.TEXT); | |
| 94 } | |
| 95 break; | 94 break; |
| 96 | 95 |
| 97 case _WebSocketOpcode.BINARY: | 96 case _WebSocketOpcode.BINARY: |
| 98 if (_currentMessageType != _WebSocketMessageType.NONE) { | 97 if (_currentMessageType != _WebSocketMessageType.NONE) { |
| 99 throw new WebSocketException("Protocol error"); | 98 throw new WebSocketException("Protocol error"); |
| 100 } | 99 } |
| 101 _currentMessageType = _WebSocketMessageType.BINARY; | 100 _currentMessageType = _WebSocketMessageType.BINARY; |
| 102 if (onMessageStart != null) { | 101 // TODO(ajohnsen): Use a faster buffer for binary data. |
| 103 onMessageStart(_WebSocketMessageType.BINARY); | 102 _buffer = []; |
| 104 } | |
| 105 break; | 103 break; |
| 106 | 104 |
| 107 case _WebSocketOpcode.CLOSE: | 105 case _WebSocketOpcode.CLOSE: |
| 108 case _WebSocketOpcode.PING: | 106 case _WebSocketOpcode.PING: |
| 109 case _WebSocketOpcode.PONG: | 107 case _WebSocketOpcode.PONG: |
| 110 // Control frames cannot be fragmented. | 108 // Control frames cannot be fragmented. |
| 111 if (!_fin) throw new WebSocketException("Protocol error"); | 109 if (!_fin) throw new WebSocketException("Protocol error"); |
| 112 break; | 110 break; |
| 113 | 111 |
| 114 default: | 112 default: |
| 115 throw new WebSocketException("Protocol error"); | 113 throw new WebSocketException("Protocol error"); |
| 116 } | 114 } |
| 117 _state = LEN_FIRST; | 115 _state = LEN_FIRST; |
| 118 break; | 116 break; |
| 119 | 117 |
| 120 case LEN_FIRST: | 118 case LEN_FIRST: |
| 121 _masked = (byte & 0x80) != 0; | 119 _masked = (byte & 0x80) != 0; |
| 122 _len = byte & 0x7F; | 120 _len = byte & 0x7F; |
| 123 if (_isControlFrame() && _len > 126) { | 121 if (_isControlFrame() && _len > 126) { |
| 124 throw new WebSocketException("Protocol error"); | 122 throw new WebSocketException("Protocol error"); |
| 125 } | 123 } |
| 126 if (_len < 126) { | 124 if (_len < 126) { |
| 127 _lengthDone(); | 125 _lengthDone(sink); |
| 128 } else if (_len == 126) { | 126 } else if (_len == 126) { |
| 129 _len = 0; | 127 _len = 0; |
| 130 _remainingLenBytes = 2; | 128 _remainingLenBytes = 2; |
| 131 _state = LEN_REST; | 129 _state = LEN_REST; |
| 132 } else if (_len == 127) { | 130 } else if (_len == 127) { |
| 133 _len = 0; | 131 _len = 0; |
| 134 _remainingLenBytes = 8; | 132 _remainingLenBytes = 8; |
| 135 _state = LEN_REST; | 133 _state = LEN_REST; |
| 136 } | 134 } |
| 137 break; | 135 break; |
| 138 | 136 |
| 139 case LEN_REST: | 137 case LEN_REST: |
| 140 _len = _len << 8 | byte; | 138 _len = _len << 8 | byte; |
| 141 _remainingLenBytes--; | 139 _remainingLenBytes--; |
| 142 if (_remainingLenBytes == 0) { | 140 if (_remainingLenBytes == 0) { |
| 143 _lengthDone(); | 141 _lengthDone(sink); |
| 144 } | 142 } |
| 145 break; | 143 break; |
| 146 | 144 |
| 147 case MASK: | 145 case MASK: |
| 148 _maskingKey = _maskingKey << 8 | byte; | 146 _maskingKey = _maskingKey << 8 | byte; |
| 149 _remainingMaskingKeyBytes--; | 147 _remainingMaskingKeyBytes--; |
| 150 if (_remainingMaskingKeyBytes == 0) { | 148 if (_remainingMaskingKeyBytes == 0) { |
| 151 _maskDone(); | 149 _maskDone(sink); |
| 152 } | 150 } |
| 153 break; | 151 break; |
| 154 | 152 |
| 155 case PAYLOAD: | 153 case PAYLOAD: |
| 156 // The payload is not handled one byte at a time but in blocks. | 154 // The payload is not handled one byte at a time but in blocks. |
| 157 int payload; | 155 int payload; |
| 158 if (lastIndex - index <= _remainingPayloadBytes) { | 156 if (lastIndex - index <= _remainingPayloadBytes) { |
| 159 payload = lastIndex - index; | 157 payload = lastIndex - index; |
| 160 } else { | 158 } else { |
| 161 payload = _remainingPayloadBytes; | 159 payload = _remainingPayloadBytes; |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 177 // Allocate a buffer for collecting the control frame | 175 // Allocate a buffer for collecting the control frame |
| 178 // payload if any. | 176 // payload if any. |
| 179 if (_controlPayload == null) { | 177 if (_controlPayload == null) { |
| 180 _controlPayload = new List<int>(); | 178 _controlPayload = new List<int>(); |
| 181 } | 179 } |
| 182 _controlPayload.addAll(buffer.getRange(index, payload)); | 180 _controlPayload.addAll(buffer.getRange(index, payload)); |
| 183 index += payload; | 181 index += payload; |
| 184 } | 182 } |
| 185 | 183 |
| 186 if (_remainingPayloadBytes == 0) { | 184 if (_remainingPayloadBytes == 0) { |
| 187 _controlFrameEnd(); | 185 _controlFrameEnd(sink); |
| 188 } | 186 } |
| 189 } else { | 187 } else { |
| 190 switch (_currentMessageType) { | 188 switch (_currentMessageType) { |
| 191 case _WebSocketMessageType.NONE: | 189 case _WebSocketMessageType.NONE: |
| 192 throw new WebSocketException("Protocol error"); | 190 throw new WebSocketException("Protocol error"); |
| 193 | 191 |
| 194 case _WebSocketMessageType.TEXT: | 192 case _WebSocketMessageType.TEXT: |
| 195 case _WebSocketMessageType.BINARY: | 193 _buffer.add(_decodeString(buffer.getRange(index, payload))); |
| 196 if (onMessageData != null) { | |
| 197 onMessageData(buffer, index, payload); | |
| 198 } | |
| 199 index += payload; | 194 index += payload; |
| 200 if (_remainingPayloadBytes == 0) { | 195 if (_remainingPayloadBytes == 0) { |
| 201 _messageFrameEnd(); | 196 _messageFrameEnd(sink); |
| 202 } | 197 } |
| 203 break; | 198 break; |
| 204 | 199 |
| 200 case _WebSocketMessageType.BINARY: | |
| 201 _buffer.addAll(buffer.getRange(index, payload)); | |
| 202 index += payload; | |
| 203 if (_remainingPayloadBytes == 0) { | |
| 204 _messageFrameEnd(sink); | |
| 205 } | |
| 206 break; | |
| 207 | |
| 205 default: | 208 default: |
| 206 throw new WebSocketException("Protocol error"); | 209 throw new WebSocketException("Protocol error"); |
| 207 } | 210 } |
| 208 } | 211 } |
| 209 | 212 |
| 210 // Hack - as we always do index++ below. | 213 // Hack - as we always do index++ below. |
| 211 index--; | 214 index--; |
| 212 break; | 215 break; |
| 213 } | 216 } |
| 214 | 217 |
| 215 // Move to the next byte. | 218 // Move to the next byte. |
| 216 index++; | 219 index++; |
| 217 } | 220 } |
| 218 } catch (e) { | 221 } catch (e, s) { |
|
Søren Gjesse
2013/03/05 09:32:29
s not used.
Anders Johnsen
2013/03/05 09:38:34
Done.
| |
| 219 if (onClosed != null) onClosed(WebSocketStatus.PROTOCOL_ERROR, | |
| 220 "Protocol error"); | |
| 221 _state = FAILURE; | 222 _state = FAILURE; |
| 223 sink.signalError(e); | |
| 222 } | 224 } |
| 223 } | 225 } |
| 224 | 226 |
| 225 /** | 227 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) { | 228 if (_masked) { |
| 237 _state = MASK; | 229 _state = MASK; |
| 238 _remainingMaskingKeyBytes = 4; | 230 _remainingMaskingKeyBytes = 4; |
| 239 } else { | 231 } else { |
| 240 _remainingPayloadBytes = _len; | 232 _remainingPayloadBytes = _len; |
| 241 _startPayload(); | 233 _startPayload(sink); |
| 242 } | 234 } |
| 243 } | 235 } |
| 244 | 236 |
| 245 void _maskDone() { | 237 void _maskDone(StreamSink sink) { |
| 246 _remainingPayloadBytes = _len; | 238 _remainingPayloadBytes = _len; |
| 247 _startPayload(); | 239 _startPayload(sink); |
| 248 } | 240 } |
| 249 | 241 |
| 250 void _startPayload() { | 242 void _startPayload(StreamSink sink) { |
| 251 // If there is no actual payload perform perform callbacks without | 243 // If there is no actual payload perform perform callbacks without |
| 252 // going through the PAYLOAD state. | 244 // going through the PAYLOAD state. |
| 253 if (_remainingPayloadBytes == 0) { | 245 if (_remainingPayloadBytes == 0) { |
| 254 if (_isControlFrame()) { | 246 if (_isControlFrame()) { |
| 255 switch (_opcode) { | 247 switch (_opcode) { |
| 256 case _WebSocketOpcode.CLOSE: | 248 case _WebSocketOpcode.CLOSE: |
| 257 if (onClosed != null) onClosed(1005, ""); | |
| 258 _state = CLOSED; | 249 _state = CLOSED; |
| 250 sink.close(); | |
| 259 break; | 251 break; |
| 260 case _WebSocketOpcode.PING: | 252 case _WebSocketOpcode.PING: |
| 261 if (onPing != null) onPing(null); | 253 // TODO(ajohnsen): Handle ping. |
| 262 break; | 254 break; |
| 263 case _WebSocketOpcode.PONG: | 255 case _WebSocketOpcode.PONG: |
| 264 if (onPong != null) onPong(null); | 256 // TODO(ajohnsen): Handle pong. |
| 265 break; | 257 break; |
| 266 } | 258 } |
| 267 _prepareForNextFrame(); | 259 _prepareForNextFrame(); |
| 268 } else { | 260 } else { |
| 269 _messageFrameEnd(); | 261 _messageFrameEnd(sink); |
| 270 } | 262 } |
| 271 } else { | 263 } else { |
| 272 _state = PAYLOAD; | 264 _state = PAYLOAD; |
| 273 } | 265 } |
| 274 } | 266 } |
| 275 | 267 |
| 276 void _messageFrameEnd() { | 268 void _messageFrameEnd(StreamSink sink) { |
| 277 if (_fin) { | 269 if (_fin) { |
| 278 if (onMessageEnd != null) onMessageEnd(); | 270 switch (_currentMessageType) { |
| 271 case _WebSocketMessageType.TEXT: | |
| 272 sink.add(_buffer.toString()); | |
| 273 break; | |
| 274 case _WebSocketMessageType.BINARY: | |
| 275 sink.add(_buffer); | |
| 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 List. | |
| 350 | 350 |
| 351 Function onMessageStart; | 351 int closeCode = 1005; |
|
Søren Gjesse
2013/03/05 09:32:29
Use WebSocketStatus.NO_STATUS_RECEIVED instead of
Anders Johnsen
2013/03/05 09:38:34
Done.
| |
| 352 Function onMessageData; | 352 String closeReason = ""; |
|
Søren Gjesse
2013/03/05 09:32:29
Shouldn't we use null instead of ""?
Anders Johnsen
2013/03/05 09:38:34
By using "" we are always sure that we never send
| |
| 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 bool clean = true; |
|
Søren Gjesse
2013/03/05 09:32:29
Looks as if clean is not used.
Anders Johnsen
2013/03/05 09:38:34
Done.
| |
| 533 }; | 526 if (_readyState == WebSocket.OPEN) { |
| 534 _processor.onMessageEnd = () { | 527 _readyState = WebSocket.CLOSING; |
| 535 if (type == _WebSocketMessageType.TEXT) { | 528 if (transformer.closeCode != WebSocketStatus.NO_STATUS_RECEIVED) { |
| 536 _controller.add(data.toString()); | 529 _close(transformer.closeCode); |
| 537 } else { | 530 } else { |
| 538 _controller.add(data); | 531 _close(); |
| 539 } | 532 clean = false; |
| 540 }; | 533 } |
| 541 _processor.onClosed = (code, reason) { | 534 _readyState = WebSocket.CLOSED; |
| 542 bool clean = true; | 535 } |
| 543 if (_readyState == WebSocket.OPEN) { | 536 _closeCode = transformer.closeCode; |
| 544 _readyState = WebSocket.CLOSING; | 537 _closeReason = transformer.closeReason; |
| 545 if (code != WebSocketStatus.NO_STATUS_RECEIVED) { | 538 _controller.close(); |
| 546 _close(code); | 539 if (_writeClosed) _socket.destroy(); |
| 547 } else { | 540 }, |
| 548 _close(); | 541 unsubscribeOnError: true); |
| 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 | 542 |
| 564 _socket.done | 543 _socket.done |
| 565 .catchError((error) { | 544 .catchError((error) { |
| 566 if (_readyState == WebSocket.CLOSED) return; | 545 if (closed) return; |
| 546 closed = true; | |
| 567 _readyState = WebSocket.CLOSED; | 547 _readyState = WebSocket.CLOSED; |
| 568 _closeCode = ABNORMAL_CLOSURE; | 548 _closeCode = WebSocketStatus.ABNORMAL_CLOSURE; |
| 569 _controller.signalError(error); | 549 _controller.signalError(error); |
| 570 _controller.close(); | 550 _controller.close(); |
| 571 _socket.destroy(); | |
| 572 }) | 551 }) |
| 573 .whenComplete(() { | 552 .whenComplete(() { |
| 574 _writeClosed = true; | 553 _writeClosed = true; |
| 575 }); | 554 }); |
| 576 } | 555 } |
| 577 | 556 |
| 578 StreamSubscription listen(void onData(message), | 557 StreamSubscription listen(void onData(message), |
| 579 {void onError(AsyncError error), | 558 {void onError(AsyncError error), |
| 580 void onDone(), | 559 void onDone(), |
| 581 bool unsubscribeOnError}) { | 560 bool unsubscribeOnError}) { |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 641 } else { | 620 } else { |
| 642 if (message is !List<int>) { | 621 if (message is !List<int>) { |
| 643 throw new ArgumentError(message); | 622 throw new ArgumentError(message); |
| 644 } | 623 } |
| 645 opcode = _WebSocketOpcode.BINARY; | 624 opcode = _WebSocketOpcode.BINARY; |
| 646 data = message; | 625 data = message; |
| 647 } | 626 } |
| 648 } else { | 627 } else { |
| 649 opcode = _WebSocketOpcode.TEXT; | 628 opcode = _WebSocketOpcode.TEXT; |
| 650 } | 629 } |
| 651 try { | 630 _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 } | 631 } |
| 658 | 632 |
| 659 void _sendFrame(int opcode, [List<int> data]) { | 633 void _sendFrame(int opcode, [List<int> data]) { |
| 660 if (_writeClosed) return; | 634 if (_writeClosed) return; |
| 661 bool mask = false; // Masking not implemented for server. | 635 bool mask = false; // Masking not implemented for server. |
| 662 int dataLength = data == null ? 0 : data.length; | 636 int dataLength = data == null ? 0 : data.length; |
| 663 // Determine the header size. | 637 // Determine the header size. |
| 664 int headerSize = (mask) ? 6 : 2; | 638 int headerSize = (mask) ? 6 : 2; |
| 665 if (dataLength > 65535) { | 639 if (dataLength > 65535) { |
| 666 headerSize += 8; | 640 headerSize += 8; |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 679 lengthBytes = 8; | 653 lengthBytes = 8; |
| 680 } else if (dataLength > 125) { | 654 } else if (dataLength > 125) { |
| 681 header[index++] = 126; | 655 header[index++] = 126; |
| 682 lengthBytes = 2; | 656 lengthBytes = 2; |
| 683 } | 657 } |
| 684 // Write the length in network byte order into the header. | 658 // Write the length in network byte order into the header. |
| 685 for (int i = 0; i < lengthBytes; i++) { | 659 for (int i = 0; i < lengthBytes; i++) { |
| 686 header[index++] = dataLength >> (((lengthBytes - 1) - i) * 8) & 0xFF; | 660 header[index++] = dataLength >> (((lengthBytes - 1) - i) * 8) & 0xFF; |
| 687 } | 661 } |
| 688 assert(index == headerSize); | 662 assert(index == headerSize); |
| 689 _socket.add(header); | 663 try { |
| 690 if (data != null) { | 664 _socket.add(header); |
| 691 _socket.add(data); | 665 if (data != null) { |
| 666 _socket.add(data); | |
| 667 } | |
| 668 } catch (_) { | |
| 669 // The socket can be closed before _socket.done have a chance | |
| 670 // to complete. | |
| 671 _writeClosed = true; | |
| 692 } | 672 } |
| 693 } | 673 } |
| 694 } | 674 } |
| OLD | NEW |