Backed out 2 changesets (bug 1563685) for causing bc perma failures in browser/base/content/test/static/browser_all_files_referenced.js CLOSED TREE
authorshindli <shindli@mozilla.com>
Mon, 08 Jul 2019 22:25:34 +0300
changeset 481704 2a5b1743e58aa2be33887c66c51e31dd12143bb1
parent 481703 9c5b3c7e1ca2240f765d62466cc917ad778407ef
child 481705 8e8e47da345236e1335f1515781d485bb62ec2f1
push id113632
push usernbeleuzu@mozilla.com
push dateTue, 09 Jul 2019 03:54:50 +0000
treeherdermozilla-inbound@d178951a5dcb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1563685
milestone69.0a1
backs out10109cbe96417ef53f8a5f7560a4f5ce43dd70d3
6525d810b0b0314ef26075a570b41bcc19eee333
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
Backed out 2 changesets (bug 1563685) for causing bc perma failures in browser/base/content/test/static/browser_all_files_referenced.js CLOSED TREE Backed out changeset 10109cbe9641 (bug 1563685) Backed out changeset 6525d810b0b0 (bug 1563685)
remote/Connection.jsm
remote/jar.mn
remote/server/WebSocket.jsm
remote/server/WebSocketHandshake.jsm
remote/server/WebSocketTransport.jsm
remote/targets/Target.jsm
--- a/remote/Connection.jsm
+++ b/remote/Connection.jsm
@@ -16,17 +16,17 @@ XPCOMUtils.defineLazyServiceGetter(
   this,
   "UUIDGen",
   "@mozilla.org/uuid-generator;1",
   "nsIUUIDGenerator"
 );
 
 class Connection {
   /**
-   * @param WebSocketTransport transport
+   * @param WebSocketDebuggerTransport transport
    * @param httpd.js's Connection httpdConnection
    */
   constructor(transport, httpdConnection) {
     this.id = UUIDGen.generateUUID().toString();
     this.transport = transport;
     this.httpdConnection = httpdConnection;
 
     this.transport.hooks = this;
--- a/remote/jar.mn
+++ b/remote/jar.mn
@@ -49,13 +49,13 @@ remote.jar:
   content/domains/parent/Network.jsm (domains/parent/Network.jsm)
   content/domains/parent/network/NetworkObserver.jsm (domains/parent/network/NetworkObserver.jsm)
   content/domains/parent/Page.jsm (domains/parent/Page.jsm)
   content/domains/parent/Target.jsm (domains/parent/Target.jsm)
 
   # transport layer
   content/server/HTTPD.jsm (../netwerk/test/httpserver/httpd.js)
   content/server/Stream.jsm (server/Stream.jsm)
-  content/server/WebSocketHandshake.jsm (server/WebSocketHandshake.jsm)
+  content/server/WebSocket.jsm (server/WebSocket.jsm)
   content/server/WebSocketTransport.jsm (server/WebSocketTransport.jsm)
 
   # imports from external folders
   content/external/EventUtils.js (../testing/mochitest/tests/SimpleTest/EventUtils.js)
rename from remote/server/WebSocketHandshake.jsm
rename to remote/server/WebSocket.jsm
--- a/remote/server/WebSocketHandshake.jsm
+++ b/remote/server/WebSocket.jsm
@@ -1,40 +1,87 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-var EXPORTED_SYMBOLS = ["WebSocketHandshake"];
+var EXPORTED_SYMBOLS = ["WebSocketServer"];
 
 // This file is an XPCOM service-ified copy of ../devtools/server/socket/websocket-server.js.
 
 const CC = Components.Constructor;
 
 const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { Stream } = ChromeUtils.import(
+  "chrome://remote/content/server/Stream.jsm"
+);
 const { XPCOMUtils } = ChromeUtils.import(
   "resource://gre/modules/XPCOMUtils.jsm"
 );
 
 XPCOMUtils.defineLazyGetter(this, "WebSocket", () => {
   return Services.appShell.hiddenDOMWindow.WebSocket;
 });
 
 const CryptoHash = CC(
   "@mozilla.org/security/hash;1",
   "nsICryptoHash",
   "initWithString"
 );
 const threadManager = Cc["@mozilla.org/thread-manager;1"].getService();
 
+// limit the header size to put an upper bound on allocated memory
+const HEADER_MAX_LEN = 8000;
+
 // TODO(ato): Merge this with httpd.js so that we can respond to both HTTP/1.1
 // as well as WebSocket requests on the same server.
 
 /**
+ * Read a line from async input stream
+ * and return promise that resolves to the line once it has been read.
+ * If the line is longer than HEADER_MAX_LEN, will throw error.
+ */
+function readLine(input) {
+  return new Promise((resolve, reject) => {
+    let line = "";
+    const wait = () => {
+      input.asyncWait(
+        stream => {
+          try {
+            const amountToRead = HEADER_MAX_LEN - line.length;
+            line += Stream.delimitedRead(input, "\n", amountToRead);
+
+            if (line.endsWith("\n")) {
+              resolve(line.trimRight());
+              return;
+            }
+
+            if (line.length >= HEADER_MAX_LEN) {
+              throw new Error(
+                `Failed to read HTTP header longer than ${HEADER_MAX_LEN} bytes`
+              );
+            }
+
+            wait();
+          } catch (ex) {
+            reject(ex);
+          }
+        },
+        0,
+        0,
+        threadManager.currentThread
+      );
+    };
+
+    wait();
+  });
+}
+
+/**
  * Write a string of bytes to async output stream
  * and return promise that resolves once all data has been written.
  * Doesn't do any UTF-16/UTF-8 conversion.
  * The string is treated as an array of bytes.
  */
 function writeString(output, data) {
   return new Promise((resolve, reject) => {
     const wait = () => {
@@ -58,16 +105,48 @@ function writeString(output, data) {
         threadManager.currentThread
       );
     };
 
     wait();
   });
 }
 
+/**
+ * Read HTTP request from async input stream.
+ *
+ * @return Request line (string) and Map of header names and values.
+ */
+const readHttpRequest = async function(input) {
+  let requestLine = "";
+  const headers = new Map();
+
+  while (true) {
+    const line = await readLine(input);
+    if (line.length == 0) {
+      break;
+    }
+
+    if (!requestLine) {
+      requestLine = line;
+    } else {
+      const colon = line.indexOf(":");
+      if (colon == -1) {
+        throw new Error(`Malformed HTTP header: ${line}`);
+      }
+
+      const name = line.slice(0, colon).toLowerCase();
+      const value = line.slice(colon + 1).trim();
+      headers.set(name, value);
+    }
+  }
+
+  return { requestLine, headers };
+};
+
 /** Write HTTP response (array of strings) to async output stream. */
 function writeHttpResponse(output, response) {
   const s = response.join("\r\n") + "\r\n\r\n";
   return writeString(output, s);
 }
 
 /**
  * Process the WebSocket handshake headers and return the key to be sent in
@@ -166,16 +245,28 @@ async function createWebSocket(transport
       output.close();
     });
 
     socket.onopen = () => resolve(socket);
     socket.onerror = err => reject(err);
   });
 }
 
+/**
+ * Accept an incoming WebSocket server connection.
+ * Takes an established nsISocketTransport in the parameters.
+ * Performs the WebSocket handshake and waits for the WebSocket to open.
+ * Returns Promise with a WebSocket ready to send and receive messages.
+ */
+async function accept(transport, input, output) {
+  const request = await readHttpRequest(input);
+  await serverHandshake(request, output);
+  return createWebSocket(transport, input, output);
+}
+
 /** Upgrade an existing HTTP request from httpd.js to WebSocket. */
 async function upgrade(request, response) {
   // handle response manually, allowing us to send arbitrary data
   response._powerSeized = true;
 
   const { transport, input, output } = response._connection;
 
   const headers = new Map();
@@ -186,9 +277,9 @@ async function upgrade(request, response
     requestLine: `${request.method} ${request.path}`,
     headers,
   };
   await serverHandshake(convertedRequest, output);
 
   return createWebSocket(transport, input, output);
 }
 
-const WebSocketHandshake = { upgrade };
+const WebSocketServer = { accept, upgrade };
--- a/remote/server/WebSocketTransport.jsm
+++ b/remote/server/WebSocketTransport.jsm
@@ -1,31 +1,31 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // This is an XPCOM service-ified copy of ../devtools/shared/transport/websocket-transport.js.
 
 "use strict";
 
-var EXPORTED_SYMBOLS = ["WebSocketTransport"];
+var EXPORTED_SYMBOLS = ["WebSocketDebuggerTransport"];
 
 const { EventEmitter } = ChromeUtils.import(
   "resource://gre/modules/EventEmitter.jsm"
 );
 
-function WebSocketTransport(socket) {
+function WebSocketDebuggerTransport(socket) {
   EventEmitter.decorate(this);
 
   this.active = false;
   this.hooks = null;
   this.socket = socket;
 }
 
-WebSocketTransport.prototype = {
+WebSocketDebuggerTransport.prototype = {
   ready() {
     if (this.active) {
       return;
     }
 
     this.socket.addEventListener("message", this);
     this.socket.addEventListener("close", this);
 
--- a/remote/targets/Target.jsm
+++ b/remote/targets/Target.jsm
@@ -4,21 +4,21 @@
 
 "use strict";
 
 var EXPORTED_SYMBOLS = ["Target"];
 
 const { Connection } = ChromeUtils.import(
   "chrome://remote/content/Connection.jsm"
 );
-const { WebSocketTransport } = ChromeUtils.import(
+const { WebSocketDebuggerTransport } = ChromeUtils.import(
   "chrome://remote/content/server/WebSocketTransport.jsm"
 );
-const { WebSocketHandshake } = ChromeUtils.import(
-  "chrome://remote/content/server/WebSocketHandshake.jsm"
+const { WebSocketServer } = ChromeUtils.import(
+  "chrome://remote/content/server/WebSocket.jsm"
 );
 
 /**
  * Base class for all the Targets.
  */
 class Target {
   /**
    * @param Targets targets
@@ -37,18 +37,18 @@ class Target {
     for (const [conn] of this.sessions) {
       conn.close();
     }
   }
 
   // nsIHttpRequestHandler
 
   async handle(request, response) {
-    const so = await WebSocketHandshake.upgrade(request, response);
-    const transport = new WebSocketTransport(so);
+    const so = await WebSocketServer.upgrade(request, response);
+    const transport = new WebSocketDebuggerTransport(so);
     const conn = new Connection(transport, response._connection);
     this.sessions.set(conn, new this.sessionClass(conn, this));
   }
 
   // XPCOM
 
   get QueryInterface() {
     return ChromeUtils.generateQI([Ci.nsIHttpRequestHandler]);