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 const String _clientNoContextTakeover = "client_no_context_takeover"; | 8 const String _clientNoContextTakeover = "client_no_context_takeover"; |
9 const String _serverNoContextTakeover = "server_no_context_takeover"; | 9 const String _serverNoContextTakeover = "server_no_context_takeover"; |
10 const String _clientMaxWindowBits = "client_max_window_bits"; | 10 const String _clientMaxWindowBits = "client_max_window_bits"; |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
43 */ | 43 */ |
44 class _CompressionMaxWindowBits { | 44 class _CompressionMaxWindowBits { |
45 String headerValue; | 45 String headerValue; |
46 int maxWindowBits; | 46 int maxWindowBits; |
47 _CompressionMaxWindowBits([this.headerValue, this.maxWindowBits]); | 47 _CompressionMaxWindowBits([this.headerValue, this.maxWindowBits]); |
48 String toString() => headerValue; | 48 String toString() => headerValue; |
49 } | 49 } |
50 | 50 |
51 /** | 51 /** |
52 * The web socket protocol transformer handles the protocol byte stream | 52 * The web socket protocol transformer handles the protocol byte stream |
53 * which is supplied through the `handleData`. As the protocol is processed, | 53 * which is supplied through the [:handleData:]. As the protocol is processed, |
54 * it'll output frame data as either a List<int> or String. | 54 * it'll output frame data as either a List<int> or String. |
55 * | 55 * |
56 * Important information about usage: Be sure you use cancelOnError, so the | 56 * Important information about usage: Be sure you use cancelOnError, so the |
57 * socket will be closed when the processor encounter an error. Not using it | 57 * socket will be closed when the processor encounter an error. Not using it |
58 * will lead to undefined behaviour. | 58 * will lead to undefined behaviour. |
59 */ | 59 */ |
60 // TODO(ajohnsen): make this transformer reusable? | 60 // TODO(ajohnsen): make this transformer reusable? |
61 class _WebSocketProtocolTransformer | 61 class _WebSocketProtocolTransformer |
62 implements EventSink<List<int>>, StreamTransformer< | 62 implements StreamTransformer<List<int>, dynamic>, EventSink<Uint8List> { |
63 List<int>, dynamic/*List<int>|_WebSocketPing|_WebSocketPong>*/> { | |
64 static const int START = 0; | 63 static const int START = 0; |
65 static const int LEN_FIRST = 1; | 64 static const int LEN_FIRST = 1; |
66 static const int LEN_REST = 2; | 65 static const int LEN_REST = 2; |
67 static const int MASK = 3; | 66 static const int MASK = 3; |
68 static const int PAYLOAD = 4; | 67 static const int PAYLOAD = 4; |
69 static const int CLOSED = 5; | 68 static const int CLOSED = 5; |
70 static const int FAILURE = 6; | 69 static const int FAILURE = 6; |
71 static const int FIN = 0x80; | 70 static const int FIN = 0x80; |
72 static const int RSV1 = 0x40; | 71 static const int RSV1 = 0x40; |
73 static const int RSV2 = 0x20; | 72 static const int RSV2 = 0x20; |
74 static const int RSV3 = 0x10; | 73 static const int RSV3 = 0x10; |
75 static const int OPCODE = 0xF; | 74 static const int OPCODE = 0xF; |
76 | 75 |
77 int _state = START; | 76 int _state = START; |
78 bool _fin = false; | 77 bool _fin = false; |
79 bool _compressed = false; | 78 bool _compressed = false; |
80 int _opcode = -1; | 79 int _opcode = -1; |
81 int _len = -1; | 80 int _len = -1; |
82 bool _masked = false; | 81 bool _masked = false; |
83 int _remainingLenBytes = -1; | 82 int _remainingLenBytes = -1; |
84 int _remainingMaskingKeyBytes = 4; | 83 int _remainingMaskingKeyBytes = 4; |
85 int _remainingPayloadBytes = -1; | 84 int _remainingPayloadBytes = -1; |
86 int _unmaskingIndex = 0; | 85 int _unmaskingIndex = 0; |
87 int _currentMessageType = _WebSocketMessageType.NONE; | 86 int _currentMessageType = _WebSocketMessageType.NONE; |
88 int closeCode = WebSocketStatus.NO_STATUS_RECEIVED; | 87 int closeCode = WebSocketStatus.NO_STATUS_RECEIVED; |
89 String closeReason = ""; | 88 String closeReason = ""; |
90 | 89 |
91 EventSink<dynamic/*List<int>|_WebSocketPing|_WebSocketPong>*/> _eventSink; | 90 EventSink _eventSink; |
92 | 91 |
93 final bool _serverSide; | 92 final bool _serverSide; |
94 final List _maskingBytes = new List(4); | 93 final List _maskingBytes = new List(4); |
95 final BytesBuilder _payload = new BytesBuilder(copy: false); | 94 final BytesBuilder _payload = new BytesBuilder(copy: false); |
96 | 95 |
97 _WebSocketPerMessageDeflate _deflate; | 96 _WebSocketPerMessageDeflate _deflate; |
98 _WebSocketProtocolTransformer([this._serverSide = false, this._deflate]); | 97 _WebSocketProtocolTransformer([this._serverSide = false, this._deflate]); |
99 | 98 |
100 Stream<dynamic/*List<int>|_WebSocketPing|_WebSocketPong>*/> bind( | 99 Stream bind(Stream stream) { |
101 Stream<List<int>> stream) { | |
102 return new Stream.eventTransformed(stream, (EventSink eventSink) { | 100 return new Stream.eventTransformed(stream, (EventSink eventSink) { |
103 if (_eventSink != null) { | 101 if (_eventSink != null) { |
104 throw new StateError("WebSocket transformer already used."); | 102 throw new StateError("WebSocket transformer already used."); |
105 } | 103 } |
106 _eventSink = eventSink; | 104 _eventSink = eventSink; |
107 return this; | 105 return this; |
108 }); | 106 }); |
109 } | 107 } |
110 | 108 |
111 void addError(Object error, [StackTrace stackTrace]) { | 109 void addError(Object error, [StackTrace stackTrace]) { |
(...skipping 277 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
389 class _WebSocketPing { | 387 class _WebSocketPing { |
390 final List<int> payload; | 388 final List<int> payload; |
391 _WebSocketPing([this.payload = null]); | 389 _WebSocketPing([this.payload = null]); |
392 } | 390 } |
393 | 391 |
394 class _WebSocketPong { | 392 class _WebSocketPong { |
395 final List<int> payload; | 393 final List<int> payload; |
396 _WebSocketPong([this.payload = null]); | 394 _WebSocketPong([this.payload = null]); |
397 } | 395 } |
398 | 396 |
399 typedef /*String|Future<String>*/ _ProtocolSelector(List<String> protocols); | |
400 | |
401 class _WebSocketTransformerImpl implements WebSocketTransformer { | 397 class _WebSocketTransformerImpl implements WebSocketTransformer { |
402 final StreamController<WebSocket> _controller = | 398 final StreamController<WebSocket> _controller = |
403 new StreamController<WebSocket>(sync: true); | 399 new StreamController<WebSocket>(sync: true); |
404 final _ProtocolSelector _protocolSelector; | 400 final Function _protocolSelector; |
405 final CompressionOptions _compression; | 401 final CompressionOptions _compression; |
406 | 402 |
407 _WebSocketTransformerImpl(this._protocolSelector, this._compression); | 403 _WebSocketTransformerImpl(this._protocolSelector, this._compression); |
408 | 404 |
409 Stream<WebSocket> bind(Stream<HttpRequest> stream) { | 405 Stream<WebSocket> bind(Stream<HttpRequest> stream) { |
410 stream.listen((request) { | 406 stream.listen((request) { |
411 _upgrade(request, _protocolSelector, _compression) | 407 _upgrade(request, _protocolSelector, _compression) |
412 .then((WebSocket webSocket) => _controller.add(webSocket)) | 408 .then((WebSocket webSocket) => _controller.add(webSocket)) |
413 .catchError(_controller.addError); | 409 .catchError(_controller.addError); |
414 }, onDone: () { | 410 }, onDone: () { |
415 _controller.close(); | 411 _controller.close(); |
416 }); | 412 }); |
417 | 413 |
418 return _controller.stream; | 414 return _controller.stream; |
419 } | 415 } |
420 | 416 |
421 static Future<WebSocket> _upgrade(HttpRequest request, | 417 static Future<WebSocket> _upgrade( |
422 _ProtocolSelector _protocolSelector, CompressionOptions compression) { | 418 HttpRequest request, _protocolSelector, CompressionOptions compression) { |
423 var response = request.response; | 419 var response = request.response; |
424 if (!_isUpgradeRequest(request)) { | 420 if (!_isUpgradeRequest(request)) { |
425 // Send error response. | 421 // Send error response. |
426 response | 422 response |
427 ..statusCode = HttpStatus.BAD_REQUEST | 423 ..statusCode = HttpStatus.BAD_REQUEST |
428 ..close(); | 424 ..close(); |
429 return new Future.error( | 425 return new Future.error( |
430 new WebSocketException("Invalid WebSocket upgrade request")); | 426 new WebSocketException("Invalid WebSocket upgrade request")); |
431 } | 427 } |
432 | 428 |
433 Future<WebSocket> upgrade(String protocol) { | 429 Future upgrade(String protocol) { |
434 // Send the upgrade response. | 430 // Send the upgrade response. |
435 response | 431 response |
436 ..statusCode = HttpStatus.SWITCHING_PROTOCOLS | 432 ..statusCode = HttpStatus.SWITCHING_PROTOCOLS |
437 ..headers.add(HttpHeaders.CONNECTION, "Upgrade") | 433 ..headers.add(HttpHeaders.CONNECTION, "Upgrade") |
438 ..headers.add(HttpHeaders.UPGRADE, "websocket"); | 434 ..headers.add(HttpHeaders.UPGRADE, "websocket"); |
439 String key = request.headers.value("Sec-WebSocket-Key"); | 435 String key = request.headers.value("Sec-WebSocket-Key"); |
440 _SHA1 sha1 = new _SHA1(); | 436 _SHA1 sha1 = new _SHA1(); |
441 sha1.add("$key$_webSocketGUID".codeUnits); | 437 sha1.add("$key$_webSocketGUID".codeUnits); |
442 String accept = _CryptoUtils.bytesToBase64(sha1.close()); | 438 String accept = _CryptoUtils.bytesToBase64(sha1.close()); |
443 response.headers.add("Sec-WebSocket-Accept", accept); | 439 response.headers.add("Sec-WebSocket-Accept", accept); |
444 if (protocol != null) { | 440 if (protocol != null) { |
445 response.headers.add("Sec-WebSocket-Protocol", protocol); | 441 response.headers.add("Sec-WebSocket-Protocol", protocol); |
446 } | 442 } |
447 | 443 |
448 var deflate = _negotiateCompression(request, response, compression); | 444 var deflate = _negotiateCompression(request, response, compression); |
449 | 445 |
450 response.headers.contentLength = 0; | 446 response.headers.contentLength = 0; |
451 return response.detachSocket().then/*<WebSocket>*/((socket) => | 447 return response.detachSocket().then((socket) => |
452 new _WebSocketImpl._fromSocket( | 448 new _WebSocketImpl._fromSocket( |
453 socket, protocol, compression, true, deflate)); | 449 socket, protocol, compression, true, deflate)); |
454 } | 450 } |
455 | 451 |
456 var protocols = request.headers['Sec-WebSocket-Protocol']; | 452 var protocols = request.headers['Sec-WebSocket-Protocol']; |
457 if (protocols != null && _protocolSelector != null) { | 453 if (protocols != null && _protocolSelector != null) { |
458 // The suggested protocols can be spread over multiple lines, each | 454 // The suggested protocols can be spread over multiple lines, each |
459 // consisting of multiple protocols. To unify all of them, first join | 455 // consisting of multiple protocols. To unify all of them, first join |
460 // the lists with ', ' and then tokenize. | 456 // the lists with ', ' and then tokenize. |
461 protocols = _HttpParser._tokenizeFieldValue(protocols.join(', ')); | 457 protocols = _HttpParser._tokenizeFieldValue(protocols.join(', ')); |
462 return new Future<String>(() => _protocolSelector(protocols)) | 458 return new Future(() => _protocolSelector(protocols)).then((protocol) { |
463 .then/*<String>*/((protocol) { | |
464 if (protocols.indexOf(protocol) < 0) { | 459 if (protocols.indexOf(protocol) < 0) { |
465 throw new WebSocketException( | 460 throw new WebSocketException( |
466 "Selected protocol is not in the list of available protocols"); | 461 "Selected protocol is not in the list of available protocols"); |
467 } | 462 } |
468 return protocol; | 463 return protocol; |
469 }).catchError((error) { | 464 }).catchError((error) { |
470 response | 465 response |
471 ..statusCode = HttpStatus.INTERNAL_SERVER_ERROR | 466 ..statusCode = HttpStatus.INTERNAL_SERVER_ERROR |
472 ..close(); | 467 ..close(); |
473 throw error; | 468 throw error; |
474 }).then/*<WebSocket>*/(upgrade); | 469 }).then(upgrade); |
475 } else { | 470 } else { |
476 return upgrade(null); | 471 return upgrade(null); |
477 } | 472 } |
478 } | 473 } |
479 | 474 |
480 static _WebSocketPerMessageDeflate _negotiateCompression(HttpRequest request, | 475 static _WebSocketPerMessageDeflate _negotiateCompression(HttpRequest request, |
481 HttpResponse response, CompressionOptions compression) { | 476 HttpResponse response, CompressionOptions compression) { |
482 var extensionHeader = request.headers.value("Sec-WebSocket-Extensions"); | 477 var extensionHeader = request.headers.value("Sec-WebSocket-Extensions"); |
483 | 478 |
484 extensionHeader ??= ""; | 479 extensionHeader ??= ""; |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
568 ZLibOption.DEFAULT_MEM_LEVEL, | 563 ZLibOption.DEFAULT_MEM_LEVEL, |
569 ZLibOption.STRATEGY_DEFAULT, | 564 ZLibOption.STRATEGY_DEFAULT, |
570 null, | 565 null, |
571 true); | 566 true); |
572 } | 567 } |
573 } | 568 } |
574 | 569 |
575 Uint8List processIncomingMessage(List<int> msg) { | 570 Uint8List processIncomingMessage(List<int> msg) { |
576 _ensureDecoder(); | 571 _ensureDecoder(); |
577 | 572 |
578 var data = <int>[]; | 573 var data = []; |
579 data.addAll(msg); | 574 data.addAll(msg); |
580 data.addAll(const [0x00, 0x00, 0xff, 0xff]); | 575 data.addAll(const [0x00, 0x00, 0xff, 0xff]); |
581 | 576 |
582 decoder.process(data, 0, data.length); | 577 decoder.process(data, 0, data.length); |
583 var result = <int>[]; | 578 var result = []; |
584 List<int> out; | 579 var out; |
585 | 580 |
586 while ((out = decoder.processed()) != null) { | 581 while ((out = decoder.processed()) != null) { |
587 result.addAll(out); | 582 result.addAll(out); |
588 } | 583 } |
589 | 584 |
590 if ((serverSide && clientNoContextTakeover) || | 585 if ((serverSide && clientNoContextTakeover) || |
591 (!serverSide && serverNoContextTakeover)) { | 586 (!serverSide && serverNoContextTakeover)) { |
592 decoder = null; | 587 decoder = null; |
593 } | 588 } |
594 | 589 |
595 return new Uint8List.fromList(result); | 590 return new Uint8List.fromList(result); |
596 } | 591 } |
597 | 592 |
598 List<int> processOutgoingMessage(List<int> msg) { | 593 List<int> processOutgoingMessage(List<int> msg) { |
599 _ensureEncoder(); | 594 _ensureEncoder(); |
600 var result = <int>[]; | 595 var result = []; |
601 Uint8List buffer; | 596 Uint8List buffer; |
| 597 var out; |
602 | 598 |
603 if (msg is! Uint8List) { | 599 if (msg is! Uint8List) { |
604 for (var i = 0; i < msg.length; i++) { | 600 for (var i = 0; i < msg.length; i++) { |
605 if (msg[i] < 0 || 255 < msg[i]) { | 601 if (msg[i] < 0 || 255 < msg[i]) { |
606 throw new ArgumentError("List element is not a byte value " | 602 throw new ArgumentError("List element is not a byte value " |
607 "(value ${msg[i]} at index $i)"); | 603 "(value ${msg[i]} at index $i)"); |
608 } | 604 } |
609 } | 605 } |
610 buffer = new Uint8List.fromList(msg); | 606 buffer = new Uint8List.fromList(msg); |
611 } else { | 607 } else { |
612 buffer = msg; | 608 buffer = msg; |
613 } | 609 } |
614 | 610 |
615 encoder.process(buffer, 0, buffer.length); | 611 encoder.process(buffer, 0, buffer.length); |
616 | 612 |
617 List<int> out; | |
618 while ((out = encoder.processed()) != null) { | 613 while ((out = encoder.processed()) != null) { |
619 result.addAll(out); | 614 result.addAll(out); |
620 } | 615 } |
621 | 616 |
622 if ((!serverSide && clientNoContextTakeover) || | 617 if ((!serverSide && clientNoContextTakeover) || |
623 (serverSide && serverNoContextTakeover)) { | 618 (serverSide && serverNoContextTakeover)) { |
624 encoder = null; | 619 encoder = null; |
625 } | 620 } |
626 | 621 |
627 if (result.length > 4) { | 622 if (result.length > 4) { |
(...skipping 10 matching lines...) Expand all Loading... |
638 final _WebSocketImpl webSocket; | 633 final _WebSocketImpl webSocket; |
639 EventSink<List<int>> _eventSink; | 634 EventSink<List<int>> _eventSink; |
640 | 635 |
641 _WebSocketPerMessageDeflate _deflateHelper; | 636 _WebSocketPerMessageDeflate _deflateHelper; |
642 | 637 |
643 _WebSocketOutgoingTransformer(this.webSocket) { | 638 _WebSocketOutgoingTransformer(this.webSocket) { |
644 _deflateHelper = webSocket._deflate; | 639 _deflateHelper = webSocket._deflate; |
645 } | 640 } |
646 | 641 |
647 Stream<List<int>> bind(Stream stream) { | 642 Stream<List<int>> bind(Stream stream) { |
648 return new Stream<List<int>>.eventTransformed( | 643 return new Stream.eventTransformed(stream, (eventSink) { |
649 stream, (EventSink<List<int>> eventSink) { | |
650 if (_eventSink != null) { | 644 if (_eventSink != null) { |
651 throw new StateError("WebSocket transformer already used"); | 645 throw new StateError("WebSocket transformer already used"); |
652 } | 646 } |
653 _eventSink = eventSink; | 647 _eventSink = eventSink; |
654 return this; | 648 return this; |
655 }); | 649 }); |
656 } | 650 } |
657 | 651 |
658 void add(message) { | 652 void add(message) { |
659 if (message is _WebSocketPong) { | 653 if (message is _WebSocketPong) { |
660 addFrame(_WebSocketOpcode.PONG, message.payload); | 654 addFrame(_WebSocketOpcode.PONG, message.payload); |
661 return; | 655 return; |
662 } | 656 } |
663 if (message is _WebSocketPing) { | 657 if (message is _WebSocketPing) { |
664 addFrame(_WebSocketOpcode.PING, message.payload); | 658 addFrame(_WebSocketOpcode.PING, message.payload); |
665 return; | 659 return; |
666 } | 660 } |
667 List<int> data; | 661 List<int> data; |
668 int opcode; | 662 int opcode; |
669 if (message != null) { | 663 if (message != null) { |
670 if (message is String) { | 664 if (message is String) { |
671 opcode = _WebSocketOpcode.TEXT; | 665 opcode = _WebSocketOpcode.TEXT; |
672 data = UTF8.encode(message); | 666 data = UTF8.encode(message); |
673 } else { | 667 } else { |
674 if (message is List<int>) { | 668 if (message is List<int>) { |
| 669 data = message; |
675 opcode = _WebSocketOpcode.BINARY; | 670 opcode = _WebSocketOpcode.BINARY; |
676 data = message; | |
677 } else { | 671 } else { |
678 throw new ArgumentError(message); | 672 throw new ArgumentError(message); |
679 } | 673 } |
680 } | 674 } |
681 | 675 |
682 if (_deflateHelper != null) { | 676 if (_deflateHelper != null) { |
683 data = _deflateHelper.processOutgoingMessage(data); | 677 data = _deflateHelper.processOutgoingMessage(data); |
684 } | 678 } |
685 } else { | 679 } else { |
686 opcode = _WebSocketOpcode.TEXT; | 680 opcode = _WebSocketOpcode.TEXT; |
(...skipping 14 matching lines...) Expand all Loading... |
701 data.add((code >> 8) & 0xFF); | 695 data.add((code >> 8) & 0xFF); |
702 data.add(code & 0xFF); | 696 data.add(code & 0xFF); |
703 if (reason != null) { | 697 if (reason != null) { |
704 data.addAll(UTF8.encode(reason)); | 698 data.addAll(UTF8.encode(reason)); |
705 } | 699 } |
706 } | 700 } |
707 addFrame(_WebSocketOpcode.CLOSE, data); | 701 addFrame(_WebSocketOpcode.CLOSE, data); |
708 _eventSink.close(); | 702 _eventSink.close(); |
709 } | 703 } |
710 | 704 |
711 void addFrame(int opcode, List<int> data) { | 705 void addFrame(int opcode, List<int> data) => createFrame( |
712 createFrame( | 706 opcode, |
713 opcode, | 707 data, |
714 data, | 708 webSocket._serverSide, |
715 webSocket._serverSide, | 709 _deflateHelper != null && |
716 _deflateHelper != null && | 710 (opcode == _WebSocketOpcode.TEXT || |
717 (opcode == _WebSocketOpcode.TEXT || | 711 opcode == _WebSocketOpcode.BINARY)).forEach((e) { |
718 opcode == _WebSocketOpcode.BINARY)) | 712 _eventSink.add(e); |
719 .forEach((e) { _eventSink.add(e); }); | 713 }); |
720 } | |
721 | 714 |
722 static Iterable<List<int>> createFrame( | 715 static Iterable<List<int>> createFrame( |
723 int opcode, List<int> data, bool serverSide, bool compressed) { | 716 int opcode, List<int> data, bool serverSide, bool compressed) { |
724 bool mask = !serverSide; // Masking not implemented for server. | 717 bool mask = !serverSide; // Masking not implemented for server. |
725 int dataLength = data == null ? 0 : data.length; | 718 int dataLength = data == null ? 0 : data.length; |
726 // Determine the header size. | 719 // Determine the header size. |
727 int headerSize = (mask) ? 6 : 2; | 720 int headerSize = (mask) ? 6 : 2; |
728 if (dataLength > 65535) { | 721 if (dataLength > 65535) { |
729 headerSize += 8; | 722 headerSize += 8; |
730 } else if (dataLength > 125) { | 723 } else if (dataLength > 125) { |
(...skipping 525 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1256 return code != null && | 1249 return code != null && |
1257 (code < WebSocketStatus.NORMAL_CLOSURE || | 1250 (code < WebSocketStatus.NORMAL_CLOSURE || |
1258 code == WebSocketStatus.RESERVED_1004 || | 1251 code == WebSocketStatus.RESERVED_1004 || |
1259 code == WebSocketStatus.NO_STATUS_RECEIVED || | 1252 code == WebSocketStatus.NO_STATUS_RECEIVED || |
1260 code == WebSocketStatus.ABNORMAL_CLOSURE || | 1253 code == WebSocketStatus.ABNORMAL_CLOSURE || |
1261 (code > WebSocketStatus.INTERNAL_SERVER_ERROR && | 1254 (code > WebSocketStatus.INTERNAL_SERVER_ERROR && |
1262 code < WebSocketStatus.RESERVED_1015) || | 1255 code < WebSocketStatus.RESERVED_1015) || |
1263 (code >= WebSocketStatus.RESERVED_1015 && code < 3000)); | 1256 (code >= WebSocketStatus.RESERVED_1015 && code < 3000)); |
1264 } | 1257 } |
1265 } | 1258 } |
OLD | NEW |