mirror of
https://github.com/cypress-io/cypress.git
synced 2026-04-30 12:01:06 -05:00
decaffeinate: Run post-processing cleanups on ca.coffee and 11 other files
This commit is contained in:
committed by
Zach Bloomquist
parent
34b52074c1
commit
900286b1fb
+231
-204
@@ -1,360 +1,387 @@
|
||||
/* eslint-disable
|
||||
brace-style,
|
||||
no-unused-vars,
|
||||
*/
|
||||
// TODO: This file was created by bulk-decaffeinate.
|
||||
// Fix any style issues and re-enable lint.
|
||||
/*
|
||||
* decaffeinate suggestions:
|
||||
* DS102: Remove unnecessary code created because of implicit returns
|
||||
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
|
||||
*/
|
||||
const _ = require("lodash");
|
||||
const { agent, allowDestroy, connect } = require("@packages/network");
|
||||
const debug = require("debug")("cypress:https-proxy");
|
||||
let fs = require("fs-extra");
|
||||
const _ = require('lodash')
|
||||
const { agent, allowDestroy, connect } = require('@packages/network')
|
||||
const debug = require('debug')('cypress:https-proxy')
|
||||
let fs = require('fs-extra')
|
||||
const {
|
||||
getProxyForUrl
|
||||
} = require("proxy-from-env");
|
||||
const https = require("https");
|
||||
const net = require("net");
|
||||
const parse = require("./util/parse");
|
||||
const Promise = require("bluebird");
|
||||
const semaphore = require("semaphore");
|
||||
const url = require("url");
|
||||
getProxyForUrl,
|
||||
} = require('proxy-from-env')
|
||||
const https = require('https')
|
||||
const net = require('net')
|
||||
const parse = require('./util/parse')
|
||||
const Promise = require('bluebird')
|
||||
const semaphore = require('semaphore')
|
||||
const url = require('url')
|
||||
|
||||
fs = Promise.promisifyAll(fs);
|
||||
fs = Promise.promisifyAll(fs)
|
||||
|
||||
let sslServers = {};
|
||||
let sslIpServers = {};
|
||||
const sslSemaphores = {};
|
||||
let sslServers = {}
|
||||
let sslIpServers = {}
|
||||
const sslSemaphores = {}
|
||||
|
||||
//# https://en.wikipedia.org/wiki/Transport_Layer_Security#TLS_record
|
||||
// https://en.wikipedia.org/wiki/Transport_Layer_Security#TLS_record
|
||||
const SSL_RECORD_TYPES = [
|
||||
22, //# Handshake
|
||||
128, 0 //# TODO: what do these unknown types mean?
|
||||
];
|
||||
22, // Handshake
|
||||
128, 0, // TODO: what do these unknown types mean?
|
||||
]
|
||||
|
||||
let onError = err => //# these need to be caught to avoid crashing but do not affect anything
|
||||
debug('server error %o', { err });
|
||||
let onError = (err) => // these need to be caught to avoid crashing but do not affect anything
|
||||
{
|
||||
return debug('server error %o', { err })
|
||||
}
|
||||
|
||||
class Server {
|
||||
constructor(_ca, _port, _options) {
|
||||
this._getServerPortForIp = this._getServerPortForIp.bind(this);
|
||||
this._ca = _ca;
|
||||
this._port = _port;
|
||||
this._options = _options;
|
||||
this._onError = null;
|
||||
this._ipServers = sslIpServers;
|
||||
constructor (_ca, _port, _options) {
|
||||
this._getServerPortForIp = this._getServerPortForIp.bind(this)
|
||||
this._ca = _ca
|
||||
this._port = _port
|
||||
this._options = _options
|
||||
this._onError = null
|
||||
this._ipServers = sslIpServers
|
||||
}
|
||||
|
||||
connect(req, browserSocket, head, options = {}) {
|
||||
//# don't buffer writes - thanks a lot, Nagle
|
||||
//# https://github.com/cypress-io/cypress/issues/3192
|
||||
browserSocket.setNoDelay(true);
|
||||
connect (req, browserSocket, head, options = {}) {
|
||||
// don't buffer writes - thanks a lot, Nagle
|
||||
// https://github.com/cypress-io/cypress/issues/3192
|
||||
browserSocket.setNoDelay(true)
|
||||
|
||||
debug("Writing browserSocket connection headers %o", { url: req.url, headLength: _.get(head, 'length'), headers: req.headers });
|
||||
debug('Writing browserSocket connection headers %o', { url: req.url, headLength: _.get(head, 'length'), headers: req.headers })
|
||||
|
||||
browserSocket.on("error", err => {
|
||||
//# TODO: shouldn't we destroy the upstream socket here?
|
||||
//# and also vise versa if the upstream socket throws?
|
||||
//# we may get this "for free" though because piping will
|
||||
//# automatically forward the TCP errors...?
|
||||
browserSocket.on('error', (err) => {
|
||||
// TODO: shouldn't we destroy the upstream socket here?
|
||||
// and also vise versa if the upstream socket throws?
|
||||
// we may get this "for free" though because piping will
|
||||
// automatically forward the TCP errors...?
|
||||
|
||||
//# nothing to do except catch here, the browser has d/c'd
|
||||
return debug("received error on client browserSocket %o", {
|
||||
err, url: req.url
|
||||
});
|
||||
});
|
||||
// nothing to do except catch here, the browser has d/c'd
|
||||
return debug('received error on client browserSocket %o', {
|
||||
err, url: req.url,
|
||||
})
|
||||
})
|
||||
|
||||
browserSocket.write("HTTP/1.1 200 OK\r\n");
|
||||
browserSocket.write('HTTP/1.1 200 OK\r\n')
|
||||
|
||||
if (req.headers["proxy-connection"] === "keep-alive") {
|
||||
browserSocket.write("Proxy-Connection: keep-alive\r\n");
|
||||
browserSocket.write("Connection: keep-alive\r\n");
|
||||
if (req.headers['proxy-connection'] === 'keep-alive') {
|
||||
browserSocket.write('Proxy-Connection: keep-alive\r\n')
|
||||
browserSocket.write('Connection: keep-alive\r\n')
|
||||
}
|
||||
|
||||
browserSocket.write("\r\n");
|
||||
browserSocket.write('\r\n')
|
||||
|
||||
//# if we somehow already have the head here
|
||||
if (_.get(head, "length")) {
|
||||
//# then immediately make up the connection
|
||||
return this._onFirstHeadBytes(req, browserSocket, head, options);
|
||||
// if we somehow already have the head here
|
||||
if (_.get(head, 'length')) {
|
||||
// then immediately make up the connection
|
||||
return this._onFirstHeadBytes(req, browserSocket, head, options)
|
||||
}
|
||||
|
||||
//# else once we get it make the connection later
|
||||
return browserSocket.once("data", data => {
|
||||
return this._onFirstHeadBytes(req, browserSocket, data, options);
|
||||
});
|
||||
// else once we get it make the connection later
|
||||
return browserSocket.once('data', (data) => {
|
||||
return this._onFirstHeadBytes(req, browserSocket, data, options)
|
||||
})
|
||||
}
|
||||
|
||||
_onFirstHeadBytes(req, browserSocket, head, options) {
|
||||
let odc;
|
||||
debug("Got first head bytes %o", { url: req.url, head: _.chain(head).invoke('toString').slice(0, 64).join('').value() });
|
||||
_onFirstHeadBytes (req, browserSocket, head, options) {
|
||||
let odc
|
||||
|
||||
browserSocket.pause();
|
||||
debug('Got first head bytes %o', { url: req.url, head: _.chain(head).invoke('toString').slice(0, 64).join('').value() })
|
||||
|
||||
if (odc = options.onDirectConnection) {
|
||||
//# if onDirectConnection return true
|
||||
//# then dont proxy, just pass this through
|
||||
browserSocket.pause()
|
||||
|
||||
odc = options.onDirectConnection
|
||||
|
||||
if (odc) {
|
||||
// if onDirectConnection return true
|
||||
// then dont proxy, just pass this through
|
||||
if (odc.call(this, req, browserSocket, head) === true) {
|
||||
return this._makeDirectConnection(req, browserSocket, head);
|
||||
} else {
|
||||
debug("Not making direct connection %o", { url: req.url });
|
||||
return this._makeDirectConnection(req, browserSocket, head)
|
||||
}
|
||||
|
||||
debug('Not making direct connection %o', { url: req.url })
|
||||
}
|
||||
|
||||
return this._onServerConnectData(req, browserSocket, head);
|
||||
return this._onServerConnectData(req, browserSocket, head)
|
||||
}
|
||||
|
||||
_onUpgrade(fn, req, browserSocket, head) {
|
||||
_onUpgrade (fn, req, browserSocket, head) {
|
||||
if (fn) {
|
||||
return fn.call(this, req, browserSocket, head);
|
||||
return fn.call(this, req, browserSocket, head)
|
||||
}
|
||||
}
|
||||
|
||||
_onRequest(fn, req, res) {
|
||||
const hostPort = parse.hostAndPort(req.url, req.headers, 443);
|
||||
_onRequest (fn, req, res) {
|
||||
const hostPort = parse.hostAndPort(req.url, req.headers, 443)
|
||||
|
||||
req.url = url.format({
|
||||
protocol: "https:",
|
||||
protocol: 'https:',
|
||||
hostname: hostPort.host,
|
||||
port: hostPort.port
|
||||
}) + req.url;
|
||||
port: hostPort.port,
|
||||
}) + req.url
|
||||
|
||||
if (fn) {
|
||||
return fn.call(this, req, res);
|
||||
return fn.call(this, req, res)
|
||||
}
|
||||
}
|
||||
|
||||
_getProxyForUrl (urlStr) {
|
||||
const port = Number(_.get(url.parse(urlStr), 'port'))
|
||||
|
||||
_getProxyForUrl(urlStr) {
|
||||
const port = Number(_.get(url.parse(urlStr), 'port'));
|
||||
|
||||
debug('getting proxy URL %o', { port, serverPort: this._port, sniPort: this._sniPort, url: urlStr });
|
||||
debug('getting proxy URL %o', { port, serverPort: this._port, sniPort: this._sniPort, url: urlStr })
|
||||
|
||||
if ([this._sniPort, this._port].includes(port)) {
|
||||
//# https://github.com/cypress-io/cypress/issues/4257
|
||||
//# this is a tunnel to the SNI server or to the main server,
|
||||
//# it should never go through a proxy
|
||||
return undefined;
|
||||
// https://github.com/cypress-io/cypress/issues/4257
|
||||
// this is a tunnel to the SNI server or to the main server,
|
||||
// it should never go through a proxy
|
||||
return undefined
|
||||
}
|
||||
|
||||
return getProxyForUrl(urlStr);
|
||||
return getProxyForUrl(urlStr)
|
||||
}
|
||||
|
||||
_makeDirectConnection(req, browserSocket, head) {
|
||||
const { port, hostname } = url.parse(`https://${req.url}`);
|
||||
_makeDirectConnection (req, browserSocket, head) {
|
||||
const { port, hostname } = url.parse(`https://${req.url}`)
|
||||
|
||||
debug(`Making connection to ${hostname}:${port}`);
|
||||
return this._makeConnection(browserSocket, head, port, hostname);
|
||||
debug(`Making connection to ${hostname}:${port}`)
|
||||
|
||||
return this._makeConnection(browserSocket, head, port, hostname)
|
||||
}
|
||||
|
||||
_makeConnection(browserSocket, head, port, hostname) {
|
||||
let upstreamProxy;
|
||||
_makeConnection (browserSocket, head, port, hostname) {
|
||||
let upstreamProxy
|
||||
const onSocket = (err, upstreamSocket) => {
|
||||
debug('received upstreamSocket callback for request %o', { port, hostname, err });
|
||||
debug('received upstreamSocket callback for request %o', { port, hostname, err })
|
||||
|
||||
onError = err => {
|
||||
browserSocket.destroy(err);
|
||||
onError = (err) => {
|
||||
browserSocket.destroy(err)
|
||||
|
||||
if (this._onError) {
|
||||
return this._onError(err, browserSocket, head, port);
|
||||
return this._onError(err, browserSocket, head, port)
|
||||
}
|
||||
};
|
||||
|
||||
if (err) {
|
||||
return onError(err);
|
||||
}
|
||||
|
||||
upstreamSocket.setNoDelay(true);
|
||||
upstreamSocket.on("error", onError);
|
||||
if (err) {
|
||||
return onError(err)
|
||||
}
|
||||
|
||||
browserSocket.emit('upstream-connected', upstreamSocket);
|
||||
upstreamSocket.setNoDelay(true)
|
||||
upstreamSocket.on('error', onError)
|
||||
|
||||
browserSocket.pipe(upstreamSocket);
|
||||
upstreamSocket.pipe(browserSocket);
|
||||
upstreamSocket.write(head);
|
||||
browserSocket.emit('upstream-connected', upstreamSocket)
|
||||
|
||||
return browserSocket.resume();
|
||||
};
|
||||
browserSocket.pipe(upstreamSocket)
|
||||
upstreamSocket.pipe(browserSocket)
|
||||
upstreamSocket.write(head)
|
||||
|
||||
if (!port) { port = "443"; }
|
||||
return browserSocket.resume()
|
||||
}
|
||||
|
||||
if (upstreamProxy = this._getProxyForUrl(`https://${hostname}:${port}`)) {
|
||||
if (!port) {
|
||||
port = '443'
|
||||
}
|
||||
|
||||
upstreamProxy = this._getProxyForUrl(`https://${hostname}:${port}`)
|
||||
|
||||
if (upstreamProxy) {
|
||||
// todo: as soon as all requests are intercepted, this can go away since this is just for pass-through
|
||||
debug("making proxied connection %o", {
|
||||
debug('making proxied connection %o', {
|
||||
host: `${hostname}:${port}`,
|
||||
proxy: upstreamProxy,
|
||||
});
|
||||
})
|
||||
|
||||
return agent.httpsAgent.createUpstreamProxyConnection({
|
||||
proxy: upstreamProxy,
|
||||
href: `https://${hostname}:${port}`,
|
||||
uri: {
|
||||
port,
|
||||
hostname
|
||||
hostname,
|
||||
},
|
||||
shouldRetry: true
|
||||
}, onSocket);
|
||||
shouldRetry: true,
|
||||
}, onSocket)
|
||||
}
|
||||
|
||||
return connect.createRetryingSocket({ port, host: hostname }, onSocket);
|
||||
return connect.createRetryingSocket({ port, host: hostname }, onSocket)
|
||||
}
|
||||
|
||||
_onServerConnectData(req, browserSocket, head) {
|
||||
let sem, sslServer;
|
||||
const firstBytes = head[0];
|
||||
_onServerConnectData (req, browserSocket, head) {
|
||||
let sem; let sslServer
|
||||
const firstBytes = head[0]
|
||||
|
||||
const makeConnection = port => {
|
||||
debug("Making intercepted connection to %s", port);
|
||||
const makeConnection = (port) => {
|
||||
debug('Making intercepted connection to %s', port)
|
||||
|
||||
return this._makeConnection(browserSocket, head, port, "localhost");
|
||||
};
|
||||
return this._makeConnection(browserSocket, head, port, 'localhost')
|
||||
}
|
||||
|
||||
if (!SSL_RECORD_TYPES.includes(firstBytes)) {
|
||||
//# if this isn't an SSL request then go
|
||||
//# ahead and make the connection now
|
||||
return makeConnection(this._port);
|
||||
// if this isn't an SSL request then go
|
||||
// ahead and make the connection now
|
||||
return makeConnection(this._port)
|
||||
}
|
||||
|
||||
//# else spin up the SNI server
|
||||
const { hostname } = url.parse(`https://${req.url}`);
|
||||
// else spin up the SNI server
|
||||
const { hostname } = url.parse(`https://${req.url}`)
|
||||
|
||||
if (sslServer = sslServers[hostname]) {
|
||||
return makeConnection(sslServer.port);
|
||||
sslServer = sslServers[hostname]
|
||||
|
||||
if (sslServer) {
|
||||
return makeConnection(sslServer.port)
|
||||
}
|
||||
|
||||
//# only be creating one SSL server per hostname at once
|
||||
// only be creating one SSL server per hostname at once
|
||||
if (!(sem = sslSemaphores[hostname])) {
|
||||
sem = (sslSemaphores[hostname] = semaphore(1));
|
||||
sem = (sslSemaphores[hostname] = semaphore(1))
|
||||
}
|
||||
|
||||
return sem.take(() => {
|
||||
const leave = () => process.nextTick(() => sem.leave());
|
||||
const leave = () => {
|
||||
return process.nextTick(() => {
|
||||
return sem.leave()
|
||||
})
|
||||
}
|
||||
|
||||
if (sslServer = sslServers[hostname]) {
|
||||
leave();
|
||||
sslServer = sslServers[hostname]
|
||||
|
||||
return makeConnection(sslServer.port);
|
||||
if (sslServer) {
|
||||
leave()
|
||||
|
||||
return makeConnection(sslServer.port)
|
||||
}
|
||||
|
||||
return this._getPortFor(hostname)
|
||||
.then(function(port) {
|
||||
sslServers[hostname] = { port };
|
||||
.then((port) => {
|
||||
sslServers[hostname] = { port }
|
||||
|
||||
leave();
|
||||
leave()
|
||||
|
||||
return makeConnection(port);
|
||||
});
|
||||
});
|
||||
return makeConnection(port)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
_normalizeKeyAndCert(certPem, privateKeyPem) {
|
||||
_normalizeKeyAndCert (certPem, privateKeyPem) {
|
||||
return {
|
||||
key: privateKeyPem,
|
||||
cert: certPem
|
||||
};
|
||||
key: privateKeyPem,
|
||||
cert: certPem,
|
||||
}
|
||||
}
|
||||
|
||||
_getCertificatePathsFor(hostname) {
|
||||
_getCertificatePathsFor (hostname) {
|
||||
return this._ca.getCertificateKeysForHostname(hostname)
|
||||
.spread(this._normalizeKeyAndCert);
|
||||
.spread(this._normalizeKeyAndCert)
|
||||
}
|
||||
|
||||
_generateMissingCertificates(hostname) {
|
||||
_generateMissingCertificates (hostname) {
|
||||
return this._ca.generateServerCertificateKeys(hostname)
|
||||
.spread(this._normalizeKeyAndCert);
|
||||
.spread(this._normalizeKeyAndCert)
|
||||
}
|
||||
|
||||
_getPortFor(hostname) {
|
||||
_getPortFor (hostname) {
|
||||
return this._getCertificatePathsFor(hostname)
|
||||
.catch(err => {
|
||||
return this._generateMissingCertificates(hostname);
|
||||
}).then((data = {}) => {
|
||||
.catch((err) => {
|
||||
return this._generateMissingCertificates(hostname)
|
||||
}).then((data = {}) => {
|
||||
if (net.isIP(hostname)) {
|
||||
return this._getServerPortForIp(hostname, data);
|
||||
return this._getServerPortForIp(hostname, data)
|
||||
}
|
||||
|
||||
this._sniServer.addContext(hostname, data);
|
||||
this._sniServer.addContext(hostname, data)
|
||||
|
||||
return this._sniPort;
|
||||
});
|
||||
return this._sniPort
|
||||
})
|
||||
}
|
||||
|
||||
_listenHttpsServer(data) {
|
||||
_listenHttpsServer (data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const server = https.createServer(data);
|
||||
const server = https.createServer(data)
|
||||
|
||||
allowDestroy(server);
|
||||
allowDestroy(server)
|
||||
|
||||
server.once("error", reject);
|
||||
server.on("upgrade", this._onUpgrade.bind(this, this._options.onUpgrade));
|
||||
server.on("request", this._onRequest.bind(this, this._options.onRequest));
|
||||
server.once('error', reject)
|
||||
server.on('upgrade', this._onUpgrade.bind(this, this._options.onUpgrade))
|
||||
server.on('request', this._onRequest.bind(this, this._options.onRequest))
|
||||
|
||||
return server.listen(0, '127.0.0.1', () => {
|
||||
const {
|
||||
port
|
||||
} = server.address();
|
||||
port,
|
||||
} = server.address()
|
||||
|
||||
server.removeListener("error", reject);
|
||||
server.on("error", onError);
|
||||
server.removeListener('error', reject)
|
||||
server.on('error', onError)
|
||||
|
||||
return resolve({ server, port });
|
||||
});
|
||||
});
|
||||
return resolve({ server, port })
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
//# browsers will not do SNI for an IP address
|
||||
//# so we need to serve 1 HTTPS server per IP
|
||||
//# https://github.com/cypress-io/cypress/issues/771
|
||||
_getServerPortForIp(ip, data) {
|
||||
let server;
|
||||
if (server = sslIpServers[ip]) {
|
||||
return server.address().port;
|
||||
// browsers will not do SNI for an IP address
|
||||
// so we need to serve 1 HTTPS server per IP
|
||||
// https://github.com/cypress-io/cypress/issues/771
|
||||
_getServerPortForIp (ip, data) {
|
||||
let server
|
||||
|
||||
server = sslIpServers[ip]
|
||||
|
||||
if (server) {
|
||||
return server.address().port
|
||||
}
|
||||
|
||||
return this._listenHttpsServer(data)
|
||||
.then(function({ server, port }) {
|
||||
sslIpServers[ip] = server;
|
||||
.then(({ server, port }) => {
|
||||
sslIpServers[ip] = server
|
||||
|
||||
debug("Created IP HTTPS Proxy Server", { port, ip });
|
||||
debug('Created IP HTTPS Proxy Server', { port, ip })
|
||||
|
||||
return port;
|
||||
});
|
||||
return port
|
||||
})
|
||||
}
|
||||
|
||||
listen() {
|
||||
this._onError = this._options.onError;
|
||||
listen () {
|
||||
this._onError = this._options.onError
|
||||
|
||||
return this._listenHttpsServer({})
|
||||
.tap(({ server, port}) => {
|
||||
this._sniPort = port;
|
||||
this._sniServer = server;
|
||||
.tap(({ server, port }) => {
|
||||
this._sniPort = port
|
||||
this._sniServer = server
|
||||
|
||||
return debug("Created SNI HTTPS Proxy Server", { port });
|
||||
});
|
||||
return debug('Created SNI HTTPS Proxy Server', { port })
|
||||
})
|
||||
}
|
||||
|
||||
close() {
|
||||
close () {
|
||||
const close = () => {
|
||||
const servers = _.values(sslIpServers).concat(this._sniServer);
|
||||
return Promise.map(servers, server => {
|
||||
const servers = _.values(sslIpServers).concat(this._sniServer)
|
||||
|
||||
return Promise.map(servers, (server) => {
|
||||
return Promise.fromCallback(server.destroy)
|
||||
.catch(onError);
|
||||
});
|
||||
};
|
||||
.catch(onError)
|
||||
})
|
||||
}
|
||||
|
||||
return close()
|
||||
.finally(module.exports.reset);
|
||||
.finally(module.exports.reset)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
reset() {
|
||||
sslServers = {};
|
||||
return sslIpServers = {};
|
||||
reset () {
|
||||
sslServers = {}
|
||||
sslIpServers = {}
|
||||
},
|
||||
|
||||
create(ca, port, options = {}) {
|
||||
const srv = new Server(ca, port, options);
|
||||
create (ca, port, options = {}) {
|
||||
const srv = new Server(ca, port, options)
|
||||
|
||||
return srv
|
||||
.listen()
|
||||
.return(srv);
|
||||
}
|
||||
};
|
||||
.return(srv)
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user