Chromium Code Reviews| Index: sdk/lib/io/websocket.dart |
| diff --git a/sdk/lib/io/websocket.dart b/sdk/lib/io/websocket.dart |
| index 599e295c60ac92923dc620303830b986b8522f96..a711786f14f1915ad80b6477d1e30d3081bc0074 100644 |
| --- a/sdk/lib/io/websocket.dart |
| +++ b/sdk/lib/io/websocket.dart |
| @@ -12,7 +12,7 @@ abstract class WebSocketStatus { |
| 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 RESERVED_1004 = 1004; |
| static const int NO_STATUS_RECEIVED = 1005; |
| static const int ABNORMAL_CLOSURE = 1006; |
| static const int INVALID_FRAME_PAYLOAD_DATA = 1007; |
| @@ -24,6 +24,141 @@ abstract class WebSocketStatus { |
| } |
| /** |
| + * The [CompressionOptions] class allows you to control |
| + * the options of WebSocket compression. |
| + */ |
| +class CompressionOptions { |
| + /** |
| + * Default WebSocket Compression options. |
| + * Compression will be enabled with the following options: |
| + * clientNoContextTakeover: false |
| + * serverNoContextTakeover: false |
| + * clientMaxWindowBits: 15 |
| + * serverMaxWindowBits: 15 |
| + */ |
| + static const CompressionOptions DEFAULT = const CompressionOptions(); |
| + |
| + /** |
| + * Disables WebSocket Compression. |
| + */ |
| + static const CompressionOptions OFF = |
| + const CompressionOptions(enabled: false); |
| + |
| + /** |
| + * Control whether the client will reuse it's compression instances. |
| + */ |
| + final bool clientNoContextTakeover; |
| + |
| + /** |
| + * Control whether the server will reuse it's compression instances. |
| + */ |
| + final bool serverNoContextTakeover; |
| + |
| + /** |
| + * Sets the Max Window Bits for the Client. |
| + */ |
| + final int clientMaxWindowBits; |
| + |
| + /** |
| + * Sets the Max Window Bits for the Server. |
| + */ |
| + final int serverMaxWindowBits; |
| + |
| + /** |
| + * Enables or disables WebSocket compression. |
| + */ |
| + final bool enabled; |
| + |
| + const CompressionOptions( |
| + {this.clientNoContextTakeover: false, |
| + this.serverNoContextTakeover: false, |
| + this.clientMaxWindowBits: _WebSocketImpl.DEFAULT_WINDOW_BITS, |
| + this.serverMaxWindowBits: _WebSocketImpl.DEFAULT_WINDOW_BITS, |
| + this.enabled: true}); |
| + |
| + /// Parses list of requested server headers to return server compression |
| + /// response headers. Uses [serverMaxWindowBits] value if set, otherwise will |
| + /// attempt to use value from headers. Defaults to |
| + /// [WebSocket.DEFAULT_WINDOW_BITS] |
| + List _createServerResponseHeader(HeaderValue requested) { |
| + var info = new List(2); |
| + |
| + int mwb; |
| + var part = requested.parameters[_serverMaxWindowBits]; |
| + if (part != null) { |
| + if(part.length >= 2 && part.startsWith('0')) { |
|
Søren Gjesse
2015/10/23 15:09:59
nit: Space after if.
Søren Gjesse
2015/10/23 15:09:59
I am not sure what is the right thing here. Accord
butlermatt
2015/10/23 15:44:12
Done.
butlermatt
2015/10/23 15:44:12
Done.
|
| + mwb = _WebSocketImpl.DEFAULT_WINDOW_BITS; |
| + } else { |
| + mwb = serverMaxWindowBits == null |
| + ? int.parse(part, |
| + onError: (source) => _WebSocketImpl.DEFAULT_WINDOW_BITS) |
| + : serverMaxWindowBits; |
| + info[0] = "; server_max_window_bits=${mwb}"; |
| + info[1] = mwb; |
| + } |
| + } else { |
| + info[1] = _WebSocketImpl.DEFAULT_WINDOW_BITS; |
| + } |
| + return info; |
| + } |
| + |
| + /// Returns default values for client compression request headers. |
| + List _createClientRequestHeader(HeaderValue requested) { |
| + var info = new List(2); |
| + |
| + info[1] = _WebSocketImpl.DEFAULT_WINDOW_BITS; |
| + if (requested != null && |
| + requested.parameters[_clientMaxWindowBits] != null) { |
| + info[0] = "; client_max_window_bits=${info[1]}"; |
| + } else { |
| + info[0] = "; client_max_window_bits"; |
| + } |
| + |
| + return info; |
| + } |
| + |
| + /// Create a Compression Header. If [requested] is null or contains |
| + /// client request headers, returns Client compression request headers. |
| + /// If [requested] contains server response headers this method returns |
| + /// a Server compression response header. |
| + List _createHeader([HeaderValue requested]) { |
| + if (!enabled) { |
| + return ["", 0]; |
| + } |
| + |
| + var info = new List(2); |
| + var header = _WebSocketImpl.PER_MESSAGE_DEFLATE; |
| + |
| + if (clientNoContextTakeover && |
| + (requested != null && |
| + requested.parameters.containsKey(_clientNoContextTakeover))) { |
| + header += "; client_no_context_takeover"; |
| + } |
| + |
| + if (serverNoContextTakeover && |
| + (requested != null && |
| + requested.parameters.containsKey(_serverNoContextTakeover))) { |
| + header += "; server_no_context_takeover"; |
| + } |
| + |
| + if (requested == null || |
| + requested.parameters.containsKey(_clientMaxWindowBits)) { |
| + var clientList = _createClientRequestHeader(requested); |
| + header += clientList[0]; |
| + info[1] = clientList[1]; |
| + } else { |
| + var headerList = _createServerResponseHeader(requested); |
| + header += headerList[0]; |
| + info[1] = headerList[1]; |
| + } |
| + |
| + info[0] = header; |
| + |
| + return info; |
| + } |
| +} |
| + |
| +/** |
| * The [WebSocketTransformer] provides the ability to upgrade a |
| * [HttpRequest] to a [WebSocket] connection. It supports both |
| * upgrading a single [HttpRequest] and upgrading a stream of |
| @@ -53,7 +188,6 @@ abstract class WebSocketStatus { |
| */ |
| abstract class WebSocketTransformer |
| implements StreamTransformer<HttpRequest, WebSocket> { |
| - |
| /** |
| * Create a new [WebSocketTransformer]. |
| * |
| @@ -62,9 +196,15 @@ abstract class WebSocketTransformer |
| * [protocolSelector] is should return either a [String] or a [Future] |
| * completing with a [String]. The [String] must exist in the list of |
| * protocols. |
| + * |
| + * If [compression] is provided, the [WebSocket] created will be configured |
| + * to negotiate with the specified [CompressionOptions]. If none is specified |
| + * then the [WebSocket] will be created with the default [CompressionOptions]. |
| */ |
| - factory WebSocketTransformer({protocolSelector(List<String> protocols)}) |
| - => new _WebSocketTransformerImpl(protocolSelector); |
| + factory WebSocketTransformer( |
| + {protocolSelector(List<String> protocols), |
| + CompressionOptions compression: CompressionOptions.DEFAULT}) => |
| + new _WebSocketTransformerImpl(protocolSelector, compression); |
| /** |
| * Upgrades a [HttpRequest] to a [WebSocket] connection. If the |
| @@ -78,10 +218,16 @@ abstract class WebSocketTransformer |
| * [protocolSelector] is should return either a [String] or a [Future] |
| * completing with a [String]. The [String] must exist in the list of |
| * protocols. |
| + * |
| + * If [compression] is provided, the [WebSocket] created will be configured |
| + * to negotiate with the specified [CompressionOptions]. If none is specified |
| + * then the [WebSocket] will be created with the default [CompressionOptions]. |
| */ |
| static Future<WebSocket> upgrade(HttpRequest request, |
| - {protocolSelector(List<String> protocols)}) { |
| - return _WebSocketTransformerImpl._upgrade(request, protocolSelector); |
| + {protocolSelector(List<String> protocols), |
| + CompressionOptions compression: CompressionOptions.DEFAULT}) { |
| + return _WebSocketTransformerImpl._upgrade( |
| + request, protocolSelector, compression); |
| } |
| /** |
| @@ -92,7 +238,6 @@ abstract class WebSocketTransformer |
| } |
| } |
| - |
| /** |
| * A two-way HTTP communication object for client or server applications. |
| * |
| @@ -152,9 +297,10 @@ abstract class WebSocket implements Stream, StreamSink { |
| * authentication when setting up the connection. |
| */ |
| static Future<WebSocket> connect(String url, |
| - {Iterable<String> protocols, |
| - Map<String, dynamic> headers}) => |
| - _WebSocketImpl.connect(url, protocols, headers); |
| + {Iterable<String> protocols, |
| + Map<String, dynamic> headers, |
| + CompressionOptions compression: CompressionOptions.DEFAULT}) => |
| + _WebSocketImpl.connect(url, protocols, headers, compression: compression); |
| @Deprecated('This constructor will be removed in Dart 2.0. Use `implements`' |
| ' instead of `extends` if implementing this abstract class.') |
| @@ -174,14 +320,21 @@ abstract class WebSocket implements Stream, StreamSink { |
| * [serverSide] must be passed explicitly. If it's `false`, the WebSocket will |
| * act as the client and mask the messages it sends. If it's `true`, it will |
| * act as the server and will not mask its messages. |
| + * |
| + * If [compression] is provided, the [WebSocket] created will be configured |
| + * to negotiate with the specified [CompressionOptions]. If none is specified |
| + * then the [WebSocket] will be created with the default [CompressionOptions]. |
| */ |
| - factory WebSocket.fromUpgradedSocket(Socket socket, {String protocol, |
| - bool serverSide}) { |
| + factory WebSocket.fromUpgradedSocket(Socket socket, |
| + {String protocol, |
| + bool serverSide, |
| + CompressionOptions compression: CompressionOptions.DEFAULT}) { |
| if (serverSide == null) { |
| throw new ArgumentError("The serverSide argument must be passed " |
| "explicitly to WebSocket.fromUpgradedSocket."); |
| } |
| - return new _WebSocketImpl._fromSocket(socket, protocol, serverSide); |
| + return new _WebSocketImpl._fromSocket( |
| + socket, protocol, compression, serverSide); |
| } |
| /** |
| @@ -238,9 +391,10 @@ abstract class WebSocket implements Stream, StreamSink { |
| Future addStream(Stream stream); |
| } |
| - |
| class WebSocketException implements IOException { |
| final String message; |
| + |
| const WebSocketException([this.message = ""]); |
| + |
| String toString() => "WebSocketException: $message"; |
| } |