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)
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
17 // test_connectonly_nonhttp tests an http channel with only connect set with a
19 // 1. OnTransportAvailable callback NOT called (checked in step 2)
20 // 2. StopRequest callback called
23 // -1 then initialized with an actual port from the serversocket
26 var socketserver_port
= -1;
28 const CC
= Components
.Constructor
;
29 const ServerSocket
= CC(
30 "@mozilla.org/network/server-socket;1",
34 const BinaryInputStream
= CC(
35 "@mozilla.org/binaryinputstream;1",
36 "nsIBinaryInputStream",
39 const BinaryOutputStream
= CC(
40 "@mozilla.org/binaryoutputstream;1",
41 "nsIBinaryOutputStream",
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
= "";
69 var state
= STATE_NONE
;
70 var transportAvailable
= false;
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);
90 Assert
.equal(accepted
, true, "socket accepted");
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
=> {
116 const bis
= new BinaryInputStream(input
);
117 var data
= bis
.readByteArray(input
.available());
121 if (state
!== STATE_COMPLETED
) {
122 input
.asyncWait(connectHandler
, 0, 0, threadManager
.mainThread
);
128 onOutputStreamReady
: output
=> {
131 QueryInterface
: iid
=> {
133 iid
.equals(Ci
.nsISupports
) ||
134 iid
.equals(Ci
.nsIInputStreamCallback
) ||
135 iid
.equals(Ci
.nsIOutputStreamCallback
)
139 throw Components
.Exception("", Cr
.NS_ERROR_NO_INTERFACE
);
143 function dataAvailable(data
) {
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
);
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
);
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);
188 do_throw("bad state: " + state
);
192 function writeData(output
) {
193 let bos
= new BinaryOutputStream(output
);
196 case STATE_WRITE_CONNECTION_ESTABLISHED
:
197 bos
.write(CONNECT_RESPONSE_STRING
, CONNECT_RESPONSE_STRING
.length
);
198 state
= STATE_CHECK_WRITE
;
200 case STATE_CHECK_READ
:
201 bos
.write(CHECK_READ_STRING
, CHECK_READ_STRING
.length
);
202 state
= STATE_CHECK_READ_WROTE
;
204 case STATE_CHECK_WRITE
:
205 bos
.write(CHECK_WRITE_STRING
, CHECK_WRITE_STRING
.length
);
206 state
= STATE_CHECK_WRITE_READ
;
209 do_throw("bad state: " + state
);
213 function makeChan(url
) {
215 url
= "https://localhost:" + socketserver_port
+ "/";
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({
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);
237 function socketAccepted(socket1
, transport
) {
240 // copied from httpd.js
241 const SEGMENT_SIZE
= 8192;
242 const SEGMENT_COUNT
= 1024;
246 state
= STATE_READ_CONNECT_REQUEST
;
252 acceptedSocket
= 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
);
264 transport
.close(Cr
.NS_BINDING_ABORTED
);
269 function stopListening() {
270 if (tests
&& tests
.length
!== 0 && do_throw
) {
271 do_throw("should never stop");
275 function createProxy() {
277 threadManager
= Cc
["@mozilla.org/thread-manager;1"].getService();
279 socket
= new ServerSocket(-1, true, 1);
280 socketserver_port
= socket
.port
;
283 onSocketAccepted
: socketAccepted
,
284 onStopListening
: stopListening
,
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
);
304 function test_connectonly_noproxy() {
306 var chan
= makeChan();
307 chan
.asyncOpen(listener
);
312 function test_connectonly_nonhttp() {
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
);
326 function nextTest() {
327 transportAvailable
= false;
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() {
356 registerCleanupFunction(clearPrefs
);