Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of dart.io; | 5 part of dart.io; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * WebSocket status codes used when closing a WebSocket connection. | 8 * WebSocket status codes used when closing a WebSocket connection. |
| 9 */ | 9 */ |
| 10 abstract class WebSocketStatus { | 10 abstract class WebSocketStatus { |
| 11 static const int NORMAL_CLOSURE = 1000; | 11 static const int NORMAL_CLOSURE = 1000; |
| 12 static const int GOING_AWAY = 1001; | 12 static const int GOING_AWAY = 1001; |
| 13 static const int PROTOCOL_ERROR = 1002; | 13 static const int PROTOCOL_ERROR = 1002; |
| 14 static const int UNSUPPORTED_DATA = 1003; | 14 static const int UNSUPPORTED_DATA = 1003; |
| 15 static const int RESERVED_1004 = 1004; | 15 static const int RESERVED_1004 = 1004; |
| 16 static const int NO_STATUS_RECEIVED = 1005; | 16 static const int NO_STATUS_RECEIVED = 1005; |
| 17 static const int ABNORMAL_CLOSURE = 1006; | 17 static const int ABNORMAL_CLOSURE = 1006; |
| 18 static const int INVALID_FRAME_PAYLOAD_DATA = 1007; | 18 static const int INVALID_FRAME_PAYLOAD_DATA = 1007; |
| 19 static const int POLICY_VIOLATION = 1008; | 19 static const int POLICY_VIOLATION = 1008; |
| 20 static const int MESSAGE_TOO_BIG = 1009; | 20 static const int MESSAGE_TOO_BIG = 1009; |
| 21 static const int MISSING_MANDATORY_EXTENSION = 1010; | 21 static const int MISSING_MANDATORY_EXTENSION = 1010; |
| 22 static const int INTERNAL_SERVER_ERROR = 1011; | 22 static const int INTERNAL_SERVER_ERROR = 1011; |
| 23 static const int RESERVED_1015 = 1015; | 23 static const int RESERVED_1015 = 1015; |
| 24 } | 24 } |
| 25 | 25 |
| 26 /** | 26 /** |
| 27 * The [CompressionOptions] class allows you to control | |
| 28 * the options of WebSocket compression. | |
| 29 */ | |
| 30 class CompressionOptions { | |
| 31 /** | |
| 32 * Default WebSocket Compression options. | |
| 33 * Compression will be enabled with the following options: | |
| 34 * clientNoContextTakeover: false | |
| 35 * serverNoContextTakeover: false | |
| 36 * clientMaxWindowBits: 15 | |
| 37 * serverMaxWindowBits: 15 | |
| 38 */ | |
| 39 static const CompressionOptions DEFAULT = const CompressionOptions(); | |
| 40 | |
| 41 /** | |
| 42 * Disables WebSocket Compression. | |
| 43 */ | |
| 44 static const CompressionOptions OFF = | |
| 45 const CompressionOptions(enabled: false); | |
| 46 | |
| 47 /** | |
| 48 * Control whether the client will reuse it's compression instances. | |
| 49 */ | |
| 50 final bool clientNoContextTakeover; | |
| 51 | |
| 52 /** | |
| 53 * Control whether the server will reuse it's compression instances. | |
| 54 */ | |
| 55 final bool serverNoContextTakeover; | |
| 56 | |
| 57 /** | |
| 58 * Sets the Max Window Bits for the Client. | |
| 59 */ | |
| 60 final int clientMaxWindowBits; | |
| 61 | |
| 62 /** | |
| 63 * Sets the Max Window Bits for the Server. | |
| 64 */ | |
| 65 final int serverMaxWindowBits; | |
| 66 | |
| 67 /** | |
| 68 * Enables or disables WebSocket compression. | |
| 69 */ | |
| 70 final bool enabled; | |
| 71 | |
| 72 const CompressionOptions( | |
| 73 {this.clientNoContextTakeover: false, | |
| 74 this.serverNoContextTakeover: false, | |
| 75 this.clientMaxWindowBits, | |
|
Søren Gjesse
2015/10/19 16:53:20
Assign default value _WebSocketImpl.DEFAULT_WINDOW
butlermatt
2015/10/22 19:36:08
Done.
| |
| 76 this.serverMaxWindowBits, | |
| 77 this.enabled: true}); | |
| 78 | |
| 79 /// Parses list of requested server headers to return server compression | |
| 80 /// response headers. Uses [serverMaxWindowBits] value if set, otherwise will | |
| 81 /// attempt to use value from headers. Defaults to | |
| 82 /// [WebSocket.DEFAULT_WINDOW_BITS] | |
| 83 List _createServerResponseHeader(HeaderValue requested) { | |
| 84 var info = new List(2); | |
| 85 | |
| 86 if (requested.parameters["server_max_window_bits"] != null) { | |
|
Søren Gjesse
2015/10/19 16:53:19
Please make the values "server_no_context_takeover
butlermatt
2015/10/22 19:36:08
Done.
| |
| 87 var part = requested.parameters["server_max_window_bits"]; | |
|
Søren Gjesse
2015/10/19 16:53:19
Move this 'var part ...' up before the if, and tes
butlermatt
2015/10/22 19:36:08
Done.
| |
| 88 var mwb = serverMaxWindowBits == null | |
| 89 ? int.parse(part, | |
|
Søren Gjesse
2015/10/19 16:53:19
According to
https://tools.ietf.org/html/draft-iet
butlermatt
2015/10/22 19:36:08
Done.
| |
| 90 onError: (source) => _WebSocketImpl.DEFAULT_WINDOW_BITS) | |
|
Søren Gjesse
2015/10/19 16:53:20
Please indent with 4 - or all the way to align wit
butlermatt
2015/10/22 19:36:08
Done.
| |
| 91 : serverMaxWindowBits; | |
| 92 info[0] = "; server_max_window_bits=${mwb}"; | |
| 93 info[1] = mwb; | |
| 94 } else { | |
| 95 info[1] = _WebSocketImpl.DEFAULT_WINDOW_BITS; | |
| 96 } | |
| 97 return info; | |
| 98 } | |
| 99 | |
| 100 /// Returns default values for client compression request headers. | |
| 101 List _createClientRequestHeader(HeaderValue requested) { | |
| 102 var info = new List(2); | |
| 103 | |
| 104 info[1] = _WebSocketImpl.DEFAULT_WINDOW_BITS; | |
| 105 if (requested != null && | |
| 106 requested.parameters["client_max_window_bits"] != null) { | |
| 107 info[0] = "; client_max_window_bits=${info[1]}"; | |
| 108 } else { | |
| 109 info[0] = "; client_max_window_bits"; | |
| 110 } | |
| 111 | |
| 112 return info; | |
| 113 } | |
| 114 | |
| 115 /// Create a Compression Header. If [requested] is null or contains | |
| 116 /// client request headers, returns Client compression request headers. | |
| 117 /// If [requested] contains server response headers this method returns | |
| 118 /// a Server compression response header. | |
| 119 List _createHeader([HeaderValue requested]) { | |
| 120 if (!enabled) { | |
| 121 return ["", 0]; | |
| 122 } | |
| 123 | |
| 124 var info = new List(2); | |
| 125 var header = _WebSocketImpl.PER_MESSAGE_DEFLATE; | |
| 126 | |
| 127 if (clientNoContextTakeover && | |
| 128 (requested != null && | |
| 129 requested.parameters.containsKey("client_no_context_takeover"))) { | |
| 130 header += "; client_no_context_takeover"; | |
| 131 } | |
| 132 | |
| 133 if (serverNoContextTakeover && | |
| 134 (requested != null && | |
| 135 requested.parameters.containsKey("server_no_context_takeover"))) { | |
| 136 header += "; server_no_context_takeover"; | |
| 137 } | |
| 138 | |
| 139 if (requested == null || | |
| 140 requested.parameters.containsKey("client_max_window_bits")) { | |
| 141 var clientList = _createClientRequestHeader(requested); | |
| 142 header += clientList[0]; | |
| 143 info[1] = clientList[1]; | |
| 144 } else { | |
| 145 var headerList = _createServerResponseHeader(requested); | |
| 146 header += headerList[0]; | |
| 147 info[1] = headerList[1]; | |
| 148 } | |
| 149 | |
| 150 info[0] = header; | |
| 151 | |
| 152 return info; | |
| 153 } | |
| 154 } | |
| 155 | |
| 156 /** | |
| 27 * The [WebSocketTransformer] provides the ability to upgrade a | 157 * The [WebSocketTransformer] provides the ability to upgrade a |
| 28 * [HttpRequest] to a [WebSocket] connection. It supports both | 158 * [HttpRequest] to a [WebSocket] connection. It supports both |
| 29 * upgrading a single [HttpRequest] and upgrading a stream of | 159 * upgrading a single [HttpRequest] and upgrading a stream of |
| 30 * [HttpRequest]s. | 160 * [HttpRequest]s. |
| 31 * | 161 * |
| 32 * To upgrade a single [HttpRequest] use the static [upgrade] method. | 162 * To upgrade a single [HttpRequest] use the static [upgrade] method. |
| 33 * | 163 * |
| 34 * HttpServer server; | 164 * HttpServer server; |
| 35 * server.listen((request) { | 165 * server.listen((request) { |
| 36 * if (...) { | 166 * if (...) { |
| 37 * WebSocketTransformer.upgrade(request).then((websocket) { | 167 * WebSocketTransformer.upgrade(request).then((websocket) { |
| 38 * ... | 168 * ... |
| 39 * }); | 169 * }); |
| 40 * } else { | 170 * } else { |
| 41 * // Do normal HTTP request processing. | 171 * // Do normal HTTP request processing. |
| 42 * } | 172 * } |
| 43 * }); | 173 * }); |
| 44 * | 174 * |
| 45 * To transform a stream of [HttpRequest] events as it implements a | 175 * To transform a stream of [HttpRequest] events as it implements a |
| 46 * stream transformer that transforms a stream of HttpRequest into a | 176 * stream transformer that transforms a stream of HttpRequest into a |
| 47 * stream of WebSockets by upgrading each HttpRequest from the HTTP or | 177 * stream of WebSockets by upgrading each HttpRequest from the HTTP or |
| 48 * HTTPS server, to the WebSocket protocol. | 178 * HTTPS server, to the WebSocket protocol. |
| 49 * | 179 * |
| 50 * server.transform(new WebSocketTransformer()).listen((webSocket) => ...); | 180 * server.transform(new WebSocketTransformer()).listen((webSocket) => ...); |
| 51 * | 181 * |
| 52 * This transformer strives to implement WebSockets as specified by RFC6455. | 182 * This transformer strives to implement WebSockets as specified by RFC6455. |
| 53 */ | 183 */ |
| 54 abstract class WebSocketTransformer | 184 abstract class WebSocketTransformer |
| 55 implements StreamTransformer<HttpRequest, WebSocket> { | 185 implements StreamTransformer<HttpRequest, WebSocket> { |
| 56 | |
| 57 /** | 186 /** |
| 58 * Create a new [WebSocketTransformer]. | 187 * Create a new [WebSocketTransformer]. |
| 59 * | 188 * |
| 60 * If [protocolSelector] is provided, [protocolSelector] will be called to | 189 * If [protocolSelector] is provided, [protocolSelector] will be called to |
| 61 * select what protocol to use, if any were provided by the client. | 190 * select what protocol to use, if any were provided by the client. |
| 62 * [protocolSelector] is should return either a [String] or a [Future] | 191 * [protocolSelector] is should return either a [String] or a [Future] |
| 63 * completing with a [String]. The [String] must exist in the list of | 192 * completing with a [String]. The [String] must exist in the list of |
| 64 * protocols. | 193 * protocols. |
|
Søren Gjesse
2015/10/19 16:53:20
Please update the dartdoc comment here with inform
butlermatt
2015/10/22 19:36:08
Done.
| |
| 65 */ | 194 */ |
| 66 factory WebSocketTransformer({protocolSelector(List<String> protocols)}) | 195 factory WebSocketTransformer( |
| 67 => new _WebSocketTransformerImpl(protocolSelector); | 196 {protocolSelector(List<String> protocols), |
| 197 CompressionOptions compression: CompressionOptions.DEFAULT}) => | |
| 198 new _WebSocketTransformerImpl(protocolSelector, compression); | |
| 68 | 199 |
| 69 /** | 200 /** |
| 70 * Upgrades a [HttpRequest] to a [WebSocket] connection. If the | 201 * Upgrades a [HttpRequest] to a [WebSocket] connection. If the |
| 71 * request is not a valid WebSocket upgrade request an HTTP response | 202 * request is not a valid WebSocket upgrade request an HTTP response |
| 72 * with status code 500 will be returned. Otherwise the returned | 203 * with status code 500 will be returned. Otherwise the returned |
| 73 * future will complete with the [WebSocket] when the upgrade pocess | 204 * future will complete with the [WebSocket] when the upgrade pocess |
| 74 * is complete. | 205 * is complete. |
| 75 * | 206 * |
| 76 * If [protocolSelector] is provided, [protocolSelector] will be called to | 207 * If [protocolSelector] is provided, [protocolSelector] will be called to |
| 77 * select what protocol to use, if any were provided by the client. | 208 * select what protocol to use, if any were provided by the client. |
| 78 * [protocolSelector] is should return either a [String] or a [Future] | 209 * [protocolSelector] is should return either a [String] or a [Future] |
| 79 * completing with a [String]. The [String] must exist in the list of | 210 * completing with a [String]. The [String] must exist in the list of |
| 80 * protocols. | 211 * protocols. |
|
Søren Gjesse
2015/10/19 16:53:19
ditto.
butlermatt
2015/10/22 19:36:08
Done.
| |
| 81 */ | 212 */ |
| 82 static Future<WebSocket> upgrade(HttpRequest request, | 213 static Future<WebSocket> upgrade(HttpRequest request, |
| 83 {protocolSelector(List<String> protocols)}) { | 214 {protocolSelector(List<String> protocols), |
| 84 return _WebSocketTransformerImpl._upgrade(request, protocolSelector); | 215 CompressionOptions compression: CompressionOptions.DEFAULT}) { |
| 216 return _WebSocketTransformerImpl._upgrade( | |
| 217 request, protocolSelector, compression); | |
| 85 } | 218 } |
| 86 | 219 |
| 87 /** | 220 /** |
| 88 * Checks whether the request is a valid WebSocket upgrade request. | 221 * Checks whether the request is a valid WebSocket upgrade request. |
| 89 */ | 222 */ |
| 90 static bool isUpgradeRequest(HttpRequest request) { | 223 static bool isUpgradeRequest(HttpRequest request) { |
| 91 return _WebSocketTransformerImpl._isUpgradeRequest(request); | 224 return _WebSocketTransformerImpl._isUpgradeRequest(request); |
| 92 } | 225 } |
| 93 } | 226 } |
| 94 | 227 |
| 95 | |
| 96 /** | 228 /** |
| 97 * A two-way HTTP communication object for client or server applications. | 229 * A two-way HTTP communication object for client or server applications. |
| 98 * | 230 * |
| 99 * The stream exposes the messages received. A text message will be of type | 231 * The stream exposes the messages received. A text message will be of type |
| 100 * [:String:] and a binary message will be of type [:List<int>:]. | 232 * [:String:] and a binary message will be of type [:List<int>:]. |
| 101 */ | 233 */ |
| 102 abstract class WebSocket implements Stream, StreamSink { | 234 abstract class WebSocket implements Stream, StreamSink { |
| 103 /** | 235 /** |
| 104 * Possible states of the connection. | 236 * Possible states of the connection. |
| 105 */ | 237 */ |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 145 * - `sec-websocket-protocol` | 277 * - `sec-websocket-protocol` |
| 146 * - `sec-websocket-version` | 278 * - `sec-websocket-version` |
| 147 * - `upgrade` | 279 * - `upgrade` |
| 148 * | 280 * |
| 149 * If any of these are passed in the `headers` map they will be ignored. | 281 * If any of these are passed in the `headers` map they will be ignored. |
| 150 * | 282 * |
| 151 * If the `url` contains user information this will be passed as basic | 283 * If the `url` contains user information this will be passed as basic |
| 152 * authentication when setting up the connection. | 284 * authentication when setting up the connection. |
| 153 */ | 285 */ |
| 154 static Future<WebSocket> connect(String url, | 286 static Future<WebSocket> connect(String url, |
| 155 {Iterable<String> protocols, | 287 {Iterable<String> protocols, |
| 156 Map<String, dynamic> headers}) => | 288 Map<String, dynamic> headers, |
| 157 _WebSocketImpl.connect(url, protocols, headers); | 289 CompressionOptions compression: CompressionOptions.DEFAULT}) => |
| 290 _WebSocketImpl.connect(url, protocols, headers, compression: compression); | |
| 158 | 291 |
| 159 @Deprecated('This constructor will be removed in Dart 2.0. Use `implements`' | 292 @Deprecated('This constructor will be removed in Dart 2.0. Use `implements`' |
| 160 ' instead of `extends` if implementing this abstract class.') | 293 ' instead of `extends` if implementing this abstract class.') |
| 161 WebSocket(); | 294 WebSocket(); |
| 162 | 295 |
| 163 /** | 296 /** |
| 164 * Creates a WebSocket from an already-upgraded socket. | 297 * Creates a WebSocket from an already-upgraded socket. |
| 165 * | 298 * |
| 166 * The initial WebSocket handshake must have occurred prior to this call. A | 299 * The initial WebSocket handshake must have occurred prior to this call. A |
| 167 * WebSocket client can automatically perform the handshake using | 300 * WebSocket client can automatically perform the handshake using |
| 168 * [WebSocket.connect], while a server can do so using | 301 * [WebSocket.connect], while a server can do so using |
| 169 * [WebSocketTransformer.upgrade]. To manually upgrade an [HttpRequest], | 302 * [WebSocketTransformer.upgrade]. To manually upgrade an [HttpRequest], |
| 170 * [HttpRequest.detachSocket] may be called. | 303 * [HttpRequest.detachSocket] may be called. |
| 171 * | 304 * |
| 172 * [protocol] should be the protocol negotiated by this handshake, if any. | 305 * [protocol] should be the protocol negotiated by this handshake, if any. |
| 173 * | 306 * |
| 174 * [serverSide] must be passed explicitly. If it's `false`, the WebSocket will | 307 * [serverSide] must be passed explicitly. If it's `false`, the WebSocket will |
| 175 * act as the client and mask the messages it sends. If it's `true`, it will | 308 * act as the client and mask the messages it sends. If it's `true`, it will |
| 176 * act as the server and will not mask its messages. | 309 * act as the server and will not mask its messages. |
|
Søren Gjesse
2015/10/19 16:53:20
ditto.
butlermatt
2015/10/22 19:36:08
Done.
| |
| 177 */ | 310 */ |
| 178 factory WebSocket.fromUpgradedSocket(Socket socket, {String protocol, | 311 factory WebSocket.fromUpgradedSocket(Socket socket, |
| 179 bool serverSide}) { | 312 {String protocol, |
| 313 bool serverSide, | |
| 314 CompressionOptions compression: CompressionOptions.DEFAULT}) { | |
| 180 if (serverSide == null) { | 315 if (serverSide == null) { |
| 181 throw new ArgumentError("The serverSide argument must be passed " | 316 throw new ArgumentError("The serverSide argument must be passed " |
| 182 "explicitly to WebSocket.fromUpgradedSocket."); | 317 "explicitly to WebSocket.fromUpgradedSocket."); |
| 183 } | 318 } |
| 184 return new _WebSocketImpl._fromSocket(socket, protocol, serverSide); | 319 return new _WebSocketImpl._fromSocket( |
| 320 socket, protocol, compression, serverSide); | |
| 185 } | 321 } |
| 186 | 322 |
| 187 /** | 323 /** |
| 188 * Returns the current state of the connection. | 324 * Returns the current state of the connection. |
| 189 */ | 325 */ |
| 190 int get readyState; | 326 int get readyState; |
| 191 | 327 |
| 192 /** | 328 /** |
| 193 * The extensions property is initially the empty string. After the | 329 * The extensions property is initially the empty string. After the |
| 194 * WebSocket connection is established this string reflects the | 330 * WebSocket connection is established this string reflects the |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 231 void add(data); | 367 void add(data); |
| 232 | 368 |
| 233 /** | 369 /** |
| 234 * Sends data from a stream on WebSocket connection. Each data event from | 370 * Sends data from a stream on WebSocket connection. Each data event from |
| 235 * [stream] will be send as a single WebSocket frame. The data from [stream] | 371 * [stream] will be send as a single WebSocket frame. The data from [stream] |
| 236 * must be either [:String:]s, or [:List<int>:]s holding bytes. | 372 * must be either [:String:]s, or [:List<int>:]s holding bytes. |
| 237 */ | 373 */ |
| 238 Future addStream(Stream stream); | 374 Future addStream(Stream stream); |
| 239 } | 375 } |
| 240 | 376 |
| 241 | |
| 242 class WebSocketException implements IOException { | 377 class WebSocketException implements IOException { |
| 243 final String message; | 378 final String message; |
| 379 | |
| 244 const WebSocketException([this.message = ""]); | 380 const WebSocketException([this.message = ""]); |
| 381 | |
| 245 String toString() => "WebSocketException: $message"; | 382 String toString() => "WebSocketException: $message"; |
| 246 } | 383 } |
| OLD | NEW |