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) { |