File: lib/marionette/drivers/tcp-sync.js
- var wire = require('json-wire-protocol');
- var debug = require('debug')('marionette:tcp-sync');
- var sockittome = require('sockit-to-me');
- var DEFAULT_HOST = 'localhost';
- var DEFAULT_PORT = 2828;
- var SOCKET_TIMEOUT_EXTRA = 500;
- function TcpSync(options) {
- if (!options) {
- options = {};
- }
- if ('port' in options) {
- this.port = options.port;
- }
- if ('host' in options) {
- this.host = options.host;
- }
- if ('connectionTimeout' in options) {
- this.connectionTimeout = options.connectionTimeout;
- }
- this.sockit = new sockittome.Sockit();
- this.sockit.setPollTimeout(this.connectionTimeout + SOCKET_TIMEOUT_EXTRA);
- };
- TcpSync.prototype.isSync = true;
- TcpSync.prototype.host = DEFAULT_HOST;
- TcpSync.prototype.port = DEFAULT_PORT;
- TcpSync.prototype.connectionTimeout = 2000;
- TcpSync.prototype.retryInterval = 300;
- TcpSync.prototype.setScriptTimeout = function(timeout) {
- this.sockit.setPollTimeout(timeout + SOCKET_TIMEOUT_EXTRA);
- };
- /**
- * Utility to wait for the marionette socket to be ready.
- *
- * @method waitForSocket
- * @param {Object} [options] for timeout.
- * @param {Number} [options.interval] time between running test.
- * @param {Number} [options.timeout] maximum wallclock time before
- * failing test.
- * @param {Function} [callback] callback.
- */
- TcpSync.prototype.waitForSocket = function(options, callback) {
- // the logic is lifted from http://dxr.mozilla.org/mozilla-central/source/testing/marionette/client/marionette/marionette.py#562
- if (typeof(options) === 'function') {
- callback = options;
- options = null;
- }
- options = options || {};
- var interval = options.interval || 0;
- var timeout = options.timeout || 30000;
- var socketTimeout = 1000;
- var sockit = new sockittome.Sockit();
- var start = Date.now();
- var self = this;
- // Use sockittome's built in polling timeout during calls to connect
- // to avoid socket misuse.
- sockit.setPollTimeout(socketTimeout);
- var socketConfig = { host: this.host, port: this.port };
- function probeSocket() {
- try {
- debug('probing socket');
- sockit.connect(socketConfig);
- var s = sockit.read(16).toString();
- sockit.close();
- if (s.indexOf(":") != -1) {
- callback();
- return;
- }
- }
- catch(e) {
- debug('exception when probing socket', e.message);
- // Above read _may_ fail so it is important to close the socket...
- sockit.close();
- }
- // timeout. Abort.
- if ((Date.now() - start) > timeout) {
- console.error('timeout connecting to b2g.');
- return;
- }
- // interval delay for the next iteration.
- setTimeout(probeSocket.bind(self), interval);
- };
- probeSocket();
- }
- TcpSync.prototype.connect = function(callback) {
- this.waitForSocket(function _connect() {
- try {
- this.sockit.connect({ host: this.host, port: this.port });
- } catch(err) {
- if (Date.now() - this._beginConnect >= this.connectionTimeout) {
- callback(err);
- }
- setTimeout(_connect.bind(this, callback), this.retryInterval);
- return;
- }
- if (!this._beginConnect) {
- this._beginConnect = Date.now();
- }
- // Ensure this method's resolution is asynchronous in all cases
- process.nextTick(function() {
- debug('socket connected');
- delete this._beginConnect;
- this._readResponse();
- callback();
- }.bind(this));
- }.bind(this));
- };
- TcpSync.prototype.defaultCallback = function(err, result) {
- if (err) {
- throw err;
- }
- return result;
- };
- // Following the json-wire-protocol implementation, read one byte at a time
- // until the 'separator' character is received. The data preceding the
- // delimiter describes the length of the JSON string.
- TcpSync.prototype._readResponse = function() {
- var stream = new wire.Stream();
- var char, data, error;
- stream.on('data', function(parsed) {
- debug('read', parsed);
- data = parsed;
- });
- stream.on('error', function(err) {
- error = err;
- });
- while (!data && !error) {
- stream.write(this.sockit.read(1));
- }
- if (error) {
- throw error;
- }
- return data;
- };
- TcpSync.prototype.send = function(command, callback) {
- debug('write', command);
- this.sockit.write(wire.stringify(command));
- return callback(this._readResponse());
- };
- TcpSync.prototype.close = function() {
- this.sockit.close();
- };
- module.exports = TcpSync;
-