Bug 1845311 - [Part 2] Use ChromeUtils.defineLazyGetter in more places r=arai,webcomp...
[gecko.git] / netwerk / test / unit / test_tls_flags.js
blob876cd0ccea0dd2ce3a96d8237919d0ff15a30b16
1 // -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
2 // Any copyright is dedicated to the Public Domain.
3 // http://creativecommons.org/publicdomain/zero/1.0/
5 "use strict";
7 // a fork of test_be_conservative
9 // Tests that nsIHttpChannelInternal.tlsFlags can be used to set the
10 // client max version level. Flags can also be used to set the
11 // level of intolerance rollback and to test out an experimental 1.3
12 // hello, though they are not tested here.
14 // Get a profile directory and ensure PSM initializes NSS.
15 do_get_profile();
16 Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
18 class InputStreamCallback {
19   constructor(output) {
20     this.output = output;
21     this.stopped = false;
22   }
24   onInputStreamReady(stream) {
25     info("input stream ready");
26     if (this.stopped) {
27       info("input stream callback stopped - bailing");
28       return;
29     }
30     let available = 0;
31     try {
32       available = stream.available();
33     } catch (e) {
34       // onInputStreamReady may fire when the stream has been closed.
35       equal(
36         e.result,
37         Cr.NS_BASE_STREAM_CLOSED,
38         "error should be NS_BASE_STREAM_CLOSED"
39       );
40     }
41     if (available > 0) {
42       let request = NetUtil.readInputStreamToString(stream, available, {
43         charset: "utf8",
44       });
45       ok(
46         request.startsWith("GET / HTTP/1.1\r\n"),
47         "Should get a simple GET / HTTP/1.1 request"
48       );
49       let response =
50         "HTTP/1.1 200 OK\r\n" +
51         "Content-Length: 2\r\n" +
52         "Content-Type: text/plain\r\n" +
53         "\r\nOK";
54       let written = this.output.write(response, response.length);
55       equal(
56         written,
57         response.length,
58         "should have been able to write entire response"
59       );
60     }
61     this.output.close();
62     info("done with input stream ready");
63   }
65   stop() {
66     this.stopped = true;
67     this.output.close();
68   }
71 class TLSServerSecurityObserver {
72   constructor(input, output, expectedVersion) {
73     this.input = input;
74     this.output = output;
75     this.expectedVersion = expectedVersion;
76     this.callbacks = [];
77     this.stopped = false;
78   }
80   onHandshakeDone(socket, status) {
81     info("TLS handshake done");
82     info(`TLS version used: ${status.tlsVersionUsed}`);
83     info(this.expectedVersion);
84     equal(
85       status.tlsVersionUsed,
86       this.expectedVersion,
87       "expected version check"
88     );
89     if (this.stopped) {
90       info("handshake done callback stopped - bailing");
91       return;
92     }
94     let callback = new InputStreamCallback(this.output);
95     this.callbacks.push(callback);
96     this.input.asyncWait(callback, 0, 0, Services.tm.currentThread);
97   }
99   stop() {
100     this.stopped = true;
101     this.input.close();
102     this.output.close();
103     this.callbacks.forEach(callback => {
104       callback.stop();
105     });
106   }
109 function startServer(
110   cert,
111   minServerVersion,
112   maxServerVersion,
113   expectedVersion
114 ) {
115   let tlsServer = Cc["@mozilla.org/network/tls-server-socket;1"].createInstance(
116     Ci.nsITLSServerSocket
117   );
118   tlsServer.init(-1, true, -1);
119   tlsServer.serverCert = cert;
120   tlsServer.setVersionRange(minServerVersion, maxServerVersion);
121   tlsServer.setSessionTickets(false);
123   let listener = {
124     securityObservers: [],
126     onSocketAccepted(socket, transport) {
127       info("accepted TLS client connection");
128       let connectionInfo = transport.securityCallbacks.getInterface(
129         Ci.nsITLSServerConnectionInfo
130       );
131       let input = transport.openInputStream(0, 0, 0);
132       let output = transport.openOutputStream(0, 0, 0);
133       let securityObserver = new TLSServerSecurityObserver(
134         input,
135         output,
136         expectedVersion
137       );
138       this.securityObservers.push(securityObserver);
139       connectionInfo.setSecurityObserver(securityObserver);
140     },
142     // For some reason we get input stream callback events after we've stopped
143     // listening, so this ensures we just drop those events.
144     onStopListening() {
145       info("onStopListening");
146       this.securityObservers.forEach(observer => {
147         observer.stop();
148       });
149     },
150   };
151   tlsServer.asyncListen(listener);
152   return tlsServer;
155 const hostname = "example.com";
157 function storeCertOverride(port, cert) {
158   let certOverrideService = Cc[
159     "@mozilla.org/security/certoverride;1"
160   ].getService(Ci.nsICertOverrideService);
161   certOverrideService.rememberValidityOverride(hostname, port, {}, cert, true);
164 function startClient(port, tlsFlags, expectSuccess) {
165   let req = new XMLHttpRequest();
166   req.open("GET", `https://${hostname}:${port}`);
167   let internalChannel = req.channel.QueryInterface(Ci.nsIHttpChannelInternal);
168   internalChannel.tlsFlags = tlsFlags;
169   return new Promise((resolve, reject) => {
170     req.onload = () => {
171       ok(
172         expectSuccess,
173         `should ${expectSuccess ? "" : "not "}have gotten load event`
174       );
175       equal(req.responseText, "OK", "response text should be 'OK'");
176       resolve();
177     };
178     req.onerror = () => {
179       ok(
180         !expectSuccess,
181         `should ${!expectSuccess ? "" : "not "}have gotten an error`
182       );
183       resolve();
184     };
186     req.send();
187   });
190 add_task(async function () {
191   Services.prefs.setIntPref("security.tls.version.max", 4);
192   Services.prefs.setCharPref("network.dns.localDomains", hostname);
193   let cert = getTestServerCertificate();
195   // server that accepts 1.1->1.3 and a client max 1.3. expect 1.3
196   info("TEST 1");
197   let server = startServer(
198     cert,
199     Ci.nsITLSClientStatus.TLS_VERSION_1_1,
200     Ci.nsITLSClientStatus.TLS_VERSION_1_3,
201     Ci.nsITLSClientStatus.TLS_VERSION_1_3
202   );
203   storeCertOverride(server.port, cert);
204   await startClient(server.port, 4, true /*should succeed*/);
205   server.close();
207   // server that accepts 1.1->1.3 and a client max 1.1. expect 1.1
208   info("TEST 2");
209   server = startServer(
210     cert,
211     Ci.nsITLSClientStatus.TLS_VERSION_1_1,
212     Ci.nsITLSClientStatus.TLS_VERSION_1_3,
213     Ci.nsITLSClientStatus.TLS_VERSION_1_1
214   );
215   storeCertOverride(server.port, cert);
216   await startClient(server.port, 2, true);
217   server.close();
219   // server that accepts 1.2->1.2 and a client max 1.3. expect 1.2
220   info("TEST 3");
221   server = startServer(
222     cert,
223     Ci.nsITLSClientStatus.TLS_VERSION_1_2,
224     Ci.nsITLSClientStatus.TLS_VERSION_1_2,
225     Ci.nsITLSClientStatus.TLS_VERSION_1_2
226   );
227   storeCertOverride(server.port, cert);
228   await startClient(server.port, 4, true);
229   server.close();
231   // server that accepts 1.2->1.2 and a client max 1.1. expect fail
232   info("TEST 4");
233   server = startServer(
234     cert,
235     Ci.nsITLSClientStatus.TLS_VERSION_1_2,
236     Ci.nsITLSClientStatus.TLS_VERSION_1_2,
237     0
238   );
239   storeCertOverride(server.port, cert);
240   await startClient(server.port, 2, false);
242   server.close();
245 registerCleanupFunction(function () {
246   Services.prefs.clearUserPref("security.tls.version.max");
247   Services.prefs.clearUserPref("network.dns.localDomains");