Skip to content
12 changes: 8 additions & 4 deletions packages/server/config/app.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
{
"development": {
"api_url": "http://localhost:1234/"
"preliminary_url": "http://localhost:1234/",
"primary_url": "http://localhost:1234/"
},
"test": {
"api_url": "http://localhost:1234/"
"preliminary_url": "http://localhost:1234/",
"primary_url": "http://localhost:4321/"
},
"staging": {
"api_url": "https://api-staging.cypress.io/"
"preliminary_url": "https://api-proxy-staging.cypress.io",
"primary_url": "https://api-staging.cypress.io/"
},
"production": {
"api_url": "https://api.cypress.io/"
"preliminary_url": "https://api-proxy.cypress.io",
"primary_url": "https://api.cypress.io/"
}
}
6 changes: 2 additions & 4 deletions packages/server/lib/cloud/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import type { AfterSpecDurations } from '@packages/types'
import { agent } from '@packages/network'
import type { CombinedAgent } from '@packages/network/lib/agent'

import { apiUrl, apiRoutes, makeRoutes } from '../routes'
import { apiUrl, preliminaryApiUrl, apiRoutes, makeRoutes } from '../routes'
import { getText } from '../../util/status_code'
import * as enc from '../encryption'
import getEnvInformationForProjectRoot from '../environment'
Expand Down Expand Up @@ -628,8 +628,6 @@ export default {
return retryWithBackoff(async (attemptIndex) => {
const { projectRoot, timeout, ...preflightRequestBody } = preflightInfo

const preflightBaseProxy = apiUrl.replace('api', 'api-proxy')

const envInformation = await getEnvInformationForProjectRoot(projectRoot, process.pid.toString())

const makeReq = (baseUrl: string, agent: CombinedAgent | null, timeout: number) => {
Expand Down Expand Up @@ -662,7 +660,7 @@ export default {

if (initialPreflightTimeout >= 0) {
try {
return await makeReq(preflightBaseProxy, null, initialPreflightTimeout)
return await makeReq(preliminaryApiUrl, null, initialPreflightTimeout)
} catch (err) {
if (err.statusCode === 412) {
throw err
Expand Down
4 changes: 3 additions & 1 deletion packages/server/lib/cloud/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import UrlParse from 'url-parse'

const app_config = require('../../config/app.json')

export const apiUrl = app_config[process.env.CYPRESS_CONFIG_ENV || process.env.CYPRESS_INTERNAL_ENV || 'development'].api_url
export const apiUrl = app_config[process.env.CYPRESS_CONFIG_ENV || process.env.CYPRESS_INTERNAL_ENV || 'development'].primary_url

export const preliminaryApiUrl = app_config[process.env.CYPRESS_CONFIG_ENV || process.env.CYPRESS_INTERNAL_ENV || 'development'].preliminary_url

const CLOUD_ENDPOINTS = {
api: '',
Expand Down
24 changes: 24 additions & 0 deletions system-tests/lib/serverStub.ts
Original file line number Diff line number Diff line change
Expand Up @@ -501,3 +501,27 @@ export const setupStubbedServer = (routes) => {

return mockServerState
}

interface StubbedServerConfig {
routes?: Record<string, any>
port: number
static?: boolean
}

export function setupPrimaryAlternateStubbedServer (servers: StubbedServerConfig[]) {
console.log('setupPrimaryAlternateStubbedServer', servers)
systemTests.setup({
servers: [
...servers.map(({ routes, port, static: isStatic }) => {
return {
port,
onServer: onServer(routes),
static: isStatic,
}
}), {
port: 3131,
static: true,
},
],
})
}
163 changes: 163 additions & 0 deletions system-tests/test/record_through_proxy_spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import DebugProxy from '@cypress/debugging-proxy'
import systemTests from '../lib/system-tests'
import {
createRoutes,
setupStubbedServer,
setupPrimaryAlternateStubbedServer as setupPreliminaryAlternateStubbedServer,
encryptBody,
} from '../lib/serverStub'
import { test as apiUrls } from '@packages/server/config/app.json'

describe('recording through proxy', () => {
let proxy: DebugProxy

const expectedPaths = [
'runs',
'runs/00748421-e035-4a3d-8604-8468cc48bdb5/instances',
'instances/e9e81b5e-cc58-4026-b2ff-8ae3161435a6/tests',
'instances/e9e81b5e-cc58-4026-b2ff-8ae3161435a6/results',
'instances/e9e81b5e-cc58-4026-b2ff-8ae3161435a6/artifacts',
'instances/e9e81b5e-cc58-4026-b2ff-8ae3161435a6/stdout',
'runs/00748421-e035-4a3d-8604-8468cc48bdb5/instances',
]
const preliminaryHost = apiUrls.preliminary_url
const primaryHost = apiUrls.primary_url
const preliminaryPort = Number(new URL(apiUrls.preliminary_url).port)
const primaryPort = Number(new URL(apiUrls.primary_url).port)

function expectedUrls (host: typeof preliminaryHost | typeof primaryHost) {
return expectedPaths.map((path) => `${host}${path}`)
}

beforeEach(async () => {
proxy = new DebugProxy({
host: 'localhost',
keepRequests: true,
})

await proxy.start(3128)

process.env.DISABLE_API_RETRIES = 'true'
process.env.HTTP_PROXY = 'http://localhost:3128'
process.env.HTTP_PROXY_TARGET_FOR_ORIGIN_REQUESTS = 'http://localhost:3128'
process.env.NO_PROXY = '<-loopback>'
})

afterEach(async () => {
await proxy.stop()
})

describe('when the initial preflight is not proxied and returns the preliminary host on preflight', () => {
setupStubbedServer(createRoutes({
sendPreflight: {
method: 'post',
url: '/preflight',
res: async (req, res) => {
const preflightResponse = { encrypt: true, apiUrl: preliminaryHost }

return res.json(await encryptBody(req, res, preflightResponse))
},
},
}))

it('sends all api requests to the preliminary host', async function () {
await systemTests.exec(this, {
key: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
configFile: 'cypress-with-project-id.config.js',
spec: 'record_pass*',
expectedExitCode: 0,
record: true,
})

const proxiedUrls = proxy.getRequests().map(({ url }) => url)

expect(proxiedUrls).not.to.include(`${preliminaryHost}/preflight`)
expect(proxiedUrls).to.include.members(expectedUrls(preliminaryHost))
})
})

describe('when the preliminary preflight is not proxied, and returns the primary host', () => {
setupPreliminaryAlternateStubbedServer([{
routes: {
sendPreflight: {
method: 'post',
url: '/preflight',
res: async (req, res) => {
const preflightResponse = { encrypt: true, apiUrl: primaryHost }

return res.json(await encryptBody(req, res, preflightResponse))
},
},
},
port: 1234,
}, {
routes: createRoutes(),
port: 4321,
}])

it('makes subsequent requests to the primary host', async function () {
await systemTests.exec(this, {
key: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
configFile: 'cypress-with-project-id.config.js',
spec: 'record_pass*',
expectedExitCode: 0,
record: true,
})

const proxiedUrls = proxy.getRequests().map(({ url }) => url)

expect(proxiedUrls).not.to.include(`${preliminaryHost}/preflight`)

expect(proxy.getRequests().map(({ url }) => url)).to.include.members(expectedUrls(primaryHost))
})
})

describe('when the preliminary preflight is not proxied and fails, a preflight is sent to the primary api host via the proxy.', () => {
let preliminaryHostPreflightCalled = false

setupPreliminaryAlternateStubbedServer(
[
{
routes: {
sendPreflight: {
method: 'post',
url: '/preflight',
res: async (req, res) => {
preliminaryHostPreflightCalled = true

return res.status(500).send('Server Error')
},
},
},
port: preliminaryPort,
},
{
routes: createRoutes(),
port: primaryPort,
},
],
)

beforeEach(() => {
preliminaryHostPreflightCalled = false
})

it('makes all subsequent requests to the primary host through the proxy', async function () {
await systemTests.exec(this, {
key: 'f858a2bc-b469-4e48-be67-0876339ee7e1',
configFile: 'cypress-with-project-id.config.js',
spec: 'record_pass*',
expectedExitCode: 0,
record: true,
})

expect(preliminaryHostPreflightCalled).to.be.true

const proxiedUrls = proxy.getRequests().map(({ url }) => url)

expect(proxiedUrls).not.to.include(`${preliminaryHost}/preflight`)
expect(proxiedUrls).not.to.include.members(expectedUrls(preliminaryHost))
expect(proxiedUrls).to.include.members(expectedUrls(primaryHost))
})
})
})
Loading