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 // VMOptions= | 5 // VMOptions= |
6 // VMOptions=--short_socket_read | 6 // VMOptions=--short_socket_read |
7 // VMOptions=--short_socket_write | 7 // VMOptions=--short_socket_write |
8 // VMOptions=--short_socket_read --short_socket_write | 8 // VMOptions=--short_socket_read --short_socket_write |
9 | 9 |
10 import "dart:async"; | 10 import "dart:async"; |
11 import "dart:convert"; | 11 import "dart:convert"; |
12 import "dart:io"; | 12 import "dart:io"; |
13 import "dart:typed_data"; | 13 import "dart:typed_data"; |
| 14 import "dart:math"; |
14 | 15 |
15 import "package:async_helper/async_helper.dart"; | 16 import "package:async_helper/async_helper.dart"; |
16 import "package:crypto/crypto.dart"; | 17 import "package:crypto/crypto.dart"; |
17 import "package:expect/expect.dart"; | 18 import "package:expect/expect.dart"; |
18 import "package:path/path.dart"; | 19 import "package:path/path.dart"; |
19 | 20 |
20 const WEB_SOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; | 21 const WEB_SOCKET_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; |
21 | 22 |
22 const String HOST_NAME = 'localhost'; | 23 const String HOST_NAME = 'localhost'; |
23 | 24 |
(...skipping 15 matching lines...) Expand all Loading... |
39 serverContext, | 40 serverContext, |
40 backlog: backlog) | 41 backlog: backlog) |
41 : HttpServer.bind(HOST_NAME, | 42 : HttpServer.bind(HOST_NAME, |
42 0, | 43 0, |
43 backlog: backlog); | 44 backlog: backlog); |
44 | 45 |
45 Future<WebSocket> createClient(int port) => | 46 Future<WebSocket> createClient(int port) => |
46 // TODO(whesse): Add client context argument to WebSocket.connect | 47 // TODO(whesse): Add client context argument to WebSocket.connect |
47 WebSocket.connect('${secure ? "wss" : "ws"}://$HOST_NAME:$port/'); | 48 WebSocket.connect('${secure ? "wss" : "ws"}://$HOST_NAME:$port/'); |
48 | 49 |
| 50 Future<HttpClientResponse> createWebsocket(String url, String headerValue) { |
| 51 HttpClient _httpClient = new HttpClient(); |
| 52 Uri uri = Uri.parse(url); |
| 53 |
| 54 Random random = new Random(); |
| 55 // Generate 16 random bytes. |
| 56 Uint8List nonceData = new Uint8List(16); |
| 57 for (int i = 0; i < 16; i++) { |
| 58 nonceData[i] = random.nextInt(256); |
| 59 } |
| 60 String nonce = CryptoUtils.bytesToBase64(nonceData); |
| 61 |
| 62 uri = new Uri( |
| 63 scheme: uri.scheme == "wss" ? "https" : "http", |
| 64 userInfo: uri.userInfo, |
| 65 host: uri.host, |
| 66 port: uri.port, |
| 67 path: uri.path, |
| 68 query: uri.query, |
| 69 fragment: uri.fragment); |
| 70 return _httpClient.openUrl("GET", uri).then((request) { |
| 71 if (uri.userInfo != null && !uri.userInfo.isEmpty) { |
| 72 // If the URL contains user information use that for basic |
| 73 // authorization. |
| 74 String auth = CryptoUtils.bytesToBase64(UTF8.encode(uri.userInfo)); |
| 75 request.headers.set(HttpHeaders.AUTHORIZATION, "Basic $auth"); |
| 76 } |
| 77 // Setup the initial handshake. |
| 78 request.headers |
| 79 ..set(HttpHeaders.CONNECTION, "Upgrade") |
| 80 ..set(HttpHeaders.UPGRADE, "websocket") |
| 81 ..set("Sec-WebSocket-Key", nonce) |
| 82 ..set("Cache-Control", "no-cache") |
| 83 ..set("Sec-WebSocket-Version", "13") |
| 84 ..set("Sec-WebSocket-Extensions", headerValue); |
| 85 |
| 86 return request.close(); |
| 87 }); |
| 88 } |
| 89 |
49 void testCompressionSupport({server: false, | 90 void testCompressionSupport({server: false, |
50 client: false, | 91 client: false, |
51 contextTakeover: false}) { | 92 contextTakeover: false}) { |
52 asyncStart(); | 93 asyncStart(); |
53 | 94 |
54 var clientOptions = new CompressionOptions( | 95 var clientOptions = new CompressionOptions( |
55 enabled: client, | 96 enabled: client, |
56 serverNoContextTakeover: contextTakeover, | 97 serverNoContextTakeover: contextTakeover, |
57 clientNoContextTakeover: contextTakeover); | 98 clientNoContextTakeover: contextTakeover); |
58 var serverOptions = new CompressionOptions( | 99 var serverOptions = new CompressionOptions( |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
123 | 164 |
124 WebSocket.connect(url).then((websocket) { | 165 WebSocket.connect(url).then((websocket) { |
125 return websocket.listen((message) { | 166 return websocket.listen((message) { |
126 Expect.equals("Hello", message); | 167 Expect.equals("Hello", message); |
127 websocket.close(); | 168 websocket.close(); |
128 }).asFuture(); | 169 }).asFuture(); |
129 }).then((_) => server.close()); | 170 }).then((_) => server.close()); |
130 }); | 171 }); |
131 } | 172 } |
132 | 173 |
| 174 void testReturnHeaders(String headerValue, String expected) { |
| 175 asyncStart(); |
| 176 createServer().then((server) { |
| 177 server.listen((request) { |
| 178 // Stuff |
| 179 Expect.isTrue(WebSocketTransformer.isUpgradeRequest(request)); |
| 180 WebSocketTransformer.upgrade(request).then((webSocket) { |
| 181 webSocket.listen((message) { |
| 182 Expect.equals("Hello World", message); |
| 183 |
| 184 webSocket.add(message); |
| 185 webSocket.close(); |
| 186 }); |
| 187 }); |
| 188 }); |
| 189 |
| 190 var url = '${secure ? "wss" : "ws"}://$HOST_NAME:${server.port}/'; |
| 191 createWebsocket(url, headerValue) |
| 192 .then((HttpClientResponse response) { |
| 193 Expect.equals(response.statusCode, HttpStatus.SWITCHING_PROTOCOLS); |
| 194 print(response.headers.value('Sec-WebSocket-Extensions')); |
| 195 Expect.equals(response.headers.value("Sec-WebSocket-Extensions"), |
| 196 expected); |
| 197 |
| 198 String accept = response.headers.value("Sec-WebSocket-Accept"); |
| 199 Expect.isNotNull(accept); |
| 200 |
| 201 var protocol = response.headers.value('Sec-WebSocket-Protocol'); |
| 202 return response.detachSocket().then((socket) => |
| 203 new WebSocket.fromUpgradedSocket( |
| 204 socket, protocol: protocol, serverSide: false)); |
| 205 }).then((websocket) { |
| 206 var future = websocket.listen((message) { |
| 207 Expect.equals("Hello", message); |
| 208 websocket.close(); |
| 209 }).asFuture(); |
| 210 websocket.add("Hello World"); |
| 211 return future; |
| 212 }).then((_) { |
| 213 server.close(); |
| 214 asyncEnd(); |
| 215 }); |
| 216 }); // End createServer |
| 217 } |
| 218 |
133 void runTests() { | 219 void runTests() { |
134 // No compression or takeover | 220 // No compression or takeover |
135 testCompressionSupport(); | 221 testCompressionSupport(); |
136 // compression no takeover | 222 // compression no takeover |
137 testCompressionSupport(server: true, client: true); | 223 testCompressionSupport(server: true, client: true); |
138 // compression and context takeover. | 224 // compression and context takeover. |
139 testCompressionSupport(server: true, client: true, contextTakeover: true); | 225 testCompressionSupport(server: true, client: true, contextTakeover: true); |
140 // Compression on client but not server. No take over | 226 // Compression on client but not server. No take over |
141 testCompressionSupport(client: true); | 227 testCompressionSupport(client: true); |
142 // Compression on server but not client. | 228 // Compression on server but not client. |
143 testCompressionSupport(server: true); | 229 testCompressionSupport(server: true); |
144 | 230 |
145 testCompressionHeaders(); | 231 testCompressionHeaders(); |
| 232 // Chrome headers |
| 233 testReturnHeaders('permessage-deflate; client_max_window_bits', |
| 234 "permessage-deflate; client_max_window_bits=15"); |
| 235 // Firefox headers |
| 236 testReturnHeaders('permessage-deflate', |
| 237 "permessage-deflate; client_max_window_bits=15"); |
| 238 // Ensure max_window_bits resize appropriately. |
| 239 testReturnHeaders('permessage-deflate; server_max_window_bits=10', |
| 240 "permessage-deflate;" |
| 241 " server_max_window_bits=10;" |
| 242 " client_max_window_bits=10"); |
146 } | 243 } |
147 } | 244 } |
148 | 245 |
149 main() { | 246 main() { |
150 new SecurityConfiguration(secure: false).runTests(); | 247 new SecurityConfiguration(secure: false).runTests(); |
151 // TODO(whesse): Make WebSocket.connect() take an optional context: parameter. | 248 // TODO(whesse): Make WebSocket.connect() take an optional context: parameter. |
152 // new SecurityConfiguration(secure: true).runTests(); | 249 // new SecurityConfiguration(secure: true).runTests(); |
153 } | 250 } |
OLD | NEW |