Bug 1457379 - [Adding Tests] Fix webSockets show only under 'other' filter. r=Honza
authorLaphets <wenqing4@illinois.edu>
Mon, 18 Mar 2019 08:57:07 +0000
changeset 464752 51ec0da695a49736e1b25417f28c525c140d0d51
parent 464751 81e80e5eaf36b19d0f64bd669208e01dd0c9cec1
child 464753 4641d251c20864795f672b873466c41fb5d014d9
push id80689
push userjodvarko@mozilla.com
push dateMon, 18 Mar 2019 08:57:41 +0000
treeherderautoland@51ec0da695a4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersHonza
bugs1457379
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1457379 - [Adding Tests] Fix webSockets show only under 'other' filter. r=Honza //Before this change, the websocket request will be filtered in to "other" subject. After this change, the websocket request will be in "ws" subject.// ## About Tests The original test has some problem that it doesn't mock websocket request well, (since it use `XMLHttpRequest` to mock websocket, so the "upgrade" header can't be added due to some browser restrictions) In the updated test file, the native `new WebSocket()` method is used to perform websoket request. The origin http based server at [[ https://searchfox.org/mozilla-central/rev/89414a1df52d06cfc35529afb9a5a8542a6e4270/devtools/client/netmonitor/test/sjs_content-type-test-server.sjs#237 | Here ]] is still utilized. However, the `ws://example.com` request will be proxy to websocket server at port 9988 instead of 8888, so the ip url is used to make sure the request is handled by the orginal test [[ https://searchfox.org/mozilla-central/rev/89414a1df52d06cfc35529afb9a5a8542a6e4270/devtools/client/netmonitor/test/sjs_content-type-test-server.sjs#237 | server ]]. Differential Revision: https://phabricator.services.mozilla.com/D23322
devtools/client/netmonitor/src/utils/filter-predicates.js
devtools/client/netmonitor/test/browser_net_filter-01.js
devtools/client/netmonitor/test/head.js
devtools/client/shared/test/frame-script-utils.js
--- a/devtools/client/netmonitor/src/utils/filter-predicates.js
+++ b/devtools/client/netmonitor/src/utils/filter-predicates.js
@@ -58,17 +58,22 @@ function isMedia({ mimeType }) {
   return mimeType && (
     mimeType.includes("audio/") ||
     mimeType.includes("video/") ||
     mimeType.includes("model/") ||
     mimeType === "application/vnd.apple.mpegurl" ||
     mimeType === "application/x-mpegurl");
 }
 
-function isWS({ requestHeaders, responseHeaders }) {
+function isWS({ requestHeaders, responseHeaders, cause }) {
+  // For the first call, the requestHeaders is not ready(empty),
+  // so checking for cause.type instead (Bug-1454962)
+  if (typeof cause.type === "string" && cause.type === "websocket") {
+    return true;
+  }
   // Detect a websocket upgrade if request has an Upgrade header with value 'websocket'
   if (!requestHeaders || !Array.isArray(requestHeaders.headers)) {
     return false;
   }
 
   // Find the 'upgrade' header.
   let upgradeHeader = requestHeaders.headers.find(header => {
     return (header.name.toLowerCase() == "upgrade");
--- a/devtools/client/netmonitor/test/browser_net_filter-01.js
+++ b/devtools/client/netmonitor/test/browser_net_filter-01.js
@@ -32,18 +32,18 @@ const REQUESTS_WITH_MEDIA = BASIC_REQUES
   { url: "sjs_content-type-test-server.sjs?fmt=hls-m3u8-alt-mime-type" },
 ]);
 
 const REQUESTS_WITH_MEDIA_AND_FLASH = REQUESTS_WITH_MEDIA.concat([
   { url: "sjs_content-type-test-server.sjs?fmt=flash" },
 ]);
 
 const REQUESTS_WITH_MEDIA_AND_FLASH_AND_WS = REQUESTS_WITH_MEDIA_AND_FLASH.concat([
-  /* "Upgrade" is a reserved header and can not be set on XMLHttpRequest */
-  { url: "sjs_content-type-test-server.sjs?fmt=ws" },
+  /* Use new WebSocket() to mock native websocket request, then "Upgrade" will be added */
+  { url: WS_URL + "sjs_content-type-test-server.sjs?fmt=ws", ws: true },
 ]);
 
 const EXPECTED_REQUESTS = [
   {
     method: "GET",
     url: getSjsURLInUnicodeIdn() + "?fmt=html",
     data: {
       fuzzyUrl: true,
@@ -182,17 +182,17 @@ const EXPECTED_REQUESTS = [
       status: 200,
       statusText: "OK",
       type: "x-shockwave-flash",
       fullMimeType: "application/x-shockwave-flash",
     },
   },
   {
     method: "GET",
-    url: CONTENT_TYPE_SJS + "?fmt=ws",
+    url: WS_CONTENT_TYPE_SJS + "?fmt=ws",
     data: {
       fuzzyUrl: true,
       status: 101,
       statusText: "Switching Protocols",
     },
   },
 ];
 
--- a/devtools/client/netmonitor/test/head.js
+++ b/devtools/client/netmonitor/test/head.js
@@ -35,16 +35,20 @@ const {
   getUrlQuery,
   getUrlScheme,
 } = require("devtools/client/netmonitor/src/utils/request-utils");
 const { EVENTS } = require("devtools/client/netmonitor/src/constants");
 
 /* eslint-disable no-unused-vars, max-len */
 const EXAMPLE_URL = "http://example.com/browser/devtools/client/netmonitor/test/";
 const HTTPS_EXAMPLE_URL = "https://example.com/browser/devtools/client/netmonitor/test/";
+/* Since the test server will proxy `ws://example.com` to websocket server on 9988,
+so we must sepecify the port explicitly */
+const WS_URL = "ws://127.0.0.1:8888/browser/devtools/client/netmonitor/test/";
+const WS_HTTP_URL = "http://127.0.0.1:8888/browser/devtools/client/netmonitor/test/";
 
 const API_CALLS_URL = EXAMPLE_URL + "html_api-calls-test-page.html";
 const SIMPLE_URL = EXAMPLE_URL + "html_simple-test-page.html";
 const NAVIGATE_URL = EXAMPLE_URL + "html_navigate-test-page.html";
 const CONTENT_TYPE_WITHOUT_CACHE_URL = EXAMPLE_URL + "html_content-type-without-cache-test-page.html";
 const CONTENT_TYPE_WITHOUT_CACHE_REQUESTS = 8;
 const CYRILLIC_URL = EXAMPLE_URL + "html_cyrillic-test-page.html";
 const STATUS_CODES_URL = EXAMPLE_URL + "html_status-codes-test-page.html";
@@ -73,16 +77,17 @@ const CURL_UTILS_URL = EXAMPLE_URL + "ht
 const SEND_BEACON_URL = EXAMPLE_URL + "html_send-beacon.html";
 const CORS_URL = EXAMPLE_URL + "html_cors-test-page.html";
 const PAUSE_URL = EXAMPLE_URL + "html_pause-test-page.html";
 const OPEN_REQUEST_IN_TAB_URL = EXAMPLE_URL + "html_open-request-in-tab.html";
 
 const SIMPLE_SJS = EXAMPLE_URL + "sjs_simple-test-server.sjs";
 const SIMPLE_UNSORTED_COOKIES_SJS = EXAMPLE_URL + "sjs_simple-unsorted-cookies-test-server.sjs";
 const CONTENT_TYPE_SJS = EXAMPLE_URL + "sjs_content-type-test-server.sjs";
+const WS_CONTENT_TYPE_SJS = WS_HTTP_URL + "sjs_content-type-test-server.sjs";
 const HTTPS_CONTENT_TYPE_SJS = HTTPS_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 METHOD_SJS = EXAMPLE_URL + "sjs_method-test-server.sjs";
 const SLOW_SJS = EXAMPLE_URL + "sjs_slow-test-server.sjs";
--- a/devtools/client/shared/test/frame-script-utils.js
+++ b/devtools/client/shared/test/frame-script-utils.js
@@ -69,16 +69,60 @@ function promiseXHR(data) {
       });
     }
 
     xhr.send(body);
   });
 }
 
 /**
+ * Performs a single websocket request and returns a promise that resolves once
+ * the request has loaded.
+ *
+ * @param Object data
+ *        { url: the url to request (default: content.location.href),
+ *          nocache: append an unique token to the query string (default: true),
+ *        }
+ *
+ * @return Promise A promise that's resolved with object
+ *         { status: websocket status(101),
+ *           response: empty string }
+ *
+ */
+function promiseWS(data) {
+  return new Promise((resolve, reject) => {
+    let url = data.url;
+
+    if (data.nocache) {
+      url += "?devtools-cachebust=" + Math.random();
+    }
+
+    /* Create websocket instance */
+    const socket = new content.WebSocket(url);
+
+    /* Since we only use HTTP server to mock websocket, so just ignore the error */
+    socket.onclose = (e) => {
+      socket.close();
+      resolve({
+        status: 101,
+        response: "",
+      });
+    };
+
+    socket.onerror = (e) => {
+      socket.close();
+      resolve({
+        status: 101,
+        response: "",
+      });
+    };
+  });
+}
+
+/**
  * Performs XMLHttpRequest request(s) in the context of the page. The data
  * parameter can be either a single object or an array of objects described
  * below. The requests will be performed one at a time in the order they appear
  * in the data.
  *
  * The objects should have following form (any of them can be omitted; defaults
  * shown below):
  * {
@@ -100,17 +144,22 @@ function promiseXHR(data) {
  *   response: XMLHttpRequest.response
  * }
  */
 addMessageListener("devtools:test:xhr", async function({ data }) {
   const requests = Array.isArray(data) ? data : [data];
   const responses = [];
 
   for (const request of requests) {
-    const response = await promiseXHR(request);
+    let response = null;
+    if (request.ws) {
+      response = await promiseWS(request);
+    } else {
+      response = await promiseXHR(request);
+    }
     responses.push(response);
   }
 
   sendAsyncMessage("devtools:test:xhr", responses);
 });
 
 addMessageListener("devtools:test:profiler", function({ data }) {
   const { method, args, id } = data;