Bug 1358988 - Write Marionette port to preference when binding; r=whimboo
authorAndreas Tolfsen <ato@mozilla.com>
Mon, 24 Apr 2017 10:28:57 +0100
changeset 354550 7b3dff3972820476b6e0ff33340a070bd838ba69
parent 354549 9b5e7f9f2816a10c4c1bc9caa5e51d03659e3222
child 354551 4b90d988f6545cba07ea63e9fb9f42bd2db136fe
push id41358
push useratolfsen@mozilla.com
push dateMon, 24 Apr 2017 15:37:10 +0000
treeherderautoland@7b3dff397282 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerswhimboo
bugs1358988
milestone55.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 1358988 - Write Marionette port to preference when binding; r=whimboo Write the allocated port to the marionette.port preference when binding the TCP socket server to the requested port. If the socket is bound to port 0, this instructs the system to atomically allocate a free port in the ephemeral range. Writing the resolved port to a preference will make it possible to communicate this port number to the client, removing any chance of a race condition occurring by the client looking for a free port (binding and releasing) for the server. MozReview-Commit-ID: JwyG2G4YQmX
testing/marionette/server.js
--- a/testing/marionette/server.js
+++ b/testing/marionette/server.js
@@ -27,16 +27,17 @@ const logger = Log.repository.getLogger(
 const {KeepWhenOffline, LoopbackOnly} = Ci.nsIServerSocket;
 
 this.EXPORTED_SYMBOLS = ["server"];
 this.server = {};
 
 const PROTOCOL_VERSION = 3;
 
 const PREF_CONTENT_LISTENER = "marionette.contentListener";
+const PREF_PORT = "marionette.port";
 const PREF_RECOMMENDED = "marionette.prefs.recommended";
 
 // Marionette sets preferences recommended for automation when it starts,
 // unless |marionette.prefs.recommended| has been set to false.
 // Where noted, some prefs should also be set in the profile passed to
 // Marionette to prevent them from affecting startup, since some of these
 // are checked before Marionette initialises.
 const RECOMMENDED_PREFS = new Map([
@@ -268,27 +269,28 @@ const RECOMMENDED_PREFS = new Map([
  */
 server.TCPListener = class {
   /**
    * @param {number} port
    *     Port for server to listen to.
    */
   constructor (port) {
     this.port = port;
+    this.socket = null;
     this.conns = new Set();
     this.nextConnID = 0;
     this.alive = false;
     this._acceptConnections = false;
     this.alteredPrefs = new Set();
   }
 
   /**
    * Function produces a GeckoDriver.
    *
-   * Determines application nameto initialise the driver with.
+   * Determines application name to initialise the driver with.
    *
    * @return {GeckoDriver}
    *     A driver instance.
    */
   driverFactory () {
     Preferences.set(PREF_CONTENT_LISTENER, false);
     return new GeckoDriver(Services.appinfo.name, this);
   }
@@ -298,16 +300,23 @@ server.TCPListener = class {
       logger.info("New connections will no longer be accepted");
     } else {
       logger.info("New connections are accepted again");
     }
 
     this._acceptConnections = value;
   }
 
+  /**
+   * Bind this listener to |port| and start accepting incoming socket
+   * connections on |onSocketAccepted|.
+   *
+   * The marionette.port preference will be populated with the value
+   * of |this.port|.
+   */
   start () {
     if (this.alive) {
       return;
     }
 
     if (Preferences.get(PREF_RECOMMENDED)) {
       // set recommended prefs if they are not already user-defined
       for (let [k, v] of RECOMMENDED_PREFS) {
@@ -316,42 +325,42 @@ server.TCPListener = class {
           Preferences.set(k, v);
           this.alteredPrefs.add(k);
         }
       }
     }
 
     const flags = KeepWhenOffline | LoopbackOnly;
     const backlog = 1;
-    this.listener = new ServerSocket(this.port, flags, backlog);
-    this.listener.asyncListen(this);
+    this.socket = new ServerSocket(this.port, flags, backlog);
+    this.socket.asyncListen(this);
+    this.port = this.socket.port;
+    Preferences.set(PREF_PORT, this.port);
 
     this.alive = true;
     this._acceptConnections = true;
   }
 
   stop () {
     if (!this.alive) {
       return;
     }
 
+    this._acceptConnections = false;
+
+    this.socket.close();
+    this.socket = null;
+
     for (let k of this.alteredPrefs) {
       logger.debug(`Resetting recommended pref ${k}`);
       Preferences.reset(k);
     }
-    this.closeListener();
 
     this.alteredPrefs.clear();
     this.alive = false;
-    this._acceptConnections = false;
-  }
-
-  closeListener () {
-    this.listener.close();
-    this.listener = null;
   }
 
   onSocketAccepted (serverSocket, clientSocket) {
     if (!this._acceptConnections) {
       logger.warn("New connections are currently not accepted");
       return;
     }