Bug 1134073 - Part 3: Show network request cause and stacktrace in netmonitor - mochitests r=ochameau a=lizzard l10n=flod
authorJarda Snajdr <jsnajdr@gmail.com>
Thu, 09 Jun 2016 16:17:43 -0500
changeset 339602 4253296f212b3cca52992ec5f6cd824b13153abd
parent 339601 7dab350597e637eb8134a58532f7a4597ced328d
child 339603 1a585c67e88e31e89aa165f6b706600a2ef92386
push id6249
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 13:59:36 +0000
treeherdermozilla-beta@bad9d4f5bf7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersochameau, lizzard
bugs1134073
milestone49.0a2
Bug 1134073 - Part 3: Show network request cause and stacktrace in netmonitor - mochitests r=ochameau a=lizzard l10n=flod MozReview-Commit-ID: HU6EOIlT8us
devtools/client/netmonitor/test/browser.ini
devtools/client/netmonitor/test/browser_net_cause.js
devtools/client/netmonitor/test/browser_net_cause_redirect.js
devtools/client/netmonitor/test/browser_net_image-tooltip.js
devtools/client/netmonitor/test/browser_net_service-worker-status.js
devtools/client/netmonitor/test/head.js
devtools/client/netmonitor/test/html_cause-test-page.html
devtools/client/netmonitor/test/service-workers/status-codes-service-worker.js
devtools/client/netmonitor/test/service-workers/status-codes.html
devtools/client/netmonitor/test/sjs_hsts-test-server.sjs
--- a/devtools/client/netmonitor/test/browser.ini
+++ b/devtools/client/netmonitor/test/browser.ini
@@ -1,14 +1,15 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 support-files =
   dropmarker.svg
   head.js
+  html_cause-test-page.html
   html_content-type-test-page.html
   html_content-type-without-cache-test-page.html
   html_cors-test-page.html
   html_custom-get-page.html
   html_single-get-page.html
   html_cyrillic-test-page.html
   html_filter-test-page.html
   html_infinite-get-page.html
@@ -28,30 +29,33 @@ support-files =
   html_statistics-test-page.html
   html_status-codes-test-page.html
   html_api-calls-test-page.html
   html_copy-as-curl.html
   html_curl-utils.html
   sjs_content-type-test-server.sjs
   sjs_cors-test-server.sjs
   sjs_https-redirect-test-server.sjs
+  sjs_hsts-test-server.sjs
   sjs_simple-test-server.sjs
   sjs_sorting-test-server.sjs
   sjs_status-codes-test-server.sjs
   test-image.png
   service-workers/status-codes.html
   service-workers/status-codes-service-worker.js
 
 [browser_net_aaa_leaktest.js]
 [browser_net_accessibility-01.js]
 [browser_net_accessibility-02.js]
 skip-if = (toolkit == "cocoa" && e10s) # bug 1252254
 [browser_net_api-calls.js]
 [browser_net_autoscroll.js]
 [browser_net_cached-status.js]
+[browser_net_cause.js]
+[browser_net_cause_redirect.js]
 [browser_net_service-worker-status.js]
 [browser_net_charts-01.js]
 [browser_net_charts-02.js]
 [browser_net_charts-03.js]
 [browser_net_charts-04.js]
 [browser_net_charts-05.js]
 [browser_net_charts-06.js]
 [browser_net_charts-07.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/browser_net_cause.js
@@ -0,0 +1,102 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Tests if request cause is reported correctly.
+ */
+
+const CAUSE_FILE_NAME = "html_cause-test-page.html";
+const CAUSE_URL = EXAMPLE_URL + CAUSE_FILE_NAME;
+
+const EXPECTED_REQUESTS = [
+  {
+    method: "GET",
+    url: CAUSE_URL,
+    causeType: "document",
+    causeUri: "",
+    // The document load is from JS function in e10s, native in non-e10s
+    hasStack: !gMultiProcessBrowser
+  },
+  {
+    method: "GET",
+    url: EXAMPLE_URL + "stylesheet_request",
+    causeType: "stylesheet",
+    causeUri: CAUSE_URL,
+    hasStack: false
+  },
+  {
+    method: "GET",
+    url: EXAMPLE_URL + "img_request",
+    causeType: "img",
+    causeUri: CAUSE_URL,
+    hasStack: false
+  },
+  {
+    method: "GET",
+    url: EXAMPLE_URL + "xhr_request",
+    causeType: "xhr",
+    causeUri: CAUSE_URL,
+    hasStack: { fn: "performXhrRequest", file: CAUSE_FILE_NAME, line: 22 }
+  },
+  {
+    method: "POST",
+    url: EXAMPLE_URL + "beacon_request",
+    causeType: "beacon",
+    causeUri: CAUSE_URL,
+    hasStack: { fn: "performBeaconRequest", file: CAUSE_FILE_NAME, line: 26 }
+  },
+];
+
+var test = Task.async(function* () {
+  // the initNetMonitor function clears the network request list after the
+  // page is loaded. That's why we first load a bogus page from SIMPLE_URL,
+  // and only then load the real thing from CAUSE_URL - we want to catch
+  // all the requests the page is making, not only the XHRs.
+  // We can't use about:blank here, because initNetMonitor checks that the
+  // page has actually made at least one request.
+  let [, debuggee, monitor] = yield initNetMonitor(SIMPLE_URL);
+  let { RequestsMenu } = monitor.panelWin.NetMonitorView;
+  RequestsMenu.lazyUpdate = false;
+
+  debuggee.location = CAUSE_URL;
+
+  yield waitForNetworkEvents(monitor, EXPECTED_REQUESTS.length);
+
+  is(RequestsMenu.itemCount, EXPECTED_REQUESTS.length,
+    "All the page events should be recorded.");
+
+  EXPECTED_REQUESTS.forEach((spec, i) => {
+    let { method, url, causeType, causeUri, hasStack } = spec;
+
+    let requestItem = RequestsMenu.getItemAtIndex(i);
+    verifyRequestItemTarget(requestItem,
+      method, url, { cause: { type: causeType, loadingDocumentUri: causeUri } }
+    );
+
+    let { stacktrace } = requestItem.attachment.cause;
+    let stackLen = stacktrace ? stacktrace.length : 0;
+
+    if (hasStack) {
+      ok(stacktrace, `Request #${i} has a stacktrace`);
+      ok(stackLen > 0,
+        `Request #${i} (${causeType}) has a stacktrace with ${stackLen} items`);
+
+      // if "hasStack" is object, check the details about the top stack frame
+      if (typeof hasStack === "object") {
+        is(stacktrace[0].functionName, hasStack.fn,
+          `Request #${i} has the correct function on top of the JS stack`);
+        is(stacktrace[0].filename.split("/").pop(), hasStack.file,
+          `Request #${i} has the correct file on top of the JS stack`);
+        is(stacktrace[0].lineNumber, hasStack.line,
+          `Request #${i} has the correct line number on top of the JS stack`);
+      }
+    } else {
+      is(stackLen, 0, `Request #${i} (${causeType}) has an empty stacktrace`);
+    }
+  });
+
+  yield teardown(monitor);
+  finish();
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/browser_net_cause_redirect.js
@@ -0,0 +1,50 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+/**
+ * Tests if request JS stack is property reported if the request is internally
+ * redirected without hitting the network (HSTS is one of such cases)
+ */
+
+var test = Task.async(function* () {
+  const EXPECTED_REQUESTS = [
+    // Request to HTTP URL, redirects to HTTPS, has callstack
+    { status: 302, hasStack: true },
+    // Serves HTTPS, sets the Strict-Transport-Security header, no stack
+    { status: 200, hasStack: false },
+    // Second request to HTTP redirects to HTTPS internally
+    { status: 200, hasStack: true },
+  ];
+
+  let [, debuggee, monitor] = yield initNetMonitor(CUSTOM_GET_URL);
+  let { RequestsMenu } = monitor.panelWin.NetMonitorView;
+  RequestsMenu.lazyUpdate = false;
+
+  debuggee.performRequests(2, HSTS_SJS);
+  yield waitForNetworkEvents(monitor, EXPECTED_REQUESTS.length);
+
+  EXPECTED_REQUESTS.forEach(({status, hasStack}, i) => {
+    let { attachment } = RequestsMenu.getItemAtIndex(i);
+
+    is(attachment.status, status, `Request #${i} has the expected status`);
+
+    let { stacktrace } = attachment.cause;
+    let stackLen = stacktrace ? stacktrace.length : 0;
+
+    if (hasStack) {
+      ok(stacktrace, `Request #${i} has a stacktrace`);
+      ok(stackLen > 0, `Request #${i} has a stacktrace with ${stackLen} items`);
+    } else {
+      is(stackLen, 0, `Request #${i} has an empty stacktrace`);
+    }
+  });
+
+  // Send a request to reset the HSTS policy to state before the test
+  debuggee.performRequests(1, HSTS_SJS + "?reset");
+  yield waitForNetworkEvents(monitor, 1);
+
+  yield teardown(monitor);
+  finish();
+});
--- a/devtools/client/netmonitor/test/browser_net_image-tooltip.js
+++ b/devtools/client/netmonitor/test/browser_net_image-tooltip.js
@@ -18,42 +18,39 @@ add_task(function* test() {
   let onEvents = waitForNetworkEvents(monitor, 7);
   let onThumbnail = waitFor(monitor.panelWin, EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED);
 
   debuggee.performRequests();
   yield onEvents;
   yield onThumbnail;
 
   info("Checking the image thumbnail after a few requests were made...");
-  yield showTooltipAndVerify(RequestsMenu.items[5]);
+  yield showTooltipAndVerify(RequestsMenu.tooltip, RequestsMenu.items[5]);
 
   // 7 XHRs as before + 1 extra document reload
   onEvents = waitForNetworkEvents(monitor, 8);
   onThumbnail = waitFor(monitor.panelWin, EVENTS.RESPONSE_IMAGE_THUMBNAIL_DISPLAYED);
 
   info("Reloading the debuggee and performing all requests again...");
   yield NetMonitorController.triggerActivity(ACTIVITY_TYPE.RELOAD.WITH_CACHE_ENABLED);
   debuggee.performRequests();
   yield onEvents;
   yield onThumbnail;
 
   info("Checking the image thumbnail after a reload.");
-  yield showTooltipAndVerify(RequestsMenu.items[6]);
+  yield showTooltipAndVerify(RequestsMenu.tooltip, RequestsMenu.items[6]);
 
   yield teardown(monitor);
   finish();
 
   /**
    * Show a tooltip on the {requestItem} and verify that it was displayed
    * with the expected content.
    */
-  function* showTooltipAndVerify(requestItem) {
-    let { tooltip } = requestItem.attachment;
-    ok(tooltip, "There should be a tooltip instance for the image request.");
-
+  function* showTooltipAndVerify(tooltip, requestItem) {
     let anchor = $(".requests-menu-file", requestItem.target);
     yield showTooltipOn(tooltip, anchor);
 
     info("Tooltip was successfully opened for the image request.");
     is(tooltip.content.querySelector("image").src, TEST_IMAGE_DATA_URI,
       "The tooltip's image content is displayed correctly.");
   }
 
--- a/devtools/client/netmonitor/test/browser_net_service-worker-status.js
+++ b/devtools/client/netmonitor/test/browser_net_service-worker-status.js
@@ -8,48 +8,64 @@
  */
 
 // Service workers only work on https
 const URL = EXAMPLE_URL.replace("http:", "https:");
 
 const TEST_URL = URL + "service-workers/status-codes.html";
 
 var test = Task.async(function* () {
-  let [tab, debuggee, monitor] = yield initNetMonitor(TEST_URL, null, true);
+  let [, debuggee, monitor] = yield initNetMonitor(TEST_URL, null, true);
   info("Starting test... ");
 
-  let { document, L10N, NetMonitorView } = monitor.panelWin;
-  let { RequestsMenu, NetworkDetails } = NetMonitorView;
+  let { NetMonitorView } = monitor.panelWin;
+  let { RequestsMenu } = NetMonitorView;
 
   const REQUEST_DATA = [
     {
       method: "GET",
       uri: URL + "service-workers/test/200",
       details: {
         status: 200,
         statusText: "OK (service worker)",
         displayedStatus: "service worker",
         type: "plain",
         fullMimeType: "text/plain; charset=UTF-8"
-      }
+      },
+      stackFunctions: ["doXHR", "performRequests"]
     },
   ];
 
   info("Registering the service worker...");
   yield debuggee.registerServiceWorker();
 
   info("Performing requests...");
   debuggee.performRequests();
   yield waitForNetworkEvents(monitor, REQUEST_DATA.length);
 
   let index = 0;
   for (let request of REQUEST_DATA) {
     let item = RequestsMenu.getItemAtIndex(index);
 
-    info("Verifying request #" + index);
+    info(`Verifying request #${index}`);
     yield verifyRequestItemTarget(item, request.method, request.uri, request.details);
 
+    let { stacktrace } = item.attachment.cause;
+    let stackLen = stacktrace ? stacktrace.length : 0;
+
+    ok(stacktrace, `Request #${index} has a stacktrace`);
+    ok(stackLen >= request.stackFunctions.length,
+      `Request #${index} has a stacktrace with enough (${stackLen}) items`);
+
+    request.stackFunctions.forEach((functionName, j) => {
+      is(stacktrace[j].functionName, functionName,
+      `Request #${index} has the correct function at position #${j} on the stack`);
+    });
+
     index++;
   }
 
+  info("Unregistering the service worker...");
+  yield debuggee.unregisterServiceWorker();
+
   yield teardown(monitor);
   finish();
 });
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -45,16 +45,20 @@ const SEND_BEACON_URL = EXAMPLE_URL + "h
 const CORS_URL = EXAMPLE_URL + "html_cors-test-page.html";
 
 const SIMPLE_SJS = EXAMPLE_URL + "sjs_simple-test-server.sjs";
 const CONTENT_TYPE_SJS = EXAMPLE_URL + "sjs_content-type-test-server.sjs";
 const STATUS_CODES_SJS = EXAMPLE_URL + "sjs_status-codes-test-server.sjs";
 const SORTING_SJS = EXAMPLE_URL + "sjs_sorting-test-server.sjs";
 const HTTPS_REDIRECT_SJS = EXAMPLE_URL + "sjs_https-redirect-test-server.sjs";
 const CORS_SJS_PATH = "/browser/devtools/client/netmonitor/test/sjs_cors-test-server.sjs";
+const HSTS_SJS = EXAMPLE_URL + "sjs_hsts-test-server.sjs";
+
+const HSTS_BASE_URL = EXAMPLE_URL;
+const HSTS_PAGE_URL = CUSTOM_GET_URL;
 
 const TEST_IMAGE = EXAMPLE_URL + "test-image.png";
 const TEST_IMAGE_DATA_URI = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==";
 
 const FRAME_SCRIPT_UTILS_URL = "chrome://devtools/content/shared/frame-script-utils.js";
 
 DevToolsUtils.testing = true;
 SimpleTest.registerCleanupFunction(() => {
@@ -279,17 +283,17 @@ function verifyRequestItemTarget(aReques
 
   let requestsMenu = aRequestItem.ownerView;
   let widgetIndex = requestsMenu.indexOfItem(aRequestItem);
   let visibleIndex = requestsMenu.visibleItems.indexOf(aRequestItem);
 
   info("Widget index of item: " + widgetIndex);
   info("Visible index of item: " + visibleIndex);
 
-  let { fuzzyUrl, status, statusText, type, fullMimeType,
+  let { fuzzyUrl, status, statusText, cause, type, fullMimeType,
         transferred, size, time, displayedStatus } = aData;
   let { attachment, target } = aRequestItem;
 
   let uri = Services.io.newURI(aUrl, null, null).QueryInterface(Ci.nsIURL);
   let unicodeUrl = NetworkHelper.convertToUnicode(unescape(aUrl));
   let name = NetworkHelper.convertToUnicode(unescape(uri.fileName || uri.filePath || "/"));
   let query = NetworkHelper.convertToUnicode(unescape(uri.query));
   let hostPort = uri.hostPort;
@@ -331,16 +335,25 @@ function verifyRequestItemTarget(aReques
     let tooltip = target.querySelector(".requests-menu-status").getAttribute("tooltiptext");
     info("Displayed status: " + value);
     info("Displayed code: " + codeValue);
     info("Tooltip status: " + tooltip);
     is(value, displayedStatus ? displayedStatus : status, "The displayed status is correct.");
     is(codeValue, status, "The displayed status code is correct.");
     is(tooltip, status + " " + statusText, "The tooltip status is correct.");
   }
+  if (cause !== undefined) {
+    let causeLabel = target.querySelector(".requests-menu-cause-label");
+    let value = causeLabel.getAttribute("value");
+    let tooltip = causeLabel.getAttribute("tooltiptext");
+    info("Displayed cause: " + value);
+    info("Tooltip cause: " + tooltip);
+    is(value, cause.type, "The displayed cause is correct.");
+    is(tooltip, cause.loadingDocumentUri, "The tooltip cause is correct.")
+  }
   if (type !== undefined) {
     let value = target.querySelector(".requests-menu-type").getAttribute("value");
     let tooltip = target.querySelector(".requests-menu-type").getAttribute("tooltiptext");
     info("Displayed type: " + value);
     info("Tooltip type: " + tooltip);
     is(value, type, "The displayed type is correct.");
     is(tooltip, fullMimeType, "The tooltip type is correct.");
   }
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/html_cause-test-page.html
@@ -0,0 +1,33 @@
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
+    <meta http-equiv="Pragma" content="no-cache" />
+    <meta http-equiv="Expires" content="0" />
+    <title>Network Monitor test page</title>
+    <link rel="stylesheet" type="text/css" href="stylesheet_request" />
+  </head>
+
+  <body>
+    <p>Request cause test</p>
+    <img src="img_request" />
+    <script type="text/javascript">
+      function performXhrRequest() {
+        var xhr = new XMLHttpRequest();
+        xhr.open("GET", "xhr_request", true);
+        xhr.send();
+      }
+
+      function performBeaconRequest() {
+        navigator.sendBeacon("beacon_request");
+      }
+
+      performXhrRequest();
+      performBeaconRequest();
+    </script>
+  </body>
+</html>
--- a/devtools/client/netmonitor/test/service-workers/status-codes-service-worker.js
+++ b/devtools/client/netmonitor/test/service-workers/status-codes-service-worker.js
@@ -1,8 +1,15 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-addEventListener("fetch", function (event) {
+"use strict";
+
+self.addEventListener("activate", event => {
+  // start controlling the already loaded page
+  event.waitUntil(self.clients.claim());
+});
+
+self.addEventListener("fetch", event => {
   let response = new Response("Service worker response");
   event.respondWith(response);
 });
--- a/devtools/client/netmonitor/test/service-workers/status-codes.html
+++ b/devtools/client/netmonitor/test/service-workers/status-codes.html
@@ -10,30 +10,50 @@
     <meta http-equiv="Expires" content="0" />
     <title>Network Monitor test page</title>
   </head>
 
   <body>
     <p>Status codes test</p>
 
     <script type="text/javascript">
-      function get(url) {
-        return new Promise(done => {
-          let iframe = document.createElement("iframe");
-          iframe.setAttribute("src", url);
-          document.documentElement.appendChild(iframe);
-          iframe.contentWindow.onload = done;
-        });
+      let swRegistration;
+
+      function registerServiceWorker() {
+        let sw = navigator.serviceWorker;
+        return sw.register("status-codes-service-worker.js")
+          .then(registration => {
+            swRegistration = registration;
+            console.log("Registered, scope is:", registration.scope);
+            return sw.ready;
+          }).then(() => {
+            // wait until the page is controlled
+            return new Promise(resolve => {
+              if (sw.controller) {
+                resolve();
+              } else {
+                sw.addEventListener('controllerchange', function onControllerChange() {
+                  sw.removeEventListener('controllerchange', onControllerChange);
+                  resolve();
+                });
+              }
+            });
+          }).catch(err => {
+            console.error("Registration failed");
+          });
       }
 
-      function registerServiceWorker() {
-        return navigator.serviceWorker.register("status-codes-service-worker.js")
-                        .then(() => navigator.serviceWorker.ready);
+      function unregisterServiceWorker() {
+        return swRegistration.unregister();
       }
 
       function performRequests() {
-        return get("test/200");
+        return new Promise(function doXHR(done) {
+          let xhr = new XMLHttpRequest();
+          xhr.open("GET", "test/200", true);
+          xhr.onreadystatechange = done;
+          xhr.send(null);
+        });
       }
-
     </script>
   </body>
 
 </html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/test/sjs_hsts-test-server.sjs
@@ -0,0 +1,22 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function handleRequest(request, response) {
+  response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
+  response.setHeader("Pragma", "no-cache");
+  response.setHeader("Expires", "0");
+
+  if (request.queryString === "reset") {
+    // Reset the HSTS policy, prevent influencing other tests
+    response.setStatusLine(request.httpVersion, 200, "OK");
+    response.setHeader("Strict-Transport-Security", "max-age=0");
+    response.write("Resetting HSTS");
+  } else if (request.scheme === "http") {
+    response.setStatusLine(request.httpVersion, 302, "Found");
+    response.setHeader("Location", "https://" + request.host + request.path);
+  } else {
+    response.setStatusLine(request.httpVersion, 200, "OK");
+    response.setHeader("Strict-Transport-Security", "max-age=100");
+    response.write("Page was accessed over HTTPS!");
+  }
+}