Bug 1313595 - Missing interface in HSTSPrimerListener r?mayhemer
MozReview-Commit-ID: Cdyyxv4dxyo
new file mode 100644
--- /dev/null
+++ b/dom/security/test/hsts/file_hsts_priming_timeout.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1313595
+Test that a timeout happens correctly
+-->
+<head>
+ <title>Test for Bug 1313595</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1313595">Mozilla Bug 1313595</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id = "content_iframe"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+var timeout = 3000;
+
+function processEvent(ev) {
+ console.log("processing event %o", ev);
+
+ is(ev.data.result, "failed", "The load should fail");
+
+ ok(Math.abs(ev.data.timeout - timeout) < (timeout * .10), "The error should happen because of timeout.");
+
+ SpecialPowers.popPrefEnv();
+ SimpleTest.finish();
+}
+
+window.addEventListener("message", processEvent, false);
+
+SpecialPowers.pushPrefEnv({'set': [['security.mixed_content.hsts_priming_request_timeout', 3000]]});
+SimpleTest.waitForExplicitFinish();
+
+// save this for last so that our listeners are registered.
+// ... this loads the testbed of good and bad requests.
+let content_iframe = document.getElementById('content_iframe');
+content_iframe.onload = function() {
+ window.postMessage("Go", "https://example.com");
+};
+content_iframe.src = 'https://example.com/tests/dom/security/test/hsts/file_timeout_host_frame.html';
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/security/test/hsts/file_timeout_host_frame.html
@@ -0,0 +1,57 @@
+<html>
+ <head>
+ <script type="text/javascript">
+ var start;
+ var target;
+
+ function parse_query_string() {
+ var q = {};
+ document.location.search.substr(1).
+ split('&').forEach(function (item, idx, ar) {
+ let [k, v] = item.split('=');
+ q[k] = unescape(v);
+ });
+ return q;
+ }
+
+ var args = parse_query_string();
+
+ function run(ev) {
+ if (ev.origin != "http://mochi.test:8888") {
+ return;
+ }
+
+ let scr = document.createElement("script");
+ target = ev.source;
+
+ scr.onerror = fail;
+ scr.onload = success;
+ scr.type = "text/javascript";
+ scr.src = "http://example.com/tests/dom/security/test/hsts/file_timeout_server.sjs?timeout="+args['timeout'];
+ let content = document.getElementById("content");
+ start = performance.now();
+ content.appendChild(scr);
+ }
+
+ function get_timeout() {
+ return (performance.now() - start);
+ }
+
+ function success() {
+ var elapsed = get_timeout();
+ console.log("Success in " + elapsed + " ms");
+ target.postMessage({result: 'success', timeout: elapsed}, "http://mochi.test:8888");
+ }
+ function fail() {
+ var elapsed = get_timeout();
+ console.log("Failed in " + elapsed + " ms");
+ target.postMessage({result: 'failed', timeout: elapsed}, "http://mochi.test:8888");
+ }
+
+ window.addEventListener("message", run, false);
+ </script>
+ </head>
+ <body>
+ <div id="content"></div>
+ </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/security/test/hsts/file_timeout_server.sjs
@@ -0,0 +1,26 @@
+// SJS file for HSTS priming timeout mochitest
+
+Components.utils.import("resource://gre/modules/NetUtil.jsm");
+Components.utils.importGlobalProperties(["URLSearchParams"]);
+
+function handleRequest(request, response)
+{
+ const query = new URLSearchParams(request.queryString);
+ // timeout in ms
+ var timeout = query.get('timeout');
+ response.processAsync();
+
+ timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
+ timer.initWithCallback(function()
+ {
+ if (!response) {
+ return;
+ }
+ // avoid confusing cache behaviors
+ response.setHeader("Cache-Control", "no-cache", false);
+ response.setHeader("Content-Type", "text/javascript", false);
+ // never sends Strict-Transport-Security
+ response.write('console.log("timeout: "+'+timeout+');');
+ response.finish();
+ }, timeout, Components.interfaces.nsITimer.TYPE_ONE_SHOT);
+}
new file mode 100644
--- /dev/null
+++ b/dom/security/test/hsts/mochitest.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+tags = hsts-priming
+support-files =
+ file_timeout_server.sjs
+ file_timeout_host_frame.html
+
+[test_hsts_priming_timeout.html]
+[test_hsts_priming_no-timeout.html]
new file mode 100644
--- /dev/null
+++ b/dom/security/test/hsts/test_hsts_priming_no-timeout.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1313595
+Test that a timeout happens correctly
+-->
+<head>
+ <title>Test for Bug 1313595</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1313595">Mozilla Bug 1313595</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id = "content_iframe"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+SpecialPowers.cleanUpSTSData('http://example.com');
+
+var timeout = 0;
+var start = performance.now();
+
+function cleanup() {
+ clearInterval(interval);
+ window.removeEventListener("message", processEvent);
+ SpecialPowers.cleanUpSTSData('http://example.com');
+ SpecialPowers.popPrefEnv();
+ SimpleTest.finish();
+}
+
+function processEvent(ev) {
+ console.log("processing event %o", ev);
+
+ if (ev.origin != "https://example.com") {
+ return;
+ }
+
+ is(ev.data.result, "failed", "The load should fail");
+
+ ok(ev.data.timeout < 100, "The error should happen not because of timeout.");
+
+ cleanup();
+}
+
+var interval = setInterval(function () {
+ if ((performance.now() - start) > (timeout*2)) {
+ ok(false, "Did not receive error in a timely manner (" + 2*timeout + "ms)");
+ cleanup();
+ }
+}, 100);
+
+window.addEventListener("message", processEvent, false);
+
+SpecialPowers.pushPrefEnv({'set': [['security.mixed_content.hsts_priming_request_timeout', timeout]]});
+SimpleTest.waitForExplicitFinish();
+
+// save this for last so that our listeners are registered.
+// ... this loads the testbed of good and bad requests.
+let content_iframe = document.getElementById('content_iframe');
+content_iframe.onload = function() {
+ content_iframe.contentWindow.postMessage("Go", "https://example.com");
+};
+content_iframe.src = 'https://example.com/tests/dom/security/test/hsts/file_timeout_host_frame.html?timeout='+timeout;
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/security/test/hsts/test_hsts_priming_timeout.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1313595
+Test that a timeout happens correctly
+-->
+<head>
+ <title>Test for Bug 1313595</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1313595">Mozilla Bug 1313595</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id = "content_iframe"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+SpecialPowers.cleanUpSTSData('http://example.com');
+
+var timeout = 1000;
+var start = performance.now();
+
+function cleanup() {
+ clearInterval(interval);
+ window.removeEventListener("message", processEvent);
+ SpecialPowers.cleanUpSTSData('http://example.com');
+ SpecialPowers.popPrefEnv();
+ SimpleTest.finish();
+}
+
+function processEvent(ev) {
+ console.log("processing event %o", ev);
+
+ if (ev.origin != "https://example.com") {
+ return;
+ }
+
+ is(ev.data.result, "failed", "The load should fail");
+
+ ok(Math.abs(ev.data.timeout - timeout) < (timeout * .10), "The error should happen because of timeout. expected: " + timeout + ", got " + ev.data.timeout);
+
+ cleanup();
+}
+
+var interval = setInterval(function () {
+ if ((performance.now() - start) > (timeout*2)) {
+ ok(false, "Did not receive timeout in a timely manner (" + 2*timeout + "ms)");
+ cleanup();
+ }
+}, 100);
+
+window.addEventListener("message", processEvent, false);
+
+SpecialPowers.pushPrefEnv({'set': [['security.mixed_content.hsts_priming_request_timeout', timeout]]});
+SimpleTest.waitForExplicitFinish();
+
+// save this for last so that our listeners are registered.
+// ... this loads the testbed of good and bad requests.
+let content_iframe = document.getElementById('content_iframe');
+content_iframe.onload = function() {
+ content_iframe.contentWindow.postMessage("Go", "https://example.com");
+};
+content_iframe.src = 'https://example.com/tests/dom/security/test/hsts/file_timeout_host_frame.html?timeout='+timeout;
+</script>
+</pre>
+</body>
+</html>
--- a/dom/security/test/moz.build
+++ b/dom/security/test/moz.build
@@ -11,16 +11,17 @@ XPCSHELL_TESTS_MANIFESTS += [
TEST_DIRS += [
'gtest',
]
MOCHITEST_MANIFESTS += [
'cors/mochitest.ini',
'csp/mochitest.ini',
'general/mochitest.ini',
+ 'hsts/mochitest.ini',
'mixedcontentblocker/mochitest.ini',
'sri/mochitest.ini',
]
MOCHITEST_CHROME_MANIFESTS += [
'general/chrome.ini',
]
--- a/netwerk/protocol/http/HSTSPrimerListener.cpp
+++ b/netwerk/protocol/http/HSTSPrimerListener.cpp
@@ -20,17 +20,18 @@
#include "mozilla/Unused.h"
namespace mozilla {
namespace net {
using namespace mozilla;
NS_IMPL_ISUPPORTS(HSTSPrimingListener, nsIStreamListener,
- nsIRequestObserver, nsIInterfaceRequestor)
+ nsIRequestObserver, nsIInterfaceRequestor,
+ nsITimerCallback)
// default to 3000ms, same as the preference
uint32_t HSTSPrimingListener::sHSTSPrimingTimeout = 3000;
HSTSPrimingListener::HSTSPrimingListener(nsIHstsPrimingCallback* aCallback)
: mCallback(aCallback)
{
@@ -66,24 +67,28 @@ HSTSPrimingListener::ReportTiming(nsresu
}
NS_IMETHODIMP
HSTSPrimingListener::OnStartRequest(nsIRequest *aRequest,
nsISupports *aContext)
{
nsCOMPtr<nsIHstsPrimingCallback> callback;
callback.swap(mCallback);
+ mCallback = nullptr;
+
if (mHSTSPrimingTimer) {
Unused << mHSTSPrimingTimer->Cancel();
mHSTSPrimingTimer = nullptr;
}
// if callback is null, we have already canceled this request and reported
// the failure
- NS_ENSURE_STATE(callback);
+ if (!callback) {
+ return NS_OK;
+ }
nsresult primingResult = CheckHSTSPrimingRequestStatus(aRequest);
ReportTiming(primingResult);
if (NS_FAILED(primingResult)) {
LOG(("HSTS Priming Failed (request was not approved)"));
return callback->OnHSTSPrimingFailed(primingResult, false);
}
@@ -169,23 +174,27 @@ HSTSPrimingListener::OnDataAvailable(nsI
/** nsITimerCallback **/
NS_IMETHODIMP
HSTSPrimingListener::Notify(nsITimer* timer)
{
nsresult rv;
nsCOMPtr<nsIHstsPrimingCallback> callback;
callback.swap(mCallback);
- NS_ENSURE_STATE(callback);
+ mCallback = nullptr;
+ if (!callback) {
+ // we already processed this channel
+ return NS_OK;
+ }
+
ReportTiming(NS_ERROR_HSTS_PRIMING_TIMEOUT);
if (mPrimingChannel) {
rv = mPrimingChannel->Cancel(NS_ERROR_HSTS_PRIMING_TIMEOUT);
if (NS_FAILED(rv)) {
- // do what?
NS_ERROR("HSTS Priming timed out, and we got an error canceling the priming channel.");
}
}
rv = callback->OnHSTSPrimingFailed(NS_ERROR_HSTS_PRIMING_TIMEOUT, false);
if (NS_FAILED(rv)) {
NS_ERROR("HSTS Priming timed out, and we got an error reporting the failure.");
}