Index: pkg/http_parser/lib/src/web_socket.dart |
diff --git a/sdk/lib/io/websocket_impl.dart b/pkg/http_parser/lib/src/web_socket.dart |
similarity index 69% |
copy from sdk/lib/io/websocket_impl.dart |
copy to pkg/http_parser/lib/src/web_socket.dart |
index 3e012108707d1659a5730d71390d0e43c205c5cc..77c9d2b873a41e2c6875f866c95ba748ceae7acf 100644 |
--- a/sdk/lib/io/websocket_impl.dart |
+++ b/pkg/http_parser/lib/src/web_socket.dart |
@@ -1,11 +1,157 @@ |
-// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
+// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
// for details. All rights reserved. Use of this source code is governed by a |
// BSD-style license that can be found in the LICENSE file. |
-part of dart.io; |
+library http_parser.web_socket; |
+ |
+import 'dart:async'; |
+import 'dart:convert'; |
+import 'dart:math'; |
+import 'dart:typed_data'; |
+ |
+import 'package:crypto/crypto.dart'; |
+ |
+import 'bytes_builder.dart'; |
+ |
+/// An implementation of the WebSocket protocol that's not specific to "dart:io" |
+/// or to any particular HTTP API. |
+/// |
+/// Because this is HTTP-API-agnostic, it doesn't handle the initial [WebSocket |
+/// handshake][]. This needs to be handled manually by the user of the code. |
+/// Once that's been done, [new CompatibleWebSocket] can be called with the |
+/// underlying socket and it will handle the remainder of the protocol. |
+/// |
+/// [WebSocket handshake]: https://tools.ietf.org/html/rfc6455#section-4 |
+abstract class CompatibleWebSocket implements Stream, StreamSink { |
+ /// The interval for sending ping signals. |
+ /// |
+ /// If a ping message is not answered by a pong message from the peer, the |
+ /// `WebSocket` is assumed disconnected and the connection is closed with a |
+ /// [WebSocketStatus.GOING_AWAY] close code. When a ping signal is sent, the |
+ /// pong message must be received within [pingInterval]. |
+ /// |
+ /// There are never two outstanding pings at any given time, and the next ping |
+ /// timer starts when the pong is received. |
+ /// |
+ /// By default, the [pingInterval] is `null`, indicating that ping messages |
+ /// are disabled. |
+ Duration pingInterval; |
+ |
+ /// The [close code][] set when the WebSocket connection is closed. |
+ /// |
+ /// [close code]: https://tools.ietf.org/html/rfc6455#section-7.1.5 |
+ /// |
+ /// Before the connection has been closed, this will be `null`. |
+ int get closeCode; |
+ |
+ /// The [close reason][] set when the WebSocket connection is closed. |
+ /// |
+ /// [close reason]: https://tools.ietf.org/html/rfc6455#section-7.1.6 |
+ /// |
+ /// Before the connection has been closed, this will be `null`. |
+ String get closeReason; |
+ |
+ /// Signs a `Sec-WebSocket-Key` header sent by a WebSocket client as part of |
+ /// the [initial handshake]. |
+ /// |
+ /// The return value should be sent back to the client in a |
+ /// `Sec-WebSocket-Accept` header. |
+ /// |
+ /// [initial handshake]: https://tools.ietf.org/html/rfc6455#section-4.2.2 |
+ static String signKey(String key) { |
+ var hash = new SHA1(); |
+ // We use [codeUnits] here rather than UTF-8-decoding the string because |
+ // [key] is expected to be base64 encoded, and so will be pure ASCII. |
+ hash.add((key + _webSocketGUID).codeUnits); |
+ return CryptoUtils.bytesToBase64(hash.close()); |
+ } |
+ |
+ /// Creates a new WebSocket handling messaging across an existing socket. |
+ /// |
+ /// Because this is HTTP-API-agnostic, the initial [WebSocket handshake][] |
+ /// must have already been completed on the socket before this is called. |
+ /// |
+ /// If [stream] is also a [StreamSink] (for example, if it's a "dart:io" |
+ /// `Socket`), it will be used for both sending and receiving data. Otherwise, |
+ /// it will be used for receiving data and [sink] will be used for sending it. |
+ /// |
+ /// If this is a WebSocket server, [serverSide] should be `true` (the |
+ /// default); if it's a client, [serverSide] should be `false`. |
+ /// |
+ /// [WebSocket handshake]: https://tools.ietf.org/html/rfc6455#section-4 |
+ factory CompatibleWebSocket(Stream<List<int>> stream, |
+ {StreamSink<List<int>> sink, bool serverSide: true}) { |
+ if (sink == null) { |
+ if (stream is! StreamSink) { |
+ throw new ArgumentError("If stream isn't also a StreamSink, sink must " |
+ "be passed explicitly."); |
+ } |
+ sink = stream as StreamSink; |
+ } |
+ |
+ return new _WebSocketImpl._fromSocket(stream, sink, serverSide); |
+ } |
+ |
+ /// Closes the web socket connection. |
+ /// |
+ /// [closeCode] and [closeReason] are the [close code][] and [reason][] sent |
+ /// to the remote peer, respectively. If they are omitted, the peer will see |
+ /// a "no status received" code with no reason. |
+ /// |
+ /// [close code]: https://tools.ietf.org/html/rfc6455#section-7.1.5 |
+ /// [reason]: https://tools.ietf.org/html/rfc6455#section-7.1.6 |
+ Future close([int closeCode, String closeReason]); |
+} |
+ |
+/// An exception thrown by [CompatibleWebSocket]. |
+class CompatibleWebSocketException implements Exception { |
+ final String message; |
+ |
+ CompatibleWebSocketException([this.message]); |
+ |
+ String toString() => message == null |
+ ? "CompatibleWebSocketException" : |
+ "CompatibleWebSocketException: $message"; |
+} |
+ |
+// The following code is copied from sdk/lib/io/websocket_impl.dart. The |
+// "dart:io" implementation isn't used directly both to support non-"dart:io" |
+// applications, and because it's incompatible with non-"dart:io" HTTP requests |
+// (issue 18172). |
+// |
+// Because it's copied directly, only modifications necessary to support the |
+// desired public API and to remove "dart:io" dependencies have been made. |
+ |
+/** |
+ * Web socket status codes used when closing a web socket connection. |
+ */ |
+abstract class _WebSocketStatus { |
+ static const int NORMAL_CLOSURE = 1000; |
+ static const int GOING_AWAY = 1001; |
+ static const int PROTOCOL_ERROR = 1002; |
+ static const int UNSUPPORTED_DATA = 1003; |
+ static const int RESERVED_1004 = 1004; |
+ static const int NO_STATUS_RECEIVED = 1005; |
+ static const int ABNORMAL_CLOSURE = 1006; |
+ static const int INVALID_FRAME_PAYLOAD_DATA = 1007; |
+ static const int POLICY_VIOLATION = 1008; |
+ static const int MESSAGE_TOO_BIG = 1009; |
+ static const int MISSING_MANDATORY_EXTENSION = 1010; |
+ static const int INTERNAL_SERVER_ERROR = 1011; |
+ static const int RESERVED_1015 = 1015; |
+} |
+ |
+abstract class _WebSocketState { |
+ static const int CONNECTING = 0; |
+ static const int OPEN = 1; |
+ static const int CLOSING = 2; |
+ static const int CLOSED = 3; |
+} |
const String _webSocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; |
+final _random = new Random(); |
+ |
// Matches _WebSocketOpcode. |
class _WebSocketMessageType { |
static const int NONE = 0; |
@@ -62,7 +208,7 @@ class _WebSocketProtocolTransformer implements StreamTransformer, EventSink { |
int _remainingPayloadBytes = -1; |
int _unmaskingIndex = 0; |
int _currentMessageType = _WebSocketMessageType.NONE; |
- int closeCode = WebSocketStatus.NO_STATUS_RECEIVED; |
+ int closeCode = _WebSocketStatus.NO_STATUS_RECEIVED; |
String closeReason = ""; |
EventSink _eventSink; |
@@ -98,10 +244,10 @@ class _WebSocketProtocolTransformer implements StreamTransformer, EventSink { |
int index = 0; |
int lastIndex = count; |
if (_state == CLOSED) { |
- throw new WebSocketException("Data on closed connection"); |
+ throw new CompatibleWebSocketException("Data on closed connection"); |
} |
if (_state == FAILURE) { |
- throw new WebSocketException("Data on failed connection"); |
+ throw new CompatibleWebSocketException("Data on failed connection"); |
} |
while ((index < lastIndex) && _state != CLOSED && _state != FAILURE) { |
int byte = buffer[index]; |
@@ -110,35 +256,35 @@ class _WebSocketProtocolTransformer implements StreamTransformer, EventSink { |
_fin = (byte & 0x80) != 0; |
if ((byte & 0x70) != 0) { |
// The RSV1, RSV2 bits RSV3 must be all zero. |
- throw new WebSocketException("Protocol error"); |
+ throw new CompatibleWebSocketException("Protocol error"); |
} |
_opcode = (byte & 0xF); |
if (_opcode <= _WebSocketOpcode.BINARY) { |
if (_opcode == _WebSocketOpcode.CONTINUATION) { |
if (_currentMessageType == _WebSocketMessageType.NONE) { |
- throw new WebSocketException("Protocol error"); |
+ throw new CompatibleWebSocketException("Protocol error"); |
} |
} else { |
assert(_opcode == _WebSocketOpcode.TEXT || |
_opcode == _WebSocketOpcode.BINARY); |
if (_currentMessageType != _WebSocketMessageType.NONE) { |
- throw new WebSocketException("Protocol error"); |
+ throw new CompatibleWebSocketException("Protocol error"); |
} |
_currentMessageType = _opcode; |
} |
} else if (_opcode >= _WebSocketOpcode.CLOSE && |
_opcode <= _WebSocketOpcode.PONG) { |
// Control frames cannot be fragmented. |
- if (!_fin) throw new WebSocketException("Protocol error"); |
+ if (!_fin) throw new CompatibleWebSocketException("Protocol error"); |
} else { |
- throw new WebSocketException("Protocol error"); |
+ throw new CompatibleWebSocketException("Protocol error"); |
} |
_state = LEN_FIRST; |
} else if (_state == LEN_FIRST) { |
_masked = (byte & 0x80) != 0; |
_len = byte & 0x7F; |
if (_isControlFrame() && _len > 125) { |
- throw new WebSocketException("Protocol error"); |
+ throw new CompatibleWebSocketException("Protocol error"); |
} |
if (_len == 126) { |
_len = 0; |
@@ -184,7 +330,7 @@ class _WebSocketProtocolTransformer implements StreamTransformer, EventSink { |
} else { |
if (_currentMessageType != _WebSocketMessageType.TEXT && |
_currentMessageType != _WebSocketMessageType.BINARY) { |
- throw new WebSocketException("Protocol error"); |
+ throw new CompatibleWebSocketException("Protocol error"); |
} |
if (_remainingPayloadBytes == 0) _messageFrameEnd(); |
} |
@@ -239,12 +385,14 @@ class _WebSocketProtocolTransformer implements StreamTransformer, EventSink { |
void _lengthDone() { |
if (_masked) { |
if (!_serverSide) { |
- throw new WebSocketException("Received masked frame from server"); |
+ throw new CompatibleWebSocketException( |
+ "Received masked frame from server"); |
} |
_state = MASK; |
} else { |
if (_serverSide) { |
- throw new WebSocketException("Received unmasked frame from client"); |
+ throw new CompatibleWebSocketException( |
+ "Received unmasked frame from client"); |
} |
_remainingPayloadBytes = _len; |
_startPayload(); |
@@ -300,15 +448,15 @@ class _WebSocketProtocolTransformer implements StreamTransformer, EventSink { |
void _controlFrameEnd() { |
switch (_opcode) { |
case _WebSocketOpcode.CLOSE: |
- closeCode = WebSocketStatus.NO_STATUS_RECEIVED; |
+ closeCode = _WebSocketStatus.NO_STATUS_RECEIVED; |
var payload = _payload.takeBytes(); |
if (payload.length > 0) { |
if (payload.length == 1) { |
- throw new WebSocketException("Protocol error"); |
+ throw new CompatibleWebSocketException("Protocol error"); |
} |
closeCode = payload[0] << 8 | payload[1]; |
- if (closeCode == WebSocketStatus.NO_STATUS_RECEIVED) { |
- throw new WebSocketException("Protocol error"); |
+ if (closeCode == _WebSocketStatus.NO_STATUS_RECEIVED) { |
+ throw new CompatibleWebSocketException("Protocol error"); |
} |
if (payload.length > 2) { |
closeReason = UTF8.decode(payload.sublist(2)); |
@@ -359,110 +507,6 @@ class _WebSocketPong { |
_WebSocketPong([this.payload = null]); |
} |
- |
-class _WebSocketTransformerImpl implements WebSocketTransformer { |
- final StreamController<WebSocket> _controller = |
- new StreamController<WebSocket>(sync: true); |
- final Function _protocolSelector; |
- |
- _WebSocketTransformerImpl(this._protocolSelector); |
- |
- Stream<WebSocket> bind(Stream<HttpRequest> stream) { |
- stream.listen((request) { |
- _upgrade(request, _protocolSelector) |
- .then((WebSocket webSocket) => _controller.add(webSocket)) |
- .catchError(_controller.addError); |
- }); |
- |
- return _controller.stream; |
- } |
- |
- static Future<WebSocket> _upgrade(HttpRequest request, _protocolSelector) { |
- var response = request.response; |
- if (!_isUpgradeRequest(request)) { |
- // Send error response. |
- response |
- ..statusCode = HttpStatus.BAD_REQUEST |
- ..close(); |
- return new Future.error( |
- new WebSocketException("Invalid WebSocket upgrade request")); |
- } |
- |
- Future upgrade(String protocol) { |
- // Send the upgrade response. |
- response |
- ..statusCode = HttpStatus.SWITCHING_PROTOCOLS |
- ..headers.add(HttpHeaders.CONNECTION, "Upgrade") |
- ..headers.add(HttpHeaders.UPGRADE, "websocket"); |
- String key = request.headers.value("Sec-WebSocket-Key"); |
- _SHA1 sha1 = new _SHA1(); |
- sha1.add("$key$_webSocketGUID".codeUnits); |
- String accept = _CryptoUtils.bytesToBase64(sha1.close()); |
- response.headers.add("Sec-WebSocket-Accept", accept); |
- if (protocol != null && protocol.isNotEmpty) { |
- response.headers.add("Sec-WebSocket-Protocol", protocol); |
- } |
- response.headers.contentLength = 0; |
- return response.detachSocket() |
- .then((socket) => new _WebSocketImpl._fromSocket( |
- socket, protocol, true)); |
- } |
- |
- var protocols = request.headers['Sec-WebSocket-Protocol']; |
- if (protocols != null && _protocolSelector != null) { |
- // The suggested protocols can be spread over multiple lines, each |
- // consisting of multiple protocols. To unify all of them, first join |
- // the lists with ', ' and then tokenize. |
- protocols = _HttpParser._tokenizeFieldValue(protocols.join(', ')); |
- return new Future(() => _protocolSelector(protocols)) |
- .then((protocol) { |
- if (protocols.indexOf(protocol) < 0) { |
- throw new WebSocketException( |
- "Selected protocol is not in the list of available protocols"); |
- } |
- return protocol; |
- }) |
- .catchError((error) { |
- response |
- ..statusCode = HttpStatus.INTERNAL_SERVER_ERROR |
- ..close(); |
- throw error; |
- }) |
- .then(upgrade); |
- } else { |
- return upgrade(null); |
- } |
- } |
- |
- static bool _isUpgradeRequest(HttpRequest request) { |
- if (request.method != "GET") { |
- return false; |
- } |
- if (request.headers[HttpHeaders.CONNECTION] == null) { |
- return false; |
- } |
- bool isUpgrade = false; |
- request.headers[HttpHeaders.CONNECTION].forEach((String value) { |
- if (value.toLowerCase() == "upgrade") isUpgrade = true; |
- }); |
- if (!isUpgrade) return false; |
- String upgrade = request.headers.value(HttpHeaders.UPGRADE); |
- if (upgrade == null || upgrade.toLowerCase() != "websocket") { |
- return false; |
- } |
- String version = request.headers.value("Sec-WebSocket-Version"); |
- if (version == null || version != "13") { |
- return false; |
- } |
- String key = request.headers.value("Sec-WebSocket-Key"); |
- if (key == null) { |
- return false; |
- } |
- return true; |
- } |
-} |
- |
- |
// TODO(ajohnsen): Make this transformer reusable. |
class _WebSocketOutgoingTransformer implements StreamTransformer, EventSink { |
final _WebSocketImpl webSocket; |
@@ -562,7 +606,8 @@ class _WebSocketOutgoingTransformer implements StreamTransformer, EventSink { |
} |
if (mask) { |
header[1] |= 1 << 7; |
- var maskBytes = _IOCrypto.getRandomBytes(4); |
+ var maskBytes = [_random.nextInt(256), _random.nextInt(256), |
+ _random.nextInt(256), _random.nextInt(256)]; |
header.setRange(index, index + 4, maskBytes); |
index += 4; |
if (data != null) { |
@@ -620,7 +665,7 @@ class _WebSocketOutgoingTransformer implements StreamTransformer, EventSink { |
class _WebSocketConsumer implements StreamConsumer { |
final _WebSocketImpl webSocket; |
- final Socket socket; |
+ final StreamSink<List<int>> sink; |
StreamController _controller; |
StreamSubscription _subscription; |
bool _issuedPause = false; |
@@ -628,7 +673,7 @@ class _WebSocketConsumer implements StreamConsumer { |
Completer _closeCompleter = new Completer(); |
Completer _completer; |
- _WebSocketConsumer(this.webSocket, this.socket); |
+ _WebSocketConsumer(this.webSocket, this.sink); |
void _onListen() { |
if (_subscription != null) { |
@@ -668,7 +713,7 @@ class _WebSocketConsumer implements StreamConsumer { |
onCancel: _onListen); |
var stream = _controller.stream.transform( |
new _WebSocketOutgoingTransformer(webSocket)); |
- socket.addStream(stream) |
+ sink.addStream(stream) |
.then((_) { |
_done(); |
_closeCompleter.complete(webSocket); |
@@ -721,7 +766,7 @@ class _WebSocketConsumer implements StreamConsumer { |
Future close() { |
_ensureController(); |
Future closeSocket() { |
- return socket.close().catchError((_) {}).then((_) => webSocket); |
+ return sink.close().catchError((_) {}).then((_) => webSocket); |
} |
_controller.close(); |
return _closeCompleter.future.then((_) => closeSocket()); |
@@ -741,16 +786,13 @@ class _WebSocketConsumer implements StreamConsumer { |
} |
-class _WebSocketImpl extends Stream implements WebSocket { |
- final String protocol; |
- |
+class _WebSocketImpl extends Stream implements CompatibleWebSocket { |
StreamController _controller; |
StreamSubscription _subscription; |
- StreamSink _sink; |
+ StreamController _sink; |
- final Socket _socket; |
final bool _serverSide; |
- int _readyState = WebSocket.CONNECTING; |
+ int _readyState = _WebSocketState.CONNECTING; |
bool _writeClosed = false; |
int _closeCode; |
String _closeReason; |
@@ -762,92 +804,15 @@ class _WebSocketImpl extends Stream implements WebSocket { |
String _outCloseReason; |
Timer _closeTimer; |
- static final HttpClient _httpClient = new HttpClient(); |
- |
- static Future<WebSocket> connect(String url, List<String> protocols) { |
- Uri uri = Uri.parse(url); |
- if (uri.scheme != "ws" && uri.scheme != "wss") { |
- throw new WebSocketException("Unsupported URL scheme '${uri.scheme}'"); |
- } |
- if (uri.userInfo != "") { |
- throw new WebSocketException("Unsupported user info '${uri.userInfo}'"); |
- } |
- |
- Random random = new Random(); |
- // Generate 16 random bytes. |
- Uint8List nonceData = new Uint8List(16); |
- for (int i = 0; i < 16; i++) { |
- nonceData[i] = random.nextInt(256); |
- } |
- String nonce = _CryptoUtils.bytesToBase64(nonceData); |
- |
- uri = new Uri(scheme: uri.scheme == "wss" ? "https" : "http", |
- userInfo: uri.userInfo, |
- host: uri.host, |
- port: uri.port, |
- path: uri.path, |
- query: uri.query, |
- fragment: uri.fragment); |
- return _httpClient.openUrl("GET", uri) |
- .then((request) { |
- // Setup the initial handshake. |
- request.headers |
- ..add(HttpHeaders.CONNECTION, "Upgrade") |
- ..set(HttpHeaders.UPGRADE, "websocket") |
- ..set("Sec-WebSocket-Key", nonce) |
- ..set("Cache-Control", "no-cache") |
- ..set("Sec-WebSocket-Version", "13"); |
- if (protocols.isNotEmpty) { |
- request.headers.add("Sec-WebSocket-Protocol", protocols); |
- } |
- return request.close(); |
- }) |
- .then((response) { |
- void error(String message) { |
- // Flush data. |
- response.detachSocket().then((socket) { |
- socket.destroy(); |
- }); |
- throw new WebSocketException(message); |
- } |
- if (response.statusCode != HttpStatus.SWITCHING_PROTOCOLS || |
- response.headers[HttpHeaders.CONNECTION] == null || |
- !response.headers[HttpHeaders.CONNECTION].any( |
- (value) => value.toLowerCase() == "upgrade") || |
- response.headers.value(HttpHeaders.UPGRADE).toLowerCase() != |
- "websocket") { |
- error("Connection to '$uri' was not upgraded to websocket"); |
- } |
- String accept = response.headers.value("Sec-WebSocket-Accept"); |
- if (accept == null) { |
- error("Response did not contain a 'Sec-WebSocket-Accept' header"); |
- } |
- _SHA1 sha1 = new _SHA1(); |
- sha1.add("$nonce$_webSocketGUID".codeUnits); |
- List<int> expectedAccept = sha1.close(); |
- List<int> receivedAccept = _CryptoUtils.base64StringToBytes(accept); |
- if (expectedAccept.length != receivedAccept.length) { |
- error("Reasponse header 'Sec-WebSocket-Accept' is the wrong length"); |
- } |
- for (int i = 0; i < expectedAccept.length; i++) { |
- if (expectedAccept[i] != receivedAccept[i]) { |
- error("Bad response 'Sec-WebSocket-Accept' header"); |
- } |
- } |
- var protocol = response.headers.value('Sec-WebSocket-Protocol'); |
- return response.detachSocket() |
- .then((socket) => new _WebSocketImpl._fromSocket(socket, protocol)); |
- }); |
- } |
- |
- _WebSocketImpl._fromSocket(this._socket, this.protocol, |
- [this._serverSide = false]) { |
- _consumer = new _WebSocketConsumer(this, _socket); |
- _sink = new _StreamSinkImpl(_consumer); |
- _readyState = WebSocket.OPEN; |
+ _WebSocketImpl._fromSocket(Stream<List<int>> stream, |
+ StreamSink<List<int>> sink, [this._serverSide = false]) { |
+ _consumer = new _WebSocketConsumer(this, sink); |
+ _sink = new StreamController(); |
+ _sink.stream.pipe(_consumer); |
+ _readyState = _WebSocketState.OPEN; |
var transformer = new _WebSocketProtocolTransformer(_serverSide); |
- _subscription = _socket.transform(transformer).listen( |
+ _subscription = stream.transform(transformer).listen( |
(data) { |
if (data is _WebSocketPing) { |
if (!_writeClosed) _consumer.add(new _WebSocketPong(data.payload)); |
@@ -861,22 +826,22 @@ class _WebSocketImpl extends Stream implements WebSocket { |
onError: (error) { |
if (_closeTimer != null) _closeTimer.cancel(); |
if (error is FormatException) { |
- _close(WebSocketStatus.INVALID_FRAME_PAYLOAD_DATA); |
+ _close(_WebSocketStatus.INVALID_FRAME_PAYLOAD_DATA); |
} else { |
- _close(WebSocketStatus.PROTOCOL_ERROR); |
+ _close(_WebSocketStatus.PROTOCOL_ERROR); |
} |
_controller.close(); |
}, |
onDone: () { |
if (_closeTimer != null) _closeTimer.cancel(); |
- if (_readyState == WebSocket.OPEN) { |
- _readyState = WebSocket.CLOSING; |
+ if (_readyState == _WebSocketState.OPEN) { |
+ _readyState = _WebSocketState.CLOSING; |
if (!_isReservedStatusCode(transformer.closeCode)) { |
_close(transformer.closeCode); |
} else { |
_close(); |
} |
- _readyState = WebSocket.CLOSED; |
+ _readyState = _WebSocketState.CLOSED; |
} |
_closeCode = transformer.closeCode; |
_closeReason = transformer.closeReason; |
@@ -914,14 +879,11 @@ class _WebSocketImpl extends Stream implements WebSocket { |
_consumer.add(new _WebSocketPing()); |
_pingTimer = new Timer(_pingInterval, () { |
// No pong received. |
- _close(WebSocketStatus.GOING_AWAY); |
+ _close(_WebSocketStatus.GOING_AWAY); |
}); |
}); |
} |
- int get readyState => _readyState; |
- |
- String get extensions => null; |
int get closeCode => _closeCode; |
String get closeReason => _closeReason; |
@@ -933,7 +895,7 @@ class _WebSocketImpl extends Stream implements WebSocket { |
Future close([int code, String reason]) { |
if (_isReservedStatusCode(code)) { |
- throw new WebSocketException("Reserved status code $code"); |
+ throw new CompatibleWebSocketException("Reserved status code $code"); |
} |
if (_outCloseCode == null) { |
_outCloseCode = code; |
@@ -961,13 +923,14 @@ class _WebSocketImpl extends Stream implements WebSocket { |
static bool _isReservedStatusCode(int code) { |
return code != null && |
- (code < WebSocketStatus.NORMAL_CLOSURE || |
- code == WebSocketStatus.RESERVED_1004 || |
- code == WebSocketStatus.NO_STATUS_RECEIVED || |
- code == WebSocketStatus.ABNORMAL_CLOSURE || |
- (code > WebSocketStatus.INTERNAL_SERVER_ERROR && |
- code < WebSocketStatus.RESERVED_1015) || |
- (code >= WebSocketStatus.RESERVED_1015 && |
+ (code < _WebSocketStatus.NORMAL_CLOSURE || |
+ code == _WebSocketStatus.RESERVED_1004 || |
+ code == _WebSocketStatus.NO_STATUS_RECEIVED || |
+ code == _WebSocketStatus.ABNORMAL_CLOSURE || |
+ (code > _WebSocketStatus.INTERNAL_SERVER_ERROR && |
+ code < _WebSocketStatus.RESERVED_1015) || |
+ (code >= _WebSocketStatus.RESERVED_1015 && |
code < 3000)); |
} |
} |
+ |