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
--- 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;
}