netwerk/test/unit/test_speculative_connect.js

changeset 1
ca08bd8f51b2
equal deleted inserted replaced
-1:000000000000 0:0f47c3f6dd1e
1 /* -*- Mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim: set ts=4 sts=4 et sw=4 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 const CC = Components.Constructor;
8 const ServerSocket = CC("@mozilla.org/network/server-socket;1",
9 "nsIServerSocket",
10 "init");
11 var serv;
12 var ios;
13
14 /** Example local IP addresses (literal IP address hostname).
15 *
16 * Note: for IPv6 Unique Local and Link Local, a wider range of addresses is
17 * set aside than those most commonly used. Technically, link local addresses
18 * include those beginning with fe80:: through febf::, although in practise
19 * only fe80:: is used. Necko code blocks speculative connections for the wider
20 * range; hence, this test considers that range too.
21 */
22 var localIPv4Literals =
23 [ // IPv4 RFC1918 \
24 "10.0.0.1", "10.10.10.10", "10.255.255.255", // 10/8
25 "172.16.0.1", "172.23.172.12", "172.31.255.255", // 172.16/20
26 "192.168.0.1", "192.168.192.168", "192.168.255.255", // 192.168/16
27 // IPv4 Link Local
28 "169.254.0.1", "169.254.192.154", "169.254.255.255" // 169.254/16
29 ];
30 var localIPv6Literals =
31 [ // IPv6 Unique Local fc00::/7
32 "fc00::1", "fdfe:dcba:9876:abcd:ef01:2345:6789:abcd",
33 "fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
34 // IPv6 Link Local fe80::/10
35 "fe80::1", "fe80::abcd:ef01:2345:6789",
36 "febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff"
37 ];
38 var localIPLiterals = localIPv4Literals.concat(localIPv6Literals);
39
40 /** Test function list and descriptions.
41 */
42 var testList =
43 [ test_speculative_connect,
44 test_hostnames_resolving_to_local_addresses,
45 test_proxies_with_local_addresses
46 ];
47
48 var testDescription =
49 [ "Expect pass with localhost",
50 "Expect failure with resolved local IPs",
51 "Expect failure for proxies with local IPs"
52 ];
53
54 var testIdx = 0;
55 var hostIdx = 0;
56
57
58 /** TestServer
59 *
60 * Implements nsIServerSocket for test_speculative_connect.
61 */
62 function TestServer() {
63 this.listener = ServerSocket(-1, true, -1);
64 this.listener.asyncListen(this);
65 }
66
67 TestServer.prototype = {
68 QueryInterface: function(iid) {
69 if (iid.equals(Ci.nsIServerSocket) ||
70 iid.equals(Ci.nsISupports))
71 return this;
72 throw Cr.NS_ERROR_NO_INTERFACE;
73 },
74 onSocketAccepted: function(socket, trans) {
75 try { this.listener.close(); } catch(e) {}
76 do_check_true(true);
77 next_test();
78 },
79
80 onStopListening: function(socket) {}
81 };
82
83 /** TestOutputStreamCallback
84 *
85 * Implements nsIOutputStreamCallback for socket layer tests.
86 */
87 function TestOutputStreamCallback(transport, hostname, proxied, expectSuccess, next) {
88 this.transport = transport;
89 this.hostname = hostname;
90 this.proxied = proxied;
91 this.expectSuccess = expectSuccess;
92 this.next = next;
93 this.dummyContent = "Dummy content";
94 }
95
96 TestOutputStreamCallback.prototype = {
97 QueryInterface: function(iid) {
98 if (iid.equals(Ci.nsIOutputStreamCallback) ||
99 iid.equals(Ci.nsISupports))
100 return this;
101 throw Cr.NS_ERROR_NO_INTERFACE;
102 },
103 onOutputStreamReady: function(stream) {
104 do_check_neq(typeof(stream), undefined);
105 try {
106 stream.write(this.dummyContent, this.dummyContent.length);
107 } catch (e) {
108 // Spec Connect FAILED.
109 do_check_instanceof(e, Ci.nsIException);
110 if (this.expectSuccess) {
111 // We may expect success, but the address could be unreachable
112 // in the test environment, so expect errors.
113 if (this.proxied) {
114 do_check_true(e.result == Cr.NS_ERROR_NET_TIMEOUT ||
115 e.result == Cr.NS_ERROR_PROXY_CONNECTION_REFUSED);
116 } else {
117 do_check_true(e.result == Cr.NS_ERROR_NET_TIMEOUT ||
118 e.result == Cr.NS_ERROR_CONNECTION_REFUSED);
119 }
120 } else {
121 // A refusal to connect speculatively should throw an error.
122 do_check_eq(e.result, Cr.NS_ERROR_CONNECTION_REFUSED);
123 }
124 this.transport.close(Cr.NS_BINDING_ABORTED);
125 this.next();
126 return;
127 }
128 // Spec Connect SUCCEEDED.
129 if (this.expectSuccess) {
130 do_check_true(true, "Success for " + this.hostname);
131 } else {
132 do_throw("Speculative Connect should have failed for " +
133 this.hostname);
134 }
135 this.transport.close(Cr.NS_BINDING_ABORTED);
136 this.next();
137 }
138 };
139
140 /** test_speculative_connect
141 *
142 * Tests a basic positive case using nsIOService.SpeculativeConnect:
143 * connecting to localhost.
144 */
145 function test_speculative_connect() {
146 serv = new TestServer();
147 var URI = ios.newURI("http://localhost:" + serv.listener.port + "/just/a/test", null, null);
148 ios.QueryInterface(Ci.nsISpeculativeConnect)
149 .speculativeConnect(URI, null);
150 }
151
152 /* Speculative connections should not be allowed for hosts with local IP
153 * addresses (Bug 853423). That list includes:
154 * -- IPv4 RFC1918 and Link Local Addresses.
155 * -- IPv6 Unique and Link Local Addresses.
156 *
157 * Two tests are required:
158 * 1. Verify IP Literals passed to the SpeculativeConnect API.
159 * 2. Verify hostnames that need to be resolved at the socket layer.
160 */
161
162 /** test_hostnames_resolving_to_addresses
163 *
164 * Common test function for resolved hostnames. Takes a list of hosts, a
165 * boolean to determine if the test is expected to succeed or fail, and a
166 * function to call the next test case.
167 */
168 function test_hostnames_resolving_to_addresses(host, expectSuccess, next) {
169 do_print(host);
170 var sts = Cc["@mozilla.org/network/socket-transport-service;1"]
171 .getService(Ci.nsISocketTransportService);
172 do_check_neq(typeof(sts), undefined);
173 var transport = sts.createTransport(null, 0, host, 80, null);
174 do_check_neq(typeof(transport), undefined);
175
176 transport.connectionFlags = Ci.nsISocketTransport.DISABLE_RFC1918;
177 transport.setTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT, 1);
178 transport.setTimeout(Ci.nsISocketTransport.TIMEOUT_READ_WRITE, 1);
179 do_check_eq(1, transport.getTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT));
180
181 var outStream = transport.openOutputStream(Ci.nsITransport.OPEN_UNBUFFERED,0,0);
182 do_check_neq(typeof(outStream), undefined);
183
184 var callback = new TestOutputStreamCallback(transport, host, false,
185 expectSuccess,
186 next);
187 do_check_neq(typeof(callback), undefined);
188
189 // Need to get main thread pointer to ensure nsSocketTransport::AsyncWait
190 // adds callback to nsOutputStreamReadyEvent on main thread, and doesn't
191 // addref off the main thread.
192 var gThreadManager = Cc["@mozilla.org/thread-manager;1"]
193 .getService(Ci.nsIThreadManager);
194 var mainThread = gThreadManager.currentThread;
195
196 try {
197 outStream.QueryInterface(Ci.nsIAsyncOutputStream)
198 .asyncWait(callback, 0, 0, mainThread);
199 } catch (e) {
200 do_throw("asyncWait should not fail!");
201 }
202 }
203
204 /**
205 * test_hostnames_resolving_to_local_addresses
206 *
207 * Creates an nsISocketTransport and simulates a speculative connect request
208 * for a hostname that resolves to a local IP address.
209 * Runs asynchronously; on test success (i.e. failure to connect), the callback
210 * will call this function again until all hostnames in the test list are done.
211 *
212 * Note: This test also uses an IP literal for the hostname. This should be ok,
213 * as the socket layer will ask for the hostname to be resolved anyway, and DNS
214 * code should return a numerical version of the address internally.
215 */
216 function test_hostnames_resolving_to_local_addresses() {
217 if (hostIdx >= localIPLiterals.length) {
218 // No more local IP addresses; move on.
219 next_test();
220 return;
221 }
222 var host = localIPLiterals[hostIdx++];
223 // Test another local IP address when the current one is done.
224 var next = test_hostnames_resolving_to_local_addresses;
225 test_hostnames_resolving_to_addresses(host, false, next);
226 }
227
228 /** test_speculative_connect_with_host_list
229 *
230 * Common test function for resolved proxy hosts. Takes a list of hosts, a
231 * boolean to determine if the test is expected to succeed or fail, and a
232 * function to call the next test case.
233 */
234 function test_proxies(proxyHost, expectSuccess, next) {
235 do_print("Proxy: " + proxyHost);
236 var sts = Cc["@mozilla.org/network/socket-transport-service;1"]
237 .getService(Ci.nsISocketTransportService);
238 do_check_neq(typeof(sts), undefined);
239 var pps = Cc["@mozilla.org/network/protocol-proxy-service;1"]
240 .getService();
241 do_check_neq(typeof(pps), undefined);
242
243 var proxyInfo = pps.newProxyInfo("http", proxyHost, 8080, 0, 1, null);
244 do_check_neq(typeof(proxyInfo), undefined);
245
246 var transport = sts.createTransport(null, 0, "dummyHost", 80, proxyInfo);
247 do_check_neq(typeof(transport), undefined);
248
249 transport.connectionFlags = Ci.nsISocketTransport.DISABLE_RFC1918;
250
251 transport.setTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT, 1);
252 do_check_eq(1, transport.getTimeout(Ci.nsISocketTransport.TIMEOUT_CONNECT));
253 transport.setTimeout(Ci.nsISocketTransport.TIMEOUT_READ_WRITE, 1);
254
255 var outStream = transport.openOutputStream(Ci.nsITransport.OPEN_UNBUFFERED,0,0);
256 do_check_neq(typeof(outStream), undefined);
257
258 var callback = new TestOutputStreamCallback(transport, proxyHost, true,
259 expectSuccess,
260 next);
261 do_check_neq(typeof(callback), undefined);
262
263 // Need to get main thread pointer to ensure nsSocketTransport::AsyncWait
264 // adds callback to nsOutputStreamReadyEvent on main thread, and doesn't
265 // addref off the main thread.
266 var gThreadManager = Cc["@mozilla.org/thread-manager;1"]
267 .getService(Ci.nsIThreadManager);
268 var mainThread = gThreadManager.currentThread;
269
270 try {
271 outStream.QueryInterface(Ci.nsIAsyncOutputStream)
272 .asyncWait(callback, 0, 0, mainThread);
273 } catch (e) {
274 do_throw("asyncWait should not fail!");
275 }
276 }
277
278 /**
279 * test_proxies_with_local_addresses
280 *
281 * Creates an nsISocketTransport and simulates a speculative connect request
282 * for a proxy that resolves to a local IP address.
283 * Runs asynchronously; on test success (i.e. failure to connect), the callback
284 * will call this function again until all proxies in the test list are done.
285 *
286 * Note: This test also uses an IP literal for the proxy. This should be ok,
287 * as the socket layer will ask for the proxy to be resolved anyway, and DNS
288 * code should return a numerical version of the address internally.
289 */
290 function test_proxies_with_local_addresses() {
291 if (hostIdx >= localIPLiterals.length) {
292 // No more local IP addresses; move on.
293 next_test();
294 return;
295 }
296 var host = localIPLiterals[hostIdx++];
297 // Test another local IP address when the current one is done.
298 var next = test_proxies_with_local_addresses;
299 test_proxies(host, false, next);
300 }
301
302 /** next_test
303 *
304 * Calls the next test in testList. Each test is responsible for calling this
305 * function when its test cases are complete.
306 */
307 function next_test() {
308 if (testIdx >= testList.length) {
309 // No more tests; we're done.
310 do_test_finished();
311 return;
312 }
313 do_print("SpeculativeConnect: " + testDescription[testIdx]);
314 hostIdx = 0;
315 // Start next test in list.
316 testList[testIdx++]();
317 }
318
319 /** run_test
320 *
321 * Main entry function for test execution.
322 */
323 function run_test() {
324 ios = Cc["@mozilla.org/network/io-service;1"]
325 .getService(Ci.nsIIOService);
326
327 do_test_pending();
328 next_test();
329 }
330

mercurial