1 /* Any copyright is dedicated to the Public Domain.
2 * http://creativecommons.org/publicdomain/zero/1.0/
7 const { HttpServer } = ChromeUtils.importESModule(
8 "resource://testing-common/httpd.sys.mjs"
11 var httpserver = new HttpServer();
13 const PORT = httpserver.identity.primaryPort;
15 function make_channel(url) {
16 return NetUtil.newChannel({
18 loadUsingSystemPrincipal: true,
19 }).QueryInterface(Ci.nsIHttpChannel);
22 let gResponseBody = "blahblah";
25 function test_handler(metadata, response) {
26 response.setHeader("Content-Type", "text/plain");
27 response.setHeader("Cache-Control", "no-cache");
28 response.setHeader("ETag", "test-etag1");
32 etag = metadata.getHeader("If-None-Match");
37 if (etag == "test-etag1") {
38 response.setStatusLine(metadata.httpVersion, 304, "Not Modified");
41 response.setStatusLine(metadata.httpVersion, 200, "OK");
42 response.bodyOutputStream.write(gResponseBody, gResponseBody.length);
47 function cached_handler(metadata, response) {
48 response.setHeader("Content-Type", "text/plain");
49 response.setHeader("Cache-Control", "max-age=3600");
50 response.setHeader("ETag", "test-etag1");
52 response.setStatusLine(metadata.httpVersion, 200, "OK");
53 response.bodyOutputStream.write(gResponseBody, gResponseBody.length);
58 let gResponseCounter = 0;
60 function checkContent(request, buffer, context, isFromCache) {
61 Assert.equal(buffer, gResponseBody);
64 request.QueryInterface(Ci.nsICacheInfoChannel).isRacing() +
78 // In this test, we manually use |TriggerNetwork| to prove we could send
79 // net and cache reqeust simultaneously. Therefore we should disable
80 // racing in the HttpChannel first.
81 Services.prefs.setBoolPref("network.http.rcwn.enabled", false);
82 httpserver.registerPathHandler("/rcwn", test_handler);
83 httpserver.registerPathHandler("/rcwn_cached", cached_handler);
88 let testGenerator = testSteps();
89 function* testSteps() {
91 * In this test, we have a relatively low timeout of 200ms and an assertion that
92 * the timer works properly by checking that the time was greater than 200ms.
93 * With a timer precision of 100ms (for example) we will clamp downwards to 200
94 * and cause the assertion to fail. To resolve this, we hardcode a precision of
97 Services.prefs.setBoolPref("privacy.reduceTimerPrecision", true);
98 Services.prefs.setIntPref(
99 "privacy.resistFingerprinting.reduceTimerPrecision.microseconds",
103 registerCleanupFunction(function () {
104 Services.prefs.clearUserPref("privacy.reduceTimerPrecision");
105 Services.prefs.clearUserPref(
106 "privacy.resistFingerprinting.reduceTimerPrecision.microseconds"
110 // Initial request. Stores the response in the cache.
111 let channel = make_channel("http://localhost:" + PORT + "/rcwn");
112 channel.asyncOpen(new ChannelListener(checkContent, null));
114 equal(gResponseCounter, 1);
115 equal(g200Counter, 1, "check number of 200 responses");
116 equal(g304Counter, 0, "check number of 304 responses");
118 // Checks that response is returned from the cache, after a 304 response.
119 channel = make_channel("http://localhost:" + PORT + "/rcwn");
120 channel.asyncOpen(new ChannelListener(checkContent, null));
122 equal(gResponseCounter, 2);
123 equal(g200Counter, 1, "check number of 200 responses");
124 equal(g304Counter, 1, "check number of 304 responses");
126 // Checks that delaying the response from the cache works.
127 channel = make_channel("http://localhost:" + PORT + "/rcwn");
129 .QueryInterface(Ci.nsIRaceCacheWithNetwork)
130 .test_delayCacheEntryOpeningBy(200);
131 let startTime = Date.now();
132 channel.asyncOpen(new ChannelListener(checkContent, null));
135 Date.now() - startTime,
137 "Check that timer works properly"
139 equal(gResponseCounter, 3);
140 equal(g200Counter, 1, "check number of 200 responses");
141 equal(g304Counter, 2, "check number of 304 responses");
143 // Checks that we can trigger the cache open immediately, even if the cache delay is set very high.
144 channel = make_channel("http://localhost:" + PORT + "/rcwn");
146 .QueryInterface(Ci.nsIRaceCacheWithNetwork)
147 .test_delayCacheEntryOpeningBy(100000);
148 channel.asyncOpen(new ChannelListener(checkContent, null));
149 do_timeout(50, function () {
151 .QueryInterface(Ci.nsIRaceCacheWithNetwork)
152 .test_triggerDelayedOpenCacheEntry();
155 equal(gResponseCounter, 4);
156 equal(g200Counter, 1, "check number of 200 responses");
157 equal(g304Counter, 3, "check number of 304 responses");
159 // Sets a high delay for the cache fetch, and triggers the network activity.
160 channel = make_channel("http://localhost:" + PORT + "/rcwn");
162 .QueryInterface(Ci.nsIRaceCacheWithNetwork)
163 .test_delayCacheEntryOpeningBy(100000);
164 channel.QueryInterface(Ci.nsIRaceCacheWithNetwork).test_triggerNetwork(50);
165 channel.asyncOpen(new ChannelListener(checkContent, null));
166 // Trigger network after 50 ms.
168 equal(gResponseCounter, 5);
169 equal(g200Counter, 2, "check number of 200 responses");
170 equal(g304Counter, 3, "check number of 304 responses");
172 // Sets a high delay for the cache fetch, and triggers the network activity.
173 // While the network response is produced, we trigger the cache fetch.
174 // Because the network response was the first, a non-conditional request is sent.
175 channel = make_channel("http://localhost:" + PORT + "/rcwn");
177 .QueryInterface(Ci.nsIRaceCacheWithNetwork)
178 .test_delayCacheEntryOpeningBy(100000);
179 channel.QueryInterface(Ci.nsIRaceCacheWithNetwork).test_triggerNetwork(50);
180 channel.asyncOpen(new ChannelListener(checkContent, null));
182 equal(gResponseCounter, 6);
183 equal(g200Counter, 3, "check number of 200 responses");
184 equal(g304Counter, 3, "check number of 304 responses");
186 // Triggers cache open before triggering network.
187 channel = make_channel("http://localhost:" + PORT + "/rcwn");
189 .QueryInterface(Ci.nsIRaceCacheWithNetwork)
190 .test_delayCacheEntryOpeningBy(100000);
191 channel.QueryInterface(Ci.nsIRaceCacheWithNetwork).test_triggerNetwork(100);
192 channel.asyncOpen(new ChannelListener(checkContent, null));
194 .QueryInterface(Ci.nsIRaceCacheWithNetwork)
195 .test_triggerDelayedOpenCacheEntry();
197 equal(gResponseCounter, 7);
198 equal(g200Counter, 3, "check number of 200 responses");
199 equal(g304Counter, 4, "check number of 304 responses");
201 // Load the cached handler so we don't need to revalidate
202 channel = make_channel("http://localhost:" + PORT + "/rcwn_cached");
203 channel.asyncOpen(new ChannelListener(checkContent, null));
205 equal(gResponseCounter, 8);
206 equal(g200Counter, 4, "check number of 200 responses");
207 equal(g304Counter, 4, "check number of 304 responses");
209 // Make sure response is loaded from the cache, not the network
210 channel = make_channel("http://localhost:" + PORT + "/rcwn_cached");
211 channel.asyncOpen(new ChannelListener(checkContent, null));
213 equal(gResponseCounter, 9);
214 equal(g200Counter, 4, "check number of 200 responses");
215 equal(g304Counter, 4, "check number of 304 responses");
217 // Cache times out, so we trigger the network
219 channel = make_channel("http://localhost:" + PORT + "/rcwn_cached");
221 .QueryInterface(Ci.nsIRaceCacheWithNetwork)
222 .test_delayCacheEntryOpeningBy(100000);
223 // trigger network after 50 ms
224 channel.QueryInterface(Ci.nsIRaceCacheWithNetwork).test_triggerNetwork(50);
225 channel.asyncOpen(new ChannelListener(checkContent, null));
227 equal(gResponseCounter, 10);
228 equal(gIsFromCache, 0, "should be from the network");
229 equal(g200Counter, 5, "check number of 200 responses");
230 equal(g304Counter, 4, "check number of 304 responses");
232 // Cache callback comes back right after network is triggered.
233 channel = make_channel("http://localhost:" + PORT + "/rcwn_cached");
235 .QueryInterface(Ci.nsIRaceCacheWithNetwork)
236 .test_delayCacheEntryOpeningBy(55);
237 channel.QueryInterface(Ci.nsIRaceCacheWithNetwork).test_triggerNetwork(50);
238 channel.asyncOpen(new ChannelListener(checkContent, null));
240 equal(gResponseCounter, 11);
241 info("IsFromCache: " + gIsFromCache + "\n");
242 info("Number of 200 responses: " + g200Counter + "\n");
243 equal(g304Counter, 4, "check number of 304 responses");
245 // Set an increasingly high timeout to trigger opening the cache entry
246 // This way we ensure that some of the entries we will get from the network,
247 // and some we will get from the cache.
249 for (var i = 0; i < 50; i++) {
250 channel = make_channel("http://localhost:" + PORT + "/rcwn_cached");
252 .QueryInterface(Ci.nsIRaceCacheWithNetwork)
253 .test_delayCacheEntryOpeningBy(i * 100);
254 channel.QueryInterface(Ci.nsIRaceCacheWithNetwork).test_triggerNetwork(10);
255 channel.asyncOpen(new ChannelListener(checkContent, null));
256 // This may be racy. The delay was chosen because the distribution of net-cache
257 // results was around 25-25 on my machine.
261 greater(gIsFromCache, 0, "Some of the responses should be from the cache");
262 less(gIsFromCache, 50, "Some of the responses should be from the net");
264 httpserver.stop(do_test_finished);