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

Unified Diff: runtime/bin/websocket_impl.dart

Issue 10262031: Add a web socket client (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Addressed review comments Created 8 years, 8 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « runtime/bin/websocket.dart ('k') | tests/standalone/io/web_socket_test.dart » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: runtime/bin/websocket_impl.dart
diff --git a/runtime/bin/websocket_impl.dart b/runtime/bin/websocket_impl.dart
index 29aba714cb049739ac1110590d5574d2bd9b0478..f85c2564c62119c524f73817c94a185867b7c437 100644
--- a/runtime/bin/websocket_impl.dart
+++ b/runtime/bin/websocket_impl.dart
@@ -2,6 +2,8 @@
// 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.
+final String _webSocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+
class _WebSocketMessageType {
static final int NONE = 0;
static final int BINARY = 1;
@@ -252,7 +254,7 @@ class _WebSocketProtocolProcessor {
*/
void closed() {
if (_state == START || _state == CLOSED || _state == FAILURE) return;
- _reportError(new WebSocketException("Protocol error"));
+ _reportError(new WebSocketException("Protocol error $_state"));
_state = CLOSED;
}
@@ -277,6 +279,7 @@ class _WebSocketProtocolProcessor {
if (_remainingPayloadBytes == 0) {
if (_currentMessageType ==_WebSocketMessageType.CLOSE) {
if (onClosed != null) onClosed(null, null);
+ _state = CLOSED;
} else {
_frameEnd();
}
@@ -342,15 +345,19 @@ class _WebSocketProtocolProcessor {
}
-class _WebSocketConnection implements WebSocketConnection {
- _WebSocketConnection(Socket this._socket) {
+class _WebSocketConnectionBase {
+ void _socketReady(DetachedSocket detached) {
+ assert(detached.socket != null);
+ _socket = detached.socket;
_WebSocketProtocolProcessor processor = new _WebSocketProtocolProcessor();
processor.onMessageStart = _onWebSocketMessageStart;
processor.onMessageData = _onWebSocketMessageData;
processor.onMessageEnd = _onWebSocketMessageEnd;
processor.onClosed = _onWebSocketClosed;
processor.onError = _onWebSocketError;
-
+ if (detached.unparsedData != null) {
+ processor.update(detached.unparsedData, 0, detached.unparsedData.length);
+ }
_socket.onData = () {
int available = _socket.available();
List<int> data = new List<int>(available);
@@ -364,14 +371,12 @@ class _WebSocketConnection implements WebSocketConnection {
// that as an error.
if (_closeTimer != null) _closeTimer.cancel();
} else {
- if (_onError != null) {
- _onError(new WebSocketException("Unexpected close"));
- }
+ _reportError(new WebSocketException("Unexpected close"));
}
_socket.close();
};
_socket.onError = (e) {
- if (_onError != null) _onError(e);
+ _reportError(e);
_socket.close();
};
}
@@ -388,7 +393,7 @@ class _WebSocketConnection implements WebSocketConnection {
_onError = callback;
}
- send(Object message) {
+ send(message) {
if (_closeSent) {
throw new WebSocketException("Connection closed");
}
@@ -428,6 +433,7 @@ class _WebSocketConnection implements WebSocketConnection {
if (_closeReceived) {
// Close the socket when the close frame has been sent - if it
// does not take too long.
+ _socket.close(true);
_socket.outputStream.onNoPendingWrites = () {
if (_closeTimer != null) _closeTimer.cancel();
_socket.close();
@@ -489,7 +495,7 @@ class _WebSocketConnection implements WebSocketConnection {
}
_onWebSocketError(e) {
- if (_onError != null) _onError(e);
+ _reportError(e);
_socket.close();
}
@@ -528,6 +534,14 @@ class _WebSocketConnection implements WebSocketConnection {
}
}
+ void _reportError(e) {
+ if (_onError != null) {
+ _onError(e);
+ } else {
+ throw e;
+ }
+ }
+
Socket _socket;
Timer _closeTimer;
@@ -543,11 +557,20 @@ class _WebSocketConnection implements WebSocketConnection {
}
+class _WebSocketConnection
+ extends _WebSocketConnectionBase implements WebSocketConnection {
+ _WebSocketConnection(DetachedSocket detached) {
+ _socketReady(detached);
+ }
+}
+
+
class _WebSocketHandler implements WebSocketHandler {
void onRequest(HttpRequest request, HttpResponse response) {
// Check that this is a web socket upgrade.
if (!_isWebSocketUpgrade(request)) {
response.statusCode = HttpStatus.BAD_REQUEST;
+ response.outputStream.close();
return;
}
@@ -555,15 +578,15 @@ class _WebSocketHandler implements WebSocketHandler {
response.statusCode = HttpStatus.SWITCHING_PROTOCOLS;
response.headers.add(HttpHeaders.CONNECTION, "Upgrade");
response.headers.add(HttpHeaders.UPGRADE, "websocket");
- String x = request.headers.value("Sec-WebSocket-Key");
- String y = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
- String z = _Base64._encode(_Sha1._hash("$x$y".charCodes()));
- response.headers.add("Sec-WebSocket-Accept", z);
+ String key = request.headers.value("Sec-WebSocket-Key");
+ String accept =
+ _Base64._encode(_Sha1._hash("$key$_webSocketGUID".charCodes()));
+ response.headers.add("Sec-WebSocket-Accept", accept);
response.contentLength = 0;
// Upgrade the connection and get the underlying socket.
- Socket socket = response.detachSocket();
- WebSocketConnection conn = new _WebSocketConnection(socket);
+ WebSocketConnection conn =
+ new _WebSocketConnection(response.detachSocket());
if (_onOpen != null) _onOpen(conn);
}
@@ -572,6 +595,9 @@ class _WebSocketHandler implements WebSocketHandler {
}
bool _isWebSocketUpgrade(HttpRequest request) {
+ if (request.method != "GET") {
+ return false;
+ }
if (request.headers[HttpHeaders.CONNECTION] == null) {
return false;
}
@@ -597,3 +623,113 @@ class _WebSocketHandler implements WebSocketHandler {
Function _onOpen;
}
+
+
+class _WebSocketClientConnection
+ extends _WebSocketConnectionBase implements WebSocketClientConnection {
+ _WebSocketClientConnection(HttpClientConnection this._conn,
+ [List<String> protocols]) {
+ _conn.onRequest = _onHttpClientRequest;
+ _conn.onResponse = _onHttpClientResponse;
+ _conn.onError = (e) => _reportError(e);
+ }
+
+ void set onRequest(void callback(HttpClientRequest request)) {
+ _onRequest = callback;
+ }
+
+ void set onOpen(void callback()) {
+ _onOpen = callback;
+ }
+
+ void set onNoUpgrade(void callback(HttpClientResponse request)) {
+ _onNoUpgrade = callback;
+ }
+
+ void _onHttpClientRequest(HttpClientRequest request) {
+ if (_onRequest != null) {
+ _onRequest(request);
+ }
+ // Setup the initial handshake.
+ _generateNonce();
+ request.headers.add(HttpHeaders.CONNECTION, "upgrade");
+ request.headers.set(HttpHeaders.UPGRADE, "websocket");
+ request.headers.set("Sec-WebSocket-Key", _nonce);
+ request.headers.set("Sec-WebSocket-Version", "13");
+ request.contentLength = 0;
+ request.outputStream.close();
+ }
+
+ void _onHttpClientResponse(HttpClientResponse response) {
+ if (response.statusCode != HttpStatus.SWITCHING_PROTOCOLS) {
+ if (_onNoUpgrade != null) {
+ _onNoUpgrade(response);
+ } else {
+ _conn.detachSocket().socket.close();
+ throw new WebSocketException("Protocol upgrade refused");
+ }
+ return;
+ }
+
+ if (!_isWebSocketUpgrade(response)) {
+ _conn.detachSocket().socket.close();
+ throw new WebSocketException("Protocol upgrade failed");
+ return;
+ }
+
+ // Connection upgrade successful.
+ _socketReady(_conn.detachSocket());
+ if (_onOpen != null) _onOpen();
+ }
+
+ void _generateNonce() {
+ assert(_nonce == null);
+ void intToBigEndianBytes(int value, List<int> bytes, int offset) {
+ bytes[offset] = (value >> 24) & 0xFF;
+ bytes[offset + 1] = (value >> 16) & 0xFF;
+ bytes[offset + 2] = (value >> 8) & 0xFF;
+ bytes[offset + 3] = value & 0xFF;
+ }
+
+ // Generate 16 random bytes.
+ List<int> nonce = new List<int>(16);
+ for (int i = 0; i < 4; i++) {
+ int r = (Math.random() * 0x100000000).toInt();
+ intToBigEndianBytes(r, nonce, i * 4);
+ }
+ _nonce = _Base64._encode(nonce);
+ }
+
+ bool _isWebSocketUpgrade(HttpClientResponse response) {
+ if (response.headers[HttpHeaders.CONNECTION] == null) {
+ return false;
+ }
+ bool isUpgrade = false;
+ response.headers[HttpHeaders.CONNECTION].forEach((String value) {
+ if (value.toLowerCase() == "upgrade") isUpgrade = true;
+ });
+ if (!isUpgrade) return false;
+ String upgrade = response.headers.value(HttpHeaders.UPGRADE);
+ if (upgrade == null || upgrade.toLowerCase() != "websocket") {
+ return false;
+ }
+ String accept = response.headers.value("Sec-WebSocket-Accept");
+ if (accept == null) {
+ return false;
+ }
+ List<int> expectedAccept =
+ _Sha1._hash("$_nonce$_webSocketGUID".charCodes());
+ List<int> receivedAccept = _Base64._decode(accept);
+ if (expectedAccept.length != receivedAccept.length) return false;
+ for (int i = 0; i < expectedAccept.length; i++) {
+ if (expectedAccept[i] != receivedAccept[i]) return false;
+ }
+ return true;
+ }
+
+ Function _onRequest;
+ Function _onOpen;
+ Function _onNoUpgrade;
+ HttpClientConnection _conn;
+ String _nonce;
+}
« no previous file with comments | « runtime/bin/websocket.dart ('k') | tests/standalone/io/web_socket_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698