Skip to content

Commit ef8112f

Browse files
Elias Faxöjleyba
Elias Faxö
authored andcommitted
Add proxy support to the NodeJS webdriver.http.Client so that it can
connect to a Selenium server through a proxy. Signed-off-by: Jason Leyba <[email protected]>
1 parent 3e1ef01 commit ef8112f

File tree

5 files changed

+99
-12
lines changed

5 files changed

+99
-12
lines changed

Diff for: javascript/node/selenium-webdriver/CHANGES.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## v2.46.0-dev
22

3+
* Added support for using a proxy server for WebDriver commands.
4+
See `Builder#usingWebDriverProxy()` for more info.
35
* Removed deprecated functions:
46
* Capabilities#toJSON()
57
* UnhandledAlertError#getAlert()

Diff for: javascript/node/selenium-webdriver/builder.js

+27-1
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ var Builder = function() {
9595
/** @private {string} */
9696
this.url_ = '';
9797

98+
/** @private {?string} */
99+
this.proxy_ = null;
100+
98101
/** @private {!webdriver.Capabilities} */
99102
this.capabilities_ = new Capabilities();
100103

@@ -156,6 +159,29 @@ Builder.prototype.getServerUrl = function() {
156159
};
157160

158161

162+
/**
163+
* Sets the URL of the proxy to use for the WebDriver's HTTP connections.
164+
* If this method is never called, the Builder will create a connection without
165+
* a proxy.
166+
*
167+
* @param {string} proxy The URL of a proxy to use.
168+
* @return {!Builder} A self reference.
169+
*/
170+
Builder.prototype.usingWebDriverProxy = function(proxy) {
171+
this.proxy_ = proxy;
172+
return this;
173+
};
174+
175+
176+
/**
177+
* @return {string} The URL of the proxy server to use for the WebDriver's HTTP
178+
* connections.
179+
*/
180+
Builder.prototype.getWebDriverProxy = function() {
181+
return this.proxy_;
182+
};
183+
184+
159185
/**
160186
* Sets the desired capabilities when requesting a new session. This will
161187
* overwrite any previously set capabilities.
@@ -415,7 +441,7 @@ Builder.prototype.build = function() {
415441
}
416442

417443
if (url) {
418-
var executor = executors.createExecutor(url);
444+
var executor = executors.createExecutor(url, this.proxy_);
419445
return WebDriver.createSession(executor, capabilities, this.flow_);
420446
}
421447

Diff for: javascript/node/selenium-webdriver/executors.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,13 @@ exports.DeferredExecutor = DeferredExecutor;
5454
* Creates a command executor that uses WebDriver's JSON wire protocol.
5555
* @param {(string|!webdriver.promise.Promise.<string>)} url The server's URL,
5656
* or a promise that will resolve to that URL.
57+
* @param {string=} opt_proxy (optional) The URL of the HTTP proxy for the
58+
* client to use.
5759
* @returns {!webdriver.CommandExecutor} The new command executor.
5860
*/
59-
exports.createExecutor = function(url) {
61+
exports.createExecutor = function(url, opt_proxy) {
6062
return new DeferredExecutor(promise.when(url, function(url) {
61-
var client = new HttpClient(url);
63+
var client = new HttpClient(url, null, opt_proxy);
6264
return new HttpExecutor(client);
6365
}));
6466
};

Diff for: javascript/node/selenium-webdriver/http/index.js

+31-7
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,12 @@ var base = require('../_base'),
3333
* @param {string} serverUrl URL for the WebDriver server to send commands to.
3434
* @param {http.Agent=} opt_agent The agent to use for each request.
3535
* Defaults to {@code http.globalAgent}.
36+
* @param {string=} opt_proxy The proxy to use for the connection to the server.
37+
* Default is to use no proxy.
3638
* @constructor
3739
* @implements {webdriver.http.Client}
3840
*/
39-
var HttpClient = function(serverUrl, opt_agent) {
41+
var HttpClient = function(serverUrl, opt_agent, opt_proxy) {
4042
var parsedUrl = url.parse(serverUrl);
4143
if (!parsedUrl.hostname) {
4244
throw new Error('Invalid server URL: ' + serverUrl);
@@ -45,6 +47,9 @@ var HttpClient = function(serverUrl, opt_agent) {
4547
/** @private {http.Agent} */
4648
this.agent_ = opt_agent;
4749

50+
/** @private {string} */
51+
this.proxy_ = opt_proxy;
52+
4853
/**
4954
* Base options for each request.
5055
* @private {!Object}
@@ -81,10 +86,12 @@ HttpClient.prototype.send = function(httpRequest, callback) {
8186
path: path,
8287
headers: httpRequest.headers
8388
};
89+
8490
if (this.agent_) {
8591
options.agent = this.agent_;
8692
}
87-
sendRequest(options, callback, data);
93+
94+
sendRequest(options, callback, data, this.proxy_);
8895
};
8996

9097

@@ -94,8 +101,25 @@ HttpClient.prototype.send = function(httpRequest, callback) {
94101
* @param {function(Error, !webdriver.http.Response=)} callback The function to
95102
* invoke with the server's response.
96103
* @param {string=} opt_data The data to send with the request.
104+
* @param {string=} opt_proxy The proxy server to use for the request.
97105
*/
98-
var sendRequest = function(options, callback, opt_data) {
106+
var sendRequest = function(options, callback, opt_data, opt_proxy) {
107+
var host = options.host;
108+
var port = options.port;
109+
110+
if (opt_proxy) {
111+
var proxy = url.parse(opt_proxy);
112+
113+
options.headers['Host'] = options.host;
114+
options.host = proxy.hostname;
115+
options.port = proxy.port;
116+
117+
if (proxy.auth) {
118+
options.headers['Proxy-Authorization'] =
119+
'Basic ' + new Buffer(proxy.auth).toString('base64');
120+
}
121+
}
122+
99123
var request = http.request(options, function(response) {
100124
if (response.statusCode == 302 || response.statusCode == 303) {
101125
try {
@@ -109,8 +133,8 @@ var sendRequest = function(options, callback, opt_data) {
109133
}
110134

111135
if (!location.hostname) {
112-
location.hostname = options.host;
113-
location.port = options.port;
136+
location.hostname = host;
137+
location.port = port;
114138
}
115139

116140
request.abort();
@@ -122,7 +146,7 @@ var sendRequest = function(options, callback, opt_data) {
122146
headers: {
123147
'Accept': 'application/json; charset=utf-8'
124148
}
125-
}, callback);
149+
}, callback, undefined, opt_proxy);
126150
return;
127151
}
128152

@@ -138,7 +162,7 @@ var sendRequest = function(options, callback, opt_data) {
138162
request.on('error', function(e) {
139163
if (e.code === 'ECONNRESET') {
140164
setTimeout(function() {
141-
sendRequest(options, callback, opt_data);
165+
sendRequest(options, callback, opt_data, opt_proxy);
142166
}, 15);
143167
} else {
144168
var message = e.message;

Diff for: javascript/node/selenium-webdriver/test/http/http_test.js

+35-2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ var promise = require('../..').promise;
2525
var test = require('../../lib/test');
2626

2727
describe('HttpClient', function() {
28+
this.timeout(4*1000);
29+
2830
var server = new Server(function(req, res) {
2931
if (req.method == 'GET' && req.url == '/echo') {
3032
res.writeHead(200, req.headers);
@@ -42,6 +44,14 @@ describe('HttpClient', function() {
4244
res.writeHead(303, {});
4345
res.end();
4446

47+
} else if (req.method == 'GET' && req.url == '/proxy') {
48+
res.writeHead(200, req.headers);
49+
res.end();
50+
51+
} else if (req.method == 'GET' && req.url == '/proxy/redirect') {
52+
res.writeHead(303, {'Location': '/proxy'});
53+
res.end();
54+
4555
} else {
4656
res.writeHead(404, {});
4757
res.end();
@@ -90,10 +100,33 @@ describe('HttpClient', function() {
90100
test.it('handles malformed redirect responses', function() {
91101
var request = new HttpRequest('GET', '/badredirect');
92102
var client = new HttpClient(server.url());
93-
return promise.checkedNodeCall(client.send.bind(client, request)).
94-
thenCatch(function(err) {
103+
return promise.checkedNodeCall(client.send.bind(client, request))
104+
.thenCatch(function(err) {
95105
assert.ok(/Failed to parse "Location"/.test(err.message),
96106
'Not the expected error: ' + err.message);
97107
});
98108
});
109+
110+
test.it('proxies requests through the webdriver proxy', function() {
111+
var request = new HttpRequest('GET', '/proxy');
112+
var client = new HttpClient(
113+
'http://another.server.com', undefined, server.url());
114+
return promise.checkedNodeCall(client.send.bind(client, request))
115+
.then(function(response) {
116+
assert.equal(200, response.status);
117+
assert.equal('another.server.com', response.headers['host']);
118+
});
119+
});
120+
121+
test.it(
122+
'proxies requests through the webdriver proxy on redirect', function() {
123+
var request = new HttpRequest('GET', '/proxy/redirect');
124+
var client = new HttpClient(
125+
'http://another.server.com', undefined, server.url());
126+
return promise.checkedNodeCall(client.send.bind(client, request))
127+
.then(function(response) {
128+
assert.equal(200, response.status);
129+
assert.equal('another.server.com', response.headers['host']);
130+
});
131+
});
99132
});

0 commit comments

Comments
 (0)