"use strict"; // Test nsITraceableChannel interface. // Replace original listener with TracingListener that modifies body of HTTP // response. Make sure that body received by original channel's listener // is correctly modified. const { HttpServer } = ChromeUtils.importESModule( "resource://testing-common/httpd.sys.mjs" ); var httpserver = new HttpServer(); httpserver.start(-1); const PORT = httpserver.identity.primaryPort; var pipe = null; var streamSink = null; var originalBody = "original http response body"; var gotOnStartRequest = false; function TracingListener() {} TracingListener.prototype = { onStartRequest(request) { dump("*** tracing listener onStartRequest\n"); gotOnStartRequest = true; request.QueryInterface(Ci.nsIHttpChannelInternal); // local/remote addresses broken in e10s: disable for now Assert.equal(request.localAddress, "127.0.0.1"); Assert.equal(request.localPort > 0, true); Assert.notEqual(request.localPort, PORT); Assert.equal(request.remoteAddress, "127.0.0.1"); Assert.equal(request.remotePort, PORT); // Make sure listener can't be replaced after OnStartRequest was called. request.QueryInterface(Ci.nsITraceableChannel); try { var newListener = new TracingListener(); newListener.listener = request.setNewListener(newListener); } catch (e) { dump("TracingListener.onStartRequest swallowing exception: " + e + "\n"); return; // OK } do_throw("replaced channel's listener during onStartRequest."); }, onStopRequest() { dump("*** tracing listener onStopRequest\n"); Assert.equal(gotOnStartRequest, true); try { var sin = Cc["@mozilla.org/scriptableinputstream;1"].createInstance( Ci.nsIScriptableInputStream ); streamSink.close(); var input = pipe.inputStream; sin.init(input); Assert.equal(sin.available(), originalBody.length); var result = sin.read(originalBody.length); Assert.equal(result, originalBody); input.close(); } catch (e) { dump("TracingListener.onStopRequest swallowing exception: " + e + "\n"); } finally { httpserver.stop(do_test_finished); } }, QueryInterface: ChromeUtils.generateQI(["nsIRequestObserver"]), listener: null, }; function HttpResponseExaminer() {} HttpResponseExaminer.prototype = { register() { Services.obs.addObserver(this, "http-on-examine-response", true); dump("Did HttpResponseExaminer.register\n"); }, // Replace channel's listener. observe(subject) { dump("In HttpResponseExaminer.observe\n"); try { subject.QueryInterface(Ci.nsITraceableChannel); var tee = Cc["@mozilla.org/network/stream-listener-tee;1"].createInstance( Ci.nsIStreamListenerTee ); var newListener = new TracingListener(); pipe = Cc["@mozilla.org/pipe;1"].createInstance(Ci.nsIPipe); pipe.init(false, false, 0, 0xffffffff, null); streamSink = pipe.outputStream; var originalListener = subject.setNewListener(tee); tee.init(originalListener, streamSink, newListener); } catch (e) { do_throw("can't replace listener " + e); } dump("Did HttpResponseExaminer.observe\n"); }, QueryInterface: ChromeUtils.generateQI([ "nsIObserver", "nsISupportsWeakReference", ]), }; function test_handler(metadata, response) { response.setHeader("Content-Type", "text/html", false); response.setStatusLine(metadata.httpVersion, 200, "OK"); response.bodyOutputStream.write(originalBody, originalBody.length); } function make_channel(url) { return NetUtil.newChannel({ uri: url, loadUsingSystemPrincipal: true, }).QueryInterface(Ci.nsIHttpChannel); } // Check if received body is correctly modified. function channel_finished() { httpserver.stop(do_test_finished); } function run_test() { var observer = new HttpResponseExaminer(); observer.register(); httpserver.registerPathHandler("/testdir", test_handler); var channel = make_channel("http://localhost:" + PORT + "/testdir"); channel.asyncOpen(new ChannelListener(channel_finished)); do_test_pending(); }