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

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

Issue 14696012: Make web-socket pass AutobanhTestSuite. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 7 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 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
68 if (_state == CLOSED) { 68 if (_state == CLOSED) {
69 throw new WebSocketException("Data on closed connection"); 69 throw new WebSocketException("Data on closed connection");
70 } 70 }
71 if (_state == FAILURE) { 71 if (_state == FAILURE) {
72 throw new WebSocketException("Data on failed connection"); 72 throw new WebSocketException("Data on failed connection");
73 } 73 }
74 while ((index < lastIndex) && _state != CLOSED && _state != FAILURE) { 74 while ((index < lastIndex) && _state != CLOSED && _state != FAILURE) {
75 int byte = buffer[index]; 75 int byte = buffer[index];
76 switch (_state) { 76 switch (_state) {
77 case START: 77 case START:
78 _fin = (byte & 0x80) != 0; 78 _fin = (byte & 0x80) != 0;
Søren Gjesse 2013/05/08 12:15:40 Add comment // The RSV1, RSV2 bits RSV3 most be a
Anders Johnsen 2013/06/11 12:23:47 Done.
79 if ((byte & 0x70) != 0) {
80 throw new WebSocketException("Protocol error");
81 }
79 _opcode = (byte & 0xF); 82 _opcode = (byte & 0xF);
80 switch (_opcode) { 83 switch (_opcode) {
81 case _WebSocketOpcode.CONTINUATION: 84 case _WebSocketOpcode.CONTINUATION:
82 if (_currentMessageType == _WebSocketMessageType.NONE) { 85 if (_currentMessageType == _WebSocketMessageType.NONE) {
83 throw new WebSocketException("Protocol error"); 86 throw new WebSocketException("Protocol error");
84 } 87 }
85 break; 88 break;
86 89
87 case _WebSocketOpcode.TEXT: 90 case _WebSocketOpcode.TEXT:
88 if (_currentMessageType != _WebSocketMessageType.NONE) { 91 if (_currentMessageType != _WebSocketMessageType.NONE) {
89 throw new WebSocketException("Protocol error"); 92 throw new WebSocketException("Protocol error");
90 } 93 }
91 _currentMessageType = _WebSocketMessageType.TEXT; 94 _currentMessageType = _WebSocketMessageType.TEXT;
92 _buffer = new StringBuffer(); 95 _controller = new StreamController();
96 _controller.stream
97 .transform(new Utf8DecoderTransformer(null))
98 .fold(new StringBuffer(), (buffer, str) => buffer..write(str))
99 .then((buffer) {
100 sink.add(buffer.toString());
101 }, onError: (error) {
102 sink.addError(error);
103 });
93 break; 104 break;
94 105
95 case _WebSocketOpcode.BINARY: 106 case _WebSocketOpcode.BINARY:
96 if (_currentMessageType != _WebSocketMessageType.NONE) { 107 if (_currentMessageType != _WebSocketMessageType.NONE) {
97 throw new WebSocketException("Protocol error"); 108 throw new WebSocketException("Protocol error");
98 } 109 }
99 _currentMessageType = _WebSocketMessageType.BINARY; 110 _currentMessageType = _WebSocketMessageType.BINARY;
100 _buffer = new _BufferList(); 111 _controller = new StreamController();
112 _controller.stream
113 .fold(new _BufferList(), (buffer, data) => buffer..add(data))
114 .then((buffer) {
115 sink.add(buffer.readBytes());
116 }, onError: (error) {
117 sink.addError(error);
118 });
101 break; 119 break;
102 120
103 case _WebSocketOpcode.CLOSE: 121 case _WebSocketOpcode.CLOSE:
104 case _WebSocketOpcode.PING: 122 case _WebSocketOpcode.PING:
105 case _WebSocketOpcode.PONG: 123 case _WebSocketOpcode.PONG:
106 // Control frames cannot be fragmented. 124 // Control frames cannot be fragmented.
107 if (!_fin) throw new WebSocketException("Protocol error"); 125 if (!_fin) throw new WebSocketException("Protocol error");
108 break; 126 break;
109 127
110 default: 128 default:
111 throw new WebSocketException("Protocol error"); 129 throw new WebSocketException("Protocol error");
112 } 130 }
113 _state = LEN_FIRST; 131 _state = LEN_FIRST;
114 break; 132 break;
115 133
116 case LEN_FIRST: 134 case LEN_FIRST:
117 _masked = (byte & 0x80) != 0; 135 _masked = (byte & 0x80) != 0;
118 _len = byte & 0x7F; 136 _len = byte & 0x7F;
119 if (_isControlFrame() && _len > 126) { 137 if (_isControlFrame() && _len > 125) {
120 throw new WebSocketException("Protocol error"); 138 throw new WebSocketException("Protocol error");
121 } 139 }
122 if (_len < 126) { 140 if (_len < 126) {
123 _lengthDone(sink); 141 _lengthDone(sink);
124 } else if (_len == 126) { 142 } else if (_len == 126) {
125 _len = 0; 143 _len = 0;
126 _remainingLenBytes = 2; 144 _remainingLenBytes = 2;
127 _state = LEN_REST; 145 _state = LEN_REST;
128 } else if (_len == 127) { 146 } else if (_len == 127) {
129 _len = 0; 147 _len = 0;
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
176 _controlPayload = new List<int>(); 194 _controlPayload = new List<int>();
177 } 195 }
178 _controlPayload.addAll(buffer.sublist(index, index + payload)); 196 _controlPayload.addAll(buffer.sublist(index, index + payload));
179 index += payload; 197 index += payload;
180 } 198 }
181 199
182 if (_remainingPayloadBytes == 0) { 200 if (_remainingPayloadBytes == 0) {
183 _controlFrameEnd(sink); 201 _controlFrameEnd(sink);
184 } 202 }
185 } else { 203 } else {
186 switch (_currentMessageType) { 204 if (_currentMessageType != _WebSocketMessageType.TEXT &&
187 case _WebSocketMessageType.NONE: 205 _currentMessageType != _WebSocketMessageType.BINARY) {
188 throw new WebSocketException("Protocol error"); 206 throw new WebSocketException("Protocol error");
189 207 }
190 case _WebSocketMessageType.TEXT: 208 _controller.add(new Uint8List.view(buffer.buffer, index, payload)) ;
191 _buffer.write(_decodeString( 209 index += payload;
192 buffer.sublist(index, index + payload))); 210 if (_remainingPayloadBytes == 0) {
193 index += payload; 211 _messageFrameEnd(sink);
194 if (_remainingPayloadBytes == 0) {
195 _messageFrameEnd(sink);
196 }
197 break;
198
199 case _WebSocketMessageType.BINARY:
200 _buffer.write(buffer.sublist(index, index + payload));
201 index += payload;
202 if (_remainingPayloadBytes == 0) {
203 _messageFrameEnd(sink);
204 }
205 break;
206
207 default:
208 throw new WebSocketException("Protocol error");
209 } 212 }
210 } 213 }
211 214
212 // Hack - as we always do index++ below. 215 // Hack - as we always do index++ below.
213 index--; 216 index--;
214 break; 217 break;
215 } 218 }
216 219
217 // Move to the next byte. 220 // Move to the next byte.
218 index++; 221 index++;
(...skipping 29 matching lines...) Expand all
248 // If there is no actual payload perform perform callbacks without 251 // If there is no actual payload perform perform callbacks without
249 // going through the PAYLOAD state. 252 // going through the PAYLOAD state.
250 if (_remainingPayloadBytes == 0) { 253 if (_remainingPayloadBytes == 0) {
251 if (_isControlFrame()) { 254 if (_isControlFrame()) {
252 switch (_opcode) { 255 switch (_opcode) {
253 case _WebSocketOpcode.CLOSE: 256 case _WebSocketOpcode.CLOSE:
254 _state = CLOSED; 257 _state = CLOSED;
255 sink.close(); 258 sink.close();
256 break; 259 break;
257 case _WebSocketOpcode.PING: 260 case _WebSocketOpcode.PING:
258 // TODO(ajohnsen): Handle ping. 261 sink.add(new _WebSocketPing());
259 break; 262 break;
260 case _WebSocketOpcode.PONG: 263 case _WebSocketOpcode.PONG:
261 // TODO(ajohnsen): Handle pong. 264 sink.add(new _WebSocketPong());
262 break; 265 break;
263 } 266 }
264 _prepareForNextFrame(); 267 _prepareForNextFrame();
265 } else { 268 } else {
266 _messageFrameEnd(sink); 269 _messageFrameEnd(sink);
267 } 270 }
268 } else { 271 } else {
269 _state = PAYLOAD; 272 _state = PAYLOAD;
270 } 273 }
271 } 274 }
272 275
273 void _messageFrameEnd(EventSink sink) { 276 void _messageFrameEnd(EventSink sink) {
274 if (_fin) { 277 if (_fin) {
275 switch (_currentMessageType) { 278 switch (_currentMessageType) {
276 case _WebSocketMessageType.TEXT: 279 case _WebSocketMessageType.TEXT:
277 sink.add(_buffer.toString()); 280 _controller.close();
278 break; 281 break;
279 case _WebSocketMessageType.BINARY: 282 case _WebSocketMessageType.BINARY:
280 sink.add(_buffer.readBytes()); 283 _controller.close();
281 break; 284 break;
282 } 285 }
283 _buffer = null; 286 _controller = null;
284 _currentMessageType = _WebSocketMessageType.NONE; 287 _currentMessageType = _WebSocketMessageType.NONE;
285 } 288 }
286 _prepareForNextFrame(); 289 _prepareForNextFrame();
287 } 290 }
288 291
289 void _controlFrameEnd(EventSink sink) { 292 void _controlFrameEnd(EventSink sink) {
290 switch (_opcode) { 293 switch (_opcode) {
291 case _WebSocketOpcode.CLOSE: 294 case _WebSocketOpcode.CLOSE:
292 closeCode = WebSocketStatus.NO_STATUS_RECEIVED; 295 closeCode = WebSocketStatus.NO_STATUS_RECEIVED;
293 if (_controlPayload.length > 0) { 296 if (_controlPayload.length > 0) {
294 if (_controlPayload.length == 1) { 297 if (_controlPayload.length == 1) {
295 throw new WebSocketException("Protocol error"); 298 throw new WebSocketException("Protocol error");
296 } 299 }
297 closeCode = _controlPayload[0] << 8 | _controlPayload[1]; 300 closeCode = _controlPayload[0] << 8 | _controlPayload[1];
298 if (closeCode == WebSocketStatus.NO_STATUS_RECEIVED) { 301 if (closeCode == WebSocketStatus.NO_STATUS_RECEIVED) {
299 throw new WebSocketException("Protocol error"); 302 throw new WebSocketException("Protocol error");
300 } 303 }
301 if (_controlPayload.length > 2) { 304 if (_controlPayload.length > 2) {
302 closeReason = _decodeString( 305 closeReason = _decodeUtf8Strict(_controlPayload.sublist(2));
303 _controlPayload.sublist(2));
304 } 306 }
305 } 307 }
306 _state = CLOSED; 308 _state = CLOSED;
307 sink.close(); 309 sink.close();
308 break; 310 break;
309 311
310 case _WebSocketOpcode.PING: 312 case _WebSocketOpcode.PING:
311 // TODO(ajohnsen): Handle ping. 313 sink.add(new _WebSocketPing(_controlPayload));
312 break; 314 break;
313 315
314 case _WebSocketOpcode.PONG: 316 case _WebSocketOpcode.PONG:
315 // TODO(ajohnsen): Handle pong. 317 sink.add(new _WebSocketPong(_controlPayload));
316 break; 318 break;
317 } 319 }
318 _prepareForNextFrame(); 320 _prepareForNextFrame();
319 } 321 }
320 322
321 bool _isControlFrame() { 323 bool _isControlFrame() {
322 return _opcode == _WebSocketOpcode.CLOSE || 324 return _opcode == _WebSocketOpcode.CLOSE ||
323 _opcode == _WebSocketOpcode.PING || 325 _opcode == _WebSocketOpcode.PING ||
324 _opcode == _WebSocketOpcode.PONG; 326 _opcode == _WebSocketOpcode.PONG;
325 } 327 }
(...skipping 18 matching lines...) Expand all
344 int _len; 346 int _len;
345 bool _masked; 347 bool _masked;
346 int _maskingKey; 348 int _maskingKey;
347 int _remainingLenBytes; 349 int _remainingLenBytes;
348 int _remainingMaskingKeyBytes; 350 int _remainingMaskingKeyBytes;
349 int _remainingPayloadBytes; 351 int _remainingPayloadBytes;
350 int _unmaskingIndex; 352 int _unmaskingIndex;
351 353
352 int _currentMessageType; 354 int _currentMessageType;
353 List<int> _controlPayload; 355 List<int> _controlPayload;
354 var _buffer; // Either StringBuffer or _BufferList. 356 StreamController _controller;
355 357
356 int closeCode = WebSocketStatus.NO_STATUS_RECEIVED; 358 int closeCode = WebSocketStatus.NO_STATUS_RECEIVED;
357 String closeReason = ""; 359 String closeReason = "";
358 } 360 }
359 361
360 362
363 class _WebSocketPing {
364 final List<int> payload;
365 _WebSocketPing([this.payload = null]);
366 }
367
368
369 class _WebSocketPong {
370 final List<int> payload;
371 _WebSocketPong([this.payload = null]);
372 }
373
374
361 class _WebSocketTransformerImpl implements WebSocketTransformer { 375 class _WebSocketTransformerImpl implements WebSocketTransformer {
362 final StreamController<WebSocket> _controller = 376 final StreamController<WebSocket> _controller =
363 new StreamController<WebSocket>(); 377 new StreamController<WebSocket>();
364 378
365 Stream<WebSocket> bind(Stream<HttpRequest> stream) { 379 Stream<WebSocket> bind(Stream<HttpRequest> stream) {
366 stream.listen((request) { 380 stream.listen((request) {
367 _upgrade(request) 381 _upgrade(request)
368 .then((WebSocket webSocket) => _controller.add(webSocket)) 382 .then((WebSocket webSocket) => _controller.add(webSocket))
369 .catchError((error) => _controller.addError(error)); 383 .catchError((error) => _controller.addError(error));
370 }); 384 });
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
427 } 441 }
428 } 442 }
429 443
430 444
431 class _WebSocketOutgoingTransformer extends StreamEventTransformer { 445 class _WebSocketOutgoingTransformer extends StreamEventTransformer {
432 final _WebSocketImpl webSocket; 446 final _WebSocketImpl webSocket;
433 447
434 _WebSocketOutgoingTransformer(_WebSocketImpl this.webSocket); 448 _WebSocketOutgoingTransformer(_WebSocketImpl this.webSocket);
435 449
436 void handleData(message, EventSink<List<int>> sink) { 450 void handleData(message, EventSink<List<int>> sink) {
451 if (message is _WebSocketPong) {
452 addFrame(_WebSocketOpcode.PONG, message.payload, sink);
453 return;
454 }
455 if (message is _WebSocketPing) {
456 addFrame(_WebSocketOpcode.PONG, message.payload, sink);
457 return;
458 }
437 List<int> data; 459 List<int> data;
438 int opcode; 460 int opcode;
439 if (message != null) { 461 if (message != null) {
440 if (message is String) { 462 if (message is String) {
441 opcode = _WebSocketOpcode.TEXT; 463 opcode = _WebSocketOpcode.TEXT;
442 data = _encodeString(message); 464 data = _encodeString(message);
443 } else { 465 } else {
444 if (message is !List<int>) { 466 if (message is !List<int>) {
445 throw new ArgumentError(message); 467 throw new ArgumentError(message);
446 } 468 }
(...skipping 16 matching lines...) Expand all
463 data.add(code & 0xFF); 485 data.add(code & 0xFF);
464 if (reason != null) { 486 if (reason != null) {
465 data.addAll(_encodeString(reason)); 487 data.addAll(_encodeString(reason));
466 } 488 }
467 } 489 }
468 addFrame(_WebSocketOpcode.CLOSE, data, sink); 490 addFrame(_WebSocketOpcode.CLOSE, data, sink);
469 sink.close(); 491 sink.close();
470 } 492 }
471 493
472 void addFrame(int opcode, List<int> data, EventSink<List<int>> sink) { 494 void addFrame(int opcode, List<int> data, EventSink<List<int>> sink) {
473 bool mask = !webSocket._serverSide; // Masking not implemented for server. 495 createFrame(opcode, data, webSocket._serverSide).forEach(sink.add);
496 }
497
498 static Iterator createFrame(int opcode, List<int> data, bool serverSide) {
499 bool mask = !serverSide; // Masking not implemented for server.
474 int dataLength = data == null ? 0 : data.length; 500 int dataLength = data == null ? 0 : data.length;
475 // Determine the header size. 501 // Determine the header size.
476 int headerSize = (mask) ? 6 : 2; 502 int headerSize = (mask) ? 6 : 2;
477 if (dataLength > 65535) { 503 if (dataLength > 65535) {
478 headerSize += 8; 504 headerSize += 8;
479 } else if (dataLength > 125) { 505 } else if (dataLength > 125) {
480 headerSize += 2; 506 headerSize += 2;
481 } 507 }
482 List<int> header = new List<int>(headerSize); 508 List<int> header = new List<int>(headerSize);
483 int index = 0; 509 int index = 0;
(...skipping 20 matching lines...) Expand all
504 index += 4; 530 index += 4;
505 if (data != null) { 531 if (data != null) {
506 var list = new Uint8List(data.length); 532 var list = new Uint8List(data.length);
507 for (int i = 0; i < data.length; i++) { 533 for (int i = 0; i < data.length; i++) {
508 list[i] = data[i] ^ maskBytes[i % 4]; 534 list[i] = data[i] ^ maskBytes[i % 4];
509 } 535 }
510 data = list; 536 data = list;
511 } 537 }
512 } 538 }
513 assert(index == headerSize); 539 assert(index == headerSize);
514 sink.add(header); 540 if (data == null) {
515 if (data != null) { 541 return [header];
516 sink.add(data); 542 } else {
543 return [header, data];
517 } 544 }
518 } 545 }
519 } 546 }
520 547
521 548
522 class _WebSocketConsumer implements StreamConsumer { 549 class _WebSocketConsumer implements StreamConsumer {
523 final _WebSocketImpl webSocket; 550 final _WebSocketImpl webSocket;
524 final Socket socket; 551 final Socket socket;
525 StreamController _controller; 552 StreamController _controller;
526 StreamSubscription _subscription; 553 StreamSubscription _subscription;
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
577 _done(); 604 _done();
578 }, 605 },
579 onError: (error) { 606 onError: (error) {
580 _done(error); 607 _done(error);
581 }, 608 },
582 cancelOnError: true); 609 cancelOnError: true);
583 return _completer.future; 610 return _completer.future;
584 } 611 }
585 612
586 Future close() { 613 Future close() {
614 _ensureController();
587 Future closeSocket() { 615 Future closeSocket() {
588 return socket.close().then((_) => webSocket); 616 return socket.close().then((_) => webSocket);
589 } 617 }
590 if (_controller == null) return closeSocket();
591 _controller.close(); 618 _controller.close();
592 return _closeCompleter.future.then((_) => closeSocket()); 619 return _closeCompleter.future.then((_) => closeSocket());
593 } 620 }
621
622 void add(data) {
623 _ensureController();
624 _controller.add(data);
625 }
594 } 626 }
595 627
596 628
597 class _WebSocketImpl extends Stream implements WebSocket { 629 class _WebSocketImpl extends Stream implements WebSocket {
598 final StreamController _controller = new StreamController(); 630 final StreamController _controller = new StreamController();
599 StreamSink _sink; 631 StreamSink _sink;
600 632
601 final Socket _socket; 633 final Socket _socket;
602 final bool _serverSide; 634 final bool _serverSide;
603 int _readyState = WebSocket.CONNECTING; 635 int _readyState = WebSocket.CONNECTING;
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
675 error("Bad response 'Sec-WebSocket-Accept' header"); 707 error("Bad response 'Sec-WebSocket-Accept' header");
676 } 708 }
677 } 709 }
678 return response.detachSocket() 710 return response.detachSocket()
679 .then((socket) => new _WebSocketImpl._fromSocket(socket)); 711 .then((socket) => new _WebSocketImpl._fromSocket(socket));
680 }); 712 });
681 } 713 }
682 714
683 _WebSocketImpl._fromSocket(Socket this._socket, 715 _WebSocketImpl._fromSocket(Socket this._socket,
684 [bool this._serverSide = false]) { 716 [bool this._serverSide = false]) {
685 _sink = new _StreamSinkImpl(new _WebSocketConsumer(this, _socket)); 717 var consumer = new _WebSocketConsumer(this, _socket);
718 _sink = new _StreamSinkImpl(consumer);
686 _readyState = WebSocket.OPEN; 719 _readyState = WebSocket.OPEN;
687 720
688 var transformer = new _WebSocketProtocolTransformer(_serverSide); 721 var transformer = new _WebSocketProtocolTransformer(_serverSide);
689 _socket.transform(transformer).listen( 722 _socket.transform(transformer).listen(
690 (data) { 723 (data) {
691 _controller.add(data); 724 if (data is _WebSocketPing) {
725 consumer.add(new _WebSocketPong(data.payload));
726 } else if (data is _WebSocketPong) {
727 // TODO(ajohnsen): Notify pong?
728 } else {
729 _controller.add(data);
730 }
692 }, 731 },
693 onError: (error) { 732 onError: (error) {
733 if (error is ArgumentError) {
734 close(WebSocketStatus.INVALID_FRAME_PAYLOAD_DATA);
735 } else {
736 close(WebSocketStatus.PROTOCOL_ERROR);
737 }
694 _controller.addError(error); 738 _controller.addError(error);
695 _controller.close(); 739 _controller.close();
696 }, 740 },
697 onDone: () { 741 onDone: () {
698 if (_readyState == WebSocket.OPEN) { 742 if (_readyState == WebSocket.OPEN) {
699 _readyState = WebSocket.CLOSING; 743 _readyState = WebSocket.CLOSING;
700 if (transformer.closeCode != WebSocketStatus.NO_STATUS_RECEIVED) { 744 if (!_isReservedStatusCode(transformer.closeCode)) {
701 close(transformer.closeCode); 745 close(transformer.closeCode);
702 } else { 746 } else {
703 close(); 747 close();
704 } 748 }
705 _readyState = WebSocket.CLOSED; 749 _readyState = WebSocket.CLOSED;
706 } 750 }
707 _closeCode = transformer.closeCode; 751 _closeCode = transformer.closeCode;
708 _closeReason = transformer.closeReason; 752 _closeReason = transformer.closeReason;
709 _controller.close(); 753 _controller.close();
710 }, 754 },
(...skipping 17 matching lines...) Expand all
728 int get closeCode => _closeCode; 772 int get closeCode => _closeCode;
729 String get closeReason => _closeReason; 773 String get closeReason => _closeReason;
730 774
731 void add(data) => _sink.add(data); 775 void add(data) => _sink.add(data);
732 void addError(error) => _sink.addError(error); 776 void addError(error) => _sink.addError(error);
733 Future addStream(Stream stream) => _sink.addStream(stream); 777 Future addStream(Stream stream) => _sink.addStream(stream);
734 Future get done => _sink.done; 778 Future get done => _sink.done;
735 779
736 Future close([int code, String reason]) { 780 Future close([int code, String reason]) {
737 if (!_writeClosed) { 781 if (!_writeClosed) {
738 if (code == WebSocketStatus.RESERVED_1004 || 782 if (_isReservedStatusCode(code)) {
739 code == WebSocketStatus.NO_STATUS_RECEIVED ||
740 code == WebSocketStatus.RESERVED_1015) {
741 throw new WebSocketException("Reserved status code $code"); 783 throw new WebSocketException("Reserved status code $code");
742 } 784 }
743 _outCloseCode = code; 785 _outCloseCode = code;
744 _outCloseReason = reason; 786 _outCloseReason = reason;
745 _writeClosed = true; 787 _writeClosed = true;
746 } 788 }
747 return _sink.close(); 789 return _sink.close();
748 } 790 }
791
792 static bool _isReservedStatusCode(int code) {
793 return code != null &&
794 (code < WebSocketStatus.NORMAL_CLOSURE ||
795 code == WebSocketStatus.RESERVED_1004 ||
796 code == WebSocketStatus.NO_STATUS_RECEIVED ||
797 code == WebSocketStatus.ABNORMAL_CLOSURE ||
798 (code > WebSocketStatus.INTERNAL_SERVER_ERROR &&
799 code < WebSocketStatus.RESERVED_1015) ||
800 (code >= WebSocketStatus.RESERVED_1015 &&
801 code < 3000));
802 }
749 } 803 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698