Do not send requests for SNI server through upstream proxy (#4275)

* add test to ensure that SNI server will never go through proxy

* prevent test from false positive

* ensure that SNI server requests never go through proxy

* e2e test that https-proxy does not pass sni reqs thru upstream

* improve debug logging in https-proxy

* fix using cwd, not workspaceFolder for terminals manager

* remove dead code

* stop the debug proxy after each test


Co-authored-by: Brian Mann <brian.mann86@gmail.com>
This commit is contained in:
Zach Bloomquist
2019-05-22 02:13:33 -04:00
committed by Brian Mann
parent ef5c38d178
commit b568e82545
8 changed files with 160 additions and 18 deletions

View File

@@ -31,7 +31,7 @@ class Server
## https://github.com/cypress-io/cypress/issues/3192
browserSocket.setNoDelay(true)
debug("Writing browserSocket connection headers %o", { url: req.url })
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?
@@ -62,6 +62,8 @@ class Server
@_onFirstHeadBytes(req, browserSocket, data, options)
_onFirstHeadBytes: (req, browserSocket, head, options) ->
debug("Got first head bytes %o", { url: req.url, head: _.chain(head).invoke('toString').slice(0, 64).join('').value() })
browserSocket.pause()
if odc = options.onDirectConnection
@@ -96,8 +98,16 @@ class Server
res.end()
.pipe(res)
_getProxyForUrl: (url) ->
if url == "https://localhost:#{@_sniPort}"
## https://github.com/cypress-io/cypress/issues/4257
## this is a tunnel to the SNI server, it should never go through a proxy
return undefined
getProxyForUrl(url)
_makeDirectConnection: (req, browserSocket, head) ->
{ port, hostname } = url.parse("http://#{req.url}")
{ port, hostname } = url.parse("https://#{req.url}")
debug("Making connection to #{hostname}:#{port}")
@_makeConnection(browserSocket, head, port, hostname)
@@ -124,7 +134,7 @@ class Server
browserSocket.resume()
if upstreamProxy = getProxyForUrl("https://#{hostname}:#{port}")
if upstreamProxy = @_getProxyForUrl("https://#{hostname}:#{port}")
# todo: as soon as all requests are intercepted, this can go away since this is just for pass-through
debug("making proxied connection %o", {
host: "#{hostname}:#{port}",
@@ -149,7 +159,7 @@ class Server
makeConnection = (port) =>
debug("Making intercepted connection to %s", port)
@_makeConnection(browserSocket, head, port)
@_makeConnection(browserSocket, head, port, "localhost")
if firstBytes not in SSL_RECORD_TYPES
## if this isn't an SSL request then go

View File

@@ -5,6 +5,7 @@ process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"
_ = require("lodash")
DebugProxy = require("@cypress/debugging-proxy")
net = require("net")
network = require("@packages/network")
path = require("path")
Promise = require("bluebird")
proxy = require("../helpers/proxy")
@@ -241,6 +242,27 @@ describe "Proxy", ->
expect(socket.destroyed).to.be.true
resolve()
## https://github.com/cypress-io/cypress/issues/4257
it "passes through to SNI when it is intercepted and not through proxy", ->
createSocket = @sandbox.stub(network.connect, 'createRetryingSocket').callsArgWith(1, new Error('stub'))
createProxyConn = @sandbox.spy(network.agent.httpsAgent, 'createUpstreamProxyConnection')
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: socket hang up' }, =>
expect(createProxyConn).to.not.be.called
expect(createSocket).to.be.calledWith({
port: @proxy._sniPort
host: 'localhost'
})
afterEach ->
@upstream.stop()
delete process.env.HTTP_PROXY

View File

@@ -5,6 +5,7 @@ sinonChai = require("sinon-chai")
sinonPromise = require("sinon-as-promised")(Promise)
global.request = require("request-promise")
global.sinon = sinon
global.supertest = require("supertest")
chai.use(sinonChai)
@@ -15,4 +16,4 @@ beforeEach ->
@sandbox = sinon.sandbox.create()
afterEach ->
@sandbox.restore()
@sandbox.restore()