From c0ad3852b28a038bf75d90271f3271914b1d7811 Mon Sep 17 00:00:00 2001 From: Mike Kalinin Date: Wed, 18 Apr 2018 19:06:03 +0300 Subject: [PATCH 1/5] run testcafe with https protocol (#1985) --- src/client/utils/url.js | 5 ++++- src/proxy/index.js | 26 +++++++++++++++++++------- src/request-pipeline/context.js | 2 ++ 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/client/utils/url.js b/src/client/utils/url.js index c9e2ea01e..b25ba7cac 100644 --- a/src/client/utils/url.js +++ b/src/client/utils/url.js @@ -25,9 +25,12 @@ export function getProxyUrl (url, opts) { /*eslint-disable no-restricted-properties*/ const proxyHostname = opts && opts.proxyHostname || location.hostname; const proxyPort = opts && opts.proxyPort || location.port.toString(); + const protocol = opts && opts.proxyProtocol || location.protocol; /*eslint-enable no-restricted-properties*/ - const proxyProtocol = parsedResourceType.isWebSocket ? 'ws:' : void 0; + const webSocketProtocol = protocol === 'https:' ? 'wss:' : 'ws:'; + const proxyProtocol = parsedResourceType.isWebSocket ? webSocketProtocol : protocol; + const sessionId = opts && opts.sessionId || settings.get().sessionId; let charset = opts && opts.charset; let reqOrigin = opts && opts.reqOrigin; diff --git a/src/proxy/index.js b/src/proxy/index.js index 5e4bf8096..77309c096 100644 --- a/src/proxy/index.js +++ b/src/proxy/index.js @@ -1,5 +1,6 @@ import Router from './router'; import http from 'http'; +import https from 'https'; import * as urlUtils from '../utils/url'; import { readSync as read } from 'read-file-relative'; import { respond204, respond500, respondWithJSON, fetchBody, preventCaching } from '../utils/http'; @@ -22,26 +23,36 @@ function parseAsJson (msg) { } } -function createServerInfo (hostname, port, crossDomainPort) { +function createServerInfo (hostname, port, crossDomainPort, protocol) { return { hostname: hostname, port: port, crossDomainPort: crossDomainPort, - domain: `http://${hostname}:${port}` + protocol: protocol, + domain: `${protocol}//${hostname}:${port}` }; } // Proxy export default class Proxy extends Router { - constructor (hostname, port1, port2) { + constructor (hostname, port1, port2, options) { super(); this.openSessions = {}; - this.server1Info = createServerInfo(hostname, port1, port2); - this.server2Info = createServerInfo(hostname, port2, port1); - this.server1 = http.createServer((req, res) => this._onRequest(req, res, this.server1Info)); - this.server2 = http.createServer((req, res) => this._onRequest(req, res, this.server2Info)); + options = options || {}; + + const serverProvider = options.ssl ? https : http; + const protocol = options.ssl ? 'https:' : 'http:'; + const serverOptions = { + key: options.key || '', + cert: options.cert || '' + }; + + this.server1Info = createServerInfo(hostname, port1, port2, protocol); + this.server2Info = createServerInfo(hostname, port2, port1, protocol); + this.server1 = serverProvider.createServer(serverOptions, (req, res) => this._onRequest(req, res, this.server1Info)); + this.server2 = serverProvider.createServer(serverOptions, (req, res) => this._onRequest(req, res, this.server2Info)); this.server1.on('upgrade', (req, socket, head) => this._onUpgradeRequest(req, socket, head, this.server1Info)); this.server2.on('upgrade', (req, socket, head) => this._onUpgradeRequest(req, socket, head, this.server2Info)); @@ -171,6 +182,7 @@ export default class Proxy extends Router { return urlUtils.getProxyUrl(url, { proxyHostname: this.server1Info.hostname, proxyPort: this.server1Info.port, + proxyProtocol: this.server1Info.protocol, sessionId: session.id }); } diff --git a/src/request-pipeline/context.js b/src/request-pipeline/context.js index b096b126a..43f2ded38 100644 --- a/src/request-pipeline/context.js +++ b/src/request-pipeline/context.js @@ -249,11 +249,13 @@ export default class RequestPipelineContext { toProxyUrl (url, isCrossDomain, resourceType, charset) { const proxyHostname = this.serverInfo.hostname; + const proxyProtocol = this.serverInfo.protocol; const proxyPort = isCrossDomain ? this.serverInfo.crossDomainPort : this.serverInfo.port; const sessionId = this.session.id; return urlUtils.getProxyUrl(url, { proxyHostname, + proxyProtocol, proxyPort, sessionId, resourceType, From 6f59040b902a0b01ec99811bc5828eeeed32156e Mon Sep 17 00:00:00 2001 From: LavrovArtem Date: Mon, 7 May 2018 17:57:51 +0300 Subject: [PATCH 2/5] fix some places, add `--ssl` option to playground, add tests --- Gulpfile.js | 9 +- src/client/utils/url.js | 20 ++-- src/proxy/index.js | 22 ++--- test/client/fixtures/utils/url-test.js | 46 +++++++-- test/playground/server.js | 4 +- test/server/data/page/expected-https.html | 113 ++++++++++++++++++++++ test/server/proxy-test.js | 40 +++++++- 7 files changed, 223 insertions(+), 31 deletions(-) create mode 100644 test/server/data/page/expected-https.html diff --git a/Gulpfile.js b/Gulpfile.js index 67810903c..55e8da62b 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -15,6 +15,8 @@ const util = require('gulp-util'); const ll = require('gulp-ll'); const path = require('path'); +const selfSignedCertificate = require('openssl-self-signed-certificate'); + ll .tasks('lint') .onlyInDebug([ @@ -193,7 +195,12 @@ gulp.task('test-client-travis', ['build'], () => { }); gulp.task('playground', ['set-dev-mode', 'build'], () => { - require('./test/playground/server.js').start(); + const sslOptions = !util.env.ssl ? void 0 : { + key: selfSignedCertificate.key, + cert: selfSignedCertificate.cert + }; + + require('./test/playground/server.js').start(sslOptions); return hang(); }); diff --git a/src/client/utils/url.js b/src/client/utils/url.js index b25ba7cac..0cbb44827 100644 --- a/src/client/utils/url.js +++ b/src/client/utils/url.js @@ -23,17 +23,18 @@ export function getProxyUrl (url, opts) { return url; /*eslint-disable no-restricted-properties*/ - const proxyHostname = opts && opts.proxyHostname || location.hostname; - const proxyPort = opts && opts.proxyPort || location.port.toString(); - const protocol = opts && opts.proxyProtocol || location.protocol; + const proxyHostname = opts && opts.proxyHostname || location.hostname; + const proxyPort = opts && opts.proxyPort || location.port.toString(); + const proxyServerProtocol = opts && opts.proxyProtocol || location.protocol; /*eslint-enable no-restricted-properties*/ - const webSocketProtocol = protocol === 'https:' ? 'wss:' : 'ws:'; - const proxyProtocol = parsedResourceType.isWebSocket ? webSocketProtocol : protocol; + const proxyProtocol = parsedResourceType.isWebSocket + ? proxyServerProtocol.replace('http', 'ws') + : proxyServerProtocol; - const sessionId = opts && opts.sessionId || settings.get().sessionId; - let charset = opts && opts.charset; - let reqOrigin = opts && opts.reqOrigin; + const sessionId = opts && opts.sessionId || settings.get().sessionId; + let charset = opts && opts.charset; + let reqOrigin = opts && opts.reqOrigin; const crossDomainPort = getCrossDomainProxyPort(proxyPort); @@ -72,7 +73,8 @@ export function getProxyUrl (url, opts) { // NOTE: It seems that the relative URL had the leading slash or dots, so that the proxy info path part was // removed by the resolver and we have an origin URL with the incorrect host and protocol. /*eslint-disable no-restricted-properties*/ - if (parsedUrl.protocol === 'http:' && parsedUrl.hostname === proxyHostname && parsedUrl.port === proxyPort) { + if (parsedUrl.protocol === proxyServerProtocol && parsedUrl.hostname === proxyHostname && + parsedUrl.port === proxyPort) { const parsedDestLocation = destLocation.getParsed(); parsedUrl.protocol = parsedDestLocation.protocol; diff --git a/src/proxy/index.js b/src/proxy/index.js index 77309c096..64dde9922 100644 --- a/src/proxy/index.js +++ b/src/proxy/index.js @@ -35,24 +35,24 @@ function createServerInfo (hostname, port, crossDomainPort, protocol) { // Proxy export default class Proxy extends Router { - constructor (hostname, port1, port2, options) { + constructor (hostname, port1, port2, sslOptions) { super(); this.openSessions = {}; - options = options || {}; - - const serverProvider = options.ssl ? https : http; - const protocol = options.ssl ? 'https:' : 'http:'; - const serverOptions = { - key: options.key || '', - cert: options.cert || '' - }; + const protocol = sslOptions ? 'https:' : 'http:'; this.server1Info = createServerInfo(hostname, port1, port2, protocol); this.server2Info = createServerInfo(hostname, port2, port1, protocol); - this.server1 = serverProvider.createServer(serverOptions, (req, res) => this._onRequest(req, res, this.server1Info)); - this.server2 = serverProvider.createServer(serverOptions, (req, res) => this._onRequest(req, res, this.server2Info)); + + if (sslOptions) { + this.server1 = https.createServer(sslOptions, (req, res) => this._onRequest(req, res, this.server1Info)); + this.server2 = https.createServer(sslOptions, (req, res) => this._onRequest(req, res, this.server2Info)); + } + else { + this.server1 = http.createServer((req, res) => this._onRequest(req, res, this.server1Info)); + this.server2 = http.createServer((req, res) => this._onRequest(req, res, this.server2Info)); + } this.server1.on('upgrade', (req, socket, head) => this._onUpgradeRequest(req, socket, head, this.server1Info)); this.server2.on('upgrade', (req, socket, head) => this._onUpgradeRequest(req, socket, head, this.server2Info)); diff --git a/test/client/fixtures/utils/url-test.js b/test/client/fixtures/utils/url-test.js index 23ef23949..bb54ec258 100644 --- a/test/client/fixtures/utils/url-test.js +++ b/test/client/fixtures/utils/url-test.js @@ -12,12 +12,13 @@ var PROXY_PORT = 1337; var PROXY_HOSTNAME = '127.0.0.1'; var PROXY_HOST = PROXY_HOSTNAME + ':' + PROXY_PORT; -function getProxyUrl (url, resourceType) { +function getProxyUrl (url, resourceType, protocol) { return urlUtils.getProxyUrl(url, { proxyHostname: PROXY_HOSTNAME, proxyPort: PROXY_PORT, sessionId: 'sessionId', - resourceType: resourceType + resourceType: resourceType, + proxyProtocol: protocol || 'http:' }); } @@ -170,7 +171,6 @@ test('already proxied', function () { var newUrl = getProxyUrl(proxyUrl, 'i'); strictEqual(urlUtils.parseProxyUrl(newUrl).resourceType, 'i'); - }); test('destination with query, path, hash and host', function () { @@ -187,6 +187,13 @@ test('destination with host only', function () { strictEqual(proxyUrl, 'http://' + PROXY_HOST + '/sessionId/' + destUrl); }); +test('destination with host only (https proxy)', function () { + var destUrl = 'http://test.example.com/'; + var proxyUrl = getProxyUrl(destUrl, '', 'https:'); + + strictEqual(proxyUrl, 'https://' + PROXY_HOST + '/sessionId/' + destUrl); +}); + test('destination with https protocol', function () { var destUrl = 'https://test.example.com:53/'; var proxyUrl = getProxyUrl(destUrl); @@ -195,13 +202,11 @@ test('destination with https protocol', function () { }); test('relative path', function () { - var destUrl = '/Image1.jpg'; - var proxyUrl = urlUtils.getProxyUrl(destUrl); + var proxyUrl = urlUtils.getProxyUrl('/Image1.jpg'); strictEqual(proxyUrl, 'http://' + location.host + '/sessionId/https://example.com/Image1.jpg'); - var relativeUrl = 'share?id=1kjQMWh7IcHdTBbTv6otRvCGYr-p02q206M7aR7dmog0'; - var parsedUrl = urlUtils.parseUrl(relativeUrl); + var parsedUrl = urlUtils.parseUrl('share?id=1kjQMWh7IcHdTBbTv6otRvCGYr-p02q206M7aR7dmog0'); ok(!parsedUrl.hostname); ok(!parsedUrl.host); @@ -211,6 +216,12 @@ test('relative path', function () { strictEqual(parsedUrl.partAfterHost, 'share?id=1kjQMWh7IcHdTBbTv6otRvCGYr-p02q206M7aR7dmog0'); }); +test('relative path (https proxy)', function () { + var proxyUrl = getProxyUrl('/Image1.jpg', '', 'https:'); + + strictEqual(proxyUrl, 'https://' + PROXY_HOST + '/sessionId/https://example.com/Image1.jpg'); +}); + if (window.navigator.platform.toLowerCase() === 'win32' && !browserUtils.isFirefox) { test('relative file path', function () { var destUrl = 'C:\\index.htm'; @@ -281,6 +292,14 @@ test('special pages (GH-339)', function () { }); }); +test('special pages (GH-339) (https proxy)', function () { + sharedUrlUtils.SPECIAL_PAGES.forEach(function (url) { + var proxyUrl = getProxyUrl(url, '', 'https:'); + + strictEqual(proxyUrl, 'https://' + PROXY_HOST + '/sessionId/' + url); + }); +}); + test('convert a charset to lower case (GH-752)', function () { var url = 'http://example.com'; var opts = { @@ -308,6 +327,19 @@ test('http', function () { strictEqual(parsingResult.sessionId, 'sessionId'); }); +test('http (https proxy)', function () { + var proxyUrl = 'https://' + PROXY_HOST + '/sessionId/http://test.example.com:53/PA/TH/?#testHash'; + var parsingResult = urlUtils.parseProxyUrl(proxyUrl); + + strictEqual(parsingResult.destUrl, 'http://test.example.com:53/PA/TH/?#testHash'); + strictEqual(parsingResult.destResourceInfo.protocol, 'http:'); + strictEqual(parsingResult.destResourceInfo.host, 'test.example.com:53'); + strictEqual(parsingResult.destResourceInfo.hostname, 'test.example.com'); + strictEqual(parsingResult.destResourceInfo.port, '53'); + strictEqual(parsingResult.destResourceInfo.partAfterHost, '/PA/TH/?#testHash'); + strictEqual(parsingResult.sessionId, 'sessionId'); +}); + test('https', function () { var proxyUrl = 'http://' + PROXY_HOST + '/sessionId/https://test.example.com:53/PA/TH/?#testHash'; var parsingResult = urlUtils.parseProxyUrl(proxyUrl); diff --git a/test/playground/server.js b/test/playground/server.js index 04687f5a1..af9a19464 100644 --- a/test/playground/server.js +++ b/test/playground/server.js @@ -27,10 +27,10 @@ function createSession () { return session; } -exports.start = () => { +exports.start = sslOptions => { const app = express(); - const proxy = new Proxy('localhost', PROXY_PORT_1, PROXY_PORT_2); const appServer = http.createServer(app); + const proxy = new Proxy('localhost', PROXY_PORT_1, PROXY_PORT_2, sslOptions); app.use(express.bodyParser()); diff --git a/test/server/data/page/expected-https.html b/test/server/data/page/expected-https.html new file mode 100644 index 000000000..3c944dc1d --- /dev/null +++ b/test/server/data/page/expected-https.html @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+
+ +
+ + +
+ + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + diff --git a/test/server/proxy-test.js b/test/server/proxy-test.js index b71150761..744d83837 100644 --- a/test/server/proxy-test.js +++ b/test/server/proxy-test.js @@ -106,6 +106,7 @@ describe('Proxy', () => { let crossDomainServer = null; let proxy = null; let session = null; + let sslOptions = null; before(() => { const app = express(); @@ -398,7 +399,7 @@ describe('Proxy', () => { session.getAuthCredentials = () => null; session.handleFileDownload = () => void 0; - proxy = new Proxy('127.0.0.1', 1836, 1837); + proxy = new Proxy('127.0.0.1', 1836, 1837, sslOptions); }); afterEach(() => { @@ -2277,6 +2278,43 @@ describe('Proxy', () => { }); }); + describe('https proxy', () => { + before(() => { + sslOptions = { + key: selfSignedCertificate.key, + cert: selfSignedCertificate.cert + }; + }); + + after(() => { + sslOptions = null; + }); + + it('Should process pages', () => { + session.id = 'sessionId'; + session.injectable.scripts.push('/script1.js'); + session.injectable.scripts.push('/script2.js'); + session.injectable.styles.push('/styles1.css'); + session.injectable.styles.push('/styles2.css'); + + const options = { + url: proxy.openSession('http://127.0.0.1:2000/page', session), + headers: { + accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*!/!*;q=0.8' + } + }; + + expect(options.url).eql('https://127.0.0.1:1836/sessionId/http://127.0.0.1:2000/page'); + + return request(options) + .then(body => { + const expected = fs.readFileSync('test/server/data/page/expected-https.html').toString(); + + compareCode(body, expected); + }); + }); + }); + describe('Regression', () => { it('Should force "Origin" header for the same-domain requests (B234325)', () => { const options = { From f14b66ac088418388c8dab42ae08be818e7fa9a8 Mon Sep 17 00:00:00 2001 From: LavrovArtem Date: Mon, 7 May 2018 18:09:08 +0300 Subject: [PATCH 3/5] make separate tasks --- Gulpfile.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Gulpfile.js b/Gulpfile.js index 55e8da62b..daa4a4c56 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -195,12 +195,16 @@ gulp.task('test-client-travis', ['build'], () => { }); gulp.task('playground', ['set-dev-mode', 'build'], () => { - const sslOptions = !util.env.ssl ? void 0 : { + require('./test/playground/server.js').start(); + + return hang(); +}); + +gulp.task('playground https', ['set-dev-mode', 'build'], () => { + require('./test/playground/server.js').start({ key: selfSignedCertificate.key, cert: selfSignedCertificate.cert - }; - - require('./test/playground/server.js').start(sslOptions); + }); return hang(); }); From fe3c7decd9832594ce44b606c5e627413382d80c Mon Sep 17 00:00:00 2001 From: LavrovArtem Date: Mon, 7 May 2018 18:15:41 +0300 Subject: [PATCH 4/5] rename tasks --- Gulpfile.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gulpfile.js b/Gulpfile.js index daa4a4c56..1aeb64211 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -194,13 +194,13 @@ gulp.task('test-client-travis', ['build'], () => { .pipe(qunitHarness(CLIENT_TESTS_SETTINGS, SAUCELABS_SETTINGS)); }); -gulp.task('playground', ['set-dev-mode', 'build'], () => { +gulp.task('http-playground', ['set-dev-mode', 'build'], () => { require('./test/playground/server.js').start(); return hang(); }); -gulp.task('playground https', ['set-dev-mode', 'build'], () => { +gulp.task('https-playground', ['set-dev-mode', 'build'], () => { require('./test/playground/server.js').start({ key: selfSignedCertificate.key, cert: selfSignedCertificate.cert From 9693b4c72d29baced305ebcfd1a2a48770342c43 Mon Sep 17 00:00:00 2001 From: LavrovArtem Date: Tue, 8 May 2018 15:15:36 +0300 Subject: [PATCH 5/5] fix tests --- test/client/fixtures/utils/url-test.js | 54 ++++++++++++----------- test/server/data/page/expected-https.html | 4 +- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/test/client/fixtures/utils/url-test.js b/test/client/fixtures/utils/url-test.js index bb54ec258..97582be8b 100644 --- a/test/client/fixtures/utils/url-test.js +++ b/test/client/fixtures/utils/url-test.js @@ -187,13 +187,6 @@ test('destination with host only', function () { strictEqual(proxyUrl, 'http://' + PROXY_HOST + '/sessionId/' + destUrl); }); -test('destination with host only (https proxy)', function () { - var destUrl = 'http://test.example.com/'; - var proxyUrl = getProxyUrl(destUrl, '', 'https:'); - - strictEqual(proxyUrl, 'https://' + PROXY_HOST + '/sessionId/' + destUrl); -}); - test('destination with https protocol', function () { var destUrl = 'https://test.example.com:53/'; var proxyUrl = getProxyUrl(destUrl); @@ -216,12 +209,6 @@ test('relative path', function () { strictEqual(parsedUrl.partAfterHost, 'share?id=1kjQMWh7IcHdTBbTv6otRvCGYr-p02q206M7aR7dmog0'); }); -test('relative path (https proxy)', function () { - var proxyUrl = getProxyUrl('/Image1.jpg', '', 'https:'); - - strictEqual(proxyUrl, 'https://' + PROXY_HOST + '/sessionId/https://example.com/Image1.jpg'); -}); - if (window.navigator.platform.toLowerCase() === 'win32' && !browserUtils.isFirefox) { test('relative file path', function () { var destUrl = 'C:\\index.htm'; @@ -292,14 +279,6 @@ test('special pages (GH-339)', function () { }); }); -test('special pages (GH-339) (https proxy)', function () { - sharedUrlUtils.SPECIAL_PAGES.forEach(function (url) { - var proxyUrl = getProxyUrl(url, '', 'https:'); - - strictEqual(proxyUrl, 'https://' + PROXY_HOST + '/sessionId/' + url); - }); -}); - test('convert a charset to lower case (GH-752)', function () { var url = 'http://example.com'; var opts = { @@ -312,10 +291,31 @@ test('convert a charset to lower case (GH-752)', function () { strictEqual(sharedUrlUtils.getProxyUrl(url, opts), 'http://localhost:5555/sessionId!utf-8/' + url); }); -module('parse proxy url'); +module('https proxy protocol'); -test('http', function () { - var proxyUrl = 'http://' + PROXY_HOST + '/sessionId/http://test.example.com:53/PA/TH/?#testHash'; +test('destination with host only', function () { + var destUrl = 'http://test.example.com/'; + var proxyUrl = getProxyUrl(destUrl, '', 'https:'); + + strictEqual(proxyUrl, 'https://' + PROXY_HOST + '/sessionId/' + destUrl); +}); + +test('relative path', function () { + var proxyUrl = getProxyUrl('/Image1.jpg', '', 'https:'); + + strictEqual(proxyUrl, 'https://' + PROXY_HOST + '/sessionId/https://example.com/Image1.jpg'); +}); + +test('special pages', function () { + sharedUrlUtils.SPECIAL_PAGES.forEach(function (url) { + var proxyUrl = getProxyUrl(url, '', 'https:'); + + strictEqual(proxyUrl, 'https://' + PROXY_HOST + '/sessionId/' + url); + }); +}); + +test('parse proxy url', function () { + var proxyUrl = 'https://' + PROXY_HOST + '/sessionId/http://test.example.com:53/PA/TH/?#testHash'; var parsingResult = urlUtils.parseProxyUrl(proxyUrl); strictEqual(parsingResult.destUrl, 'http://test.example.com:53/PA/TH/?#testHash'); @@ -327,8 +327,10 @@ test('http', function () { strictEqual(parsingResult.sessionId, 'sessionId'); }); -test('http (https proxy)', function () { - var proxyUrl = 'https://' + PROXY_HOST + '/sessionId/http://test.example.com:53/PA/TH/?#testHash'; +module('parse proxy url'); + +test('http', function () { + var proxyUrl = 'http://' + PROXY_HOST + '/sessionId/http://test.example.com:53/PA/TH/?#testHash'; var parsingResult = urlUtils.parseProxyUrl(proxyUrl); strictEqual(parsingResult.destUrl, 'http://test.example.com:53/PA/TH/?#testHash'); diff --git a/test/server/data/page/expected-https.html b/test/server/data/page/expected-https.html index 3c944dc1d..906120628 100644 --- a/test/server/data/page/expected-https.html +++ b/test/server/data/page/expected-https.html @@ -1,8 +1,8 @@ - - + +