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

Unified Diff: sdk/lib/io/websocket_impl.dart

Issue 1158593002: Initial Implementation of WebSocket Compression (Closed) Base URL: https://github.com/dart-lang/sdk.git@master
Patch Set: Fix Type Annotations Created 5 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sdk/lib/io/websocket_impl.dart
diff --git a/sdk/lib/io/websocket_impl.dart b/sdk/lib/io/websocket_impl.dart
index 265760690923f502512435c8e6d8df3d1fada22a..5b4168bdc86e6beaffe3f639bb6ff37ebf2cae64 100644
--- a/sdk/lib/io/websocket_impl.dart
+++ b/sdk/lib/io/websocket_impl.dart
@@ -38,8 +38,8 @@ class _WebSocketOpcode {
* which is supplied through the [:handleData:]. As the protocol is processed,
* it'll output frame data as either a List<int> or String.
*
- * Important infomation about usage: Be sure you use cancelOnError, so the
- * socket will be closed when the processer encounter an error. Not using it
+ * Important information about usage: Be sure you use cancelOnError, so the
+ * socket will be closed when the processor encounter an error. Not using it
* will lead to undefined behaviour.
*/
// TODO(ajohnsen): make this transformer reusable?
@@ -71,6 +71,8 @@ class _WebSocketProtocolTransformer implements StreamTransformer, EventSink {
final List _maskingBytes = new List(4);
final BytesBuilder _payload = new BytesBuilder(copy: false);
+ _WebSocketPerMessageDeflateHelper _deflateHelper;
+
_WebSocketProtocolTransformer([this._serverSide = false]);
Stream bind(Stream stream) {
@@ -286,10 +288,18 @@ class _WebSocketProtocolTransformer implements StreamTransformer, EventSink {
if (_fin) {
Søren Gjesse 2015/05/26 09:11:33 Move getting bytes and optionally decompression up
switch (_currentMessageType) {
case _WebSocketMessageType.TEXT:
- _eventSink.add(UTF8.decode(_payload.takeBytes()));
+ var bytes = _payload.takeBytes();
+ if (_deflateHelper != null) {
+ bytes = _deflateHelper.processIncomingMessage(bytes);
+ }
+ _eventSink.add(UTF8.decode(bytes));
break;
case _WebSocketMessageType.BINARY:
- _eventSink.add(_payload.takeBytes());
+ var bytes = _payload.takeBytes();
+ if (_deflateHelper != null) {
+ bytes = _deflateHelper.processIncomingMessage(bytes);
+ }
+ _eventSink.add(bytes);
break;
}
_currentMessageType = _WebSocketMessageType.NONE;
@@ -404,10 +414,26 @@ class _WebSocketTransformerImpl implements WebSocketTransformer {
if (protocol != null) {
response.headers.add("Sec-WebSocket-Protocol", protocol);
}
+
+ var extensionHeader = request.headers.value("Sec-WebSocket-Extensions");
+
+ if (extensionHeader == null) {
+ extensionHeader = "";
+ }
+
+ Iterable<List<String>> extensions = extensionHeader.split(",").map((it) => it.split("; "));
Søren Gjesse 2015/05/26 09:11:33 I think it will be helpful to create a class to re
Søren Gjesse 2015/05/26 09:11:33 Long line - more below.
+
+ _WebSocketPerMessageDeflateHelper deflateHelper;
+
+ if (extensions.any((x) => x[0] == "permessage-deflate")) {
+ response.headers.add("Sec-WebSocket-Extensions", "permessage-deflate");
+ deflateHelper = new _WebSocketPerMessageDeflateHelper();
+ }
+
response.headers.contentLength = 0;
return response.detachSocket()
.then((socket) => new _WebSocketImpl._fromSocket(
- socket, protocol, true));
+ socket, protocol, true).._deflateHelper = deflateHelper);
Søren Gjesse 2015/05/26 09:11:33 Please pass the arguments used for handling compre
}
var protocols = request.headers['Sec-WebSocket-Protocol'];
@@ -464,12 +490,38 @@ class _WebSocketTransformerImpl implements WebSocketTransformer {
}
}
+class _WebSocketPerMessageDeflateHelper {
Søren Gjesse 2015/05/26 09:11:33 I would just call this _WebSocketPerMessageDeflate
+ int windowBits;
+
+ ZLibDecoder decoder;
+ ZLibEncoder encoder;
+
+ _WebSocketPerMessageDeflateHelper({this.windowBits}) {
+ decoder = windowBits != null ? new ZLibDecoder(windowBits: windowBits) : new ZLibDecoder();
+ encoder = windowBits != null ? new ZLibEncoder(windowBits: windowBits) : new ZLibEncoder();
+ }
+
+ List<int> processIncomingMessage(List<int> msg) {
+ var builder = new BytesBuilder();
+ builder.add(msg);
+ builder.add(const [0x00, 0x00, 0xff, 0xff]);
+ return decoder.convert(builder.takeBytes());
+ }
+
+ List<int> processOutgoingMessage(List<int> msg) {
+ var c = encoder.convert(msg);
+ c = c.sublist(0, c.length - 4);
+ return c;
+ }
+}
// TODO(ajohnsen): Make this transformer reusable.
class _WebSocketOutgoingTransformer implements StreamTransformer, EventSink {
final _WebSocketImpl webSocket;
EventSink _eventSink;
+ _WebSocketPerMessageDeflateHelper _deflateHelper;
+
_WebSocketOutgoingTransformer(this.webSocket);
Stream bind(Stream stream) {
@@ -499,12 +551,20 @@ class _WebSocketOutgoingTransformer implements StreamTransformer, EventSink {
if (message is String) {
opcode = _WebSocketOpcode.TEXT;
data = UTF8.encode(message);
+
+ if (_deflateHelper != null) {
+ data = _deflateHelper.processOutgoingMessage(data);
+ }
} else {
if (message is !List<int>) {
throw new ArgumentError(message);
}
opcode = _WebSocketOpcode.BINARY;
data = message;
+
+ if (_deflateHelper != null) {
+ data = _deflateHelper.processOutgoingMessage(data);
+ }
}
} else {
opcode = _WebSocketOpcode.TEXT;
@@ -766,6 +826,7 @@ class _WebSocketImpl extends Stream with _ServiceObject implements WebSocket {
int _outCloseCode;
String _outCloseReason;
Timer _closeTimer;
+ _WebSocketPerMessageDeflateHelper _deflateHelper;
static final HttpClient _httpClient = new HttpClient();
@@ -809,7 +870,8 @@ class _WebSocketImpl extends Stream with _ServiceObject implements WebSocket {
..set(HttpHeaders.UPGRADE, "websocket")
..set("Sec-WebSocket-Key", nonce)
..set("Cache-Control", "no-cache")
- ..set("Sec-WebSocket-Version", "13");
+ ..set("Sec-WebSocket-Version", "13")
+ ..set("Sec-WebSocket-Extensions", "permessage-deflate");
Søren Gjesse 2015/05/26 09:11:33 There should be some way of controlling aspects of
if (protocols != null) {
request.headers.add("Sec-WebSocket-Protocol", protocols.toList());
}
@@ -848,8 +910,25 @@ class _WebSocketImpl extends Stream with _ServiceObject implements WebSocket {
}
}
var protocol = response.headers.value('Sec-WebSocket-Protocol');
+
+ String extensionHeader = response.headers.value('Sec-WebSocket-Extensions');
+
+ if (extensionHeader == null) {
+ extensionHeader = "";
+ }
+
+ Iterable<List<String>> extensions = extensionHeader.split(", ").map((it) => it.split("; "));
+
+ _WebSocketPerMessageDeflateHelper deflateHelper;
+
+ if (extensions.any((x) => x[0] == "permessage-deflate")) {
+ response.headers.add("Sec-WebSocket-Extensions", "permessage-deflate");
Søren Gjesse 2015/05/26 09:11:33 Even for the simplest implementation you need to s
+ deflateHelper = new _WebSocketPerMessageDeflateHelper();
+ }
+
return response.detachSocket()
- .then((socket) => new _WebSocketImpl._fromSocket(socket, protocol));
+ .then((socket) => new _WebSocketImpl._fromSocket(socket, protocol)
+ .._deflateHelper = deflateHelper);
});
}
@@ -860,6 +939,7 @@ class _WebSocketImpl extends Stream with _ServiceObject implements WebSocket {
_readyState = WebSocket.OPEN;
var transformer = new _WebSocketProtocolTransformer(_serverSide);
+ transformer._deflateHelper = _deflateHelper;
_subscription = _socket.transform(transformer).listen(
(data) {
if (data is _WebSocketPing) {
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698