remove core- prefixes from packages

This commit is contained in:
Chris Breidng
2017-04-21 10:34:19 -04:00
parent 2b34b80ee2
commit 6efcf64005
992 changed files with 0 additions and 0 deletions

37
packages/https-proxy/.gitignore vendored Normal file
View File

@@ -0,0 +1,37 @@
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
node_modules
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
ca
.http-mitm-proxy

View File

@@ -0,0 +1 @@
6.5.0

View File

@@ -0,0 +1,8 @@
{
"commitMessage": "release %s",
"tagAnnotation": "release %s",
"github": {
"release": false,
"releaseName": "release %s"
}
}

View File

@@ -0,0 +1,29 @@
# Cypress Core Extension
## Developing
**Testing**
```bash
npm run test-once
```
## Changelog
#### 0.1.5 - *(04/20/17)*
- bump cypress coffee script and releaser dep
#### 0.1.4 - *(10/21/16)*
- add server destroy to https helper
#### 0.1.3 - *(08/24/16)*
- ref fork
#### 0.1.2 - *(08/24/16)*
- fix segfault errors
#### 0.1.1 - *(08/24/16)*
- fix devDep to dep
#### 0.1.0 - *(06/28/16)*
- initial release

View File

@@ -0,0 +1,7 @@
machine:
node:
version: 6.5.0
test:
override:
- npm run test-ci

View File

@@ -0,0 +1,2 @@
require("coffee-script/register")
require("./test/helpers/https_server").start(8443)

View File

@@ -0,0 +1,3 @@
require("@cypress/coffee-script")
module.exports = require("./lib/proxy")

View File

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

View File

@@ -0,0 +1,13 @@
CA = require("./ca")
Server = require("./server")
module.exports = {
create: (dir, port, options) ->
CA.create(dir)
.then (ca) ->
Server.create(ca, port, options)
reset: ->
Server.reset()
}

View File

@@ -0,0 +1,184 @@
_ = require("lodash")
fs = require("fs-extra")
net = require("net")
url = require("url")
https = require("https")
Promise = require("bluebird")
semaphore = require("semaphore")
allowDestroy = require("server-destroy-vvo")
parse = require("./util/parse")
fs = Promise.promisifyAll(fs)
sslServers = {}
sslSemaphores = {}
class Server
constructor: (@_ca, @_port) ->
@_onError = null
connect: (req, socket, head, options = {}) ->
if not head or head.length is 0
socket.once "data", (data) =>
@connect(req, socket, data, options)
socket.write "HTTP/1.1 200 OK\r\n"
if req.headers["proxy-connection"] is "keep-alive"
socket.write("Proxy-Connection: keep-alive\r\n")
socket.write("Connection: keep-alive\r\n")
return socket.write("\r\n")
else
if odc = options.onDirectConnection
## if onDirectConnection return true
## then dont proxy, just pass this through
if odc.call(@, req, socket, head) is true
return @_makeDirectConnection(req, socket, head)
socket.pause()
@_onServerConnectData(req, socket, head)
_onUpgrade: (fn, req, socket, head) ->
if fn
fn.call(@, req, socket, head)
_onRequest: (fn, req, res) ->
hostPort = parse.hostAndPort(req.url, req.headers, 443)
req.url = url.format({
protocol: "https:"
hostname: hostPort.host
port: hostPort.port
}) + req.url
if fn
return fn.call(@, req, res)
req.pipe(request(req.url))
.on "error", ->
res.statusCode = 500
res.end()
.pipe(res)
_makeDirectConnection: (req, socket, head) ->
directUrl = url.parse("http://#{req.url}")
@_makeConnection(socket, head, directUrl.port, directUrl.hostname)
_makeConnection: (socket, head, port, hostname) ->
cb = ->
socket.pipe(conn)
conn.pipe(socket)
socket.emit("data", head)
socket.resume()
## compact out hostname when undefined
args = _.compact([port, hostname, cb])
conn = net.connect.apply(net, args)
conn.on "error", (err) =>
if @_onError
@_onError(err, socket, head, port)
_onServerConnectData: (req, socket, head) ->
firstBytes = head[0]
makeConnection = (port) =>
@_makeConnection(socket, head, port)
if firstBytes is 0x16 or firstBytes is 0x80 or firstBytes is 0x00
{hostname} = url.parse("http://#{req.url}")
if sslServer = sslServers[hostname]
return makeConnection(sslServer.port)
if not sem = sslSemaphores[hostname]
sem = sslSemaphores[hostname] = semaphore(1)
sem.take =>
leave = ->
process.nextTick ->
sem.leave()
if sslServer = sslServers[hostname]
leave()
return makeConnection(sslServer.port)
@_getPortFor(hostname)
.then (port) ->
sslServers[hostname] = { port: port }
leave()
makeConnection(port)
else
makeConnection(@_port)
_normalizeKeyAndCert: (certPem, privateKeyPem) ->
return {
key: privateKeyPem
cert: certPem
}
_getCertificatePathsFor: (hostname) ->
@_ca.getCertificateKeysForHostname(hostname)
.spread(@_normalizeKeyAndCert)
_generateMissingCertificates: (hostname) ->
@_ca.generateServerCertificateKeys(hostname)
.spread(@_normalizeKeyAndCert)
_getPortFor: (hostname) ->
@_getCertificatePathsFor(hostname)
.catch (err) =>
@_generateMissingCertificates(hostname)
.then (data = {}) =>
@_sniServer.addContext(hostname, data)
return @_sniPort
listen: (options = {}) ->
new Promise (resolve) =>
@_onError = options.onError
@_sniServer = https.createServer({})
allowDestroy(@_sniServer)
@_sniServer.on "upgrade", @_onUpgrade.bind(@, options.onUpgrade)
@_sniServer.on "request", @_onRequest.bind(@, options.onRequest)
@_sniServer.listen =>
## store the port of our current sniServer
@_sniPort = @_sniServer.address().port
resolve()
close: ->
close = =>
new Promise (resolve) =>
@_sniServer.destroy(resolve)
close()
.finally ->
sslServers = {}
module.exports = {
reset: ->
sslServers = {}
create: (ca, port, options = {}) ->
srv = new Server(ca, port)
srv
.listen(options)
.return(srv)
}

View File

@@ -0,0 +1,35 @@
url = require("url")
module.exports = {
parseHost: (hostString, defaultPort) ->
if m = hostString.match(/^http:\/\/(.*)/)
parsedUrl = url.parse(hostString)
return {
host: parsedUrl.hostname
port: parsedUrl.port
}
hostPort = hostString.split(':')
host = hostPort[0]
port = if hostPort.length is 2 then +hostPort[1] else defaultPort
return {
host: host
port: port
}
hostAndPort: (reqUrl, headers, defaultPort) ->
host = headers.host
hostPort = @parseHost(host, defaultPort)
## this handles paths which include the full url. This could happen if it's a proxy
if m = reqUrl.match(/^http:\/\/([^\/]*)\/?(.*)$/)
parsedUrl = url.parse(reqUrl)
hostPort.host = parsedUrl.hostname
hostPort.port = parsedUrl.port
reqUrl = parsedUrl.path
hostPort
}

View File

@@ -0,0 +1,45 @@
{
"name": "@cypress/core-https-proxy",
"version": "0.1.5",
"description": "",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "NODE_ENV=test mocha --watch",
"test-ci": "NODE_ENV=test mocha",
"https": "node https.js",
"release": "releaser"
},
"repository": {
"type": "git",
"url": "git+https://github.com/cypress-io/cypress-core-https-proxy.git"
},
"author": "Brian Mann",
"license": "MIT",
"bugs": {
"url": "https://github.com/cypress-io/cypress-core-https-proxy/issues"
},
"homepage": "https://github.com/cypress-io/cypress-core-https-proxy#readme",
"devDependencies": {
"@cypress/releaser": "0.1.12",
"chai": "^3.5.0",
"http-mitm-proxy": "^0.5.1",
"mocha": "^2.5.3",
"request": "^2.72.0",
"request-promise": "^3.0.0",
"sinon": "^1.17.4",
"sinon-as-promised": "^4.0.0",
"sinon-chai": "^2.8.0",
"supertest": "^1.2.0",
"supertest-as-promised": "^3.1.0"
},
"dependencies": {
"@cypress/coffee-script": "0.1.2",
"bluebird": "^3.4.0",
"fs-extra": "^0.30.0",
"node-forge": "^0.6.39",
"semaphore": "^1.0.5",
"server-destroy-vvo": "1.0.1",
"ssl-root-cas": "^1.1.10"
}
}

View File

@@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEHjCCAwagAwIBAgIJALopHKFY/15aMA0GCSqGSIb3DQEBBQUAMGcxCzAJBgNV
BAYTAlVTMQ0wCwYDVQQIEwRVdGFoMQ4wDAYDVQQHEwVQcm92bzEjMCEGA1UEChMa
QUNNRSBTaWduaW5nIEF1dGhvcml0eSBJbmMxFDASBgNVBAMTC2V4YW1wbGUuY29t
MB4XDTE2MDYwODE3MTExOVoXDTE5MDMyOTE3MTExOVowZzELMAkGA1UEBhMCVVMx
DTALBgNVBAgTBFV0YWgxDjAMBgNVBAcTBVByb3ZvMSMwIQYDVQQKExpBQ01FIFNp
Z25pbmcgQXV0aG9yaXR5IEluYzEUMBIGA1UEAxMLZXhhbXBsZS5jb20wggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDG/vgku+Ugjy+BtPMGyhoOk89Ef6Cp
KintiwE4JI1LkaGJj2cJLBW7qll5fjSXOYbYVsEA5naa7hSfcIjUVidrsGSeDiLd
vOc2Vz/Dl163R384TgPBnfQ/OVI5r43/Kjkr2jWXOhvCGIbF1BBSz3aNNBFTgEy8
HkFwrxISzInt/GMpXn4eVDPIQpT1SoWNF301vANDPdni51W8ftsMtyKpCWFEsLHA
2bznrz2Mzb0BL+FvnrCmyPNTNHlYDZZ4mzUDXs77eO1sWhfM9J8B2tY1TrUwUEjy
Ggf1PX9tZr2VLCoEvFRZu6lxbQlVhdkE3ioXQLV83hDn9pUtYbkDD60lAgMBAAGj
gcwwgckwHQYDVR0OBBYEFK7xCs6E4Vt/O4KwbJgOHN/pCQT/MIGZBgNVHSMEgZEw
gY6AFK7xCs6E4Vt/O4KwbJgOHN/pCQT/oWukaTBnMQswCQYDVQQGEwJVUzENMAsG
A1UECBMEVXRhaDEOMAwGA1UEBxMFUHJvdm8xIzAhBgNVBAoTGkFDTUUgU2lnbmlu
ZyBBdXRob3JpdHkgSW5jMRQwEgYDVQQDEwtleGFtcGxlLmNvbYIJALopHKFY/15a
MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAH7EVGG1sxYH0QHbArxu
WIqzz79K5egLdnfrvvah/dDcqsJEH7BZSNeIWlBIs47ofVY4xUM/r4hL1TKgIrwq
IsKeRql02gNxQPZV7iudGuOBp41cmIxvM+cQsxWmK3Ja/Tf1tE0nsm2L7J+a7ktJ
cHJ5yzJA4gY5pVby/TvHzJ5R2BZwd5B+EVRStgxG2Yt4YvvlcDXCxIMzrFYUCmDf
AHo7KXsyA/R2mtihSvZEudivbxBt01wKvcAEMeUOsLuEZgSdqFmk3npIhOyHpPSM
pHM22Lf0jvz4WeCmgzB0ZinQh1k+E4kxMqP9g+R2dhlZITwL/z5TUdy90fh/iOzD
4ek=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAxv74JLvlII8vgbTzBsoaDpPPRH+gqSop7YsBOCSNS5GhiY9n
CSwVu6pZeX40lzmG2FbBAOZ2mu4Un3CI1FYna7Bkng4i3bznNlc/w5det0d/OE4D
wZ30PzlSOa+N/yo5K9o1lzobwhiGxdQQUs92jTQRU4BMvB5BcK8SEsyJ7fxjKV5+
HlQzyEKU9UqFjRd9NbwDQz3Z4udVvH7bDLciqQlhRLCxwNm85689jM29AS/hb56w
psjzUzR5WA2WeJs1A17O+3jtbFoXzPSfAdrWNU61MFBI8hoH9T1/bWa9lSwqBLxU
WbupcW0JVYXZBN4qF0C1fN4Q5/aVLWG5Aw+tJQIDAQABAoIBAH9KpezJjH3RWfA8
kaDsMtLUVidZBKpxYDSlUHhbWU7Xr19RLfW+D4DmLSn8QyPhFpLYm8k5ovDkDqkW
0VASdFD8msBIBqGUrsoh8ZXqBBp1T7nynZCCu7SdtC1WURzCI6Qbh2BfOVZlXgC5
8F8oeotEnTiuv2cua2nrc0I3OJvUT2AFblIPaCiKeUA6GEQNbe1SbD1VWhfyIMgL
59rUNtsq/NI2lS8Be52r5cE/KeEEi5+8p3hXPo0VcmHCU1Oyffidwr9bPcseCTjy
aKnzn/8FMmGlVGJ/rJNsuX68a1SQVbECc5ulb9+P9kMOztGAu/gaZJcrJdD8YFTa
8gKfDNkCgYEA7ns4+pqguqbZjpPu9ncKI+0exm9gU78AIdHY5hUifkfr2ynaQfu8
WlJOtta9K8jUI2N9jN/4ysSxoFUpsGoY7QKpgMF58JH4+HAGUJhUR4xsXu+P2tt+
vunWL5LngzreIzxPObqs/83shE+WxkzhHybu1A5Z55yS/VqmHF2gGFcCgYEA1Z0z
tA16t6Akb+XQN0wMYlEZp9195gFvri5zlydCqDfS1yf+rsF4oDQ8j90zyHvL/VWQ
wrmLlpuBhQz5VpztemlzLNp2/LdtJoBv0SbgbAgN0cWMD1C0R3M7XQXH65WHR7yK
urX2eKhmh/XXtclxm7ubC55edyouJwby0oJHqOMCgYEAlD6O/eFPFpgPVz0IZ7c5
23lUDyA+7fAmQd+zh9sNdRh6OeO7ZBb7T94oRioYr/YIQPNgoUi83DcG/9bQsnRR
iEuGWJ5skan77Vud4U2/3jYhS6Z5cx/Mmxq65RnZxk66tYaQ7R7o2Z8Fbn4XCK/T
pUzLW5CMPJitGsbVyX49vcMCgYEAl1bTjanLGpNDnV9lH/gqAfHRSmOa0byMwgu4
6wrup709DCASyP7bFi1MBuTBzjUe9bGMaNkJsz8jCP+DG0D84rAY4Fs615qgoxl1
nul7MC6Yk9jwfN1BjClwklwJIrgCaumCI/vMzfkJAyRCmlFIwvusQhQGe0iQAChl
Go0gdd8CgYAtMQlL6jJn9JpI3o6vZoFoTfGcMId7T8/VSPZ5qMyptP9lh/xDU8QN
y92AqG6nYxMY9CRRW1aP0HJRxu/BwKLF6QBid9efntYcT53GUoAqIyiwyjpIzlv2
F9RZj0L64mcmJTFvI+kvKrAeZVdUK/Q5OJyTHtOyA9o30LOBWibJ9A==
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1 @@
AA71608ABEAE0A1B

View File

@@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEHjCCAwagAwIBAgIJALopHKFY/15aMA0GCSqGSIb3DQEBBQUAMGcxCzAJBgNV
BAYTAlVTMQ0wCwYDVQQIEwRVdGFoMQ4wDAYDVQQHEwVQcm92bzEjMCEGA1UEChMa
QUNNRSBTaWduaW5nIEF1dGhvcml0eSBJbmMxFDASBgNVBAMTC2V4YW1wbGUuY29t
MB4XDTE2MDYwODE3MTExOVoXDTE5MDMyOTE3MTExOVowZzELMAkGA1UEBhMCVVMx
DTALBgNVBAgTBFV0YWgxDjAMBgNVBAcTBVByb3ZvMSMwIQYDVQQKExpBQ01FIFNp
Z25pbmcgQXV0aG9yaXR5IEluYzEUMBIGA1UEAxMLZXhhbXBsZS5jb20wggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDG/vgku+Ugjy+BtPMGyhoOk89Ef6Cp
KintiwE4JI1LkaGJj2cJLBW7qll5fjSXOYbYVsEA5naa7hSfcIjUVidrsGSeDiLd
vOc2Vz/Dl163R384TgPBnfQ/OVI5r43/Kjkr2jWXOhvCGIbF1BBSz3aNNBFTgEy8
HkFwrxISzInt/GMpXn4eVDPIQpT1SoWNF301vANDPdni51W8ftsMtyKpCWFEsLHA
2bznrz2Mzb0BL+FvnrCmyPNTNHlYDZZ4mzUDXs77eO1sWhfM9J8B2tY1TrUwUEjy
Ggf1PX9tZr2VLCoEvFRZu6lxbQlVhdkE3ioXQLV83hDn9pUtYbkDD60lAgMBAAGj
gcwwgckwHQYDVR0OBBYEFK7xCs6E4Vt/O4KwbJgOHN/pCQT/MIGZBgNVHSMEgZEw
gY6AFK7xCs6E4Vt/O4KwbJgOHN/pCQT/oWukaTBnMQswCQYDVQQGEwJVUzENMAsG
A1UECBMEVXRhaDEOMAwGA1UEBxMFUHJvdm8xIzAhBgNVBAoTGkFDTUUgU2lnbmlu
ZyBBdXRob3JpdHkgSW5jMRQwEgYDVQQDEwtleGFtcGxlLmNvbYIJALopHKFY/15a
MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAH7EVGG1sxYH0QHbArxu
WIqzz79K5egLdnfrvvah/dDcqsJEH7BZSNeIWlBIs47ofVY4xUM/r4hL1TKgIrwq
IsKeRql02gNxQPZV7iudGuOBp41cmIxvM+cQsxWmK3Ja/Tf1tE0nsm2L7J+a7ktJ
cHJ5yzJA4gY5pVby/TvHzJ5R2BZwd5B+EVRStgxG2Yt4YvvlcDXCxIMzrFYUCmDf
AHo7KXsyA/R2mtihSvZEudivbxBt01wKvcAEMeUOsLuEZgSdqFmk3npIhOyHpPSM
pHM22Lf0jvz4WeCmgzB0ZinQh1k+E4kxMqP9g+R2dhlZITwL/z5TUdy90fh/iOzD
4ek=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,9 @@
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwQE+xnWNY3G84R8Pl64K
VlbvP4qF8z0jsgCjaZmQWSPEgbPEwYY4NUb/ROZrIiwwo9HSpkfExrfEL2nflv65
mMRqyWVB5OgTFdmQTpc0r+YeSS/rSHEg0n+hz3Bugw6JZOFeaBvJQ3xyPQA621tx
ahIq0kAfum77aAaX3aFOOn5/IVjY3n4uy2qQGFv3Jh0ix5wnVgrKPldKwBOlLYnu
KQJFJmgUhwi4cFugAD6rnvy/vpDav13Ua7KahU6usByLSvw2jTjwYec9wrNxNH7u
CjEWAdLnwkVWu803EtLtFn9A29k2LTb96Z/3XsNw4UMAn1PtbVnljgMQrWMFPqMR
ywIDAQAB
-----END PUBLIC KEY-----

View File

@@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIEHjCCAwagAwIBAgIJALopHKFY/15aMA0GCSqGSIb3DQEBBQUAMGcxCzAJBgNV
BAYTAlVTMQ0wCwYDVQQIEwRVdGFoMQ4wDAYDVQQHEwVQcm92bzEjMCEGA1UEChMa
QUNNRSBTaWduaW5nIEF1dGhvcml0eSBJbmMxFDASBgNVBAMTC2V4YW1wbGUuY29t
MB4XDTE2MDYwODE3MTExOVoXDTE5MDMyOTE3MTExOVowZzELMAkGA1UEBhMCVVMx
DTALBgNVBAgTBFV0YWgxDjAMBgNVBAcTBVByb3ZvMSMwIQYDVQQKExpBQ01FIFNp
Z25pbmcgQXV0aG9yaXR5IEluYzEUMBIGA1UEAxMLZXhhbXBsZS5jb20wggEiMA0G
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDG/vgku+Ugjy+BtPMGyhoOk89Ef6Cp
KintiwE4JI1LkaGJj2cJLBW7qll5fjSXOYbYVsEA5naa7hSfcIjUVidrsGSeDiLd
vOc2Vz/Dl163R384TgPBnfQ/OVI5r43/Kjkr2jWXOhvCGIbF1BBSz3aNNBFTgEy8
HkFwrxISzInt/GMpXn4eVDPIQpT1SoWNF301vANDPdni51W8ftsMtyKpCWFEsLHA
2bznrz2Mzb0BL+FvnrCmyPNTNHlYDZZ4mzUDXs77eO1sWhfM9J8B2tY1TrUwUEjy
Ggf1PX9tZr2VLCoEvFRZu6lxbQlVhdkE3ioXQLV83hDn9pUtYbkDD60lAgMBAAGj
gcwwgckwHQYDVR0OBBYEFK7xCs6E4Vt/O4KwbJgOHN/pCQT/MIGZBgNVHSMEgZEw
gY6AFK7xCs6E4Vt/O4KwbJgOHN/pCQT/oWukaTBnMQswCQYDVQQGEwJVUzENMAsG
A1UECBMEVXRhaDEOMAwGA1UEBxMFUHJvdm8xIzAhBgNVBAoTGkFDTUUgU2lnbmlu
ZyBBdXRob3JpdHkgSW5jMRQwEgYDVQQDEwtleGFtcGxlLmNvbYIJALopHKFY/15a
MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAH7EVGG1sxYH0QHbArxu
WIqzz79K5egLdnfrvvah/dDcqsJEH7BZSNeIWlBIs47ofVY4xUM/r4hL1TKgIrwq
IsKeRql02gNxQPZV7iudGuOBp41cmIxvM+cQsxWmK3Ja/Tf1tE0nsm2L7J+a7ktJ
cHJ5yzJA4gY5pVby/TvHzJ5R2BZwd5B+EVRStgxG2Yt4YvvlcDXCxIMzrFYUCmDf
AHo7KXsyA/R2mtihSvZEudivbxBt01wKvcAEMeUOsLuEZgSdqFmk3npIhOyHpPSM
pHM22Lf0jvz4WeCmgzB0ZinQh1k+E4kxMqP9g+R2dhlZITwL/z5TUdy90fh/iOzD
4ek=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,20 @@
-----BEGIN CERTIFICATE-----
MIIDRjCCAi4CCQCqcWCKvq4KGzANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJV
UzENMAsGA1UECBMEVXRhaDEOMAwGA1UEBxMFUHJvdm8xIzAhBgNVBAoTGkFDTUUg
U2lnbmluZyBBdXRob3JpdHkgSW5jMRQwEgYDVQQDEwtleGFtcGxlLmNvbTAeFw0x
NjA2MDgxNzExMTlaFw0xNzEwMjExNzExMTlaMGMxCzAJBgNVBAYTAlVTMQ0wCwYD
VQQIEwRVdGFoMQ4wDAYDVQQHEwVQcm92bzEWMBQGA1UEChMNQUNNRSBUZWNoIElu
YzEdMBsGA1UEAxMUbG9jYWwubGRzY29ubmVjdC5vcmcwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQDBAT7GdY1jcbzhHw+XrgpWVu8/ioXzPSOyAKNpmZBZ
I8SBs8TBhjg1Rv9E5msiLDCj0dKmR8TGt8Qvad+W/rmYxGrJZUHk6BMV2ZBOlzSv
5h5JL+tIcSDSf6HPcG6DDolk4V5oG8lDfHI9ADrbW3FqEirSQB+6bvtoBpfdoU46
fn8hWNjefi7LapAYW/cmHSLHnCdWCso+V0rAE6Utie4pAkUmaBSHCLhwW6AAPque
/L++kNq/XdRrspqFTq6wHItK/DaNOPBh5z3Cs3E0fu4KMRYB0ufCRVa7zTcS0u0W
f0Db2TYtNv3pn/dew3DhQwCfU+1tWeWOAxCtYwU+oxHLAgMBAAEwDQYJKoZIhvcN
AQEFBQADggEBAFmj6aJg1/euvIjfnr4oWoeLKEThPdZDCBwOMesDH3RiZyAQ+UOK
kGIIQydgEKWazv8k4FzoPRDf1x1l7/iQofT7pxQgT8Mcv5LBAOPomG2tupaWy0b8
B2t4EKFH6AoO6Id48mkWWVMgonVen4rzcPslBi9E92306lnPZEevi9lKT/+hllqX
coCNhAuZ4eDS95e7gydPwdMY6P8Azx94buG6DFddSbtDppBy8sb+YOUCGLwwY8zy
8JPlk0V2LJLBeFuMAwRb7nULh25x3p+EXbYF3vNvmMdVgLiwvOheCNCTfwkjyotk
5+PqZrsL0ci+j0IfLHOBJBcBwjDCk1j7RR4=
-----END CERTIFICATE-----

View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAwQE+xnWNY3G84R8Pl64KVlbvP4qF8z0jsgCjaZmQWSPEgbPE
wYY4NUb/ROZrIiwwo9HSpkfExrfEL2nflv65mMRqyWVB5OgTFdmQTpc0r+YeSS/r
SHEg0n+hz3Bugw6JZOFeaBvJQ3xyPQA621txahIq0kAfum77aAaX3aFOOn5/IVjY
3n4uy2qQGFv3Jh0ix5wnVgrKPldKwBOlLYnuKQJFJmgUhwi4cFugAD6rnvy/vpDa
v13Ua7KahU6usByLSvw2jTjwYec9wrNxNH7uCjEWAdLnwkVWu803EtLtFn9A29k2
LTb96Z/3XsNw4UMAn1PtbVnljgMQrWMFPqMRywIDAQABAoIBAQCmoHCV3f8HD7bF
lI9doEAEcmFzNiCW8v3SO4LZNF7O1nZck9sWSM5WEMlprpRnmEcV55w5OlTESvrx
BDKopZmOLmUTInxVJ5bykjZn41r1UXJsNRaoICL6GH6WHLcMJoyvr3JV50dHIDVw
PLaTksrDjJOzmPDLf+1Ezgxmp1BmZjZQx3rViUs6w+DB8y7zep1R6Brt2KtRuiRo
C2QC4y8l8Wl0e2PbZWsNIX/FGZ0HEVSf0KSeztMh4jnMix6SqZWc5GxtpBaOE+7q
amgNXZ6NCe5pHX/5TL6xLzTTmcObp7AmGDlUqyNccYZ9OJMcdSte8UbGY9Q3D9Xv
wi4VTZ9BAoGBAPCRplUp6GqdDg2/EOUSGI+Uq6MNu5bajGtgcKSVH6rk9bb1qKqT
em9EgqZVOcqu1RxoSltxxCXWJBx1VNq2C/Gjv5NtSZG9tYpzgkVhxvu1vF+gCQIm
YDSdfbAjsSnw66PZ8K9QN/fsn46O7p2szGdMNER+ZdFaBG7d5/1nxxepAoGBAM1i
jVemTg66Q9fovYm//6YOFcJZxqmBoUFGeyZR6Gqkff+DYW2Tl9ACi3lyuijNoXoa
niUtHc8DWkfF7oVHCTujwLZfOOuQE2GfCSBsGAM4vsqHmKopXa+Vdgf2SX3Xk4YR
ok7hm+9r8KHvhpENFIZynJpB2Y2cfmtfamPVyvZTAoGBAMLMbe/QO475FgQzjy6u
dIzvSfyChkAlQEdClHPw/O+Y7w7z5Vwah63JwDGIGX/yCkcye+OIP9iTijIxIEDp
4MoOWna6rDQDdhliZz5fao0wIBUetayz/nscfEZA4JihqW3IcIjFYEy1oo4BV17+
KZYLstczsqbPSX+s2MntWt3hAoGAUlbqV3Ni4+ClBlBWfH0u3sHS02HulVJU9bht
8sTvqqFbqi5fxvBe8pOymvP10hnuXOeC82CNDMtJTHD72Q7Lks9pmTO/vCC8xGxf
2lG7pr7/Lm8CM2X2G0iOU07X082zZRJ4wasNOwVJKYK/rjNSQTx9sBIqR+2veRh9
rJyvi+UCgYEAlnGa/NxBH9urVxSCk2m3azgxW5sYlxXa9rfuQQBMaRrtmjUzArhZ
B5L0vKrV185qZGUflEyAeDxj4aDP9Yp4ksZ46Jcvqosv5FKVwRZU3DpZ/tEaJ0XV
K3zgH/422QidN0Ik1gjSjph4jIWfQXENvDvVj0tGDbksrJErvS+jPzc=
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,24 @@
http = require("http")
Promise = require("bluebird")
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)
res.end("<html><body>http server</body></html>")
module.exports = {
srv: srv
start: ->
new Promise (resolve) ->
srv.listen 8080, ->
console.log "server listening on port: 8080"
resolve(srv)
stop: ->
new Promise (resolve) ->
srv.close(resolve)
}

View File

@@ -0,0 +1,48 @@
fs = require("fs")
path = require("path")
https = require("https")
Promise = require("bluebird")
sslRootCas = require('ssl-root-cas')
allowDestroy = require("server-destroy-vvo")
sslRootCas
.inject()
.addFile(path.join(__dirname, "certs", "server", "my-root-ca.crt.pem"))
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"))
}
onRequest = (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)
res.end("<html><head></head><body>https server</body></html>")
servers = []
module.exports = {
start: (port) ->
new Promise (resolve) ->
srv = https.createServer(options, onRequest)
allowDestroy(srv)
servers.push(srv)
srv.listen port, ->
console.log "server listening on port: #{port}"
resolve(srv)
stop: ->
stop = (srv) ->
new Promise (resolve) ->
srv.destroy(resolve)
Promise.map(servers, stop)
.then ->
servers = []
}

View File

@@ -0,0 +1,17 @@
Promise = require("bluebird")
Proxy = require("http-mitm-proxy")
proxy = Proxy()
proxy.onRequest (ctx, cb) ->
cb()
module.exports = {
start: ->
new Promise (resolve) ->
proxy.listen({port: 8081, forceSNI: true}, resolve)
stop: ->
proxy.close()
}

View File

@@ -0,0 +1,67 @@
http = require("http")
path = require("path")
httpsProxy = require("../../lib/proxy")
prx = null
pipe = (req, res) ->
req.pipe(request(req.url))
.on "error", ->
console.log "**ERROR**", req.url
req.statusCode = 500
res.end()
.pipe(res)
onConnect = (req, socket, head, proxy) ->
proxy.connect(req, socket, head, {
onDirectConnection: (req, socket, head) ->
req.url is "localhost:8444"
})
onRequest = (req, res) ->
pipe(req, res)
module.exports = {
reset: ->
httpsProxy.reset()
start: (port) ->
prx = http.createServer()
dir = path.join(process.cwd(), "ca")
httpsProxy.create(dir, port, {
onUpgrade: (req, socket, head) ->
onRequest: (req, res) ->
console.log "ON REQUEST FROM OUTER PROXY", req.url, req.headers, req.method
if req.url.includes("replace")
write = res.write
res.write = (chunk) ->
chunk = new Buffer(chunk.toString().replace("https server", "replaced content"))
write.call(@, chunk)
pipe(req, res)
else
pipe(req, res)
})
.then (proxy) =>
prx.on "request", onRequest
prx.on "connect", (req, socket, head) ->
onConnect(req, socket, head, proxy)
new Promise (resolve) ->
prx.listen port, ->
prx.proxy = proxy
console.log "server listening on port: #{port}"
resolve(proxy)
stop: ->
new Promise (resolve) ->
prx.close(resolve)
.then ->
prx.proxy.close()
}

View File

@@ -0,0 +1,284 @@
fs = require("fs-extra")
net = require("net")
url = require("url")
path = require("path")
http = require("http")
https = require("https")
request = require("request")
Promise = require("bluebird")
sempahore = require("semaphore")
CA = require("../../lib/ca")
Promise.promisifyAll(fs)
ca = null
httpsSrv = null
httpsPort = null
sslServers = {}
sslSemaphores = {}
onClientError = (err) ->
console.log "CLIENT ERROR", err
onError = (err) ->
console.log "ERROR", err
onRequest = (req, res) ->
console.log "onRequest!!!!!!!!!", req.url, req.headers, req.method
hostPort = parseHostAndPort(req)
# req.pause()
opts = {
url: req.url
baseUrl: "https://" + hostPort.host + ":" + hostPort.port
method: req.method
headers: req.headers
}
req.pipe(request(opts))
.on "error", ->
console.log "**ERROR", req.url
res.statusCode = 500
res.end()
.pipe(res)
parseHostAndPort = (req, defaultPort) ->
host = req.headers.host
return null if not host
hostPort = parseHost(host, defaultPort)
## this handles paths which include the full url. This could happen if it's a proxy
if m = req.url.match(/^http:\/\/([^\/]*)\/?(.*)$/)
parsedUrl = url.parse(req.url)
hostPort.host = parsedUrl.hostname
hostPort.port = parsedUrl.port
req.url = parsedUrl.path
hostPort
parseHost = (hostString, defaultPort) ->
if m = hostString.match(/^http:\/\/(.*)/)
parsedUrl = url.parse(hostString)
return {
host: parsedUrl.hostname
port: parsedUrl.port
}
hostPort = hostString.split(':')
host = hostPort[0]
port = if hostPort.length is 2 then +hostPort[1] else defaultPort
return {
host: host
port: port
}
onConnect = (req, socket, head) ->
console.log "ON CONNECT!!!!!!!!!!!!!!!"
## tell the client that the connection is established
# socket.write('HTTP/' + req.httpVersion + ' 200 OK\r\n\r\n', 'UTF-8', function() {
# // creating pipes in both ends
# conn.pipe(socket);
# socket.pipe(conn);
# });
console.log "URL", req.url
console.log "HEADERS", req.headers
console.log "HEAD IS", head
console.log "HEAD LENGTH", head.length
# srvUrl = url.parse("http://#{req.url}")
# conn = null
# cb = ->
# socket.write('HTTP/1.1 200 Connection Established\r\n' +
# 'Proxy-agent: Cypress\r\n' +
# '\r\n')
# conn.write(head)
# conn.pipe(socket)
# socket.pipe(conn)
# conn = net.connect(srvUrl.port, srvUrl.hostname, cb)
# conn.on "error", (err) ->
# ## TODO: attach error handling here
# console.log "*******ERROR CONNECTING", err, err.stack
# # conn.on "close", ->
# # console.log "CONNECTION CLOSED", arguments
# return
# URL www.cypress.io:443
# HEADERS { host: 'www.cypress.io:443',
# 'proxy-connection': 'keep-alive',
# 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2609.0 Safari/537.36' }
# HEAD IS <Buffer >
# HEAD LENGTH 0
getHttpsServer = (hostname) ->
onCertificateRequired(hostname)
.then (certPaths) ->
Promise.props({
keyFileExists: fs.statAsync(certPaths.keyFile)
certFileExists: fs.statAsync(certPaths.certFile)
})
.catch (err) ->
onCertificateMissing(certPaths)
.then (data = {}) ->
return {
key: data.keyFileData
cert: data.certFileData
hosts: data.hosts
}
.then (data = {}) ->
hosts = [hostname]
delete data.hosts
hosts.forEach (host) ->
console.log "ADD CONTEXT", host, data
httpsSrv.addContext(host, data)
# sslServers[host] = { port: httpsPort }
# return cb(null, self.httpsPort)
return httpsPort
onCertificateMissing = (certPaths) ->
hosts = certPaths.hosts #or [ctx.hostname]
ca.generateServerCertificateKeys(hosts)
.spread (certPEM, privateKeyPEM) ->
return {
hosts: hosts
keyFileData: privateKeyPEM
certFileData: certPEM
}
onCertificateRequired = (hostname) ->
Promise.resolve({
keyFile: ""
certFile: ""
hosts: [hostname]
})
makeConnection = (port) ->
console.log "makeConnection", port
conn = net.connect port, ->
console.log "connected to", port#, socket, conn, head
socket.pipe(conn)
conn.pipe(socket)
socket.emit("data", head)
return socket.resume()
conn.on "error", onError
onServerConnectData = (head) ->
firstBytes = head[0]
if firstBytes is 0x16 or firstBytes is 0x80 or firstBytes is 0x00
{hostname} = url.parse("http://#{req.url}")
if sslServer = sslServers[hostname]
return makeConnection(sslServer.port)
wildcardhost = hostname.replace(/[^\.]+\./, "*.")
sem = sslSemaphores[wildcardhost]
if not sem
sem = sslSemaphores[wildcardhost] = sempahore(1)
sem.take ->
leave = ->
process.nextTick ->
console.log "leaving sem"
sem.leave()
if sslServer = sslServers[hostname]
leave()
return makeConnection(sslServer.port)
if sslServer = sslServers[wildcardhost]
leave()
sslServers[hostname] = {
port: sslServer
}
return makeConnection(sslServers[hostname].port)
getHttpsServer(hostname)
.then (port) ->
leave()
makeConnection(port)
else
throw new Error("@httpPort")
makeConnection(@httpPort)
if not head or head.length is 0
socket.once "data", onConnect.bind(@, req, socket)
socket.write "HTTP/1.1 200 OK\r\n"
if req.headers["proxy-connection"] is "keep-alive"
socket.write("Proxy-Connection: keep-alive\r\n")
socket.write("Connection: keep-alive\r\n")
return socket.write("\r\n")
else
socket.pause()
onServerConnectData(head)
prx = http.createServer()
prx.on("connect", onConnect)
prx.on("request", onRequest)
prx.on("clientError", onClientError)
prx.on("error", onError)
module.exports = {
prx: prx
startHttpsSrv: ->
new Promise (resolve) ->
httpsSrv = https.createServer({})
# httpsSrv.timeout = 0
httpsSrv.on("connect", onConnect)
httpsSrv.on("request", onRequest)
httpsSrv.on("clientError", onClientError)
httpsSrv.on("error", onError)
httpsSrv.listen ->
resolve([httpsSrv.address().port, httpsSrv])
start: ->
dir = path.join(process.cwd(), "ca")
CA.create(dir)
.then (c) =>
ca = c
@startHttpsSrv()
.spread (port, httpsSrv) ->
httpsPort = port
new Promise (resolve) ->
prx.listen 3333, ->
console.log "server listening on port: 3333"
resolve(prx)
stop: ->
new Promise (resolve) ->
prx.close(resolve)
}

View File

@@ -0,0 +1,138 @@
require("../spec_helper")
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"
path = require("path")
Promise = require("bluebird")
proxy = require("../helpers/proxy")
mitmProxy = require("../helpers/mitm")
httpServer = require("../helpers/http_server")
httpsServer = require("../helpers/https_server")
describe "Proxy", ->
beforeEach ->
Promise.join(
httpServer.start()
# mitmProxy.start()
httpsServer.start(8443)
httpsServer.start(8444)
proxy.start(3333)
.then (@proxy) =>
)
afterEach ->
Promise.join(
httpServer.stop()
httpsServer.stop()
proxy.stop()
)
it "can request the googles", ->
Promise.all([
request({
strictSSL: false
proxy: "http://localhost:3333"
url: "https://www.google.com"
})
request({
strictSSL: false
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"
})
.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"
})
.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"
})
.then (html) ->
expect(html).to.include("https server")
it "can boot the httpServer", ->
request({
strictSSL: false
url: "http://localhost:8080/"
proxy: "http://localhost:3333"
})
.then (html) ->
expect(html).to.include("http server")
context "generating certificates", ->
it "reuses existing certificates", ->
request({
strictSSL: false
url: "https://localhost:8443/"
proxy: "http://localhost:3333"
})
.then =>
proxy.reset()
## force this to reject if its called
@sandbox.stub(@proxy, "_generateMissingCertificates").rejects(new Error("should not call"))
request({
strictSSL: false
url: "https://localhost:8443/"
proxy: "http://localhost:3333"
})
context "closing", ->
it "resets sslServers and can reopen", ->
request({
strictSSL: false
url: "https://localhost:8443/"
proxy: "http://localhost:3333"
})
.then =>
proxy.stop()
.then =>
proxy.start(3333)
.then =>
## force this to reject if its called
@sandbox.stub(@proxy, "_generateMissingCertificates").rejects(new Error("should not call"))
request({
strictSSL: false
url: "https://localhost:8443/"
proxy: "http://localhost:3333"
})

View File

@@ -0,0 +1,5 @@
test/unit
test/integration
--reporter spec
--compilers coffee:@cypress/coffee-script
--recursive

View File

@@ -0,0 +1,18 @@
chai = require("chai")
sinon = require("sinon")
Promise = require("bluebird")
sinonChai = require("sinon-chai")
sinonPromise = require("sinon-as-promised")(Promise)
global.request = require("request-promise")
global.supertest = require("supertest-as-promised")(Promise)
chai.use(sinonChai)
global.expect = chai.expect
beforeEach ->
@sandbox = sinon.sandbox.create()
afterEach ->
@sandbox.restore()

View File

@@ -0,0 +1,76 @@
require("../spec_helper")
fs = require("fs-extra")
path = require("path")
Promise = require("bluebird")
CA = require("../../lib/ca")
fs = Promise.promisifyAll(fs)
describe "lib/ca", ->
beforeEach ->
@timeout(5000)
@dir = path.join(process.cwd(), "tmp")
fs.ensureDirAsync(@dir)
.then =>
CA.create(@dir)
.then (@ca) =>
afterEach ->
fs.removeAsync(@dir)
context "#generateServerCertificateKeys", ->
it "generates certs for each host", ->
@ca.generateServerCertificateKeys("www.cypress.io")
.spread (certPem, keyPrivatePem) ->
expect(certPem).to.include("-----BEGIN CERTIFICATE-----")
expect(keyPrivatePem).to.include("-----BEGIN RSA PRIVATE KEY-----")
context ".create", ->
it "returns a new CA instance", ->
expect(@ca).to.be.an.instanceof(CA)
it "creates certs + keys dir", ->
Promise.join(
fs.statAsync(path.join(@dir, "certs"))
fs.statAsync(path.join(@dir, "keys"))
)
it "writes certs/ca.pem", ->
fs.statAsync(path.join(@dir, "certs", "ca.pem"))
it "writes keys/ca.private.key", ->
fs.statAsync(path.join(@dir, "keys", "ca.private.key"))
it "writes keys/ca.public.key", ->
fs.statAsync(path.join(@dir, "keys", "ca.public.key"))
it "sets ca.CAcert", ->
expect(@ca.CAcert).to.be.an("object")
it "sets ca.CAkeys", ->
expect(@ca.CAkeys).to.be.an("object")
expect(@ca.CAkeys).to.have.a.property("privateKey")
expect(@ca.CAkeys).to.have.a.property("publicKey")
describe "existing CA folder", ->
beforeEach ->
@sandbox.spy(CA.prototype, "loadCA")
@sandbox.spy(CA.prototype, "generateCA")
CA.create(@dir)
.then (@ca2) =>
it "calls loadCA and not generateCA", ->
expect(CA.prototype.loadCA).to.be.calledOnce
expect(CA.prototype.generateCA).not.to.be.called
it "sets ca.CAcert", ->
expect(@ca2.CAcert).to.be.an("object")
it "sets ca.CAkeys", ->
expect(@ca2.CAkeys).to.be.an("object")
expect(@ca2.CAkeys).to.have.a.property("privateKey")
expect(@ca2.CAkeys).to.have.a.property("publicKey")

View File

@@ -0,0 +1,51 @@
require("../spec_helper")
Promise = require("bluebird")
proxy = require("../helpers/proxy")
Server = require("../../lib/server")
describe "lib/server", ->
beforeEach ->
@setup = (options = {}) =>
@ca = {}
@port = 12345
Server.create(@ca, @port, options)
context "#listen", ->
it "calls options.onUpgrade with req, socket head", ->
onUpgrade = @sandbox.stub()
@setup({onUpgrade: onUpgrade})
.then (srv) ->
srv._sniServer.emit("upgrade", 1, 2, 3)
expect(onUpgrade).to.be.calledWith(1,2,3)
it "calls options.onRequest with req, res", ->
onRequest = @sandbox.stub()
req = {url: "https://www.foobar.com", headers: {host: "www.foobar.com"}}
res = {}
@setup({onRequest: onRequest})
.then (srv) ->
srv._sniServer.emit("request", req, res)
expect(onRequest).to.be.calledWith(req, res)
it "calls options.onError with err and port", (done) ->
onError = @sandbox.stub()
socket = {}
head = {}
@setup({onError: onError})
.then (srv) ->
conn = srv._makeDirectConnection({url: "localhost:8444"}, socket, head)
conn.once "error", ->
err = onError.getCall(0).args[0]
expect(err.message).to.eq("connect ECONNREFUSED 127.0.0.1:8444")
expect(onError).to.be.calledWithMatch(err, socket, head, 8444)
done()