diff --git a/.eslintrc.js b/.eslintrc.js
index 79f8658..ec71f90 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -16,7 +16,9 @@ module.exports = {
"indent": ["error", 2],
"linebreak-style": [ "error", "unix"],
"semi": ["error", "always"],
- "eol-last": ["error", "always"]
+ "eol-last": ["error", "always"],
+ "keyword-spacing": [1],
+ "no-trailing-spaces": ["error", { "skipBlankLines": true }]
},
"overrides": [{
"files": ["src/requestsDebugger.js", "src/commandLine.js", "test/**/*.test.js"],
diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml
new file mode 100644
index 0000000..b382911
--- /dev/null
+++ b/.github/workflows/unit-tests.yml
@@ -0,0 +1,32 @@
+name: 'unit-tests'
+on:
+ pull_request:
+ paths:
+ - './**'
+ - '.github/workflows/unit-tests*'
+ push:
+ paths:
+ - './**'
+ - '.github/workflows/unit-tests*'
+
+jobs:
+ unit-tests:
+ runs-on: ${{ matrix.operating-system }}
+ strategy:
+ matrix:
+ operating-system: [ubuntu-latest, macos-latest]
+ steps:
+ - uses: actions/checkout@v2
+
+ - name: Set Node.js 4.9.1
+ uses: actions/setup-node@master
+ with:
+ node-version: 4.9.1
+
+ - name: npm install
+ working-directory: ./
+ run: npm install
+
+ - name: Lint and Unit tests
+ working-directory: ./
+ run: npm run test
diff --git a/.gitignore b/.gitignore
index 3f8f6b0..b208277 100644
--- a/.gitignore
+++ b/.gitignore
@@ -108,3 +108,9 @@ dist
RequestsDebugger-Mac
RequestsDebugger.exe
RequestsDebugger-Linux*
+
+# VS code settings
+.vscode/
+
+# Others
+.DS_Store
diff --git a/README.md b/README.md
index 12d276f..0fedb34 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,17 @@
## Requests Debugger
+
+
+
+
+
+
+
+
+
### Tool for debugging client side failure of requests, leading to requests getting dropped or not reaching BrowserStack.
## Features
-- Proxy Server to intercept requests fired by client bindings to keep track of their flow.
+- Server to intercept requests fired by client bindings to keep track of their flow.
- Connectivity Checker : To check for the reachability of BrowserStack components, i.e. Rails & Hub.
- Multi-Platform Stats Compatibility : Ability to collect stats of CPU, Network & Memory Stats.
- Retry Mechanism in case a request fails at the client side
@@ -13,6 +22,8 @@
- Start Requests Debugger with the required arguments: `npm run start -- `.
- Supported `args`:
- `--port `: Port on which the Requests Debugger Tool's Proxy will run. Default: 9687
+ - `--reverse-proxy-port `: Port on which the Requests Debugger Tool's Reverse Proxy will run. Default: 9688
+ - `--scheme `: Scheme for requests to browserstack. Default: https
- `--proxy-host `: Hostname of the Upstream Proxy
- `--proxy-port `: Port of the Upstream Proxy. Default: 3128 (if hostname is provided)
- `--proxy-user `: Username for auth of the Upstream Proxy
@@ -30,7 +41,8 @@
- Windows: `RequestsDebugger.exe `
## How to use
-- Since the tool acts like a proxy, you will have to set the proxy to be used by your client binding to `localhost:9687`. i.e.
+- To use tool as reverse proxy, you will have to replace `hub-cloud.browserstack.com` in hub url with `localhost:9688`.
+- To use tool as a proxy, you will have to set the proxy to be used by your client binding to `localhost:9687`. i.e.
- For Java:
- ```
System.getProperties().put("http.proxyHost", "localhost");
@@ -40,7 +52,7 @@
- Set your system's env variable `http_proxy=localhost:9687` and Ruby's Selenium Client Binding will pick the value. Or,
- Run you test by giving the environment variable to your command itself, i.e. `http_proxy=localhost:9687 ruby `
- Similarly, you can also set proxy for other client bindings.
-
+
## Steps to build the executables
- Linux
- `npm run build:linux`
diff --git a/config/constants.js b/config/constants.js
index 346bd9c..fbec60a 100644
--- a/config/constants.js
+++ b/config/constants.js
@@ -1,6 +1,12 @@
-module.exports.VERSION = '1.0.0';
-module.exports.HUB_STATUS_URL = 'http://hub-cloud.browserstack.com/wd/hub/status';
-module.exports.RAILS_AUTOMATE = 'http://automate.browserstack.com';
+module.exports.VERSION = '1.1.0';
+module.exports.BS_DOMAIN = 'browserstack.com';
+module.exports.HUB_HOST = 'hub-cloud.' + this.BS_DOMAIN;
+module.exports.RAILS_HOST = 'automate.' + this.BS_DOMAIN;
+module.exports.HUB_STATUS_PATH = '/wd/hub/status';
+module.exports.HUB_STATUS_URL = 'http://' + this.HUB_HOST + this.HUB_STATUS_PATH;
+module.exports.HUB_STATUS_URL_HTTPS = 'https://' + this.HUB_HOST + this.HUB_STATUS_PATH;
+module.exports.RAILS_AUTOMATE = 'http://' + this.RAILS_HOST;
+module.exports.RAILS_AUTOMATE_HTTPS = 'https://' + this.RAILS_HOST;
module.exports.CONNECTIVITY_REQ_TIMEOUT = 30000;
module.exports.DEFAULT_PROXY_PORT = 3128;
module.exports.CUSTOM_ERROR_RESPONSE_CODE = 502;
@@ -10,6 +16,8 @@ module.exports.PORTS = {
MAX: 65535,
MIN: 1
};
+
+module.exports.LINE_LENGTH = 70;
module.exports.PROTOCOL_REGEX = /(^\w+:|^)\/\//;
module.exports.LOGS = Object.freeze({
@@ -23,13 +31,15 @@ module.exports.LOGS = Object.freeze({
module.exports.RdGlobalConfig = {
RETRY_DELAY: 1000, // in ms
- RD_HANDLER_PORT: process.env.NODE_ENV === 'test' ? 8787 : 9687,
+ RD_HANDLER_PROXY_PORT: process.env.NODE_ENV === 'test' ? 8787 : 9687,
+ RD_HANDLER_REVERSE_PROXY_PORT: process.env.NODE_ENV === 'test' ? 8788 : 9688,
CLIENT_REQ_TIMEOUT: 260000, // in ms
+ SCHEME: 'https'
};
module.exports.COMMON = Object.freeze({
- PING_HUB: 'ping -c 5 hub-cloud.browserstack.com',
- PING_AUTOMATE: 'ping -c 5 automate.browserstack.com'
+ PING_HUB: 'ping -c 5 ' + this.HUB_HOST,
+ PING_AUTOMATE: 'ping -c 5 ' + this.RAILS_HOST
});
module.exports.MAC = Object.freeze({
@@ -49,8 +59,8 @@ module.exports.WIN = Object.freeze({
NETSTAT_ROUTING_TABLE: 'netstat -r',
IPCONFIG_ALL: 'ipconfig /all',
SWAP_USAGE: 'pagefile get AllocatedBaseSize, CurrentUsage', // this is a WMIC command. Prefix with WMIC Path
- PING_HUB: 'ping -n 5 hub-cloud.browserstack.com',
- PING_AUTOMATE: 'ping -n 5 automate.browserstack.com',
+ PING_HUB: 'ping -n 5 ' + this.HUB_HOST,
+ PING_AUTOMATE: 'ping -n 5 ' + this.RAILS_HOST,
LOAD_PERCENTAGE: 'cpu get loadpercentage', // prefix wmic path
});
@@ -78,8 +88,10 @@ module.exports.STATIC_MESSAGES = Object.freeze({
CHECK_NETWORK_STATS: 'Stats : Checking Network Stats',
CHECK_MEMORY_STATS: 'Stats : Checking Memory Stats',
CHECK_CONNECTIVITY: 'Checks : Checking Connectivity With BrowserStack',
- ERR_STARTING_TOOL: 'Error in starting Requests Debugger Tool Proxy: ',
- TOOL_STARTED_ON_PORT: 'Requests Debugger Tool Proxy Started on Port: ',
+ ERR_STARTING_TOOL_PROXY: 'Error in starting Requests Debugger Tool Proxy: ',
+ ERR_STARTING_TOOL_REVERSE_PROXY: 'Error in starting Requests Debugger Tool Reverse Proxy: ',
+ TOOL_PROXY_STARTED_ON_PORT: 'Requests Debugger Tool Proxy Server Started on Port: ',
+ TOOL_REVESE_PROXY_STARTED_ON_PORT: 'Requests Debugger Tool Reverse Proxy Server Started on Port: ',
CPU_STATS_COLLECTED: 'Stats : Initial CPU Stats Collected',
NETWORK_STATS_COLLECTED: 'Stats : Initial Network Stats Collected',
MEMORY_STATS_COLLECTED: 'Stats : Initial Memory Stats Collected',
diff --git a/package.json b/package.json
index 492ca4d..254a817 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,7 @@
"scripts": {
"start": "NODE_ENV=prod node src/requestsDebugger.js",
"lint": "./node_modules/.bin/eslint 'src/*' 'test/*' 'config/*.js'",
- "test": "npm run lint; NODE_ENV=test nyc --reporter=html ./node_modules/mocha/bin/mocha 'test/**/*.test.js'",
+ "test": "npm run lint; NODE_ENV=test ./node_modules/nyc/bin/nyc.js --reporter=html ./node_modules/mocha/bin/mocha 'test/**/*.test.js'",
"build:mac": "npm install; ./node_modules/pkg/lib-es5/bin.js -t node4-macos-x64 src/requestsDebugger.js; mv requestsDebugger RequestsDebugger-Mac",
"build:linux-x86": "npm install; ./node_modules/pkg/lib-es5/bin.js -t node4-linux-x86 src/requestsDebugger.js; mv requestsDebugger RequestsDebugger-Linux-x86",
"build:linux-x64": "npm install; ./node_modules/pkg/lib-es5/bin.js -t node4-linux-x64 src/requestsDebugger.js; mv requestsDebugger RequestsDebugger-Linux-x64",
@@ -34,6 +34,8 @@
"sinon": "7.5.0"
},
"dependencies": {
+ "http-proxy-agent": "^2.1.0",
+ "https-proxy-agent": "^2.1.0",
"uuid": "3.4.0",
"winston": "2.4.4"
},
diff --git a/src/commandLine.js b/src/commandLine.js
index cd1a665..c0850ea 100644
--- a/src/commandLine.js
+++ b/src/commandLine.js
@@ -2,8 +2,8 @@
* Command Line Manager to parse the command line arguments
* and set the necessary fields in RdGlobalConfig.
*/
-
var constants = require('../config/constants');
+
var RdGlobalConfig = constants.RdGlobalConfig;
var CommandLineManager = {
@@ -13,21 +13,25 @@ var CommandLineManager = {
+ "\n"
+ "Usage: RequestsDebugger [ARGUMENTS]\n\n"
+ "ARGUMENTS:\n"
- + " --port : Port on which the Requests Debugger Tool's Proxy will run\n"
- + " Default: " + RdGlobalConfig.RD_HANDLER_PORT + "\n"
- + " --proxy-host : Hostname of the Upstream Proxy\n"
- + " --proxy-port : Port of the Upstream Proxy. Default: " + constants.DEFAULT_PROXY_PORT + " (if hostname is provided)\n"
- + " --proxy-user : Username for auth of the Upstream Proxy\n"
- + " --proxy-pass : Password for auth of the Upstream Proxy\n"
- + " --retry-delay : Delay for the retry of a failed request. Default: " + RdGlobalConfig.RETRY_DELAY + "ms\n"
- + " --request-timeout : Hard timeout for the requests being fired by the tool before receiving any response\n"
- + " Default: " + RdGlobalConfig.CLIENT_REQ_TIMEOUT + "ms\n"
- + " --logs-path : Directory where the '" + constants.LOGS_FOLDER + "' folder will be created\n"
- + " for storing logs. Default: Current Working Directory\n"
- + " --del-logs : Deletes any existing logs from the " + constants.LOGS_FOLDER + "/ directory and initializes\n"
- + " new files for logging\n"
- + " --help : Help for Requests Debugger Tool\n"
- + " --version : Version of the Requests Debugger Tool\n";
+ + " --proxy-port : Port on which the Requests Debugger Tool's Proxy will run\n"
+ + " Default: " + RdGlobalConfig.RD_HANDLER_PROXY_PORT + "\n"
+ + " --reverse-proxy-port : Port on which the Requests Debugger Tool's Reverse Proxy will run\n"
+ + " Default: " + RdGlobalConfig.RD_HANDLER_REVERSE_PROXY_PORT + "\n"
+ + " --scheme : Scheme for requests to browserstack.\n"
+ + " Default: " + RdGlobalConfig.SCHEME + "\n"
+ + " --proxy-host : Hostname of the Upstream Proxy\n"
+ + " --proxy-port : Port of the Upstream Proxy. Default: " + constants.DEFAULT_PROXY_PORT + " (if hostname is provided)\n"
+ + " --proxy-user : Username for auth of the Upstream Proxy\n"
+ + " --proxy-pass : Password for auth of the Upstream Proxy\n"
+ + " --retry-delay : Delay for the retry of a failed request. Default: " + RdGlobalConfig.RETRY_DELAY + "ms\n"
+ + " --request-timeout : Hard timeout for the requests being fired by the tool before receiving any response\n"
+ + " Default: " + RdGlobalConfig.CLIENT_REQ_TIMEOUT + "ms\n"
+ + " --logs-path : Directory where the '" + constants.LOGS_FOLDER + "' folder will be created\n"
+ + " for storing logs. Default: Current Working Directory\n"
+ + " --del-logs : Deletes any existing logs from the " + constants.LOGS_FOLDER + "/ directory and initializes\n"
+ + " new files for logging\n"
+ + " --help : Help for Requests Debugger Tool\n"
+ + " --version : Version of the Requests Debugger Tool\n";
console.log(helpOutput);
},
@@ -38,7 +42,7 @@ var CommandLineManager = {
/**
* Processes the args from the given input array and sets the global config.
- * @param {Array} argv
+ * @param {Array} argv
*/
processArgs: function (argv) {
@@ -67,7 +71,7 @@ var CommandLineManager = {
if (CommandLineManager.validArgValue(argv[index + 1])) {
var probablePort = parseInt(argv[index + 1]);
if (!isNaN(probablePort) && (probablePort <= constants.PORTS.MAX) && (probablePort >= constants.PORTS.MIN)) {
- RdGlobalConfig.RD_HANDLER_PORT = probablePort;
+ RdGlobalConfig.RD_HANDLER_PROXY_PORT = probablePort;
} else {
console.log("\nPort can only range from:", constants.PORTS.MIN, "to:", constants.PORTS.MAX);
invalidArgs.add('--port');
@@ -79,6 +83,24 @@ var CommandLineManager = {
}
}
+ // port for Requests Debugger Reverse Proxy
+ index = argv.indexOf('--reverse-proxy-port');
+ if (index !== -1) {
+ if (CommandLineManager.validArgValue(argv[index + 1])) {
+ var probableReverseProxyPort = parseInt(argv[index + 1]);
+ if (!isNaN(probableReverseProxyPort) && (probableReverseProxyPort <= constants.PORTS.MAX) && (probableReverseProxyPort >= constants.PORTS.MIN)) {
+ RdGlobalConfig.RD_HANDLER_REVERSE_PROXY_PORT = probableReverseProxyPort;
+ } else {
+ console.log("\nPort can only range from:", constants.PORTS.MIN, "to:", constants.PORTS.MAX);
+ invalidArgs.add('--reverse-proxy-port');
+ }
+ argv.splice(index, 2);
+ } else {
+ invalidArgs.add('--reverse-proxy-port');
+ argv.splice(index, 1);
+ }
+ }
+
// delay for retries in case of request failures
index = argv.indexOf('--retry-delay');
if (index !== -1) {
@@ -115,12 +137,34 @@ var CommandLineManager = {
}
}
+ // process proxy host
+ index = argv.indexOf('--scheme');
+ if (index !== -1) {
+ if (CommandLineManager.validArgValue(argv[index + 1])) {
+ var scheme = argv[index + 1];
+ if (!(scheme === 'http' || scheme === 'https')){
+ console.log("\nScheme can only be http/https");
+ invalidArgs.add('--scheme');
+ argv.splice(index, 2);
+ }
+ else {
+ RdGlobalConfig.SCHEME = scheme;
+ argv.splice(index, 2);
+ }
+ } else {
+ invalidArgs.add('--scheme');
+ argv.splice(index, 1);
+ }
+ }
+
// process proxy host
index = argv.indexOf('--proxy-host');
if (index !== -1) {
if (CommandLineManager.validArgValue(argv[index + 1])) {
var host = argv[index + 1];
- host = host.replace(constants.PROTOCOL_REGEX, '');
+ if (host.lastIndexOf("http") !== 0){
+ host = 'http://' + host;
+ }
RdGlobalConfig.proxy = RdGlobalConfig.proxy || {};
RdGlobalConfig.proxy.host = host;
argv.splice(index, 2);
@@ -151,7 +195,7 @@ var CommandLineManager = {
argv.splice(index, 1);
}
}
-
+
// if proxy port value in invalid or doesn't exist and host exists, set the default value
if (RdGlobalConfig.proxy && RdGlobalConfig.proxy.host && (invalidArgs.has('--proxy-port') || !RdGlobalConfig.proxy.port)) {
console.log('\nSetting Default Proxy Port:', constants.DEFAULT_PROXY_PORT, '\n');
@@ -190,7 +234,7 @@ var CommandLineManager = {
argv.splice(index, 1);
}
}
-
+
// if proxy pass is invalid or doesn't exist and username exists, set the password as empty
if (RdGlobalConfig.proxy && RdGlobalConfig.proxy.username && (invalidArgs.has('--proxy-pass') || !RdGlobalConfig.proxy.password)) {
console.log('Setting Proxy Password as Empty\n');
diff --git a/src/connectivity.js b/src/connectivity.js
index 529fd1a..001a651 100644
--- a/src/connectivity.js
+++ b/src/connectivity.js
@@ -12,14 +12,16 @@ var constants = require('../config/constants');
var RdGlobalConfig = constants.RdGlobalConfig;
var Utils = require('./utils');
var https = require('https');
+var HttpProxyAgent = require('http-proxy-agent');
+var HttpsProxyAgent = require('https-proxy-agent');
/**
* Fires the requests to perform connectivity checks
- * @param {Object} requestOptions
- * @param {'http'|'https'} requestType
- * @param {String} description
- * @param {Array} successCodes
- * @param {Function} callback
+ * @param {Object} requestOptions
+ * @param {'http'|'https'} requestType
+ * @param {String} description
+ * @param {Array} successCodes
+ * @param {Function} callback
*/
var fireRequest = function (requestOptions, requestType, description, successCodes, callback) {
var httpOrHttps = requestType === 'http' ? http : https;
@@ -66,7 +68,8 @@ var ConnectivityChecker = {
connectionChecks: [],
reqOpsWithoutProxy: function () {},
- reqOpsWithProxy: function () {},
+ reqOpsHttpWithProxy: function () {},
+ reqOpsHttpsWithProxy: function () {},
httpToHubWithoutProxy: function (callback) {
var requestUrl = constants.HUB_STATUS_URL;
@@ -85,7 +88,7 @@ var ConnectivityChecker = {
},
httpsToHubWithoutProxy: function (callback) {
- var requestUrl = constants.HUB_STATUS_URL;
+ var requestUrl = constants.HUB_STATUS_URL_HTTPS;
var requestOptions = ConnectivityChecker.reqOpsWithoutProxy(requestUrl, 'https');
fireRequest(requestOptions, 'https', 'HTTPS Request To ' + requestUrl + ' Without Proxy', [200], function (response) {
callback(response);
@@ -93,7 +96,7 @@ var ConnectivityChecker = {
},
httpsToRailsWithoutProxy: function (callback) {
- var requestUrl = constants.RAILS_AUTOMATE;
+ var requestUrl = constants.RAILS_AUTOMATE_HTTPS;
var requestOptions = ConnectivityChecker.reqOpsWithoutProxy(requestUrl, 'https');
fireRequest(requestOptions, 'https', 'HTTPS Request to ' + requestUrl + ' Without Proxy', [301, 302], function (response) {
callback(response);
@@ -102,7 +105,7 @@ var ConnectivityChecker = {
httpToHubWithProxy: function (callback) {
var requestUrl = constants.HUB_STATUS_URL;
- var requestOptions = ConnectivityChecker.reqOpsWithProxy(requestUrl, 'http');
+ var requestOptions = ConnectivityChecker.reqOpsHttpWithProxy(requestUrl, 'http');
fireRequest(requestOptions, 'http', 'HTTP Request To ' + requestUrl + ' With Proxy', [200], function (response) {
callback(response);
});
@@ -110,12 +113,27 @@ var ConnectivityChecker = {
httpToRailsWithProxy: function (callback) {
var requestUrl = constants.RAILS_AUTOMATE;
- var requestOptions = ConnectivityChecker.reqOpsWithProxy(requestUrl, 'http');
- fireRequest(requestOptions, 'http', 'HTTP Request To ' + requestUrl + ' With Proxy', [301], function (response) {
+ var requestOptions = ConnectivityChecker.reqOpsHttpWithProxy(requestUrl, 'http');
+ fireRequest(requestOptions, 'http', 'HTTP Request To ' + requestUrl + ' With Proxy', [301, 302], function (response) {
callback(response);
});
},
+ httpsToHubWithProxy: function (callback) {
+ var requestUrl = constants.HUB_STATUS_URL_HTTPS;
+ var requestOptions = ConnectivityChecker.reqOpsHttpsWithProxy(requestUrl, 'https');
+ fireRequest(requestOptions, 'https', 'HTTPS Request To ' + requestUrl + ' With Proxy', [200], function (response) {
+ callback(response);
+ });
+ },
+
+ httpsToRailsWithProxy: function (callback) {
+ var requestUrl = constants.RAILS_AUTOMATE_HTTPS;
+ var requestOptions = ConnectivityChecker.reqOpsHttpsWithProxy(requestUrl, 'https');
+ fireRequest(requestOptions, 'https', 'HTTPS Request To ' + requestUrl + ' With Proxy', [301, 302], function (response) {
+ callback(response);
+ });
+ },
/**
* Decides the checks to perform based on whether any proxy is provided by the
@@ -137,21 +155,40 @@ var ConnectivityChecker = {
return reqOptions;
};
+
if (RdGlobalConfig.proxy) {
+ var proxyOpts = url.parse(RdGlobalConfig.proxy.host + ":" +RdGlobalConfig.proxy.port);
+ if (RdGlobalConfig.proxy.username && RdGlobalConfig.proxy.password) {
+ proxyOpts.auth = RdGlobalConfig.proxy.username + ":" + RdGlobalConfig.proxy.password;
+ }
+
ConnectivityChecker.connectionChecks.push(this.httpToHubWithProxy, this.httpToRailsWithProxy);
/* eslint-disable-next-line no-unused-vars */
- ConnectivityChecker.reqOpsWithProxy = function (reqUrl, reqType) {
+ ConnectivityChecker.reqOpsHttpWithProxy = function (reqUrl, reqType) {
+ var parsedUrl = url.parse(reqUrl);
+ var reqOptions = {
+ method: 'GET',
+ headers: {},
+ host: parsedUrl.hostname,
+ port: parsedUrl.port || 80,
+ path: parsedUrl.path,
+ agent: new HttpProxyAgent(proxyOpts)
+ };
+ return reqOptions;
+ };
+
+ ConnectivityChecker.connectionChecks.push(this.httpsToHubWithProxy, this.httpsToRailsWithProxy);
+ /* eslint-disable-next-line no-unused-vars */
+ ConnectivityChecker.reqOpsHttpsWithProxy = function (reqUrl, reqType) {
var parsedUrl = url.parse(reqUrl);
var reqOptions = {
method: 'GET',
headers: {},
- host: RdGlobalConfig.proxy.host,
- port: RdGlobalConfig.proxy.port,
- path: parsedUrl.href
+ host: parsedUrl.hostname,
+ port: parsedUrl.port || 443,
+ path: parsedUrl.path,
+ agent: new HttpsProxyAgent(proxyOpts)
};
- if (RdGlobalConfig.proxy.username && RdGlobalConfig.proxy.password) {
- reqOptions.headers['Proxy-Authorization'] = Utils.proxyAuthToBase64(RdGlobalConfig.proxy);
- }
return reqOptions;
};
}
@@ -160,9 +197,9 @@ var ConnectivityChecker = {
/**
* Fires the Connectivity Checks in Async Manner
- * @param {String} topic
- * @param {Number|String} uuid
- * @param {Function} callback
+ * @param {String} topic
+ * @param {Number|String} uuid
+ * @param {Function} callback
*/
fireChecks: function (topic, uuid, callback) {
ConnectivityChecker.decideConnectionChecks();
diff --git a/src/logger.js b/src/logger.js
index 6bbdc43..efdccb0 100644
--- a/src/logger.js
+++ b/src/logger.js
@@ -19,7 +19,7 @@ var LogManager = {
* Initializes a logger to the given file and returns the methods to
* interact with the logger.
* Currently, 'info' and 'error' are defined. THe rest can be taken up later if required.
- * @param {String} filename
+ * @param {String} filename
*/
initializeLogger: function (filename) {
var newLogger = LogManager.getLogger(filename);
@@ -36,7 +36,7 @@ var LogManager = {
newLogger.transports.file.json = false;
newLogger.info("************* LOGGER INITIALIZED **************\r\n");
-
+
return {
info: function (topic, message, stringify, data, uuid) {
stringify = stringify || false;
diff --git a/src/proxy.js b/src/proxy.js
new file mode 100644
index 0000000..bf18c67
--- /dev/null
+++ b/src/proxy.js
@@ -0,0 +1,177 @@
+/**
+ * Proxy Server to Intercept the client's requests and handle them on their behalf.
+ * Initiates stats and connectivity checks when a requests fails.
+ * It also responds in selenium understandable error when a request fails
+ * at tool.
+ */
+
+var http = require('http');
+var url = require('url');
+var uuidv4 = require('uuid/v4');
+var Utils = require('./utils');
+var constants = require('../config/constants');
+var ReqLib = require('./requestLib');
+
+var RdGlobalConfig = constants.RdGlobalConfig;
+
+var RdHandler = {
+
+ _requestCounter: 0,
+
+ /**
+ * Generates the request options for firing requests
+ * @param {http.IncomingMessage} clientRequest
+ * @returns {Object}
+ */
+ _generateRequestOptions: function (clientRequest) {
+ var requestOptions = {
+ headers: {}
+ };
+ var parsedClientUrl = url.parse(clientRequest.url);
+ requestOptions.host = parsedClientUrl.host;
+ requestOptions.port = RdGlobalConfig.SCHEME == 'http' ? 80 : 443;
+ requestOptions.path = parsedClientUrl.path;
+ requestOptions.method = clientRequest.method;
+ requestOptions.headers = clientRequest.headers;
+ requestOptions.headers['X-Requests-Debugger'] = clientRequest.id;
+ if (parsedClientUrl.auth) {
+ requestOptions.headers['Authorization'] = Utils.proxyAuthToBase64(parsedClientUrl.auth);
+ }
+ return requestOptions;
+ },
+
+ /**
+ * Frames the error response based on the type of request.
+ * i.e., if its a request originating for Hub, the response
+ * is in the format which the client binding would understand.
+ * @param {Object} parsedRequest
+ * @param {String} errorMessage
+ */
+ _frameErrorResponse: function (parsedRequest, errorMessage) {
+ errorMessage += '. ' + constants.STATIC_MESSAGES.REQ_FAILED_MSG;
+ var parseSessionId = parsedRequest.path.match(/\/wd\/hub\/session\/([a-z0-9]+)\/*/);
+ if (parseSessionId) {
+ var sessionId = parseSessionId[1];
+ return {
+ data: {
+ sessionId: sessionId,
+ status: 13,
+ value: {
+ message: errorMessage,
+ error: constants.STATIC_MESSAGES.REQ_FAILED_MSG
+ },
+ state: 'error'
+ },
+ statusCode: constants.CUSTOM_ERROR_RESPONSE_CODE
+ };
+ } else {
+ return {
+ data: {
+ message: errorMessage,
+ error: constants.STATIC_MESSAGES.REQ_FAILED_MSG
+ },
+ statusCode: constants.CUSTOM_ERROR_RESPONSE_CODE
+ };
+ }
+ },
+
+ /**
+ * Handler for incoming requests to Requests Debugger Tool server.
+ * @param {http.IncomingMessage} clientRequest
+ * @param {http.ServerResponse} clientResponse
+ */
+ requestHandler: function (clientRequest, clientResponse) {
+ var parsedClientUrl = url.parse(clientRequest.url);
+ var metaData = {
+ clientRequestUrl: clientRequest.url,
+ toolRequestUrl: RdGlobalConfig.SCHEME + "://" + parsedClientUrl.host + parsedClientUrl.path
+ };
+ clientRequest.id = ++RdHandler._requestCounter + '::' + uuidv4();
+ var request = {
+ method: clientRequest.method,
+ url: clientRequest.url,
+ headers: clientRequest.headers,
+ data: []
+ };
+ RdGlobalConfig.reqLogger.info(constants.TOPICS.CLIENT_REQUEST_START, clientRequest.method + ' ' + metaData.clientRequestUrl,
+ false, {
+ headers: request.headers
+ },
+ clientRequest.id);
+
+ var furtherRequestOptions = RdHandler._generateRequestOptions(clientRequest);
+
+ var paramsForRequest = {
+ request: request,
+ furtherRequestOptions: furtherRequestOptions
+ };
+
+ ReqLib.call(paramsForRequest, clientRequest, metaData)
+ .then(function (response) {
+ RdGlobalConfig.reqLogger.info(constants.TOPICS.CLIENT_RESPONSE_END, clientRequest.method + ' ' + metaData.clientRequestUrl + ', Status Code: ' + response.statusCode,
+ false, {
+ data: response.data,
+ headers: response.headers,
+ },
+ clientRequest.id);
+
+ clientResponse.writeHead(response.statusCode, response.headers);
+ clientResponse.end(response.data);
+ })
+ .catch(function (err) {
+ RdGlobalConfig.reqLogger.error(err.customTopic || constants.TOPICS.UNEXPECTED_ERROR, clientRequest.method + ' ' + metaData.toolRequestUrl,
+ false, {
+ errorMessage: err.message.toString()
+ },
+ clientRequest.id);
+
+ var errorResponse = RdHandler._frameErrorResponse(furtherRequestOptions, err.message.toString());
+ RdGlobalConfig.reqLogger.error(constants.TOPICS.CLIENT_RESPONSE_END, clientRequest.method + ' ' + metaData.clientRequestUrl + ', Status Code: ' + errorResponse.statusCode,
+ false,
+ errorResponse.data,
+ clientRequest.id);
+
+ clientResponse.writeHead(errorResponse.statusCode);
+ clientResponse.end(JSON.stringify(errorResponse.data));
+ });
+ },
+
+ /**
+ * Starts the proxy server on the given port
+ * @param {String|Number} port
+ * @param {Function} callback
+ */
+ startProxyServer: function (port, callback) {
+ try {
+ RdHandler.proxyServer = http.createServer(RdHandler.requestHandler);
+ RdHandler.proxyServer.listen(port);
+ RdHandler.proxyServer.on('listening', function () {
+ callback(null, port);
+ });
+ RdHandler.proxyServer.on('error', function (err) {
+ callback(err.toString(), null);
+ });
+ } catch (e) {
+ callback(e.toString(), null);
+ }
+ },
+
+ /**
+ * Stops the currently running proxy server
+ * @param {Function} callback
+ */
+ stopProxyServer: function (callback) {
+ try {
+ if (RdHandler.proxyServer) {
+ RdHandler.proxyServer.close();
+ RdHandler.proxyServer = null;
+ }
+ callback(null, true);
+ } catch (e) {
+ callback(e.toString(), null);
+ }
+ },
+
+};
+
+module.exports = RdHandler;
diff --git a/src/requestLib.js b/src/requestLib.js
index 4e5e318..159663b 100644
--- a/src/requestLib.js
+++ b/src/requestLib.js
@@ -1,29 +1,41 @@
var http = require('http');
+var https = require('https');
var constants = require('../config/constants');
var Utils = require('./utils');
-var keepAliveAgent = new http.Agent({
- keepAlive: true
-});
-var RdGlobalConfig = constants.RdGlobalConfig;
+var url = require('url');
+var HttpProxyAgent = require('http-proxy-agent');
+var HttpsProxyAgent = require('https-proxy-agent');
+var RdGlobalConfig = constants.RdGlobalConfig;
+var httpKeepAliveAgent = new http.Agent({keepAlive: true});
+var httpsKeepAliveAgent = new https.Agent({keepAlive: true});
+var httpProxyAgent = null;
+var httpsProxyAgent = null;
var RequestLib = {
+
/**
* Method to perform the request on behalf of the client
- * @param {{request: Object, furtherRequestOptions: Object}} params
- * @param {http.IncomingMessage} clientRequest
- * @param {Number} retries
+ * @param {schemeObj: Object} schemeObj
+ * @param {{request: Object, furtherRequestOptions: Object}} params
+ * @param {http.IncomingMessage} clientRequest
+ * @param {Number} retries
*/
- _makeRequest: function (params, clientRequest, retries) {
+ _makeRequest: function (schemeObj, params, clientRequest, metaData, retries) {
return new Promise(function (resolve, reject) {
- var requestOptions = Object.assign({}, params.furtherRequestOptions, {
- agent: keepAliveAgent
- });
-
- // Adding a custom header for usage and debugging purpose at BrowserStack
- requestOptions.headers['X-Requests-Debugger'] = clientRequest.id;
-
- // Initialize the request to be fired on behalf of the client
- var request = http.request(requestOptions, function (response) {
+ var requestOptions = Object.assign({}, params.furtherRequestOptions);
+ requestOptions.agent = RdGlobalConfig.SCHEME === 'http' ? httpKeepAliveAgent : httpsKeepAliveAgent;
+ if (RdGlobalConfig.proxy) {
+ if (!httpProxyAgent && !httpsProxyAgent) {
+ var proxyOpts = url.parse(RdGlobalConfig.proxy.host + ":" +RdGlobalConfig.proxy.port);
+ if (RdGlobalConfig.proxy.username && RdGlobalConfig.proxy.password) {
+ proxyOpts.auth = RdGlobalConfig.proxy.username + ":" + RdGlobalConfig.proxy.password;
+ }
+ httpProxyAgent = new HttpProxyAgent(proxyOpts);
+ httpsProxyAgent = new HttpsProxyAgent(proxyOpts);
+ }
+ requestOptions.agent = RdGlobalConfig.SCHEME === 'http' ? httpProxyAgent : httpsProxyAgent;
+ }
+ var request = schemeObj.request(requestOptions, function (response) {
var responseToSend = {
statusCode: response.statusCode,
headers: response.headers,
@@ -49,9 +61,8 @@ var RequestLib = {
// Log the request that will be initiated on behalf of the client
request.on('finish', function () {
- RdGlobalConfig.reqLogger.info(constants.TOPICS.TOOL_REQUEST_WITH_RETRIES + retries, clientRequest.method + ' ' + clientRequest.url,
- false,
- Object.assign({}, params.furtherRequestOptions, {
+ RdGlobalConfig.reqLogger.info(constants.TOPICS.TOOL_REQUEST_WITH_RETRIES + retries,
+ clientRequest.method + ' ' + metaData.toolRequestUrl, false, Object.assign({}, params.furtherRequestOptions, {
data: Buffer.concat(params.request.data).toString()
}),
clientRequest.id);
@@ -86,7 +97,7 @@ var RequestLib = {
});
}
});
-
+
clientRequest.on('error', function (err) {
request.end();
reject({
@@ -94,9 +105,9 @@ var RequestLib = {
customTopic: constants.TOPICS.CLIENT_REQUEST_WITH_RETRIES + retries
});
});
-
+
clientRequest.on('end', function () {
- RdGlobalConfig.reqLogger.info(constants.TOPICS.CLIENT_REQUEST_END, params.request.method + ' ' + params.request.url, false, {
+ RdGlobalConfig.reqLogger.info(constants.TOPICS.CLIENT_REQUEST_END, params.request.method + ' ' + metaData.clientRequestUrl, false, {
data: Buffer.concat(params.request.data).toString()
},
clientRequest.id);
@@ -111,13 +122,14 @@ var RequestLib = {
/**
* Handler for performing request. Includes the retry mechanism when request fails.
- * @param {{request: Object, furtherRequestOptions: Object}} params
- * @param {http.IncomingMessage} clientRequest
- * @param {Number} retries
+ * @param {{request: Object, furtherRequestOptions: Object}} params
+ * @param {http.IncomingMessage} clientRequest
+ * @param {Number} retries
*/
- call: function (params, clientRequest, retries) {
+ call: function (params, clientRequest, metaData, retries) {
retries = (typeof retries === 'number') ? Math.min(constants.MAX_RETRIES, Math.max(retries, 0)) : constants.MAX_RETRIES;
- return RequestLib._makeRequest(params, clientRequest, retries)
+ var schemeObj = RdGlobalConfig.SCHEME === "http" ? http : https;
+ return RequestLib._makeRequest(schemeObj, params, clientRequest, metaData, retries)
.catch(function (err) {
var errTopic = err.customTopic || constants.TOPICS.UNEXPECTED_ERROR;
// Collect Network & Connectivity Logs whenever a request fails
@@ -125,7 +137,7 @@ var RequestLib = {
RdGlobalConfig.connHandler(errTopic, clientRequest.id);
if (retries > 0) {
- RdGlobalConfig.reqLogger.error(errTopic, clientRequest.method + ' ' + clientRequest.url,
+ RdGlobalConfig.reqLogger.error(errTopic, clientRequest.method + ' ' + metaData.toolRequestUrl,
false, {
errorMessage: err.message.toString()
},
@@ -133,7 +145,7 @@ var RequestLib = {
return Utils.delay(RdGlobalConfig.RETRY_DELAY)
.then(function () {
- return RequestLib.call(params, clientRequest, retries - 1, false);
+ return RequestLib.call(params, clientRequest, metaData, retries - 1, false);
});
} else {
throw err;
@@ -143,4 +155,3 @@ var RequestLib = {
};
module.exports = RequestLib;
-
diff --git a/src/requestsDebugger.js b/src/requestsDebugger.js
index 765e76e..b35f718 100644
--- a/src/requestsDebugger.js
+++ b/src/requestsDebugger.js
@@ -11,7 +11,8 @@ var RdGlobalConfig = constants.RdGlobalConfig;
var STATIC_MESSAGES = constants.STATIC_MESSAGES;
var CommandLineManager = require('./commandLine');
var ConnectivityChecker = require('./connectivity');
-var RdHandler = require('./server');
+var proxy = require('./proxy');
+var reverseProxy = require('./reverseProxy');
var StatsFactory = require('./stats/statsFactory');
var LogManager = require('./logger');
var fs = require('fs');
@@ -111,42 +112,51 @@ var RdTool = {
*/
start: function () {
CommandLineManager.processArgs(process.argv);
- console.log(Utils.formatAndBeautifyLine(STATIC_MESSAGES.STARTING_TOOL, '-', '-', 60, true));
+ console.log(Utils.formatAndBeautifyLine(STATIC_MESSAGES.STARTING_TOOL, '-', '-', constants.LINE_LENGTH, true));
RdTool.initLoggersAndHandlers();
/* eslint-disable indent */
console.log(Utils.formatAndBeautifyLine("Refer '" + RdGlobalConfig.LOGS_DIRECTORY + "' folder for CPU/Network/Memory" +
" Stats and Connectivity Checks with BrowserStack components",
'', '-', 60, true));
/*eslint-enable indent*/
-
- console.log(Utils.formatAndBeautifyLine(STATIC_MESSAGES.CHECK_CPU_STATS, '', '-', 60, true));
+ console.log(Utils.formatAndBeautifyLine(STATIC_MESSAGES.CHECK_CPU_STATS, '', '-', constants.LINE_LENGTH, true));
RdGlobalConfig.cpuLogHandler('Initial CPU', null, function () {
- console.log(Utils.formatAndBeautifyLine(STATIC_MESSAGES.CPU_STATS_COLLECTED, '', '-', 60, true));
+ console.log(Utils.formatAndBeautifyLine(STATIC_MESSAGES.CPU_STATS_COLLECTED, '', '-', constants.LINE_LENGTH, true));
});
- console.log(Utils.formatAndBeautifyLine(STATIC_MESSAGES.CHECK_NETWORK_STATS, '', '-', 60, true));
+ console.log(Utils.formatAndBeautifyLine(STATIC_MESSAGES.CHECK_NETWORK_STATS, '', '-', constants.LINE_LENGTH, true));
RdGlobalConfig.networkLogHandler('Initial Network', null, function () {
- console.log(Utils.formatAndBeautifyLine(STATIC_MESSAGES.NETWORK_STATS_COLLECTED, '', '-', 60, true));
+ console.log(Utils.formatAndBeautifyLine(STATIC_MESSAGES.NETWORK_STATS_COLLECTED, '', '-', constants.LINE_LENGTH, true));
});
- console.log(Utils.formatAndBeautifyLine(STATIC_MESSAGES.CHECK_MEMORY_STATS, '', '-', 60, true));
+ console.log(Utils.formatAndBeautifyLine(STATIC_MESSAGES.CHECK_MEMORY_STATS, '', '-', constants.LINE_LENGTH, true));
RdGlobalConfig.memLogHandler('Initial Memory', null, function () {
- console.log(Utils.formatAndBeautifyLine(STATIC_MESSAGES.MEMORY_STATS_COLLECTED, '', '-', 60, true));
+ console.log(Utils.formatAndBeautifyLine(STATIC_MESSAGES.MEMORY_STATS_COLLECTED, '', '-', constants.LINE_LENGTH, true));
});
- console.log(Utils.formatAndBeautifyLine(STATIC_MESSAGES.CHECK_CONNECTIVITY, '', '-', 60, true));
+ console.log(Utils.formatAndBeautifyLine(STATIC_MESSAGES.CHECK_CONNECTIVITY, '', '-', constants.LINE_LENGTH, true));
RdGlobalConfig.connHandler('Initial Connectivity', null, function () {
- console.log(Utils.formatAndBeautifyLine(STATIC_MESSAGES.CONNECTIVITY_CHECKS_DONE, '', '-', 60, true));
+ console.log(Utils.formatAndBeautifyLine(STATIC_MESSAGES.CONNECTIVITY_CHECKS_DONE, '', '-', constants.LINE_LENGTH, true));
});
- RdHandler.startProxy(RdGlobalConfig.RD_HANDLER_PORT, function (err, result) {
+ proxy.startProxyServer(RdGlobalConfig.RD_HANDLER_PROXY_PORT, function (err, result) {
if (err) {
- console.log(STATIC_MESSAGES.ERR_STARTING_TOOL, err);
- console.log('Exiting the Tool...');
+ console.log(STATIC_MESSAGES.ERR_STARTING_TOOL_PROXY, err);
+ console.log('Exiting the Proxy Server...');
process.exit(1);
}
- console.log(Utils.formatAndBeautifyLine(STATIC_MESSAGES.TOOL_STARTED_ON_PORT + result, '', '-', 60, true));
+ console.log(Utils.formatAndBeautifyLine(STATIC_MESSAGES.TOOL_PROXY_STARTED_ON_PORT + result, '', '-', constants.LINE_LENGTH, true));
});
+
+ reverseProxy.startReverseProxyServer(RdGlobalConfig.RD_HANDLER_REVERSE_PROXY_PORT, function (err, result) {
+ if (err) {
+ console.log(STATIC_MESSAGES.ERR_STARTING_TOOL_REVERSE_PROXY, err);
+ console.log('Exiting the Reverse Proxy Server...');
+ process.exit(1);
+ }
+ console.log(Utils.formatAndBeautifyLine(STATIC_MESSAGES.TOOL_REVESE_PROXY_STARTED_ON_PORT + result, '', '-', constants.LINE_LENGTH, true));
+ });
+
}
};
diff --git a/src/reverseProxy.js b/src/reverseProxy.js
new file mode 100644
index 0000000..0b7580b
--- /dev/null
+++ b/src/reverseProxy.js
@@ -0,0 +1,178 @@
+/**
+ * Reverse Proxy Server to Intercept the client's requests and handle them on their behalf.
+ * Initiates stats and connectivity checks when a requests fails.
+ * It also responds in selenium understandable error when a request fails
+ * at tool.
+ */
+
+var http = require('http');
+var url = require('url');
+var uuidv4 = require('uuid/v4');
+var Utils = require('./utils');
+var constants = require('../config/constants');
+var ReqLib = require('./requestLib');
+
+var RdGlobalConfig = constants.RdGlobalConfig;
+
+var RdHandler = {
+
+ _requestCounter: 0,
+
+ /**
+ * Generates the request options for firing requests
+ * @param {http.IncomingMessage} clientRequest
+ * @returns {Object}
+ */
+ _generateRequestOptions: function (clientRequest) {
+ var requestOptions = {
+ headers: {}
+ };
+ var parsedClientUrl = url.parse(clientRequest.url);
+ requestOptions.port = RdGlobalConfig.SCHEME == 'http' ? 80 : 443;
+ requestOptions.path = parsedClientUrl.path;
+ requestOptions.method = clientRequest.method;
+ // Below line copies clientRequest.headers object to requestOptions.headers instead of refering same object
+ requestOptions.headers = Object.assign({}, clientRequest.headers);
+ requestOptions.host = constants.HUB_HOST;
+ requestOptions.headers.host = constants.HUB_HOST;
+ requestOptions.headers['X-Requests-Debugger'] = clientRequest.id;
+ if (parsedClientUrl.auth) {
+ requestOptions.headers['Authorization'] = Utils.proxyAuthToBase64(parsedClientUrl.auth);
+ }
+ return requestOptions;
+ },
+
+ /**
+ * Frames the error response based on the type of request.
+ * i.e., if its a request originating for Hub, the response
+ * is in the format which the client binding would understand.
+ * @param {Object} parsedRequest
+ * @param {String} errorMessage
+ */
+ _frameErrorResponse: function (parsedRequest, errorMessage) {
+ errorMessage += '. ' + constants.STATIC_MESSAGES.REQ_FAILED_MSG;
+ var parseSessionId = parsedRequest.path.match(/\/wd\/hub\/session\/([a-z0-9]+)\/*/);
+ if (parseSessionId) {
+ var sessionId = parseSessionId[1];
+ return {
+ data: {
+ sessionId: sessionId,
+ status: 13,
+ value: {
+ message: errorMessage,
+ error: constants.STATIC_MESSAGES.REQ_FAILED_MSG
+ },
+ state: 'error'
+ },
+ statusCode: constants.CUSTOM_ERROR_RESPONSE_CODE
+ };
+ } else {
+ return {
+ data: {
+ message: errorMessage,
+ error: constants.STATIC_MESSAGES.REQ_FAILED_MSG
+ },
+ statusCode: constants.CUSTOM_ERROR_RESPONSE_CODE
+ };
+ }
+ },
+
+ /**
+ * Handler for incoming requests to Requests Debugger Tool server.
+ * @param {http.IncomingMessage} clientRequest
+ * @param {http.ServerResponse} clientResponse
+ */
+ requestHandler: function (clientRequest, clientResponse) {
+ var parsedClientUrl = url.parse(clientRequest.url);
+ var metaData = {
+ clientRequestUrl: "http://" + clientRequest.headers.host + parsedClientUrl.path,
+ toolRequestUrl: RdGlobalConfig.SCHEME + "://" + constants.HUB_HOST + parsedClientUrl.path
+ };
+ clientRequest.id = ++RdHandler._requestCounter + '::' + uuidv4();
+ var request = {
+ method: clientRequest.method,
+ url: clientRequest.url,
+ headers: clientRequest.headers,
+ data: []
+ };
+ RdGlobalConfig.reqLogger.info(constants.TOPICS.CLIENT_REQUEST_START, clientRequest.method + ' ' + metaData.clientRequestUrl,
+ false, {
+ headers: request.headers
+ },
+ clientRequest.id);
+
+ var furtherRequestOptions = RdHandler._generateRequestOptions(clientRequest);
+
+ var paramsForRequest = {
+ request: request,
+ furtherRequestOptions: furtherRequestOptions
+ };
+ ReqLib.call(paramsForRequest, clientRequest, metaData)
+ .then(function (response) {
+ RdGlobalConfig.reqLogger.info(constants.TOPICS.CLIENT_RESPONSE_END, clientRequest.method + ' ' + metaData.clientRequestUrl + ', Status Code: ' + response.statusCode,
+ false, {
+ data: response.data,
+ headers: response.headers,
+ },
+ clientRequest.id);
+
+ clientResponse.writeHead(response.statusCode, response.headers);
+ clientResponse.end(response.data);
+ })
+ .catch(function (err) {
+ RdGlobalConfig.reqLogger.error(err.customTopic || constants.TOPICS.UNEXPECTED_ERROR, clientRequest.method + ' ' + metaData.toolRequestUrl,
+ false, {
+ errorMessage: err.message.toString()
+ },
+ clientRequest.id);
+
+ var errorResponse = RdHandler._frameErrorResponse(furtherRequestOptions, err.message.toString());
+ RdGlobalConfig.reqLogger.error(constants.TOPICS.CLIENT_RESPONSE_END, clientRequest.method + ' ' + metaData.clientRequestUrl + ', Status Code: ' + errorResponse.statusCode,
+ false,
+ errorResponse.data,
+ clientRequest.id);
+
+ clientResponse.writeHead(errorResponse.statusCode);
+ clientResponse.end(JSON.stringify(errorResponse.data));
+ });
+ },
+
+ /**
+ * Starts the reverse proxy server on the given port
+ * @param {String|Number} port
+ * @param {Function} callback
+ */
+ startReverseProxyServer: function (port, callback) {
+ try {
+ RdHandler.reverseProxyServer = http.createServer(RdHandler.requestHandler);
+ RdHandler.reverseProxyServer.listen(port);
+ RdHandler.reverseProxyServer.on('listening', function () {
+ callback(null, port);
+ });
+ RdHandler.reverseProxyServer.on('error', function (err) {
+ callback(err.toString(), null);
+ });
+ } catch (e) {
+ callback(e.toString(), null);
+ }
+ },
+
+ /**
+ * Stops the currently running reverse proxy server
+ * @param {Function} callback
+ */
+ stopReverseProxyServer: function (callback) {
+ try {
+ if (RdHandler.reverseProxyServer) {
+ RdHandler.reverseProxyServer.close();
+ RdHandler.reverseProxyServer = null;
+ }
+ callback(null, true);
+ } catch (e) {
+ callback(e.toString(), null);
+ }
+ }
+
+};
+
+module.exports = RdHandler;
diff --git a/src/server.js b/src/server.js
deleted file mode 100644
index 451f326..0000000
--- a/src/server.js
+++ /dev/null
@@ -1,211 +0,0 @@
-/**
- * Server to Intercept the client's requests and handle them on their behalf.
- * Initiates stats and connectivity checks when a requests fails.
- * It also responds in selenium understandable error when a request fails
- * at tool.
- */
-
-var http = require('http');
-var url = require('url');
-var uuidv4 = require('uuid/v4');
-var Utils = require('./utils');
-var constants = require('../config/constants');
-var ReqLib = require('./requestLib');
-var RdGlobalConfig = constants.RdGlobalConfig;
-
-var RdHandler = {
-
- _requestCounter: 0,
-
- /**
- * Generates the request options template for firing requests based on
- * whether the user had provided any proxy input or not.
- */
- generatorForRequestOptionsObject: function () {
- RdHandler._reqObjTemplate = {
- method: null,
- headers: {},
- host: null,
- port: null,
- path: null
- };
-
- if (RdGlobalConfig.proxy) {
- RdHandler._reqObjTemplate.host = RdGlobalConfig.proxy.host;
- RdHandler._reqObjTemplate.port = RdGlobalConfig.proxy.port;
-
- if (RdGlobalConfig.proxy.username && RdGlobalConfig.proxy.password) {
- RdHandler._reqObjTemplate.headers['Proxy-Authorization'] = Utils.proxyAuthToBase64(RdGlobalConfig.proxy);
- }
-
- /**
- * Sets the internal method to generate request options if external/upstream
- * proxy exists
- * @param {http.IncomingMessage} clientRequest
- * @returns {Object}
- */
- RdHandler._generateRequestOptions = function (clientRequest) {
- var parsedClientUrl = url.parse(clientRequest.url);
- var headersCopy = Object.assign({}, clientRequest.headers, RdHandler._reqObjTemplate.headers);
- var requestOptions = Object.assign({}, RdHandler._reqObjTemplate);
- requestOptions.path = parsedClientUrl.href;
- requestOptions.method = clientRequest.method;
- requestOptions.headers = headersCopy;
- return requestOptions;
- };
- } else {
-
- /**
- * Sets the internal method to generate request options if external/upstream proxy
- * doesn't exists
- * @param {http.IncomingMessage} clientRequest
- * @returns {Object}
- */
- RdHandler._generateRequestOptions = function (clientRequest) {
- var parsedClientUrl = url.parse(clientRequest.url);
- var requestOptions = Object.assign({}, RdHandler._reqObjTemplate);
- requestOptions.host = parsedClientUrl.hostname;
- requestOptions.port = parsedClientUrl.port || 80;
- requestOptions.path = parsedClientUrl.path;
- requestOptions.method = clientRequest.method;
- requestOptions.headers = clientRequest.headers;
- if (parsedClientUrl.auth) {
- requestOptions.headers['authorization'] = Utils.proxyAuthToBase64(parsedClientUrl.auth);
- }
- return requestOptions;
- };
- }
- },
-
- /**
- * Frames the error response based on the type of request.
- * i.e., if its a request originating for Hub, the response
- * is in the format which the client binding would understand.
- * @param {Object} parsedRequest
- * @param {String} errorMessage
- */
- _frameErrorResponse: function (parsedRequest, errorMessage) {
- errorMessage += '. ' + constants.STATIC_MESSAGES.REQ_FAILED_MSG;
- var parseSessionId = parsedRequest.path.match(/\/wd\/hub\/session\/([a-z0-9]+)\/*/);
- if (parseSessionId) {
- var sessionId = parseSessionId[1];
- return {
- data: {
- sessionId: sessionId,
- status: 13,
- value: {
- message: errorMessage,
- error: constants.STATIC_MESSAGES.REQ_FAILED_MSG
- },
- state: 'error'
- },
- statusCode: constants.CUSTOM_ERROR_RESPONSE_CODE
- };
- } else {
- return {
- data: {
- message: errorMessage,
- error: constants.STATIC_MESSAGES.REQ_FAILED_MSG
- },
- statusCode: constants.CUSTOM_ERROR_RESPONSE_CODE
- };
- }
- },
-
- /**
- * Handler for incoming requests to Requests Debugger Tool proxy server.
- * @param {http.IncomingMessage} clientRequest
- * @param {http.ServerResponse} clientResponse
- */
- requestHandler: function (clientRequest, clientResponse) {
- clientRequest.id = ++RdHandler._requestCounter + '::' + uuidv4();
-
- var request = {
- method: clientRequest.method,
- url: clientRequest.url,
- headers: clientRequest.headers,
- data: []
- };
-
- RdGlobalConfig.reqLogger.info(constants.TOPICS.CLIENT_REQUEST_START, request.method + ' ' + request.url,
- false, {
- headers: request.headers
- },
- clientRequest.id);
-
- var furtherRequestOptions = RdHandler._generateRequestOptions(clientRequest);
-
- var paramsForRequest = {
- request: request,
- furtherRequestOptions: furtherRequestOptions
- };
-
- ReqLib.call(paramsForRequest, clientRequest)
- .then(function (response) {
- RdGlobalConfig.reqLogger.info(constants.TOPICS.CLIENT_RESPONSE_END, clientRequest.method + ' ' + clientRequest.url + ', Status Code: ' + response.statusCode,
- false, {
- data: response.data,
- headers: response.headers,
- },
- clientRequest.id);
-
- clientResponse.writeHead(response.statusCode, response.headers);
- clientResponse.end(response.data);
- })
- .catch(function (err) {
- RdGlobalConfig.reqLogger.error(err.customTopic || constants.TOPICS.UNEXPECTED_ERROR, clientRequest.method + ' ' + clientRequest.url,
- false, {
- errorMessage: err.message.toString()
- },
- clientRequest.id);
-
- var errorResponse = RdHandler._frameErrorResponse(furtherRequestOptions, err.message.toString());
- RdGlobalConfig.reqLogger.error(constants.TOPICS.CLIENT_RESPONSE_END, clientRequest.method + ' ' + clientRequest.url + ', Status Code: ' + errorResponse.statusCode,
- false,
- errorResponse.data,
- clientRequest.id);
-
- clientResponse.writeHead(errorResponse.statusCode);
- clientResponse.end(JSON.stringify(errorResponse.data));
- });
- },
-
- /**
- * Starts the proxy server on the given port
- * @param {String|Number} port
- * @param {Function} callback
- */
- startProxy: function (port, callback) {
- try {
- RdHandler.generatorForRequestOptionsObject();
- RdHandler.server = http.createServer(RdHandler.requestHandler);
- RdHandler.server.listen(port);
- RdHandler.server.on('listening', function () {
- callback(null, port);
- });
- RdHandler.server.on('error', function (err) {
- callback(err.toString(), null);
- });
- } catch (e) {
- callback(e.toString(), null);
- }
- },
-
- /**
- * Stops the currently running proxy server
- * @param {Function} callback
- */
- stopProxy: function (callback) {
- try {
- if (RdHandler.server) {
- RdHandler.server.close();
- RdHandler.server = null;
- }
- callback(null, true);
- } catch (e) {
- callback(e.toString(), null);
- }
- }
-};
-
-module.exports = RdHandler;
diff --git a/src/stats/linuxStats.js b/src/stats/linuxStats.js
index 3aff278..4c70d72 100644
--- a/src/stats/linuxStats.js
+++ b/src/stats/linuxStats.js
@@ -45,7 +45,6 @@ LinuxStats.mem = function (callback) {
memStats.free = parseInt(Utils.fetchPropertyValue(memStatLines, 'memfree'));
memStats.free = memStats.free ? memStats.free * 1024 : os.freemem();
memStats.used = memStats.total - memStats.free;
-
memStats.swapTotal = parseInt(Utils.fetchPropertyValue(memStatLines, 'swaptotal'));
memStats.swapTotal = memStats.swapTotal ? memStats.swapTotal * 1024 : 0;
memStats.swapFree = parseInt(Utils.fetchPropertyValue(memStatLines, 'swapfree'));
diff --git a/src/stats/macStats.js b/src/stats/macStats.js
index 49afc7d..fe32928 100644
--- a/src/stats/macStats.js
+++ b/src/stats/macStats.js
@@ -49,11 +49,11 @@ MacStats.mem = function (callback) {
case 'total':
memStats.swapTotal = parseFloat(statLines[index].split('=')[1].trim()) * 1024 * 1024;
break;
-
+
case 'used':
memStats.swapUsed = parseFloat(statLines[index].split('=')[1].trim()) * 1024 * 1024;
break;
-
+
case 'free':
memStats.swapFree = parseFloat(statLines[index].split('=')[1].trim()) * 1024 * 1024;
break;
diff --git a/src/utils.js b/src/utils.js
index e71821a..20c69b3 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -8,7 +8,7 @@ var fs = require('fs');
* Input can be in the form of:
* 1. 'user:pass'
* 2. { username: 'user', password: 'pass' }
- * @param {String|{username: String, password: String, host: String, port: String|Number}} proxyObj
+ * @param {String|{username: String, password: String, host: String, port: String|Number}} proxyObj
* @returns {String}
*/
var proxyAuthToBase64 = function (proxyObj) {
@@ -23,9 +23,9 @@ var proxyAuthToBase64 = function (proxyObj) {
/**
* Fetch the property value from the string array of content, each separated by a separator
- * @param {Array} content
- * @param {String} propertyToFetch
- * @param {String} separator
+ * @param {Array} content
+ * @param {String} propertyToFetch
+ * @param {String} separator
* @returns {String}
*/
var fetchPropertyValue = function (content, propertyToFetch, separator) {
@@ -48,11 +48,11 @@ var fetchPropertyValue = function (content, propertyToFetch, separator) {
/**
* Beautifies the lines and add prefix/suffix characters to make the line of the required length.
- * @param {String} line
- * @param {String} prefix
- * @param {String} suffix
- * @param {Number} idealLength
- * @param {Boolean} newLine
+ * @param {String} line
+ * @param {String} prefix
+ * @param {String} suffix
+ * @param {Number} idealLength
+ * @param {Boolean} newLine
* @returns {String}
*/
var formatAndBeautifyLine = function (line, prefix, suffix, idealLength, newLine) {
@@ -79,10 +79,10 @@ var formatAndBeautifyLine = function (line, prefix, suffix, idealLength, newLine
/**
* Generates header and footer for the given content.
- * @param {String} content
- * @param {String} title
- * @param {Date} generatedAt
- * @param {Date} startTime
+ * @param {String} content
+ * @param {String} title
+ * @param {Date} generatedAt
+ * @param {Date} startTime
* @returns {String}
*/
var generateHeaderAndFooter = function (content, title, generatedAt, startTime) {
@@ -109,8 +109,8 @@ var generateHeaderAndFooter = function (content, title, generatedAt, startTime)
/**
* Performs multiple exec commands asynchronously and returns the
* result in the same order of the commands array.
- * @param {Array} commands
- * @param {Function} callback
+ * @param {Array} commands
+ * @param {Function} callback
* @returns {Array