summaryrefslogtreecommitdiffstats
path: root/netwerk/test/unit/test_synthesized_response.js
diff options
context:
space:
mode:
Diffstat (limited to 'netwerk/test/unit/test_synthesized_response.js')
-rw-r--r--netwerk/test/unit/test_synthesized_response.js294
1 files changed, 294 insertions, 0 deletions
diff --git a/netwerk/test/unit/test_synthesized_response.js b/netwerk/test/unit/test_synthesized_response.js
new file mode 100644
index 0000000000..91a491388e
--- /dev/null
+++ b/netwerk/test/unit/test_synthesized_response.js
@@ -0,0 +1,294 @@
+"use strict";
+
+const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
+
+XPCOMUtils.defineLazyGetter(this, "URL", function() {
+ return "http://localhost:" + httpServer.identity.primaryPort;
+});
+
+var httpServer = null;
+
+function make_uri(url) {
+ var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
+ return ios.newURI(url);
+}
+
+function isParentProcess() {
+ let appInfo = Cc["@mozilla.org/xre/app-info;1"];
+ return (
+ !appInfo ||
+ appInfo.getService(Ci.nsIXULRuntime).processType ==
+ Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT
+ );
+}
+
+if (isParentProcess()) {
+ // ensure the cache service is prepped when running the test
+ // We only do this in the main process, as the cache storage service leaks
+ // when instantiated in the content process.
+ Cc["@mozilla.org/netwerk/cache-storage-service;1"].getService(
+ Ci.nsICacheStorageService
+ );
+}
+
+var gotOnProgress;
+var gotOnStatus;
+
+function make_channel(url, body, cb) {
+ gotOnProgress = false;
+ gotOnStatus = false;
+ var chan = NetUtil.newChannel({
+ uri: url,
+ loadUsingSystemPrincipal: true,
+ }).QueryInterface(Ci.nsIHttpChannel);
+ chan.notificationCallbacks = {
+ numChecks: 0,
+ QueryInterface: ChromeUtils.generateQI([
+ "nsINetworkInterceptController",
+ "nsIInterfaceRequestor",
+ "nsIProgressEventSink",
+ ]),
+ getInterface(iid) {
+ return this.QueryInterface(iid);
+ },
+ onProgress(request, progress, progressMax) {
+ gotOnProgress = true;
+ },
+ onStatus(request, status, statusArg) {
+ gotOnStatus = true;
+ },
+ shouldPrepareForIntercept() {
+ Assert.equal(this.numChecks, 0);
+ this.numChecks++;
+ return true;
+ },
+ channelIntercepted(channel) {
+ channel.QueryInterface(Ci.nsIInterceptedChannel);
+ if (body) {
+ var synthesized = Cc[
+ "@mozilla.org/io/string-input-stream;1"
+ ].createInstance(Ci.nsIStringInputStream);
+ synthesized.data = body;
+
+ channel.startSynthesizedResponse(synthesized, null, null, "", false);
+ channel.finishSynthesizedResponse();
+ }
+ if (cb) {
+ cb(channel);
+ }
+ return {
+ dispatch() {},
+ };
+ },
+ };
+ return chan;
+}
+
+const REMOTE_BODY = "http handler body";
+const NON_REMOTE_BODY = "synthesized body";
+const NON_REMOTE_BODY_2 = "synthesized body #2";
+
+function bodyHandler(metadata, response) {
+ response.setHeader("Content-Type", "text/plain");
+ response.write(REMOTE_BODY);
+}
+
+function run_test() {
+ httpServer = new HttpServer();
+ httpServer.registerPathHandler("/body", bodyHandler);
+ httpServer.start(-1);
+
+ run_next_test();
+}
+
+function handle_synthesized_response(request, buffer) {
+ Assert.equal(buffer, NON_REMOTE_BODY);
+ Assert.ok(gotOnStatus);
+ Assert.ok(gotOnProgress);
+ run_next_test();
+}
+
+function handle_synthesized_response_2(request, buffer) {
+ Assert.equal(buffer, NON_REMOTE_BODY_2);
+ Assert.ok(gotOnStatus);
+ Assert.ok(gotOnProgress);
+ run_next_test();
+}
+
+function handle_remote_response(request, buffer) {
+ Assert.equal(buffer, REMOTE_BODY);
+ Assert.ok(gotOnStatus);
+ Assert.ok(gotOnProgress);
+ run_next_test();
+}
+
+// hit the network instead of synthesizing
+add_test(function() {
+ var chan = make_channel(URL + "/body", null, function(chan) {
+ chan.resetInterception();
+ });
+ chan.asyncOpen(new ChannelListener(handle_remote_response, null));
+});
+
+// synthesize a response
+add_test(function() {
+ var chan = make_channel(URL + "/body", NON_REMOTE_BODY);
+ chan.asyncOpen(
+ new ChannelListener(handle_synthesized_response, null, CL_ALLOW_UNKNOWN_CL)
+ );
+});
+
+// hit the network instead of synthesizing, to test that no previous synthesized
+// cache entry is used.
+add_test(function() {
+ var chan = make_channel(URL + "/body", null, function(chan) {
+ chan.resetInterception();
+ });
+ chan.asyncOpen(new ChannelListener(handle_remote_response, null));
+});
+
+// synthesize a different response to ensure no previous response is cached
+add_test(function() {
+ var chan = make_channel(URL + "/body", NON_REMOTE_BODY_2);
+ chan.asyncOpen(
+ new ChannelListener(
+ handle_synthesized_response_2,
+ null,
+ CL_ALLOW_UNKNOWN_CL
+ )
+ );
+});
+
+// ensure that the channel waits for a decision and synthesizes headers correctly
+add_test(function() {
+ var chan = make_channel(URL + "/body", null, function(channel) {
+ do_timeout(100, function() {
+ var synthesized = Cc[
+ "@mozilla.org/io/string-input-stream;1"
+ ].createInstance(Ci.nsIStringInputStream);
+ synthesized.data = NON_REMOTE_BODY;
+ channel.synthesizeHeader("Content-Length", NON_REMOTE_BODY.length);
+ channel.startSynthesizedResponse(synthesized, null, null, "", false);
+ channel.finishSynthesizedResponse();
+ });
+ });
+ chan.asyncOpen(new ChannelListener(handle_synthesized_response, null));
+});
+
+// ensure that the channel waits for a decision
+add_test(function() {
+ var chan = make_channel(URL + "/body", null, function(chan) {
+ do_timeout(100, function() {
+ chan.resetInterception();
+ });
+ });
+ chan.asyncOpen(new ChannelListener(handle_remote_response, null));
+});
+
+// ensure that the intercepted channel supports suspend/resume
+add_test(function() {
+ var chan = make_channel(URL + "/body", null, function(intercepted) {
+ var synthesized = Cc[
+ "@mozilla.org/io/string-input-stream;1"
+ ].createInstance(Ci.nsIStringInputStream);
+ synthesized.data = NON_REMOTE_BODY;
+
+ // set the content-type to ensure that the stream converter doesn't hold up notifications
+ // and cause the test to fail
+ intercepted.synthesizeHeader("Content-Type", "text/plain");
+ intercepted.startSynthesizedResponse(synthesized, null, null, "", false);
+ intercepted.finishSynthesizedResponse();
+ });
+ chan.asyncOpen(
+ new ChannelListener(
+ handle_synthesized_response,
+ null,
+ CL_ALLOW_UNKNOWN_CL | CL_SUSPEND | CL_EXPECT_3S_DELAY
+ )
+ );
+});
+
+// ensure that the intercepted channel can be cancelled
+add_test(function() {
+ var chan = make_channel(URL + "/body", null, function(intercepted) {
+ intercepted.cancelInterception(Cr.NS_BINDING_ABORTED);
+ });
+ chan.asyncOpen(new ChannelListener(run_next_test, null, CL_EXPECT_FAILURE));
+});
+
+// ensure that the channel can't be cancelled via nsIInterceptedChannel after making a decision
+add_test(function() {
+ var chan = make_channel(URL + "/body", null, function(chan) {
+ chan.resetInterception();
+ do_timeout(0, function() {
+ var gotexception = false;
+ try {
+ chan.cancelInterception();
+ } catch (x) {
+ gotexception = true;
+ }
+ Assert.ok(gotexception);
+ });
+ });
+ chan.asyncOpen(new ChannelListener(handle_remote_response, null));
+});
+
+// ensure that the intercepted channel can be canceled during the response
+add_test(function() {
+ var chan = make_channel(URL + "/body", null, function(intercepted) {
+ var synthesized = Cc[
+ "@mozilla.org/io/string-input-stream;1"
+ ].createInstance(Ci.nsIStringInputStream);
+ synthesized.data = NON_REMOTE_BODY;
+
+ let channel = intercepted.channel;
+ intercepted.startSynthesizedResponse(synthesized, null, null, "", false);
+ intercepted.finishSynthesizedResponse();
+ channel.cancel(Cr.NS_BINDING_ABORTED);
+ });
+ chan.asyncOpen(
+ new ChannelListener(
+ run_next_test,
+ null,
+ CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL
+ )
+ );
+});
+
+// ensure that the intercepted channel can be canceled before the response
+add_test(function() {
+ var chan = make_channel(URL + "/body", null, function(intercepted) {
+ var synthesized = Cc[
+ "@mozilla.org/io/string-input-stream;1"
+ ].createInstance(Ci.nsIStringInputStream);
+ synthesized.data = NON_REMOTE_BODY;
+
+ intercepted.channel.cancel(Cr.NS_BINDING_ABORTED);
+
+ // This should not throw, but result in the channel firing callbacks
+ // with an error status.
+ intercepted.startSynthesizedResponse(synthesized, null, null, "", false);
+ intercepted.finishSynthesizedResponse();
+ });
+ chan.asyncOpen(
+ new ChannelListener(
+ run_next_test,
+ null,
+ CL_EXPECT_FAILURE | CL_ALLOW_UNKNOWN_CL
+ )
+ );
+});
+
+// Ensure that nsIInterceptedChannel.channelIntercepted() can return an error.
+// In this case we should automatically ResetInterception() and complete the
+// network request.
+add_test(function() {
+ var chan = make_channel(URL + "/body", null, function(chan) {
+ throw "boom";
+ });
+ chan.asyncOpen(new ChannelListener(handle_remote_response, null));
+});
+
+add_test(function() {
+ httpServer.stop(run_next_test);
+});