Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(187)

Side by Side Diff: sdk/lib/io/websocket_impl.dart

Issue 12387085: Improve web-socket implementation by using making _WebSocketProtocolProcessor a StreamTransformer. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698