Bug 1931425 - Limit how often moz-label's #setStyles runs r=reusable-components-revie...
[gecko.git] / netwerk / test / unit / test_proxyconnect.js
blob00cf0c4715251639c3aeeb3282d0f2b060bc2b67
1 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 // test_connectonly tests happy path of proxy connect
7 // 1. CONNECT to localhost:socketserver_port
8 // 2. Write 200 Connection established
9 // 3. Write data to the tunnel (and read server-side)
10 // 4. Read data from the tunnel (and write server-side)
11 // 5. done
12 // test_connectonly_noproxy tests an http channel with only connect set but
13 // no proxy configured.
14 // 1. OnTransportAvailable callback NOT called (checked in step 2)
15 // 2. StopRequest callback called
16 // 3. done
17 // test_connectonly_nonhttp tests an http channel with only connect set with a
18 // non-http proxy.
19 // 1. OnTransportAvailable callback NOT called (checked in step 2)
20 // 2. StopRequest callback called
21 // 3. done
23 // -1 then initialized with an actual port from the serversocket
24 "use strict";
26 var socketserver_port = -1;
28 const CC = Components.Constructor;
29 const ServerSocket = CC(
30 "@mozilla.org/network/server-socket;1",
31 "nsIServerSocket",
32 "init"
34 const BinaryInputStream = CC(
35 "@mozilla.org/binaryinputstream;1",
36 "nsIBinaryInputStream",
37 "setInputStream"
39 const BinaryOutputStream = CC(
40 "@mozilla.org/binaryoutputstream;1",
41 "nsIBinaryOutputStream",
42 "setOutputStream"
45 const STATE_NONE = 0;
46 const STATE_READ_CONNECT_REQUEST = 1;
47 const STATE_WRITE_CONNECTION_ESTABLISHED = 2;
48 const STATE_CHECK_WRITE = 3; // write to the tunnel
49 const STATE_CHECK_WRITE_READ = 4; // wrote to the tunnel, check connection data
50 const STATE_CHECK_READ = 5; // read from the tunnel
51 const STATE_CHECK_READ_WROTE = 6; // wrote to connection, check tunnel data
52 const STATE_COMPLETED = 100;
54 const CONNECT_RESPONSE_STRING = "HTTP/1.1 200 Connection established\r\n\r\n";
55 const CHECK_WRITE_STRING = "hello";
56 const CHECK_READ_STRING = "world";
57 const ALPN = "webrtc";
59 var connectRequest = "";
60 var checkWriteData = "";
61 var checkReadData = "";
63 var threadManager;
64 var socket;
65 var streamIn;
66 var streamOut;
67 var accepted = false;
68 var acceptedSocket;
69 var state = STATE_NONE;
70 var transportAvailable = false;
71 var proxiedChannel;
72 var listener = {
73 expectedCode: -1, // uninitialized
75 onStartRequest: function test_onStartR() {},
77 onDataAvailable: function test_ODA() {
78 do_throw("Should not get any data!");
81 onStopRequest: function test_onStopR(request, status) {
82 if (state === STATE_COMPLETED) {
83 Assert.equal(transportAvailable, false, "transport available not called");
84 Assert.equal(status, 0x80004005, "error code matches");
85 Assert.equal(proxiedChannel.httpProxyConnectResponseCode, 200);
86 nextTest();
87 return;
90 Assert.equal(accepted, true, "socket accepted");
91 accepted = false;
95 var upgradeListener = {
96 onTransportAvailable: (transport, socketIn, socketOut) => {
97 if (!transport || !socketIn || !socketOut) {
98 do_throw("on transport available failed");
101 if (state !== STATE_CHECK_WRITE) {
102 do_throw("bad state");
105 transportAvailable = true;
107 socketIn.asyncWait(connectHandler, 0, 0, threadManager.mainThread);
108 socketOut.asyncWait(connectHandler, 0, 0, threadManager.mainThread);
110 QueryInterface: ChromeUtils.generateQI(["nsIHttpUpgradeListener"]),
113 var connectHandler = {
114 onInputStreamReady: input => {
115 try {
116 const bis = new BinaryInputStream(input);
117 var data = bis.readByteArray(input.available());
119 dataAvailable(data);
121 if (state !== STATE_COMPLETED) {
122 input.asyncWait(connectHandler, 0, 0, threadManager.mainThread);
124 } catch (e) {
125 do_throw(e);
128 onOutputStreamReady: output => {
129 writeData(output);
131 QueryInterface: iid => {
132 if (
133 iid.equals(Ci.nsISupports) ||
134 iid.equals(Ci.nsIInputStreamCallback) ||
135 iid.equals(Ci.nsIOutputStreamCallback)
137 return this;
139 throw Components.Exception("", Cr.NS_ERROR_NO_INTERFACE);
143 function dataAvailable(data) {
144 switch (state) {
145 case STATE_READ_CONNECT_REQUEST:
146 connectRequest += String.fromCharCode.apply(String, data);
147 const headerEnding = connectRequest.indexOf("\r\n\r\n");
148 const alpnHeaderIndex = connectRequest.indexOf(`ALPN: ${ALPN}`);
150 if (headerEnding != -1) {
151 const requestLine = `CONNECT localhost:${socketserver_port} HTTP/1.1`;
152 Assert.equal(connectRequest.indexOf(requestLine), 0, "connect request");
153 Assert.equal(headerEnding, connectRequest.length - 4, "req head only");
154 Assert.notEqual(alpnHeaderIndex, -1, "alpn header found");
156 state = STATE_WRITE_CONNECTION_ESTABLISHED;
157 streamOut.asyncWait(connectHandler, 0, 0, threadManager.mainThread);
160 break;
161 case STATE_CHECK_WRITE_READ:
162 checkWriteData += String.fromCharCode.apply(String, data);
164 if (checkWriteData.length >= CHECK_WRITE_STRING.length) {
165 Assert.equal(checkWriteData, CHECK_WRITE_STRING, "correct write data");
167 state = STATE_CHECK_READ;
168 streamOut.asyncWait(connectHandler, 0, 0, threadManager.mainThread);
171 break;
172 case STATE_CHECK_READ_WROTE:
173 checkReadData += String.fromCharCode.apply(String, data);
175 if (checkReadData.length >= CHECK_READ_STRING.length) {
176 Assert.equal(checkReadData, CHECK_READ_STRING, "correct read data");
178 state = STATE_COMPLETED;
180 streamIn.asyncWait(null, 0, 0, null);
181 acceptedSocket.close(0);
183 nextTest();
186 break;
187 default:
188 do_throw("bad state: " + state);
192 function writeData(output) {
193 let bos = new BinaryOutputStream(output);
195 switch (state) {
196 case STATE_WRITE_CONNECTION_ESTABLISHED:
197 bos.write(CONNECT_RESPONSE_STRING, CONNECT_RESPONSE_STRING.length);
198 state = STATE_CHECK_WRITE;
199 break;
200 case STATE_CHECK_READ:
201 bos.write(CHECK_READ_STRING, CHECK_READ_STRING.length);
202 state = STATE_CHECK_READ_WROTE;
203 break;
204 case STATE_CHECK_WRITE:
205 bos.write(CHECK_WRITE_STRING, CHECK_WRITE_STRING.length);
206 state = STATE_CHECK_WRITE_READ;
207 break;
208 default:
209 do_throw("bad state: " + state);
213 function makeChan(url) {
214 if (!url) {
215 url = "https://localhost:" + socketserver_port + "/";
218 var flags =
219 Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL |
220 Ci.nsILoadInfo.SEC_DONT_FOLLOW_REDIRECTS |
221 Ci.nsILoadInfo.SEC_COOKIES_OMIT;
223 var chan = NetUtil.newChannel({
224 uri: url,
225 loadUsingSystemPrincipal: true,
226 securityFlags: flags,
228 chan = chan.QueryInterface(Ci.nsIHttpChannel);
230 var internal = chan.QueryInterface(Ci.nsIHttpChannelInternal);
231 internal.HTTPUpgrade(ALPN, upgradeListener);
232 internal.setConnectOnly(false);
234 return chan;
237 function socketAccepted(socket1, transport) {
238 accepted = true;
240 // copied from httpd.js
241 const SEGMENT_SIZE = 8192;
242 const SEGMENT_COUNT = 1024;
244 switch (state) {
245 case STATE_NONE:
246 state = STATE_READ_CONNECT_REQUEST;
247 break;
248 default:
249 return;
252 acceptedSocket = transport;
254 try {
255 streamIn = transport
256 .openInputStream(0, SEGMENT_SIZE, SEGMENT_COUNT)
257 .QueryInterface(Ci.nsIAsyncInputStream);
258 streamOut = transport
259 .openOutputStream(0, 0, 0)
260 .QueryInterface(Ci.nsIAsyncOutputStream);
262 streamIn.asyncWait(connectHandler, 0, 0, threadManager.mainThread);
263 } catch (e) {
264 transport.close(Cr.NS_BINDING_ABORTED);
265 do_throw(e);
269 function stopListening() {
270 if (tests && tests.length !== 0 && do_throw) {
271 do_throw("should never stop");
275 function createProxy() {
276 try {
277 threadManager = Cc["@mozilla.org/thread-manager;1"].getService();
279 socket = new ServerSocket(-1, true, 1);
280 socketserver_port = socket.port;
282 socket.asyncListen({
283 onSocketAccepted: socketAccepted,
284 onStopListening: stopListening,
286 } catch (e) {
287 do_throw(e);
291 function test_connectonly() {
292 Services.prefs.setCharPref("network.proxy.ssl", "localhost");
293 Services.prefs.setIntPref("network.proxy.ssl_port", socketserver_port);
294 Services.prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true);
295 Services.prefs.setIntPref("network.proxy.type", 1);
297 var chan = makeChan();
298 proxiedChannel = chan.QueryInterface(Ci.nsIProxiedChannel);
299 chan.asyncOpen(listener);
301 do_test_pending();
304 function test_connectonly_noproxy() {
305 clearPrefs();
306 var chan = makeChan();
307 chan.asyncOpen(listener);
309 do_test_pending();
312 function test_connectonly_nonhttp() {
313 clearPrefs();
315 Services.prefs.setCharPref("network.proxy.socks", "localhost");
316 Services.prefs.setIntPref("network.proxy.socks_port", socketserver_port);
317 Services.prefs.setBoolPref("network.proxy.allow_hijacking_localhost", true);
318 Services.prefs.setIntPref("network.proxy.type", 1);
320 var chan = makeChan();
321 chan.asyncOpen(listener);
323 do_test_pending();
326 function nextTest() {
327 transportAvailable = false;
329 if (!tests.length) {
330 do_test_finished();
331 return;
334 tests.shift()();
335 do_test_finished();
338 var tests = [
339 test_connectonly,
340 test_connectonly_noproxy,
341 test_connectonly_nonhttp,
344 function clearPrefs() {
345 Services.prefs.clearUserPref("network.proxy.ssl");
346 Services.prefs.clearUserPref("network.proxy.ssl_port");
347 Services.prefs.clearUserPref("network.proxy.socks");
348 Services.prefs.clearUserPref("network.proxy.socks_port");
349 Services.prefs.clearUserPref("network.proxy.allow_hijacking_localhost");
350 Services.prefs.clearUserPref("network.proxy.type");
353 function run_test() {
354 createProxy();
356 registerCleanupFunction(clearPrefs);
358 nextTest();
359 do_test_pending();