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: _WebSocketImpl.DEFAULT_WINDOW_BITS, | |
76 this.serverMaxWindowBits: _WebSocketImpl.DEFAULT_WINDOW_BITS, | |
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 int mwb; | |
87 var part = requested.parameters[_serverMaxWindowBits]; | |
88 if (part != null) { | |
89 if (part.length >= 2 && part.startsWith('0')) { | |
90 throw new ArgumentError("Illegal 0 padding on value."); | |
91 } else { | |
92 mwb = serverMaxWindowBits == null | |
93 ? int.parse(part, | |
94 onError: (source) => _WebSocketImpl.DEFAULT_WINDOW_BITS) | |
95 : serverMaxWindowBits; | |
96 info[0] = "; server_max_window_bits=${mwb}"; | |
97 info[1] = mwb; | |
98 } | |
99 } else { | |
100 info[1] = _WebSocketImpl.DEFAULT_WINDOW_BITS; | |
101 } | |
102 return info; | |
103 } | |
104 | |
105 /// Returns default values for client compression request headers. | |
106 List _createClientRequestHeader(HeaderValue requested) { | |
107 var info = new List(2); | |
108 | |
109 info[1] = _WebSocketImpl.DEFAULT_WINDOW_BITS; | |
110 if (requested != null && | |
111 requested.parameters[_clientMaxWindowBits] != null) { | |
112 info[0] = "; client_max_window_bits=${info[1]}"; | |
113 } else { | |
114 info[0] = "; client_max_window_bits"; | |
115 } | |
116 | |
117 return info; | |
118 } | |
119 | |
120 /// Create a Compression Header. If [requested] is null or contains | |
121 /// client request headers, returns Client compression request headers. | |
122 /// If [requested] contains server response headers this method returns | |
123 /// a Server compression response header. | |
124 List _createHeader([HeaderValue requested]) { | |
125 if (!enabled) { | |
126 return ["", 0]; | |
127 } | |
128 | |
129 var info = new List(2); | |
130 var header = _WebSocketImpl.PER_MESSAGE_DEFLATE; | |
131 | |
132 if (clientNoContextTakeover && | |
133 (requested != null && | |
134 requested.parameters.containsKey(_clientNoContextTakeover))) { | |
135 header += "; client_no_context_takeover"; | |
136 } | |
137 | |
138 if (serverNoContextTakeover && | |
139 (requested != null && | |
140 requested.parameters.containsKey(_serverNoContextTakeover))) { | |
141 header += "; server_no_context_takeover"; | |
142 } | |
143 | |
144 if (requested == null || | |
145 requested.parameters.containsKey(_clientMaxWindowBits)) { | |
146 var clientList = _createClientRequestHeader(requested); | |
147 header += clientList[0]; | |
148 info[1] = clientList[1]; | |
149 } else { | |
150 var headerList = _createServerResponseHeader(requested); | |
151 header += headerList[0]; | |
152 info[1] = headerList[1]; | |
153 } | |
154 | |
155 info[0] = header; | |
156 | |
157 return info; | |
158 } | |
159 } | |
160 | |
161 /** | |
162 * The [WebSocketTransformer] provides the ability to upgrade a | 27 * The [WebSocketTransformer] provides the ability to upgrade a |
163 * [HttpRequest] to a [WebSocket] connection. It supports both | 28 * [HttpRequest] to a [WebSocket] connection. It supports both |
164 * upgrading a single [HttpRequest] and upgrading a stream of | 29 * upgrading a single [HttpRequest] and upgrading a stream of |
165 * [HttpRequest]s. | 30 * [HttpRequest]s. |
166 * | 31 * |
167 * To upgrade a single [HttpRequest] use the static [upgrade] method. | 32 * To upgrade a single [HttpRequest] use the static [upgrade] method. |
168 * | 33 * |
169 * HttpServer server; | 34 * HttpServer server; |
170 * server.listen((request) { | 35 * server.listen((request) { |
171 * if (...) { | 36 * if (...) { |
172 * WebSocketTransformer.upgrade(request).then((websocket) { | 37 * WebSocketTransformer.upgrade(request).then((websocket) { |
173 * ... | 38 * ... |
174 * }); | 39 * }); |
175 * } else { | 40 * } else { |
176 * // Do normal HTTP request processing. | 41 * // Do normal HTTP request processing. |
177 * } | 42 * } |
178 * }); | 43 * }); |
179 * | 44 * |
180 * To transform a stream of [HttpRequest] events as it implements a | 45 * To transform a stream of [HttpRequest] events as it implements a |
181 * stream transformer that transforms a stream of HttpRequest into a | 46 * stream transformer that transforms a stream of HttpRequest into a |
182 * stream of WebSockets by upgrading each HttpRequest from the HTTP or | 47 * stream of WebSockets by upgrading each HttpRequest from the HTTP or |
183 * HTTPS server, to the WebSocket protocol. | 48 * HTTPS server, to the WebSocket protocol. |
184 * | 49 * |
185 * server.transform(new WebSocketTransformer()).listen((webSocket) => ...); | 50 * server.transform(new WebSocketTransformer()).listen((webSocket) => ...); |
186 * | 51 * |
187 * This transformer strives to implement WebSockets as specified by RFC6455. | 52 * This transformer strives to implement WebSockets as specified by RFC6455. |
188 */ | 53 */ |
189 abstract class WebSocketTransformer | 54 abstract class WebSocketTransformer |
190 implements StreamTransformer<HttpRequest, WebSocket> { | 55 implements StreamTransformer<HttpRequest, WebSocket> { |
| 56 |
191 /** | 57 /** |
192 * Create a new [WebSocketTransformer]. | 58 * Create a new [WebSocketTransformer]. |
193 * | 59 * |
194 * If [protocolSelector] is provided, [protocolSelector] will be called to | 60 * If [protocolSelector] is provided, [protocolSelector] will be called to |
195 * select what protocol to use, if any were provided by the client. | 61 * select what protocol to use, if any were provided by the client. |
196 * [protocolSelector] is should return either a [String] or a [Future] | 62 * [protocolSelector] is should return either a [String] or a [Future] |
197 * completing with a [String]. The [String] must exist in the list of | 63 * completing with a [String]. The [String] must exist in the list of |
198 * protocols. | 64 * protocols. |
199 * | |
200 * If [compression] is provided, the [WebSocket] created will be configured | |
201 * to negotiate with the specified [CompressionOptions]. If none is specified | |
202 * then the [WebSocket] will be created with the default [CompressionOptions]. | |
203 */ | 65 */ |
204 factory WebSocketTransformer( | 66 factory WebSocketTransformer({protocolSelector(List<String> protocols)}) |
205 {protocolSelector(List<String> protocols), | 67 => new _WebSocketTransformerImpl(protocolSelector); |
206 CompressionOptions compression: CompressionOptions.DEFAULT}) => | |
207 new _WebSocketTransformerImpl(protocolSelector, compression); | |
208 | 68 |
209 /** | 69 /** |
210 * Upgrades a [HttpRequest] to a [WebSocket] connection. If the | 70 * Upgrades a [HttpRequest] to a [WebSocket] connection. If the |
211 * request is not a valid WebSocket upgrade request an HTTP response | 71 * request is not a valid WebSocket upgrade request an HTTP response |
212 * with status code 500 will be returned. Otherwise the returned | 72 * with status code 500 will be returned. Otherwise the returned |
213 * future will complete with the [WebSocket] when the upgrade pocess | 73 * future will complete with the [WebSocket] when the upgrade pocess |
214 * is complete. | 74 * is complete. |
215 * | 75 * |
216 * If [protocolSelector] is provided, [protocolSelector] will be called to | 76 * If [protocolSelector] is provided, [protocolSelector] will be called to |
217 * select what protocol to use, if any were provided by the client. | 77 * select what protocol to use, if any were provided by the client. |
218 * [protocolSelector] is should return either a [String] or a [Future] | 78 * [protocolSelector] is should return either a [String] or a [Future] |
219 * completing with a [String]. The [String] must exist in the list of | 79 * completing with a [String]. The [String] must exist in the list of |
220 * protocols. | 80 * protocols. |
221 * | |
222 * If [compression] is provided, the [WebSocket] created will be configured | |
223 * to negotiate with the specified [CompressionOptions]. If none is specified | |
224 * then the [WebSocket] will be created with the default [CompressionOptions]. | |
225 */ | 81 */ |
226 static Future<WebSocket> upgrade(HttpRequest request, | 82 static Future<WebSocket> upgrade(HttpRequest request, |
227 {protocolSelector(List<String> protocols), | 83 {protocolSelector(List<String> protocols)}) { |
228 CompressionOptions compression: CompressionOptions.DEFAULT}) { | 84 return _WebSocketTransformerImpl._upgrade(request, protocolSelector); |
229 return _WebSocketTransformerImpl._upgrade( | |
230 request, protocolSelector, compression); | |
231 } | 85 } |
232 | 86 |
233 /** | 87 /** |
234 * Checks whether the request is a valid WebSocket upgrade request. | 88 * Checks whether the request is a valid WebSocket upgrade request. |
235 */ | 89 */ |
236 static bool isUpgradeRequest(HttpRequest request) { | 90 static bool isUpgradeRequest(HttpRequest request) { |
237 return _WebSocketTransformerImpl._isUpgradeRequest(request); | 91 return _WebSocketTransformerImpl._isUpgradeRequest(request); |
238 } | 92 } |
239 } | 93 } |
240 | 94 |
| 95 |
241 /** | 96 /** |
242 * A two-way HTTP communication object for client or server applications. | 97 * A two-way HTTP communication object for client or server applications. |
243 * | 98 * |
244 * The stream exposes the messages received. A text message will be of type | 99 * The stream exposes the messages received. A text message will be of type |
245 * [:String:] and a binary message will be of type [:List<int>:]. | 100 * [:String:] and a binary message will be of type [:List<int>:]. |
246 */ | 101 */ |
247 abstract class WebSocket implements Stream, StreamSink { | 102 abstract class WebSocket implements Stream, StreamSink { |
248 /** | 103 /** |
249 * Possible states of the connection. | 104 * Possible states of the connection. |
250 */ | 105 */ |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
290 * - `sec-websocket-protocol` | 145 * - `sec-websocket-protocol` |
291 * - `sec-websocket-version` | 146 * - `sec-websocket-version` |
292 * - `upgrade` | 147 * - `upgrade` |
293 * | 148 * |
294 * If any of these are passed in the `headers` map they will be ignored. | 149 * If any of these are passed in the `headers` map they will be ignored. |
295 * | 150 * |
296 * If the `url` contains user information this will be passed as basic | 151 * If the `url` contains user information this will be passed as basic |
297 * authentication when setting up the connection. | 152 * authentication when setting up the connection. |
298 */ | 153 */ |
299 static Future<WebSocket> connect(String url, | 154 static Future<WebSocket> connect(String url, |
300 {Iterable<String> protocols, | 155 {Iterable<String> protocols, |
301 Map<String, dynamic> headers, | 156 Map<String, dynamic> headers}) => |
302 CompressionOptions compression: CompressionOptions.DEFAULT}) => | 157 _WebSocketImpl.connect(url, protocols, headers); |
303 _WebSocketImpl.connect(url, protocols, headers, compression: compression); | |
304 | 158 |
305 @Deprecated('This constructor will be removed in Dart 2.0. Use `implements`' | 159 @Deprecated('This constructor will be removed in Dart 2.0. Use `implements`' |
306 ' instead of `extends` if implementing this abstract class.') | 160 ' instead of `extends` if implementing this abstract class.') |
307 WebSocket(); | 161 WebSocket(); |
308 | 162 |
309 /** | 163 /** |
310 * Creates a WebSocket from an already-upgraded socket. | 164 * Creates a WebSocket from an already-upgraded socket. |
311 * | 165 * |
312 * The initial WebSocket handshake must have occurred prior to this call. A | 166 * The initial WebSocket handshake must have occurred prior to this call. A |
313 * WebSocket client can automatically perform the handshake using | 167 * WebSocket client can automatically perform the handshake using |
314 * [WebSocket.connect], while a server can do so using | 168 * [WebSocket.connect], while a server can do so using |
315 * [WebSocketTransformer.upgrade]. To manually upgrade an [HttpRequest], | 169 * [WebSocketTransformer.upgrade]. To manually upgrade an [HttpRequest], |
316 * [HttpRequest.detachSocket] may be called. | 170 * [HttpRequest.detachSocket] may be called. |
317 * | 171 * |
318 * [protocol] should be the protocol negotiated by this handshake, if any. | 172 * [protocol] should be the protocol negotiated by this handshake, if any. |
319 * | 173 * |
320 * [serverSide] must be passed explicitly. If it's `false`, the WebSocket will | 174 * [serverSide] must be passed explicitly. If it's `false`, the WebSocket will |
321 * act as the client and mask the messages it sends. If it's `true`, it will | 175 * act as the client and mask the messages it sends. If it's `true`, it will |
322 * act as the server and will not mask its messages. | 176 * act as the server and will not mask its messages. |
323 * | |
324 * If [compression] is provided, the [WebSocket] created will be configured | |
325 * to negotiate with the specified [CompressionOptions]. If none is specified | |
326 * then the [WebSocket] will be created with the default [CompressionOptions]. | |
327 */ | 177 */ |
328 factory WebSocket.fromUpgradedSocket(Socket socket, | 178 factory WebSocket.fromUpgradedSocket(Socket socket, {String protocol, |
329 {String protocol, | 179 bool serverSide}) { |
330 bool serverSide, | |
331 CompressionOptions compression: CompressionOptions.DEFAULT}) { | |
332 if (serverSide == null) { | 180 if (serverSide == null) { |
333 throw new ArgumentError("The serverSide argument must be passed " | 181 throw new ArgumentError("The serverSide argument must be passed " |
334 "explicitly to WebSocket.fromUpgradedSocket."); | 182 "explicitly to WebSocket.fromUpgradedSocket."); |
335 } | 183 } |
336 return new _WebSocketImpl._fromSocket( | 184 return new _WebSocketImpl._fromSocket(socket, protocol, serverSide); |
337 socket, protocol, compression, serverSide); | |
338 } | 185 } |
339 | 186 |
340 /** | 187 /** |
341 * Returns the current state of the connection. | 188 * Returns the current state of the connection. |
342 */ | 189 */ |
343 int get readyState; | 190 int get readyState; |
344 | 191 |
345 /** | 192 /** |
346 * The extensions property is initially the empty string. After the | 193 * The extensions property is initially the empty string. After the |
347 * WebSocket connection is established this string reflects the | 194 * WebSocket connection is established this string reflects the |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
384 void add(data); | 231 void add(data); |
385 | 232 |
386 /** | 233 /** |
387 * Sends data from a stream on WebSocket connection. Each data event from | 234 * Sends data from a stream on WebSocket connection. Each data event from |
388 * [stream] will be send as a single WebSocket frame. The data from [stream] | 235 * [stream] will be send as a single WebSocket frame. The data from [stream] |
389 * must be either [:String:]s, or [:List<int>:]s holding bytes. | 236 * must be either [:String:]s, or [:List<int>:]s holding bytes. |
390 */ | 237 */ |
391 Future addStream(Stream stream); | 238 Future addStream(Stream stream); |
392 } | 239 } |
393 | 240 |
| 241 |
394 class WebSocketException implements IOException { | 242 class WebSocketException implements IOException { |
395 final String message; | 243 final String message; |
396 | |
397 const WebSocketException([this.message = ""]); | 244 const WebSocketException([this.message = ""]); |
398 | |
399 String toString() => "WebSocketException: $message"; | 245 String toString() => "WebSocketException: $message"; |
400 } | 246 } |
OLD | NEW |