decaffeinate: Run post-processing cleanups on ca.coffee and 11 other files

This commit is contained in:
decaffeinate
2020-02-20 12:04:58 -05:00
committed by Zach Bloomquist
parent 34b52074c1
commit 900286b1fb
12 changed files with 1074 additions and 919 deletions

View File

@@ -1,262 +1,268 @@
// TODO: This file was created by bulk-decaffeinate.
// Sanity-check the conversion and remove this comment.
/*
* 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");
let fs = require("fs-extra");
const os = require("os");
const path = require("path");
const Forge = require("node-forge");
const Promise = require("bluebird");
const _ = require('lodash')
let fs = require('fs-extra')
const os = require('os')
const path = require('path')
const Forge = require('node-forge')
const Promise = require('bluebird')
fs = Promise.promisifyAll(fs);
fs = Promise.promisifyAll(fs)
const {
pki
} = Forge;
pki,
} = Forge
const generateKeyPairAsync = Promise.promisify(pki.rsa.generateKeyPair);
const generateKeyPairAsync = Promise.promisify(pki.rsa.generateKeyPair)
const ipAddressRe = /^[\d\.]+$/;
const asterisksRe = /\*/g;
const ipAddressRe = /^[\d\.]+$/
const asterisksRe = /\*/g
const CAattrs = [{
name: "commonName",
value: "CypressProxyCA"
name: 'commonName',
value: 'CypressProxyCA',
}, {
name: "countryName",
value: "Internet"
name: 'countryName',
value: 'Internet',
}, {
shortName: "ST",
value: "Internet"
shortName: 'ST',
value: 'Internet',
}, {
name: "localityName",
value: "Internet"
name: 'localityName',
value: 'Internet',
}, {
name: "organizationName",
value: "Cypress.io"
name: 'organizationName',
value: 'Cypress.io',
}, {
shortName: "OU",
value: "CA"
}];
shortName: 'OU',
value: 'CA',
}]
const CAextensions = [{
name: "basicConstraints",
cA: true
name: 'basicConstraints',
cA: true,
}, {
name: "keyUsage",
name: 'keyUsage',
keyCertSign: true,
digitalSignature: true,
nonRepudiation: true,
keyEncipherment: true,
dataEncipherment: true
dataEncipherment: true,
}, {
name: "extKeyUsage",
name: 'extKeyUsage',
serverAuth: true,
clientAuth: true,
codeSigning: true,
emailProtection: true,
timeStamping: true
timeStamping: true,
}, {
name: "nsCertType",
name: 'nsCertType',
client: true,
server: true,
email: true,
objsign: true,
sslCA: true,
emailCA: true,
objCA: true
objCA: true,
}, {
name: "subjectKeyIdentifier"
}];
name: 'subjectKeyIdentifier',
}]
const ServerAttrs = [{
name: "countryName",
value: "Internet"
name: 'countryName',
value: 'Internet',
}, {
shortName: "ST",
value: "Internet"
shortName: 'ST',
value: 'Internet',
}, {
name: "localityName",
value: "Internet"
name: 'localityName',
value: 'Internet',
}, {
name: "organizationName",
value: "Cypress Proxy CA"
name: 'organizationName',
value: 'Cypress Proxy CA',
}, {
shortName: "OU",
value: "Cypress Proxy Server Certificate"
}];
shortName: 'OU',
value: 'Cypress Proxy Server Certificate',
}]
const ServerExtensions = [{
name: "basicConstraints",
cA: false
name: 'basicConstraints',
cA: false,
}, {
name: "keyUsage",
name: 'keyUsage',
keyCertSign: false,
digitalSignature: true,
nonRepudiation: false,
keyEncipherment: true,
dataEncipherment: true
dataEncipherment: true,
}, {
name: "extKeyUsage",
name: 'extKeyUsage',
serverAuth: true,
clientAuth: true,
codeSigning: false,
emailProtection: false,
timeStamping: false
timeStamping: false,
}, {
name: "nsCertType",
name: 'nsCertType',
client: true,
server: true,
email: false,
objsign: false,
sslCA: false,
emailCA: false,
objCA: false
objCA: false,
}, {
name: "subjectKeyIdentifier"
}];
name: 'subjectKeyIdentifier',
}]
class CA {
constructor(caFolder) {
constructor (caFolder) {
if (!caFolder) {
caFolder = path.join(os.tmpdir(), 'cy-ca');
caFolder = path.join(os.tmpdir(), 'cy-ca')
}
this.baseCAFolder = caFolder;
this.certsFolder = path.join(this.baseCAFolder, "certs");
this.keysFolder = path.join(this.baseCAFolder, "keys");
this.baseCAFolder = caFolder
this.certsFolder = path.join(this.baseCAFolder, 'certs')
this.keysFolder = path.join(this.baseCAFolder, 'keys')
}
removeAll() {
removeAll () {
return fs
.removeAsync(this.baseCAFolder)
.catchReturn({ code: "ENOENT" });
.catchReturn({ code: 'ENOENT' })
}
randomSerialNumber() {
//# generate random 16 bytes hex string
let sn = "";
randomSerialNumber () {
// generate random 16 bytes hex string
let sn = ''
for (let i = 1; i <= 4; i++) {
sn += ("00000000" + Math.floor(Math.random()*Math.pow(256, 4)).toString(16)).slice(-8);
sn += (`00000000${Math.floor(Math.random() * Math.pow(256, 4)).toString(16)}`).slice(-8)
}
return sn;
return sn
}
generateCA() {
return generateKeyPairAsync({bits: 512})
.then(keys => {
const cert = pki.createCertificate();
cert.publicKey = keys.publicKey;
cert.serialNumber = this.randomSerialNumber();
generateCA () {
return generateKeyPairAsync({ bits: 512 })
.then((keys) => {
const cert = pki.createCertificate()
cert.validity.notBefore = new Date();
cert.validity.notAfter = new Date();
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 10);
cert.setSubject(CAattrs);
cert.setIssuer(CAattrs);
cert.setExtensions(CAextensions);
cert.sign(keys.privateKey, Forge.md.sha256.create());
cert.publicKey = keys.publicKey
cert.serialNumber = this.randomSerialNumber()
this.CAcert = cert;
this.CAkeys = keys;
cert.validity.notBefore = new Date()
cert.validity.notAfter = new Date()
cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 10)
cert.setSubject(CAattrs)
cert.setIssuer(CAattrs)
cert.setExtensions(CAextensions)
cert.sign(keys.privateKey, Forge.md.sha256.create())
this.CAcert = cert
this.CAkeys = keys
return Promise.all([
fs.outputFileAsync(path.join(this.certsFolder, "ca.pem"), pki.certificateToPem(cert)),
fs.outputFileAsync(path.join(this.keysFolder, "ca.private.key"), pki.privateKeyToPem(keys.privateKey)),
fs.outputFileAsync(path.join(this.keysFolder, "ca.public.key"), pki.publicKeyToPem(keys.publicKey))
]);
});
}
loadCA() {
return Promise.props({
certPEM: fs.readFileAsync(path.join(this.certsFolder, "ca.pem"), "utf-8"),
keyPrivatePEM: fs.readFileAsync(path.join(this.keysFolder, "ca.private.key"), "utf-8"),
keyPublicPEM: fs.readFileAsync(path.join(this.keysFolder, "ca.public.key"), "utf-8")
fs.outputFileAsync(path.join(this.certsFolder, 'ca.pem'), pki.certificateToPem(cert)),
fs.outputFileAsync(path.join(this.keysFolder, 'ca.private.key'), pki.privateKeyToPem(keys.privateKey)),
fs.outputFileAsync(path.join(this.keysFolder, 'ca.public.key'), pki.publicKeyToPem(keys.publicKey)),
])
})
.then(results => {
this.CAcert = pki.certificateFromPem(results.certPEM);
return this.CAkeys = {
}
loadCA () {
return Promise.props({
certPEM: fs.readFileAsync(path.join(this.certsFolder, 'ca.pem'), 'utf-8'),
keyPrivatePEM: fs.readFileAsync(path.join(this.keysFolder, 'ca.private.key'), 'utf-8'),
keyPublicPEM: fs.readFileAsync(path.join(this.keysFolder, 'ca.public.key'), 'utf-8'),
})
.then((results) => {
this.CAcert = pki.certificateFromPem(results.certPEM)
this.CAkeys = {
privateKey: pki.privateKeyFromPem(results.keyPrivatePEM),
publicKey: pki.publicKeyFromPem(results.keyPublicPEM)
};
})
.return(undefined);
publicKey: pki.publicKeyFromPem(results.keyPublicPEM),
}
})
.return(undefined)
}
generateServerCertificateKeys(hosts) {
hosts = [].concat(hosts);
generateServerCertificateKeys (hosts) {
hosts = [].concat(hosts)
const mainHost = hosts[0];
const keysServer = pki.rsa.generateKeyPair(1024);
const certServer = pki.createCertificate();
const mainHost = hosts[0]
const keysServer = pki.rsa.generateKeyPair(1024)
const certServer = pki.createCertificate()
certServer.publicKey = keysServer.publicKey;
certServer.serialNumber = this.randomSerialNumber();
certServer.validity.notBefore = new Date;
certServer.validity.notAfter = new Date;
certServer.validity.notAfter.setFullYear(certServer.validity.notBefore.getFullYear() + 2);
certServer.publicKey = keysServer.publicKey
certServer.serialNumber = this.randomSerialNumber()
certServer.validity.notBefore = new Date
certServer.validity.notAfter = new Date
certServer.validity.notAfter.setFullYear(certServer.validity.notBefore.getFullYear() + 2)
const attrsServer = _.clone(ServerAttrs)
const attrsServer = _.clone(ServerAttrs);
attrsServer.unshift({
name: "commonName",
value: mainHost
});
name: 'commonName',
value: mainHost,
})
certServer.setSubject(attrsServer);
certServer.setIssuer(this.CAcert.issuer.attributes);
certServer.setSubject(attrsServer)
certServer.setIssuer(this.CAcert.issuer.attributes)
certServer.setExtensions(ServerExtensions.concat([{
name: "subjectAltName",
altNames: hosts.map(function(host) {
name: 'subjectAltName',
altNames: hosts.map((host) => {
if (host.match(ipAddressRe)) {
return {type: 7, ip: host};
} else {
return {type: 2, value: host};
}})
}]));
return { type: 7, ip: host }
}
certServer.sign(this.CAkeys.privateKey, Forge.md.sha256.create());
return { type: 2, value: host }
}),
}]))
const certPem = pki.certificateToPem(certServer);
const keyPrivatePem = pki.privateKeyToPem(keysServer.privateKey);
const keyPublicPem = pki.publicKeyToPem(keysServer.publicKey);
certServer.sign(this.CAkeys.privateKey, Forge.md.sha256.create())
const dest = mainHost.replace(asterisksRe, "_");
const certPem = pki.certificateToPem(certServer)
const keyPrivatePem = pki.privateKeyToPem(keysServer.privateKey)
const keyPublicPem = pki.publicKeyToPem(keysServer.publicKey)
const dest = mainHost.replace(asterisksRe, '_')
return Promise.all([
fs.outputFileAsync(path.join(this.certsFolder, dest + ".pem"), certPem),
fs.outputFileAsync(path.join(this.keysFolder, dest + ".key"), keyPrivatePem),
fs.outputFileAsync(path.join(this.keysFolder, dest + ".public.key"), keyPublicPem)
fs.outputFileAsync(path.join(this.certsFolder, `${dest}.pem`), certPem),
fs.outputFileAsync(path.join(this.keysFolder, `${dest}.key`), keyPrivatePem),
fs.outputFileAsync(path.join(this.keysFolder, `${dest}.public.key`), keyPublicPem),
])
.return([certPem, keyPrivatePem]);
.return([certPem, keyPrivatePem])
}
getCertificateKeysForHostname(hostname) {
const dest = hostname.replace(asterisksRe, "_");
getCertificateKeysForHostname (hostname) {
const dest = hostname.replace(asterisksRe, '_')
return Promise.all([
fs.readFileAsync(path.join(this.certsFolder, dest + ".pem")),
fs.readFileAsync(path.join(this.keysFolder, dest + ".key"))
]);
fs.readFileAsync(path.join(this.certsFolder, `${dest}.pem`)),
fs.readFileAsync(path.join(this.keysFolder, `${dest}.key`)),
])
}
getCACertPath() {
return path.join(this.certsFolder, "ca.pem");
getCACertPath () {
return path.join(this.certsFolder, 'ca.pem')
}
static create(caFolder) {
const ca = new CA(caFolder);
static create (caFolder) {
const ca = new CA(caFolder)
return fs.statAsync(path.join(ca.certsFolder, "ca.pem"))
return fs.statAsync(path.join(ca.certsFolder, 'ca.pem'))
.bind(ca)
.then(ca.loadCA)
.catch(ca.generateCA)
.return(ca);
.return(ca)
}
}
module.exports = CA;
module.exports = CA

View File

@@ -1,23 +1,27 @@
// TODO: This file was created by bulk-decaffeinate.
// Sanity-check the conversion and remove this comment.
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
const CA = require("./ca");
const Server = require("./server");
const CA = require('./ca')
const Server = require('./server')
module.exports = {
create(dir, port, options) {
create (dir, port, options) {
return CA.create(dir)
.then(ca => Server.create(ca, port, options));
.then((ca) => {
return Server.create(ca, port, options)
})
},
reset() {
return Server.reset();
reset () {
return Server.reset()
},
httpsServer(onRequest) {
return require("../test/helpers/https_server").create(onRequest);
}
httpsServer (onRequest) {
return require('../test/helpers/https_server').create(onRequest)
},
};
}

View File

@@ -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)
},
}

View File

@@ -1,48 +1,56 @@
// TODO: This file was created by bulk-decaffeinate.
// Sanity-check the conversion and remove this comment.
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
const url = require("url");
const url = require('url')
module.exports = {
parseHost(hostString, defaultPort) {
let m;
if (m = hostString.match(/^http:\/\/(.*)/)) {
const parsedUrl = url.parse(hostString);
parseHost (hostString, defaultPort) {
let m
m = hostString.match(/^http:\/\/(.*)/)
if (m) {
const parsedUrl = url.parse(hostString)
return {
host: parsedUrl.hostname,
port: parsedUrl.port
};
port: parsedUrl.port,
}
}
const hostPort = hostString.split(':');
const host = hostPort[0];
const port = hostPort.length === 2 ? +hostPort[1] : defaultPort;
const hostPort = hostString.split(':')
const host = hostPort[0]
const port = hostPort.length === 2 ? +hostPort[1] : defaultPort
return {
host,
port
};
port,
}
},
hostAndPort(reqUrl, headers, defaultPort) {
let m;
hostAndPort (reqUrl, headers, defaultPort) {
let m
const {
host
} = headers;
host,
} = headers
const hostPort = this.parseHost(host, defaultPort);
const hostPort = this.parseHost(host, defaultPort)
//# this handles paths which include the full url. This could happen if it's a proxy
if (m = reqUrl.match(/^http:\/\/([^\/]*)\/?(.*)$/)) {
const parsedUrl = url.parse(reqUrl);
hostPort.host = parsedUrl.hostname;
hostPort.port = parsedUrl.port;
reqUrl = parsedUrl.path;
// this handles paths which include the full url. This could happen if it's a proxy
m = reqUrl.match(/^http:\/\/([^\/]*)\/?(.*)$/)
if (m) {
const parsedUrl = url.parse(reqUrl)
hostPort.host = parsedUrl.hostname
hostPort.port = parsedUrl.port
reqUrl = parsedUrl.path
}
return hostPort;
}
};
return hostPort
},
}

View File

@@ -1,14 +1,16 @@
const fs = require("fs");
const path = require("path");
const sslRootCas = require('ssl-root-cas');
// TODO: This file was created by bulk-decaffeinate.
// Sanity-check the conversion and remove this comment.
const fs = require('fs')
const path = require('path')
const sslRootCas = require('ssl-root-cas')
sslRootCas
.inject()
.addFile(path.join(__dirname, "certs", "server", "my-root-ca.crt.pem"));
.addFile(path.join(__dirname, 'certs', 'server', 'my-root-ca.crt.pem'))
const options = {
key: fs.readFileSync(path.join(__dirname, "certs", "server", "my-server.key.pem")),
cert: fs.readFileSync(path.join(__dirname, "certs", "server", "my-server.crt.pem"))
};
key: fs.readFileSync(path.join(__dirname, 'certs', 'server', 'my-server.key.pem')),
cert: fs.readFileSync(path.join(__dirname, 'certs', 'server', 'my-server.crt.pem')),
}
module.exports = options;
module.exports = options

View File

@@ -1,31 +1,42 @@
/* eslint-disable
no-console,
*/
// 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 http = require("http");
const Promise = require("bluebird");
const http = require('http')
const Promise = require('bluebird')
const srv = http.createServer(function(req, res) {
console.log("HTTP SERVER REQUEST URL:", req.url);
console.log("HTTP SERVER REQUEST HEADERS:", req.headers);
const srv = http.createServer((req, res) => {
console.log('HTTP SERVER REQUEST URL:', req.url)
console.log('HTTP SERVER REQUEST HEADERS:', req.headers)
res.setHeader("Content-Type", "text/html");
res.writeHead(200);
return res.end("<html><body>http server</body></html>");
});
res.setHeader('Content-Type', 'text/html')
res.writeHead(200)
return res.end('<html><body>http server</body></html>')
})
module.exports = {
srv,
start() {
return new Promise(resolve => srv.listen(8080, function() {
console.log("server listening on port: 8080");
return resolve(srv);
}));
start () {
return new Promise((resolve) => {
return srv.listen(8080, () => {
console.log('server listening on port: 8080')
return resolve(srv)
})
})
},
stop() {
return new Promise(resolve => srv.close(resolve));
}
};
stop () {
return new Promise((resolve) => {
return srv.close(resolve)
})
},
}

View File

@@ -1,49 +1,64 @@
/* eslint-disable
no-console,
*/
// 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
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
const https = require("https");
const Promise = require("bluebird");
const { allowDestroy } = require("@packages/network");
const certs = require("./certs");
const https = require('https')
const Promise = require('bluebird')
const { allowDestroy } = require('@packages/network')
const certs = require('./certs')
const defaultOnRequest = function(req, res) {
console.log("HTTPS SERVER REQUEST URL:", req.url);
console.log("HTTPS SERVER REQUEST HEADERS:", req.headers);
const defaultOnRequest = function (req, res) {
console.log('HTTPS SERVER REQUEST URL:', req.url)
console.log('HTTPS SERVER REQUEST HEADERS:', req.headers)
res.setHeader("Content-Type", "text/html");
res.writeHead(200);
return res.end("<html><head></head><body>https server</body></html>");
};
res.setHeader('Content-Type', 'text/html')
res.writeHead(200)
let servers = [];
return res.end('<html><head></head><body>https server</body></html>')
}
const create = onRequest => https.createServer(certs, onRequest != null ? onRequest : defaultOnRequest);
let servers = []
const create = (onRequest) => {
return https.createServer(certs, onRequest != null ? onRequest : defaultOnRequest)
}
module.exports = {
create,
start(port, onRequest) {
return new Promise(function(resolve) {
const srv = create(onRequest);
start (port, onRequest) {
return new Promise((resolve) => {
const srv = create(onRequest)
allowDestroy(srv);
allowDestroy(srv)
servers.push(srv);
servers.push(srv)
return srv.listen(port, function() {
console.log(`server listening on port: ${port}`);
return resolve(srv);
});
});
return srv.listen(port, () => {
console.log(`server listening on port: ${port}`)
return resolve(srv)
})
})
},
stop() {
const stop = srv => new Promise(resolve => srv.destroy(resolve));
stop () {
const stop = (srv) => {
return new Promise((resolve) => {
return srv.destroy(resolve)
})
}
return Promise.map(servers, stop)
.then(() => servers = []);
}
};
.then(() => {
return servers = []
})
},
}

View File

@@ -1,78 +1,101 @@
/* eslint-disable
no-console,
no-undef,
*/
// 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 { allowDestroy } = require("@packages/network");
const http = require("http");
const path = require("path");
const httpsProxy = require("../../lib/proxy");
const { allowDestroy } = require('@packages/network')
const http = require('http')
const path = require('path')
const httpsProxy = require('../../lib/proxy')
let prx = null;
let prx = null
const pipe = (req, res) => req.pipe(request(req.url))
.on("error", function() {
console.log("**ERROR**", req.url);
req.statusCode = 500;
return res.end();
}).pipe(res);
const pipe = (req, res) => {
return req.pipe(request(req.url))
.on('error', () => {
console.log('**ERROR**', req.url)
req.statusCode = 500
const onConnect = (req, socket, head, proxy) => proxy.connect(req, socket, head, {
onDirectConnection(req, socket, head) {
return ["localhost:8444", "localhost:12344"].includes(req.url);
}
});
return res.end()
}).pipe(res)
}
const onRequest = (req, res) => pipe(req, res);
const onConnect = (req, socket, head, proxy) => {
return proxy.connect(req, socket, head, {
onDirectConnection (req, socket, head) {
return ['localhost:8444', 'localhost:12344'].includes(req.url)
},
})
}
const onRequest = (req, res) => {
return pipe(req, res)
}
module.exports = {
reset() {
return httpsProxy.reset();
reset () {
return httpsProxy.reset()
},
start(port) {
prx = http.createServer();
start (port) {
prx = http.createServer()
allowDestroy(prx);
allowDestroy(prx)
const dir = path.join(process.cwd(), "ca");
const dir = path.join(process.cwd(), 'ca')
return httpsProxy.create(dir, port, {
onUpgrade(req, socket, head) {},
onUpgrade (req, socket, head) {},
onRequest(req, res) {
console.log("ON REQUEST FROM OUTER PROXY", req.url, req.headers, req.method);
onRequest (req, res) {
console.log('ON REQUEST FROM OUTER PROXY', req.url, req.headers, req.method)
if (req.url.includes("replace")) {
if (req.url.includes('replace')) {
const {
write
} = res;
res.write = function(chunk) {
chunk = Buffer.from(chunk.toString().replace("https server", "replaced content"));
write,
} = res
return write.call(this, chunk);
};
res.write = function (chunk) {
chunk = Buffer.from(chunk.toString().replace('https server', 'replaced content'))
return pipe(req, res);
} else {
return pipe(req, res);
return write.call(this, chunk)
}
return pipe(req, res)
}
}
return pipe(req, res)
},
})
.then(proxy => {
prx.on("request", onRequest);
.then((proxy) => {
prx.on('request', onRequest)
prx.on("connect", (req, socket, head) => onConnect(req, socket, head, proxy));
prx.on('connect', (req, socket, head) => {
return onConnect(req, socket, head, proxy)
})
return new Promise(resolve => prx.listen(port, function() {
prx.proxy = proxy;
console.log(`server listening on port: ${port}`);
return resolve(proxy);
}));
});
return new Promise((resolve) => {
return prx.listen(port, () => {
prx.proxy = proxy
console.log(`server listening on port: ${port}`)
return resolve(proxy)
})
})
})
},
stop() {
return new Promise(resolve => prx.destroy(resolve)).then(() => prx.proxy.close());
}
};
stop () {
return new Promise((resolve) => {
return prx.destroy(resolve)
}).then(() => {
return prx.proxy.close()
})
},
}

View File

@@ -1,26 +1,33 @@
/* eslint-disable
brace-style,
no-undef,
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
*/
require("../spec_helper");
require('../spec_helper')
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
const _ = require("lodash");
const DebugProxy = require("@cypress/debugging-proxy");
const fs = require("fs-extra");
const https = require("https");
const net = require("net");
const network = require("@packages/network");
const path = require("path");
const Promise = require("bluebird");
const proxy = require("../helpers/proxy");
const httpServer = require("../helpers/http_server");
const httpsServer = require("../helpers/https_server");
const _ = require('lodash')
const DebugProxy = require('@cypress/debugging-proxy')
const fs = require('fs-extra')
const https = require('https')
const net = require('net')
const network = require('@packages/network')
const path = require('path')
const Promise = require('bluebird')
const proxy = require('../helpers/proxy')
const httpServer = require('../helpers/http_server')
const httpsServer = require('../helpers/https_server')
describe("Proxy", function() {
beforeEach(function() {
describe('Proxy', () => {
beforeEach(function () {
return Promise.join(
httpServer.start(),
@@ -29,293 +36,326 @@ describe("Proxy", function() {
httpsServer.start(8444),
proxy.start(3333)
.then(proxy1 => {
this.proxy = proxy1;
})
);
});
.then((proxy1) => {
this.proxy = proxy1
})
)
})
afterEach(() => Promise.join(
httpServer.stop(),
httpsServer.stop(),
proxy.stop()
));
afterEach(() => {
return Promise.join(
httpServer.stop(),
httpsServer.stop(),
proxy.stop()
)
})
it("can request the googles", function() {
//# give some padding to external
//# network request
this.timeout(10000);
it('can request the googles', function () {
// give some padding to external
// network request
this.timeout(10000)
return Promise.all([
request({
strictSSL: false,
proxy: "http://localhost:3333",
url: "https://www.google.com"
proxy: 'http://localhost:3333',
url: 'https://www.google.com',
}),
request({
strictSSL: false,
proxy: "http://localhost:3333",
url: "https://mail.google.com"
proxy: 'http://localhost:3333',
url: 'https://mail.google.com',
}),
request({
strictSSL: false,
proxy: "http://localhost:3333",
url: "https://google.com"
})
]);
});
it("can call the httpsDirectly without a proxy", () => request({
strictSSL: false,
url: "https://localhost:8443"
}));
it("can boot the httpsServer", () => request({
strictSSL: false,
url: "https://localhost:8443/",
proxy: "http://localhost:3333"
proxy: 'http://localhost:3333',
url: 'https://google.com',
}),
])
})
.then(html => expect(html).to.include("https server")));
it("yields the onRequest callback", () => request({
strictSSL: false,
url: "https://localhost:8443/replace",
proxy: "http://localhost:3333"
it('can call the httpsDirectly without a proxy', () => {
return request({
strictSSL: false,
url: 'https://localhost:8443',
})
})
.then(html => expect(html).to.include("replaced content")));
it("can pass directly through", () => //# this will fail due to dynamic cert
//# generation when strict ssl is true
request({
strictSSL: false,
url: "https://localhost:8444/replace",
proxy: "http://localhost:3333"
it('can boot the httpsServer', () => {
return request({
strictSSL: false,
url: 'https://localhost:8443/',
proxy: 'http://localhost:3333',
})
.then((html) => {
expect(html).to.include('https server')
})
})
.then(html => expect(html).to.include("https server")));
it("retries 5 times", function() {
this.sandbox.spy(net, 'connect');
it('yields the onRequest callback', () => {
return request({
strictSSL: false,
url: 'https://localhost:8443/replace',
proxy: 'http://localhost:3333',
})
.then((html) => {
expect(html).to.include('replaced content')
})
})
it('can pass directly through', () => // this will fail due to dynamic cert
// generation when strict ssl is true
{
return request({
strictSSL: false,
url: 'https://localhost:8444/replace',
proxy: 'http://localhost:3333',
})
.then((html) => {
expect(html).to.include('https server')
})
})
it('retries 5 times', function () {
this.sandbox.spy(net, 'connect')
return request({
strictSSL: false,
url: "https://localhost:12344",
proxy: "http://localhost:3333"
url: 'https://localhost:12344',
proxy: 'http://localhost:3333',
})
.then(function() {
throw new Error("should not reach");}).catch(() => expect(net.connect).to.have.callCount(5));
});
.then(() => {
throw new Error('should not reach')
}).catch(() => {
expect(net.connect).to.have.callCount(5)
})
})
it("closes outgoing connections when client disconnects", function() {
this.sandbox.spy(net, 'connect');
it('closes outgoing connections when client disconnects', function () {
this.sandbox.spy(net, 'connect')
return request({
strictSSL: false,
url: "https://localhost:8444/replace",
proxy: "http://localhost:3333",
resolveWithFullResponse: true
url: 'https://localhost:8444/replace',
proxy: 'http://localhost:3333',
resolveWithFullResponse: true,
})
.then(res => {
//# ensure client has disconnected
expect(res.socket.destroyed).to.be.true;
//# ensure the outgoing socket created for this connection was destroyed
.then((res) => {
// ensure client has disconnected
expect(res.socket.destroyed).to.be.true
// ensure the outgoing socket created for this connection was destroyed
const socket = net.connect.getCalls()
.find(call => {
return (call.args[0].port === "8444") && (call.args[0].host === "localhost");
}).returnValue;
return expect(socket.destroyed).to.be.true;
});
});
.find((call) => {
return (call.args[0].port === '8444') && (call.args[0].host === 'localhost')
}).returnValue
it("can boot the httpServer", () => request({
strictSSL: false,
url: "http://localhost:8080/",
proxy: "http://localhost:3333"
expect(socket.destroyed).to.be.true
})
})
.then(html => expect(html).to.include("http server")));
it('can boot the httpServer', () => {
return request({
strictSSL: false,
url: 'http://localhost:8080/',
proxy: 'http://localhost:3333',
})
context("generating certificates", function() {
it("reuses existing certificates", function() {
.then((html) => {
expect(html).to.include('http server')
})
})
context('generating certificates', () => {
it('reuses existing certificates', function () {
return request({
strictSSL: false,
url: "https://localhost:8443/",
proxy: "http://localhost:3333"
url: 'https://localhost:8443/',
proxy: 'http://localhost:3333',
})
.then(() => {
proxy.reset();
proxy.reset()
//# force this to reject if its called
this.sandbox.stub(this.proxy, "_generateMissingCertificates").rejects(new Error("should not call"));
// force this to reject if its called
this.sandbox.stub(this.proxy, '_generateMissingCertificates').rejects(new Error('should not call'))
return request({
strictSSL: false,
url: "https://localhost:8443/",
proxy: "http://localhost:3333"
});
});
});
url: 'https://localhost:8443/',
proxy: 'http://localhost:3333',
})
})
})
//# https://github.com/cypress-io/cypress/issues/771
return it("generates certs and can proxy requests for HTTPS requests to IPs", function() {
this.sandbox.spy(this.proxy, "_generateMissingCertificates");
this.sandbox.spy(this.proxy, "_getServerPortForIp");
// https://github.com/cypress-io/cypress/issues/771
it('generates certs and can proxy requests for HTTPS requests to IPs', function () {
this.sandbox.spy(this.proxy, '_generateMissingCertificates')
this.sandbox.spy(this.proxy, '_getServerPortForIp')
return Promise.all([
httpsServer.start(8445),
this.proxy._ca.removeAll()
this.proxy._ca.removeAll(),
])
.then(() => {
return request({
strictSSL: false,
url: "https://127.0.0.1:8445/",
proxy: "http://localhost:3333"
});
}).then(() => {
//# this should not stand up its own https server
url: 'https://127.0.0.1:8445/',
proxy: 'http://localhost:3333',
})
}).then(() => {
// this should not stand up its own https server
return request({
strictSSL: false,
url: "https://localhost:8443/",
proxy: "http://localhost:3333"
});
url: 'https://localhost:8443/',
proxy: 'http://localhost:3333',
})
}).then(() => {
expect(this.proxy._ipServers["127.0.0.1"]).to.be.an.instanceOf(https.Server);
expect(this.proxy._getServerPortForIp).to.be.calledWith('127.0.0.1').and.be.calledOnce;
return expect(this.proxy._generateMissingCertificates).to.be.calledTwice;
});
});
});
expect(this.proxy._ipServers['127.0.0.1']).to.be.an.instanceOf(https.Server)
expect(this.proxy._getServerPortForIp).to.be.calledWith('127.0.0.1').and.be.calledOnce
context("closing", () => it("resets sslServers and can reopen", function() {
return request({
strictSSL: false,
url: "https://localhost:8443/",
proxy: "http://localhost:3333"
})
.then(() => {
return proxy.stop();
}).then(() => {
return proxy.start(3333);
}).then(() => {
//# force this to reject if its called
this.sandbox.stub(this.proxy, "_generateMissingCertificates").rejects(new Error("should not call"));
return request({
strictSSL: false,
url: "https://localhost:8443/",
proxy: "http://localhost:3333"
});
});
}));
return context("with an upstream proxy", function() {
beforeEach(function() {
process.env.NO_PROXY = "";
process.env.HTTP_PROXY = (process.env.HTTPS_PROXY = "http://localhost:9001");
this.upstream = new DebugProxy({
keepRequests: true
});
return this.upstream.start(9001);
});
it("passes a request to an https server through the upstream", function() {
this.upstream._onConnect = function(domain, port) {
expect(domain).to.eq('localhost');
expect(port).to.eq('8444');
return true;
};
return request({
strictSSL: false,
url: "https://localhost:8444/",
proxy: "http://localhost:3333"
}).then(res => {
return expect(res).to.contain("https server");
});
});
it("uses HTTP basic auth when provided", function() {
this.upstream.setAuth({
username: 'foo',
password: 'bar'
});
this.upstream._onConnect = function(domain, port) {
expect(domain).to.eq('localhost');
expect(port).to.eq('8444');
return true;
};
process.env.HTTP_PROXY = (process.env.HTTPS_PROXY = "http://foo:bar@localhost:9001");
return request({
strictSSL: false,
url: "https://localhost:8444/",
proxy: "http://localhost:3333"
}).then(res => {
return expect(res).to.contain("https server");
});
});
it("closes outgoing connections when client disconnects", function() {
this.sandbox.spy(net, 'connect');
return request({
strictSSL: false,
url: "https://localhost:8444/replace",
proxy: "http://localhost:3333",
resolveWithFullResponse: true,
forever: false
expect(this.proxy._generateMissingCertificates).to.be.calledTwice
})
.then(res => {
//# ensure client has disconnected
expect(res.socket.destroyed).to.be.true;
//# ensure the outgoing socket created for this connection was destroyed
const socket = net.connect.getCalls()
.find(call => {
return (call.args[0].port === 9001) && (call.args[0].host === "localhost");
}).returnValue;
return new Promise(resolve => socket.on('close', () => {
expect(socket.destroyed).to.be.true;
return resolve();
}));
});
});
//# https://github.com/cypress-io/cypress/issues/4257
it("passes through to SNI when it is intercepted and not through proxy", function() {
const createSocket = this.sandbox.stub(network.connect, 'createRetryingSocket').callsArgWith(1, new Error('stub'));
const createProxyConn = this.sandbox.spy(network.agent.httpsAgent, 'createUpstreamProxyConnection');
})
})
context('closing', () => {
it('resets sslServers and can reopen', function () {
return request({
strictSSL: false,
url: "https://localhost:8443",
proxy: "http://localhost:3333",
resolveWithFullResponse: true,
forever: false
url: 'https://localhost:8443/',
proxy: 'http://localhost:3333',
})
.then(() => {
throw new Error('should not succeed');
}).catch({ message: 'Error: Client network socket disconnected before secure TLS connection was established' }, () => {
expect(createProxyConn).to.not.be.called;
return expect(createSocket).to.be.calledWith({
port: this.proxy._sniPort,
host: 'localhost'
});
});
});
return proxy.stop()
}).then(() => {
return proxy.start(3333)
}).then(() => {
// force this to reject if its called
this.sandbox.stub(this.proxy, '_generateMissingCertificates').rejects(new Error('should not call'))
return afterEach(function() {
this.upstream.stop();
delete process.env.HTTP_PROXY;
delete process.env.HTTPS_PROXY;
return delete process.env.NO_PROXY;
});
});
});
return request({
strictSSL: false,
url: 'https://localhost:8443/',
proxy: 'http://localhost:3333',
})
})
})
})
context('with an upstream proxy', () => {
beforeEach(function () {
process.env.NO_PROXY = ''
process.env.HTTP_PROXY = (process.env.HTTPS_PROXY = 'http://localhost:9001')
this.upstream = new DebugProxy({
keepRequests: true,
})
return this.upstream.start(9001)
})
it('passes a request to an https server through the upstream', function () {
this.upstream._onConnect = function (domain, port) {
expect(domain).to.eq('localhost')
expect(port).to.eq('8444')
return true
}
return request({
strictSSL: false,
url: 'https://localhost:8444/',
proxy: 'http://localhost:3333',
}).then((res) => {
expect(res).to.contain('https server')
})
})
it('uses HTTP basic auth when provided', function () {
this.upstream.setAuth({
username: 'foo',
password: 'bar',
})
this.upstream._onConnect = function (domain, port) {
expect(domain).to.eq('localhost')
expect(port).to.eq('8444')
return true
}
process.env.HTTP_PROXY = (process.env.HTTPS_PROXY = 'http://foo:bar@localhost:9001')
return request({
strictSSL: false,
url: 'https://localhost:8444/',
proxy: 'http://localhost:3333',
}).then((res) => {
expect(res).to.contain('https server')
})
})
it('closes outgoing connections when client disconnects', function () {
this.sandbox.spy(net, 'connect')
return request({
strictSSL: false,
url: 'https://localhost:8444/replace',
proxy: 'http://localhost:3333',
resolveWithFullResponse: true,
forever: false,
})
.then((res) => {
// ensure client has disconnected
expect(res.socket.destroyed).to.be.true
// ensure the outgoing socket created for this connection was destroyed
const socket = net.connect.getCalls()
.find((call) => {
return (call.args[0].port === 9001) && (call.args[0].host === 'localhost')
}).returnValue
return new Promise((resolve) => {
return socket.on('close', () => {
expect(socket.destroyed).to.be.true
return resolve()
})
})
})
})
// https://github.com/cypress-io/cypress/issues/4257
it('passes through to SNI when it is intercepted and not through proxy', function () {
const createSocket = this.sandbox.stub(network.connect, 'createRetryingSocket').callsArgWith(1, new Error('stub'))
const createProxyConn = this.sandbox.spy(network.agent.httpsAgent, 'createUpstreamProxyConnection')
return request({
strictSSL: false,
url: 'https://localhost:8443',
proxy: 'http://localhost:3333',
resolveWithFullResponse: true,
forever: false,
})
.then(() => {
throw new Error('should not succeed')
}).catch({ message: 'Error: Client network socket disconnected before secure TLS connection was established' }, () => {
expect(createProxyConn).to.not.be.called
expect(createSocket).to.be.calledWith({
port: this.proxy._sniPort,
host: 'localhost',
})
})
})
return afterEach(function () {
this.upstream.stop()
delete process.env.HTTP_PROXY
delete process.env.HTTPS_PROXY
return delete process.env.NO_PROXY
})
})
})

View File

@@ -1,26 +1,31 @@
/* eslint-disable
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 chai = require("chai");
const sinon = require("sinon");
const Promise = require("bluebird");
const sinonChai = require("sinon-chai");
const sinonPromise = require("sinon-as-promised")(Promise);
const chai = require('chai')
const sinon = require('sinon')
const Promise = require('bluebird')
const sinonChai = require('sinon-chai')
const sinonPromise = require('sinon-as-promised')(Promise)
global.request = require("request-promise");
global.sinon = sinon;
global.supertest = require("supertest");
global.request = require('request-promise')
global.sinon = sinon
global.supertest = require('supertest')
chai.use(sinonChai);
chai.use(sinonChai)
global.expect = chai.expect;
global.expect = chai.expect
beforeEach(function() {
return this.sandbox = sinon.sandbox.create();
});
beforeEach(function () {
this.sandbox = sinon.sandbox.create()
})
afterEach(function() {
return this.sandbox.restore();
});
afterEach(function () {
return this.sandbox.restore()
})

View File

@@ -1,104 +1,110 @@
// TODO: This file was created by bulk-decaffeinate.
// Sanity-check the conversion and remove this comment.
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
require("../spec_helper");
require('../spec_helper')
let fs = require("fs-extra");
const path = require("path");
const Promise = require("bluebird");
const CA = require("../../lib/ca");
let fs = require('fs-extra')
const path = require('path')
const Promise = require('bluebird')
const CA = require('../../lib/ca')
fs = Promise.promisifyAll(fs);
fs = Promise.promisifyAll(fs)
describe("lib/ca", function() {
beforeEach(function() {
this.timeout(5000);
describe('lib/ca', () => {
beforeEach(function () {
this.timeout(5000)
this.dir = path.join(process.cwd(), "tmp");
this.dir = path.join(process.cwd(), 'tmp')
return fs.ensureDirAsync(this.dir)
.then(() => {
return CA.create(this.dir);
}).then(ca => {
this.ca = ca;
});
});
return CA.create(this.dir)
}).then((ca) => {
this.ca = ca
})
})
afterEach(function() {
return fs.removeAsync(this.dir);
});
afterEach(function () {
return fs.removeAsync(this.dir)
})
context("#generateServerCertificateKeys", () => it("generates certs for each host", function() {
return this.ca.generateServerCertificateKeys("www.cypress.io")
.spread(function(certPem, keyPrivatePem) {
expect(certPem).to.include("-----BEGIN CERTIFICATE-----");
return expect(keyPrivatePem).to.include("-----BEGIN RSA PRIVATE KEY-----");
});
}));
context('#generateServerCertificateKeys', () => {
it('generates certs for each host', function () {
return this.ca.generateServerCertificateKeys('www.cypress.io')
.spread((certPem, keyPrivatePem) => {
expect(certPem).to.include('-----BEGIN CERTIFICATE-----')
return context(".create", function() {
it("returns a new CA instance", function() {
return expect(this.ca).to.be.an.instanceof(CA);
});
expect(keyPrivatePem).to.include('-----BEGIN RSA PRIVATE KEY-----')
})
})
})
it("creates certs + keys dir", function() {
context('.create', () => {
it('returns a new CA instance', function () {
expect(this.ca).to.be.an.instanceof(CA)
})
it('creates certs + keys dir', function () {
return Promise.join(
fs.statAsync(path.join(this.dir, "certs")),
fs.statAsync(path.join(this.dir, "keys"))
);
});
fs.statAsync(path.join(this.dir, 'certs')),
fs.statAsync(path.join(this.dir, 'keys'))
)
})
it("writes certs/ca.pem", function() {
return fs.statAsync(path.join(this.dir, "certs", "ca.pem"));
});
it('writes certs/ca.pem', function () {
return fs.statAsync(path.join(this.dir, 'certs', 'ca.pem'))
})
it("writes keys/ca.private.key", function() {
return fs.statAsync(path.join(this.dir, "keys", "ca.private.key"));
});
it('writes keys/ca.private.key', function () {
return fs.statAsync(path.join(this.dir, 'keys', 'ca.private.key'))
})
it("writes keys/ca.public.key", function() {
return fs.statAsync(path.join(this.dir, "keys", "ca.public.key"));
});
it('writes keys/ca.public.key', function () {
return fs.statAsync(path.join(this.dir, 'keys', 'ca.public.key'))
})
it("sets ca.CAcert", function() {
return expect(this.ca.CAcert).to.be.an("object");
});
it('sets ca.CAcert', function () {
expect(this.ca.CAcert).to.be.an('object')
})
it("sets ca.CAkeys", function() {
expect(this.ca.CAkeys).to.be.an("object");
expect(this.ca.CAkeys).to.have.a.property("privateKey");
return expect(this.ca.CAkeys).to.have.a.property("publicKey");
});
it('sets ca.CAkeys', function () {
expect(this.ca.CAkeys).to.be.an('object')
expect(this.ca.CAkeys).to.have.a.property('privateKey')
return describe("existing CA folder", function() {
beforeEach(function() {
this.sandbox.spy(CA.prototype, "loadCA");
this.sandbox.spy(CA.prototype, "generateCA");
expect(this.ca.CAkeys).to.have.a.property('publicKey')
})
describe('existing CA folder', () => {
beforeEach(function () {
this.sandbox.spy(CA.prototype, 'loadCA')
this.sandbox.spy(CA.prototype, 'generateCA')
return CA.create(this.dir)
.then(ca2 => {
this.ca2 = ca2;
});
});
.then((ca2) => {
this.ca2 = ca2
})
})
it("calls loadCA and not generateCA", function() {
expect(CA.prototype.loadCA).to.be.calledOnce;
return expect(CA.prototype.generateCA).not.to.be.called;
});
it('calls loadCA and not generateCA', () => {
expect(CA.prototype.loadCA).to.be.calledOnce
it("sets ca.CAcert", function() {
return expect(this.ca2.CAcert).to.be.an("object");
});
expect(CA.prototype.generateCA).not.to.be.called
})
return it("sets ca.CAkeys", function() {
expect(this.ca2.CAkeys).to.be.an("object");
expect(this.ca2.CAkeys).to.have.a.property("privateKey");
return expect(this.ca2.CAkeys).to.have.a.property("publicKey");
});
});
});
});
it('sets ca.CAcert', function () {
expect(this.ca2.CAcert).to.be.an('object')
})
it('sets ca.CAkeys', function () {
expect(this.ca2.CAkeys).to.be.an('object')
expect(this.ca2.CAkeys).to.have.a.property('privateKey')
expect(this.ca2.CAkeys).to.have.a.property('publicKey')
})
})
})
})

View File

@@ -1,133 +1,141 @@
/* eslint-disable
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
*/
require("../spec_helper");
require('../spec_helper')
const EE = require("events");
const Promise = require("bluebird");
const proxy = require("../helpers/proxy");
const Server = require("../../lib/server");
const EE = require('events')
const Promise = require('bluebird')
const proxy = require('../helpers/proxy')
const Server = require('../../lib/server')
describe("lib/server", function() {
beforeEach(function() {
return this.setup = (options = {}) => {
this.ca = {};
this.port = 12345;
describe('lib/server', () => {
beforeEach(function () {
this.setup = (options = {}) => {
this.ca = {}
this.port = 12345
return Server.create(this.ca, this.port, options);
};
});
return Server.create(this.ca, this.port, options)
}
})
afterEach(function() {
delete process.env.HTTPS_PROXY;
return delete process.env.NO_PROXY;
});
afterEach(() => {
delete process.env.HTTPS_PROXY
return context("#listen", function() {
it("calls options.onUpgrade with req, socket head", function() {
const onUpgrade = this.sandbox.stub();
return delete process.env.NO_PROXY
})
return this.setup({onUpgrade})
.then(function(srv) {
srv._sniServer.emit("upgrade", 1, 2, 3);
context('#listen', () => {
it('calls options.onUpgrade with req, socket head', function () {
const onUpgrade = this.sandbox.stub()
return expect(onUpgrade).to.be.calledWith(1,2,3);
});
});
return this.setup({ onUpgrade })
.then((srv) => {
srv._sniServer.emit('upgrade', 1, 2, 3)
it("calls options.onRequest with req, res", function() {
const onRequest = this.sandbox.stub();
const req = {url: "https://www.foobar.com", headers: {host: "www.foobar.com"}};
const res = {};
expect(onUpgrade).to.be.calledWith(1, 2, 3)
})
})
return this.setup({onRequest})
.then(function(srv) {
srv._sniServer.emit("request", req, res);
it('calls options.onRequest with req, res', function () {
const onRequest = this.sandbox.stub()
const req = { url: 'https://www.foobar.com', headers: { host: 'www.foobar.com' } }
const res = {}
return expect(onRequest).to.be.calledWith(req, res);
});
});
return this.setup({ onRequest })
.then((srv) => {
srv._sniServer.emit('request', req, res)
it("calls options.onError with err and port and destroys the client socket", function(done) {
const socket = new EE();
socket.destroy = this.sandbox.stub();
const head = {};
expect(onRequest).to.be.calledWith(req, res)
})
})
const onError = function(err, socket2, head2, port) {
expect(err.message).to.eq("connect ECONNREFUSED 127.0.0.1:8444");
it('calls options.onError with err and port and destroys the client socket', function (done) {
const socket = new EE()
expect(socket).to.eq(socket2);
expect(head).to.eq(head2);
expect(port).to.eq("8444");
socket.destroy = this.sandbox.stub()
const head = {}
expect(socket.destroy).to.be.calledOnce;
const onError = function (err, socket2, head2, port) {
expect(err.message).to.eq('connect ECONNREFUSED 127.0.0.1:8444')
return done();
};
expect(socket).to.eq(socket2)
expect(head).to.eq(head2)
expect(port).to.eq('8444')
expect(socket.destroy).to.be.calledOnce
return done()
}
this.setup({ onError })
.then(function(srv) {
let conn;
return conn = srv._makeDirectConnection({url: "localhost:8444"}, socket, head);
});
.then((srv) => {
let conn
});
conn = srv._makeDirectConnection({ url: 'localhost:8444' }, socket, head)
})
})
//# https://github.com/cypress-io/cypress/issues/3250
it("does not crash when an erroneous URL is provided, just destroys socket", function(done) {
const socket = new EE();
socket.destroy = this.sandbox.stub();
const head = {};
// https://github.com/cypress-io/cypress/issues/3250
it('does not crash when an erroneous URL is provided, just destroys socket', function (done) {
const socket = new EE()
const onError = function(err, socket2, head2, port) {
expect(err.message).to.eq("connect ECONNREFUSED 127.0.0.1:443");
socket.destroy = this.sandbox.stub()
const head = {}
expect(socket).to.eq(socket2);
expect(head).to.eq(head2);
expect(port).to.eq("443");
const onError = function (err, socket2, head2, port) {
expect(err.message).to.eq('connect ECONNREFUSED 127.0.0.1:443')
expect(socket.destroy).to.be.calledOnce;
expect(socket).to.eq(socket2)
expect(head).to.eq(head2)
expect(port).to.eq('443')
return done();
};
expect(socket.destroy).to.be.calledOnce
return done()
}
this.setup({ onError })
.then(function(srv) {
let conn;
return conn = srv._makeDirectConnection({url: "%7Balgolia_application_id%7D-dsn.algolia.net:443"}, socket, head);
});
.then((srv) => {
let conn
});
conn = srv._makeDirectConnection({ url: '%7Balgolia_application_id%7D-dsn.algolia.net:443' }, socket, head)
})
})
return it("with proxied connection calls options.onError with err and port and destroys the client socket", function(done) {
const socket = new EE();
socket.destroy = this.sandbox.stub();
const head = {};
it('with proxied connection calls options.onError with err and port and destroys the client socket', function (done) {
const socket = new EE()
const onError = function(err, socket2, head2, port) {
expect(err.message).to.eq("A connection to the upstream proxy could not be established: connect ECONNREFUSED 127.0.0.1:8444");
socket.destroy = this.sandbox.stub()
const head = {}
expect(socket).to.eq(socket2);
expect(head).to.eq(head2);
expect(port).to.eq("11111");
const onError = function (err, socket2, head2, port) {
expect(err.message).to.eq('A connection to the upstream proxy could not be established: connect ECONNREFUSED 127.0.0.1:8444')
expect(socket.destroy).to.be.calledOnce;
expect(socket).to.eq(socket2)
expect(head).to.eq(head2)
expect(port).to.eq('11111')
return done();
};
expect(socket.destroy).to.be.calledOnce
process.env.HTTPS_PROXY = 'http://localhost:8444';
process.env.NO_PROXY = '';
return done()
}
process.env.HTTPS_PROXY = 'http://localhost:8444'
process.env.NO_PROXY = ''
this.setup({ onError })
.then(function(srv) {
let conn;
return conn = srv._makeDirectConnection({url: "should-not-reach.invalid:11111"}, socket, head);
});
});
});
});
.then((srv) => {
let conn
conn = srv._makeDirectConnection({ url: 'should-not-reach.invalid:11111' }, socket, head)
})
})
})
})