mirror of
https://github.com/cypress-io/cypress.git
synced 2026-05-05 06:20:44 -05:00
chore: (multi-domain) support multiple remote states (#20752)
This commit is contained in:
@@ -19,6 +19,7 @@ import RequestMiddleware from './request-middleware'
|
||||
import ResponseMiddleware from './response-middleware'
|
||||
import { DeferredSourceMapCache } from '@packages/rewriter'
|
||||
import type { Browser } from '@packages/server/lib/browsers/types'
|
||||
import type { RemoteStates } from '@packages/server/lib/remote_states'
|
||||
|
||||
export const debugVerbose = Debug('cypress-verbose:proxy:http')
|
||||
|
||||
@@ -61,7 +62,7 @@ export type ServerCtx = Readonly<{
|
||||
shouldCorrelatePreRequests?: () => boolean
|
||||
getCurrentBrowser: () => Browser | Partial<Browser> & Pick<Browser, 'family'> | null
|
||||
getFileServerToken: () => string
|
||||
getRemoteState: CyServer.getRemoteState
|
||||
remoteStates: RemoteStates
|
||||
getRenderedHTMLOrigins: Http['getRenderedHTMLOrigins']
|
||||
netStubbingState: NetStubbingState
|
||||
middleware: HttpMiddlewareStacks
|
||||
@@ -74,7 +75,6 @@ const READONLY_MIDDLEWARE_KEYS: (keyof HttpMiddlewareThis<{}>)[] = [
|
||||
'buffers',
|
||||
'config',
|
||||
'getFileServerToken',
|
||||
'getRemoteState',
|
||||
'netStubbingState',
|
||||
'next',
|
||||
'end',
|
||||
@@ -201,7 +201,7 @@ export class Http {
|
||||
deferredSourceMapCache: DeferredSourceMapCache
|
||||
getCurrentBrowser: () => Browser | Partial<Browser> & Pick<Browser, 'family'> | null
|
||||
getFileServerToken: () => string
|
||||
getRemoteState: () => any
|
||||
remoteStates: RemoteStates
|
||||
middleware: HttpMiddlewareStacks
|
||||
netStubbingState: NetStubbingState
|
||||
preRequests: PreRequests = new PreRequests()
|
||||
@@ -219,7 +219,7 @@ export class Http {
|
||||
this.shouldCorrelatePreRequests = opts.shouldCorrelatePreRequests || (() => false)
|
||||
this.getCurrentBrowser = opts.getCurrentBrowser
|
||||
this.getFileServerToken = opts.getFileServerToken
|
||||
this.getRemoteState = opts.getRemoteState
|
||||
this.remoteStates = opts.remoteStates
|
||||
this.middleware = opts.middleware
|
||||
this.netStubbingState = opts.netStubbingState
|
||||
this.socket = opts.socket
|
||||
@@ -240,7 +240,7 @@ export class Http {
|
||||
shouldCorrelatePreRequests: this.shouldCorrelatePreRequests,
|
||||
getCurrentBrowser: this.getCurrentBrowser,
|
||||
getFileServerToken: this.getFileServerToken,
|
||||
getRemoteState: this.getRemoteState,
|
||||
remoteStates: this.remoteStates,
|
||||
request: this.request,
|
||||
middleware: _.cloneDeep(this.middleware),
|
||||
netStubbingState: this.netStubbingState,
|
||||
|
||||
@@ -141,9 +141,10 @@ function reqNeedsBasicAuthHeaders (req, { auth, origin }: Cypress.RemoteState) {
|
||||
}
|
||||
|
||||
const MaybeSetBasicAuthHeaders: RequestMiddleware = function () {
|
||||
const remoteState = this.getRemoteState()
|
||||
// get the remote state for the proxied url
|
||||
const remoteState = this.remoteStates.get(this.req.proxiedUrl)
|
||||
|
||||
if (remoteState.auth && reqNeedsBasicAuthHeaders(this.req, remoteState)) {
|
||||
if (remoteState?.auth && reqNeedsBasicAuthHeaders(this.req, remoteState)) {
|
||||
const { auth } = remoteState
|
||||
const base64 = Buffer.from(`${auth.username}:${auth.password}`).toString('base64')
|
||||
|
||||
@@ -164,12 +165,12 @@ const SendRequestOutgoing: RequestMiddleware = function () {
|
||||
|
||||
const requestBodyBuffered = !!this.req.body
|
||||
|
||||
const { strategy, origin, fileServer } = this.getRemoteState()
|
||||
const { strategy, origin, fileServer } = this.remoteStates.current()
|
||||
|
||||
if (strategy === 'file' && requestOptions.url.startsWith(origin)) {
|
||||
this.req.headers['x-cypress-authorization'] = this.getFileServerToken()
|
||||
|
||||
requestOptions.url = requestOptions.url.replace(origin, fileServer)
|
||||
requestOptions.url = requestOptions.url.replace(origin, fileServer as string)
|
||||
}
|
||||
|
||||
if (requestBodyBuffered) {
|
||||
|
||||
@@ -230,16 +230,22 @@ const PatchExpressSetHeader: ResponseMiddleware = function () {
|
||||
}
|
||||
|
||||
const MaybeDelayForMultiDomain: ResponseMiddleware = function () {
|
||||
const isCrossDomain = !reqMatchesOriginPolicy(this.req, this.getRemoteState())
|
||||
const isCrossDomain = !reqMatchesOriginPolicy(this.req, this.remoteStates.current())
|
||||
const isPreviousOrigin = this.remoteStates.isInOriginStack(this.req.proxiedUrl)
|
||||
const isHTML = resContentTypeIs(this.incomingRes, 'text/html')
|
||||
const isRenderedHTML = reqWillRenderHtml(this.req)
|
||||
const isAUTFrame = this.req.isAUTFrame
|
||||
|
||||
if (this.config.experimentalMultiDomain && isCrossDomain && isAUTFrame && (isHTML || isRenderedHTML)) {
|
||||
this.debug('is cross-domain, delay until domain:ready event')
|
||||
// delay the response if this is a cross-origin (and not returning to a previous origin) html request from the AUT iframe
|
||||
if (this.config.experimentalMultiDomain && isCrossDomain && !isPreviousOrigin && isAUTFrame && (isHTML || isRenderedHTML)) {
|
||||
this.debug('is cross-domain, delay until ready:for:domain event')
|
||||
|
||||
this.serverBus.once('ready:for:domain', () => {
|
||||
this.debug('ready for domain, let it go')
|
||||
this.serverBus.once('ready:for:domain', ({ failed }) => {
|
||||
this.debug(`ready for domain${failed ? ' failed' : ''}, let it go`)
|
||||
|
||||
if (!failed) {
|
||||
this.res.wantsInjection = 'fullMultiDomain'
|
||||
}
|
||||
|
||||
this.next()
|
||||
})
|
||||
@@ -267,7 +273,7 @@ const SetInjectionLevel: ResponseMiddleware = function () {
|
||||
|
||||
this.debug('determine injection')
|
||||
|
||||
const isReqMatchOriginPolicy = reqMatchesOriginPolicy(this.req, this.getRemoteState())
|
||||
const isReqMatchOriginPolicy = reqMatchesOriginPolicy(this.req, this.remoteStates.current())
|
||||
const getInjectionLevel = () => {
|
||||
if (this.incomingRes.headers['x-cypress-file-server-error'] && !this.res.isInitial) {
|
||||
this.debug('- partial injection (x-cypress-file-server-error)')
|
||||
@@ -275,10 +281,11 @@ const SetInjectionLevel: ResponseMiddleware = function () {
|
||||
return 'partial'
|
||||
}
|
||||
|
||||
const isSecondaryOrigin = this.remoteStates.isSecondaryOrigin(this.req.proxiedUrl)
|
||||
const isHTML = resContentTypeIs(this.incomingRes, 'text/html')
|
||||
const isAUTFrame = this.req.isAUTFrame
|
||||
|
||||
if (this.config.experimentalMultiDomain && !isReqMatchOriginPolicy && isAUTFrame && (isHTML || isRenderedHTML)) {
|
||||
if (this.config.experimentalMultiDomain && isSecondaryOrigin && isAUTFrame && (isHTML || isRenderedHTML)) {
|
||||
this.debug('- multi-domain injection')
|
||||
|
||||
return 'fullMultiDomain'
|
||||
@@ -397,7 +404,7 @@ const determineIfNeedsMultiDomainHandling = (ctx: HttpMiddlewareThis<ResponseMid
|
||||
!!ctx.req.isAUTFrame &&
|
||||
(
|
||||
(previousAUTRequestUrl && !cors.urlOriginsMatch(previousAUTRequestUrl, ctx.req.proxiedUrl))
|
||||
|| !reqMatchesOriginPolicy(ctx.req, ctx.getRemoteState())
|
||||
|| !ctx.remoteStates.isPrimaryOrigin(ctx.req.proxiedUrl)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -490,7 +497,7 @@ const MaybeSendRedirectToClient: ResponseMiddleware = function () {
|
||||
return this.next()
|
||||
}
|
||||
|
||||
setInitialCookie(this.res, this.getRemoteState(), true)
|
||||
setInitialCookie(this.res, this.remoteStates.current(), true)
|
||||
|
||||
debug('redirecting to new url %o', { statusCode, newUrl })
|
||||
this.res.redirect(Number(statusCode), newUrl)
|
||||
@@ -504,7 +511,7 @@ const CopyResponseStatusCode: ResponseMiddleware = function () {
|
||||
}
|
||||
|
||||
const ClearCyInitialCookie: ResponseMiddleware = function () {
|
||||
setInitialCookie(this.res, this.getRemoteState(), false)
|
||||
setInitialCookie(this.res, this.remoteStates.current(), false)
|
||||
this.next()
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"@cypress/request-promise": "4.2.6",
|
||||
"@cypress/sinon-chai": "2.9.1",
|
||||
"@packages/resolve-dist": "0.0.0-development",
|
||||
"@packages/server": "0.0.0-development",
|
||||
"@types/express": "4.17.2",
|
||||
"@types/supertest": "2.0.10",
|
||||
"express": "4.17.1",
|
||||
|
||||
@@ -11,13 +11,14 @@ import { expect } from 'chai'
|
||||
import supertest from 'supertest'
|
||||
import { allowDestroy } from '@packages/network'
|
||||
import { EventEmitter } from 'events'
|
||||
import { RemoteStates } from '@packages/server/lib/remote_states'
|
||||
|
||||
const Request = require('@packages/server/lib/request')
|
||||
const getFixture = async () => {}
|
||||
|
||||
context('network stubbing', () => {
|
||||
let config
|
||||
let remoteState
|
||||
let remoteStates: RemoteStates
|
||||
let netStubbingState: NetStubbingState
|
||||
let app
|
||||
let destinationApp
|
||||
@@ -27,7 +28,7 @@ context('network stubbing', () => {
|
||||
|
||||
beforeEach((done) => {
|
||||
config = {}
|
||||
remoteState = {}
|
||||
remoteStates = new RemoteStates(() => {})
|
||||
socket = new EventEmitter()
|
||||
socket.toDriver = sinon.stub()
|
||||
app = express()
|
||||
@@ -39,7 +40,7 @@ context('network stubbing', () => {
|
||||
config,
|
||||
middleware: defaultMiddleware,
|
||||
getCurrentBrowser: () => ({ family: 'chromium' }),
|
||||
getRemoteState: () => remoteState,
|
||||
remoteStates,
|
||||
getFileServerToken: () => 'fake-token',
|
||||
request: new Request(),
|
||||
getRenderedHTMLOrigins: () => ({}),
|
||||
@@ -62,6 +63,7 @@ context('network stubbing', () => {
|
||||
|
||||
server = allowDestroy(destinationApp.listen(() => {
|
||||
destinationPort = server.address().port
|
||||
remoteStates.set(`http://localhost:${destinationPort}`)
|
||||
done()
|
||||
}))
|
||||
})
|
||||
@@ -71,26 +73,12 @@ context('network stubbing', () => {
|
||||
})
|
||||
|
||||
it('can make a vanilla request', (done) => {
|
||||
remoteState.strategy = 'http'
|
||||
remoteState.props = {
|
||||
port: `${destinationPort}`,
|
||||
tld: 'localhost',
|
||||
domain: '',
|
||||
}
|
||||
|
||||
supertest(app)
|
||||
.get(`/http://localhost:${destinationPort}`)
|
||||
.expect('it worked', done)
|
||||
})
|
||||
|
||||
it('does not add CORS headers to all responses', () => {
|
||||
remoteState.strategy = 'http'
|
||||
remoteState.props = {
|
||||
port: `${destinationPort}`,
|
||||
tld: 'localhost',
|
||||
domain: '',
|
||||
}
|
||||
|
||||
return supertest(app)
|
||||
.get(`/http://localhost:${destinationPort}`)
|
||||
.then((res) => {
|
||||
@@ -241,13 +229,6 @@ context('network stubbing', () => {
|
||||
})
|
||||
})
|
||||
|
||||
remoteState.strategy = 'http'
|
||||
remoteState.props = {
|
||||
port: `${destinationPort}`,
|
||||
tld: 'localhost',
|
||||
domain: '',
|
||||
}
|
||||
|
||||
// capture unintercepted content-length
|
||||
await supertest(app)
|
||||
.post(`/http://localhost:${destinationPort}`)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { HttpMiddleware, _runStage } from '../../../lib/http'
|
||||
import { HttpMiddleware, HttpStages, _runStage } from '../../../lib/http'
|
||||
|
||||
export function testMiddleware (middleware: HttpMiddleware<any>[], ctx = {}) {
|
||||
const fullCtx = {
|
||||
@@ -6,7 +6,6 @@ export function testMiddleware (middleware: HttpMiddleware<any>[], ctx = {}) {
|
||||
req: {},
|
||||
res: {},
|
||||
config: {},
|
||||
getRemoteState: () => {},
|
||||
|
||||
middleware: {
|
||||
0: middleware,
|
||||
@@ -15,5 +14,9 @@ export function testMiddleware (middleware: HttpMiddleware<any>[], ctx = {}) {
|
||||
...ctx,
|
||||
}
|
||||
|
||||
return _runStage(0, fullCtx)
|
||||
const onError = (error) => {
|
||||
throw error
|
||||
}
|
||||
|
||||
return _runStage(HttpStages.IncomingRequest, fullCtx, onError)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import sinon from 'sinon'
|
||||
describe('http', function () {
|
||||
context('Http.handle', function () {
|
||||
let config
|
||||
let getRemoteState
|
||||
let middleware
|
||||
let incomingRequest
|
||||
let incomingResponse
|
||||
@@ -14,8 +13,6 @@ describe('http', function () {
|
||||
|
||||
beforeEach(function () {
|
||||
config = {}
|
||||
getRemoteState = sinon.stub().returns({})
|
||||
|
||||
incomingRequest = sinon.stub()
|
||||
incomingResponse = sinon.stub()
|
||||
error = sinon.stub()
|
||||
@@ -26,7 +23,7 @@ describe('http', function () {
|
||||
[HttpStages.Error]: [error],
|
||||
}
|
||||
|
||||
httpOpts = { config, getRemoteState, middleware }
|
||||
httpOpts = { config, middleware }
|
||||
})
|
||||
|
||||
it('calls IncomingRequest stack, then IncomingResponse stack', function () {
|
||||
@@ -99,7 +96,7 @@ describe('http', function () {
|
||||
const resAdded = {}
|
||||
const errorAdded = {}
|
||||
|
||||
let expectedKeys = ['req', 'res', 'config', 'getRemoteState', 'middleware']
|
||||
let expectedKeys = ['req', 'res', 'config', 'middleware']
|
||||
|
||||
incomingRequest.callsFake(function () {
|
||||
expect(this).to.include.keys(expectedKeys)
|
||||
|
||||
@@ -4,6 +4,7 @@ import { expect } from 'chai'
|
||||
import { testMiddleware } from './helpers'
|
||||
import { CypressIncomingRequest, CypressOutgoingResponse } from '../../../lib'
|
||||
import { HttpBuffer, HttpBuffers } from '../../../lib/http/util/buffers'
|
||||
import { RemoteStates } from '@packages/server/lib/remote_states'
|
||||
|
||||
describe('http/request-middleware', () => {
|
||||
it('exports the members in the correct order', () => {
|
||||
@@ -119,4 +120,117 @@ describe('http/request-middleware', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('MaybeSetBasicAuthHeaders', () => {
|
||||
const { MaybeSetBasicAuthHeaders } = RequestMiddleware
|
||||
|
||||
it('adds auth header from remote state', async () => {
|
||||
const headers = {}
|
||||
const remoteStates = new RemoteStates(() => {})
|
||||
|
||||
remoteStates.set('https://www.cypress.io/', { auth: { username: 'u', password: 'p' } })
|
||||
|
||||
const ctx = {
|
||||
req: {
|
||||
proxiedUrl: 'https://www.cypress.io/',
|
||||
headers,
|
||||
},
|
||||
res: {} as Partial<CypressOutgoingResponse>,
|
||||
remoteStates,
|
||||
}
|
||||
|
||||
await testMiddleware([MaybeSetBasicAuthHeaders], ctx)
|
||||
.then(() => {
|
||||
const expectedAuthHeader = `Basic ${Buffer.from('u:p').toString('base64')}`
|
||||
|
||||
expect(ctx.req.headers['authorization']).to.equal(expectedAuthHeader)
|
||||
})
|
||||
})
|
||||
|
||||
it('does not add auth header if origins do not match', async () => {
|
||||
const headers = {}
|
||||
const remoteStates = new RemoteStates(() => {})
|
||||
|
||||
remoteStates.set('https://cypress.io/', { auth: { username: 'u', password: 'p' } }) // does not match due to subdomain
|
||||
|
||||
const ctx = {
|
||||
req: {
|
||||
proxiedUrl: 'https://www.cypress.io/',
|
||||
headers,
|
||||
},
|
||||
res: {} as Partial<CypressOutgoingResponse>,
|
||||
remoteStates,
|
||||
}
|
||||
|
||||
await testMiddleware([MaybeSetBasicAuthHeaders], ctx)
|
||||
.then(() => {
|
||||
expect(ctx.req.headers['authorization']).to.be.undefined
|
||||
})
|
||||
})
|
||||
|
||||
it('does not add auth header if remote does not have auth', async () => {
|
||||
const headers = {}
|
||||
const remoteStates = new RemoteStates(() => {})
|
||||
|
||||
remoteStates.set('https://www.cypress.io/')
|
||||
|
||||
const ctx = {
|
||||
req: {
|
||||
proxiedUrl: 'https://www.cypress.io/',
|
||||
headers,
|
||||
},
|
||||
res: {} as Partial<CypressOutgoingResponse>,
|
||||
remoteStates,
|
||||
}
|
||||
|
||||
await testMiddleware([MaybeSetBasicAuthHeaders], ctx)
|
||||
.then(() => {
|
||||
expect(ctx.req.headers['authorization']).to.be.undefined
|
||||
})
|
||||
})
|
||||
|
||||
it('does not add auth header if remote not found', async () => {
|
||||
const headers = {}
|
||||
const remoteStates = new RemoteStates(() => {})
|
||||
|
||||
remoteStates.set('http://localhost:3500', { auth: { username: 'u', password: 'p' } })
|
||||
|
||||
const ctx = {
|
||||
req: {
|
||||
proxiedUrl: 'https://www.cypress.io/',
|
||||
headers,
|
||||
},
|
||||
res: {} as Partial<CypressOutgoingResponse>,
|
||||
remoteStates,
|
||||
}
|
||||
|
||||
await testMiddleware([MaybeSetBasicAuthHeaders], ctx)
|
||||
.then(() => {
|
||||
expect(ctx.req.headers['authorization']).to.be.undefined
|
||||
})
|
||||
})
|
||||
|
||||
it('does not update auth header from remote if request already has auth', async () => {
|
||||
const headers = {
|
||||
authorization: 'token',
|
||||
}
|
||||
const remoteStates = new RemoteStates(() => {})
|
||||
|
||||
remoteStates.set('https://www.cypress.io/', { auth: { username: 'u', password: 'p' } })
|
||||
|
||||
const ctx = {
|
||||
req: {
|
||||
proxiedUrl: 'https://www.cypress.io/',
|
||||
headers,
|
||||
},
|
||||
res: {} as Partial<CypressOutgoingResponse>,
|
||||
remoteStates,
|
||||
}
|
||||
|
||||
await testMiddleware([MaybeSetBasicAuthHeaders], ctx)
|
||||
.then(() => {
|
||||
expect(ctx.req.headers['authorization']).to.equal('token')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,9 +3,9 @@ import ResponseMiddleware from '../../../lib/http/response-middleware'
|
||||
import { debugVerbose } from '../../../lib/http'
|
||||
import { expect } from 'chai'
|
||||
import sinon from 'sinon'
|
||||
import {
|
||||
testMiddleware,
|
||||
} from './helpers'
|
||||
import { testMiddleware } from './helpers'
|
||||
import { RemoteStates } from '@packages/server/lib/remote_states'
|
||||
import EventEmitter from 'events'
|
||||
|
||||
describe('http/response-middleware', function () {
|
||||
it('exports the members in the correct order', function () {
|
||||
@@ -173,6 +173,57 @@ describe('http/response-middleware', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('doesn\'t do anything when request is for a previous origin in the stack', function () {
|
||||
prepareContext({
|
||||
req: {
|
||||
isAUTFrame: true,
|
||||
proxiedUrl: 'http://www.foobar.com/test',
|
||||
},
|
||||
incomingRes: {
|
||||
headers: {
|
||||
'content-type': 'text/html',
|
||||
},
|
||||
},
|
||||
secondaryOrigins: ['http://foobar.com', 'http://example.com'],
|
||||
config: {
|
||||
experimentalMultiDomain: true,
|
||||
},
|
||||
})
|
||||
|
||||
return testMiddleware([MaybeDelayForMultiDomain], ctx)
|
||||
.then(() => {
|
||||
expect(ctx.serverBus.emit).not.to.be.called
|
||||
})
|
||||
})
|
||||
|
||||
it('waits for server signal if req is not of a previous origin, letting it continue after receiving ready:for:domain', function () {
|
||||
prepareContext({
|
||||
req: {
|
||||
isAUTFrame: true,
|
||||
proxiedUrl: 'http://www.idp.com/test',
|
||||
},
|
||||
incomingRes: {
|
||||
headers: {
|
||||
'content-type': 'text/html',
|
||||
},
|
||||
},
|
||||
secondaryOrigins: ['http://foobar.com', 'http://example.com'],
|
||||
config: {
|
||||
experimentalMultiDomain: true,
|
||||
},
|
||||
})
|
||||
|
||||
const promise = testMiddleware([MaybeDelayForMultiDomain], ctx)
|
||||
|
||||
expect(ctx.serverBus.emit).to.be.calledWith('cross:domain:delaying:html', { href: 'http://www.idp.com/test' })
|
||||
|
||||
ctx.serverBus.once.withArgs('ready:for:domain').args[0][1]({ originPolicy: 'http://idp.com' })
|
||||
|
||||
expect(ctx.res.wantsInjection).to.equal('fullMultiDomain')
|
||||
|
||||
return promise
|
||||
})
|
||||
|
||||
it('waits for server signal if res is html, letting it continue after receiving ready:for:domain', function () {
|
||||
prepareContext({
|
||||
incomingRes: {
|
||||
@@ -182,7 +233,7 @@ describe('http/response-middleware', function () {
|
||||
},
|
||||
req: {
|
||||
isAUTFrame: true,
|
||||
proxiedUrl: 'protocol://host/originalUrl',
|
||||
proxiedUrl: 'http://www.foobar.com/test',
|
||||
},
|
||||
config: {
|
||||
experimentalMultiDomain: true,
|
||||
@@ -191,9 +242,9 @@ describe('http/response-middleware', function () {
|
||||
|
||||
const promise = testMiddleware([MaybeDelayForMultiDomain], ctx)
|
||||
|
||||
expect(ctx.serverBus.emit).to.be.calledWith('cross:domain:delaying:html', { href: 'protocol://host/originalUrl' })
|
||||
expect(ctx.serverBus.emit).to.be.calledWith('cross:domain:delaying:html', { href: 'http://www.foobar.com/test' })
|
||||
|
||||
ctx.serverBus.once.withArgs('ready:for:domain').args[0][1]()
|
||||
ctx.serverBus.once.withArgs('ready:for:domain').args[0][1]({ originPolicy: 'http://foobar.com' })
|
||||
|
||||
return promise
|
||||
})
|
||||
@@ -208,7 +259,7 @@ describe('http/response-middleware', function () {
|
||||
],
|
||||
},
|
||||
isAUTFrame: true,
|
||||
proxiedUrl: 'protocol://host/originalUrl',
|
||||
proxiedUrl: 'http://www.foobar.com/test',
|
||||
},
|
||||
config: {
|
||||
experimentalMultiDomain: true,
|
||||
@@ -217,9 +268,37 @@ describe('http/response-middleware', function () {
|
||||
|
||||
const promise = testMiddleware([MaybeDelayForMultiDomain], ctx)
|
||||
|
||||
expect(ctx.serverBus.emit).to.be.calledWith('cross:domain:delaying:html', { href: 'protocol://host/originalUrl' })
|
||||
expect(ctx.serverBus.emit).to.be.calledWith('cross:domain:delaying:html', { href: 'http://www.foobar.com/test' })
|
||||
|
||||
ctx.serverBus.once.withArgs('ready:for:domain').args[0][1]()
|
||||
ctx.serverBus.once.withArgs('ready:for:domain').args[0][1]({ originPolicy: 'http://foobar.com' })
|
||||
|
||||
expect(ctx.res.wantsInjection).to.equal('fullMultiDomain')
|
||||
|
||||
return promise
|
||||
})
|
||||
|
||||
it('waits for server signal, letting it continue after receiving ready:for:domain failed', function () {
|
||||
prepareContext({
|
||||
req: {
|
||||
isAUTFrame: true,
|
||||
proxiedUrl: 'http://www.idp.com/test',
|
||||
},
|
||||
incomingRes: {
|
||||
headers: {
|
||||
'content-type': 'text/html',
|
||||
},
|
||||
},
|
||||
secondaryOrigins: ['http://foobar.com', 'http://example.com'],
|
||||
config: {
|
||||
experimentalMultiDomain: true,
|
||||
},
|
||||
})
|
||||
|
||||
const promise = testMiddleware([MaybeDelayForMultiDomain], ctx)
|
||||
|
||||
expect(ctx.serverBus.emit).to.be.calledWith('cross:domain:delaying:html', { href: 'http://www.idp.com/test' })
|
||||
|
||||
ctx.serverBus.once.withArgs('ready:for:domain').args[0][1]({ failed: true })
|
||||
|
||||
expect(ctx.res.wantsInjection).to.be.undefined
|
||||
|
||||
@@ -227,6 +306,18 @@ describe('http/response-middleware', function () {
|
||||
})
|
||||
|
||||
function prepareContext (props) {
|
||||
const remoteStates = new RemoteStates(() => {})
|
||||
const eventEmitter = new EventEmitter()
|
||||
|
||||
// set the primary remote state
|
||||
remoteStates.set('http://127.0.0.1:3501')
|
||||
|
||||
// set the secondary remote states
|
||||
remoteStates.addEventListeners(eventEmitter)
|
||||
props.secondaryOrigins?.forEach((originPolicy) => {
|
||||
eventEmitter.emit('ready:for:domain', { originPolicy })
|
||||
})
|
||||
|
||||
ctx = {
|
||||
incomingRes: {
|
||||
headers: {},
|
||||
@@ -245,11 +336,7 @@ describe('http/response-middleware', function () {
|
||||
emit: sinon.stub(),
|
||||
once: sinon.stub(),
|
||||
},
|
||||
getRemoteState () {
|
||||
return {
|
||||
strategy: 'foo',
|
||||
}
|
||||
},
|
||||
remoteStates,
|
||||
debug () {},
|
||||
onError (error) {
|
||||
throw error
|
||||
@@ -353,6 +440,32 @@ describe('http/response-middleware', function () {
|
||||
'content-type': 'text/html',
|
||||
},
|
||||
},
|
||||
secondaryOrigins: ['http://foobar.com'],
|
||||
config: {
|
||||
experimentalMultiDomain: true,
|
||||
},
|
||||
})
|
||||
|
||||
return testMiddleware([SetInjectionLevel], ctx)
|
||||
.then(() => {
|
||||
expect(ctx.res.wantsInjection).to.equal('fullMultiDomain')
|
||||
})
|
||||
})
|
||||
|
||||
it('injects "fullMultiDomain" when request is in origin stack for cross-domain html"', function () {
|
||||
prepareContext({
|
||||
req: {
|
||||
proxiedUrl: 'http://example.com',
|
||||
isAUTFrame: true,
|
||||
cookies: {},
|
||||
headers: {},
|
||||
},
|
||||
incomingRes: {
|
||||
headers: {
|
||||
'content-type': 'text/html',
|
||||
},
|
||||
},
|
||||
secondaryOrigins: ['http://example.com', 'http://foobar.com'],
|
||||
config: {
|
||||
experimentalMultiDomain: true,
|
||||
},
|
||||
@@ -416,6 +529,40 @@ describe('http/response-middleware', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('injects partial when request is for top-level origin', function () {
|
||||
prepareContext({
|
||||
renderedHTMLOrigins: {},
|
||||
getRenderedHTMLOrigins () {
|
||||
return this.renderedHTMLOrigins
|
||||
},
|
||||
req: {
|
||||
proxiedUrl: 'http://127.0.0.1:3501/',
|
||||
isAUTFrame: true,
|
||||
cookies: {},
|
||||
headers: {
|
||||
'accept': [
|
||||
'text/html',
|
||||
'application/xhtml+xml',
|
||||
],
|
||||
},
|
||||
},
|
||||
incomingRes: {
|
||||
headers: {
|
||||
'content-type': 'text/html',
|
||||
},
|
||||
},
|
||||
secondaryOrigins: ['http://foobar.com'],
|
||||
config: {
|
||||
experimentalMultiDomain: true,
|
||||
},
|
||||
})
|
||||
|
||||
return testMiddleware([SetInjectionLevel], ctx)
|
||||
.then(() => {
|
||||
expect(ctx.res.wantsInjection).to.equal('partial')
|
||||
})
|
||||
})
|
||||
|
||||
it('does not set Origin-Agent-Cluster header to false when injection is not expected', function () {
|
||||
prepareContext({})
|
||||
|
||||
@@ -442,6 +589,18 @@ describe('http/response-middleware', function () {
|
||||
})
|
||||
|
||||
function prepareContext (props) {
|
||||
const remoteStates = new RemoteStates(() => {})
|
||||
const eventEmitter = new EventEmitter()
|
||||
|
||||
// set the primary remote state
|
||||
remoteStates.set('http://127.0.0.1:3501')
|
||||
|
||||
// set the secondary remote states
|
||||
remoteStates.addEventListeners(eventEmitter)
|
||||
props.secondaryOrigins?.forEach((originPolicy) => {
|
||||
eventEmitter.emit('ready:for:domain', { originPolicy })
|
||||
})
|
||||
|
||||
ctx = {
|
||||
incomingRes: {
|
||||
headers: {},
|
||||
@@ -460,14 +619,7 @@ describe('http/response-middleware', function () {
|
||||
},
|
||||
...props.req,
|
||||
},
|
||||
getRemoteState () {
|
||||
return {
|
||||
strategy: 'http',
|
||||
props: {
|
||||
port: '3501', tld: '127.0.0.1', domain: '',
|
||||
},
|
||||
}
|
||||
},
|
||||
remoteStates,
|
||||
debug: (formatter, ...args) => {
|
||||
debugVerbose(`%s %s %s ${formatter}`, ctx.req.method, ctx.req.proxiedUrl, ctx.stage, ...args)
|
||||
},
|
||||
@@ -577,13 +729,6 @@ describe('http/response-middleware', function () {
|
||||
getPreviousAUTRequestUrl () {
|
||||
return 'https://different.site'
|
||||
},
|
||||
getRemoteState () {
|
||||
// nonsense, but it's the simplest way to match origin policy
|
||||
return {
|
||||
strategy: 'file',
|
||||
origin: 'http',
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
await testMiddleware([CopyCookiesFromIncomingRes], ctx)
|
||||
@@ -622,17 +767,11 @@ describe('http/response-middleware', function () {
|
||||
},
|
||||
req: {
|
||||
isAUTFrame: true,
|
||||
proxiedUrl: 'http://www.foobar.com/multi-domain.html',
|
||||
},
|
||||
res: {
|
||||
append: appendStub,
|
||||
},
|
||||
getRemoteState () {
|
||||
// nonsense, but it's the simplest way to match origin policy
|
||||
return {
|
||||
strategy: 'file',
|
||||
origin: 'http',
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
await testMiddleware([CopyCookiesFromIncomingRes], ctx)
|
||||
@@ -651,17 +790,11 @@ describe('http/response-middleware', function () {
|
||||
},
|
||||
req: {
|
||||
isAUTFrame: true,
|
||||
proxiedUrl: 'http://www.foobar.com/multi-domain.html',
|
||||
},
|
||||
res: {
|
||||
append: appendStub,
|
||||
},
|
||||
getRemoteState () {
|
||||
// nonsense, but it's the simplest way to match origin policy
|
||||
return {
|
||||
strategy: 'file',
|
||||
origin: 'http',
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
ctx.getPreviousAUTRequestUrl = () => ctx.req.proxiedUrl
|
||||
@@ -778,6 +911,18 @@ describe('http/response-middleware', function () {
|
||||
})
|
||||
|
||||
function prepareContext (props) {
|
||||
const remoteStates = new RemoteStates(() => {})
|
||||
const eventEmitter = new EventEmitter()
|
||||
|
||||
// set the primary remote state
|
||||
remoteStates.set('http://foobar.com')
|
||||
|
||||
// set the secondary remote states
|
||||
remoteStates.addEventListeners(eventEmitter)
|
||||
props.secondaryOrigins?.forEach((originPolicy) => {
|
||||
eventEmitter.emit('ready:for:domain', { originPolicy })
|
||||
})
|
||||
|
||||
return {
|
||||
incomingRes: {
|
||||
headers: {},
|
||||
@@ -789,7 +934,7 @@ describe('http/response-middleware', function () {
|
||||
...props.res,
|
||||
},
|
||||
req: {
|
||||
proxiedUrl: 'http:127.0.0.1:3501/multi-domain.html',
|
||||
proxiedUrl: 'http://127.0.0.1:3501/multi-domain.html',
|
||||
headers: {},
|
||||
...props.req,
|
||||
},
|
||||
@@ -805,11 +950,7 @@ describe('http/response-middleware', function () {
|
||||
return { family: 'chromium' }
|
||||
},
|
||||
getPreviousAUTRequestUrl () {},
|
||||
getRemoteState () {
|
||||
return {
|
||||
strategy: 'foo',
|
||||
}
|
||||
},
|
||||
remoteStates,
|
||||
debug () {},
|
||||
onError (error) {
|
||||
throw error
|
||||
@@ -841,33 +982,3 @@ describe('http/response-middleware', function () {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// beforeEach(function () {
|
||||
// ctx = {
|
||||
// req: {
|
||||
// proxiedUrl: 'http://proxy.com',
|
||||
// cookies: {
|
||||
// '__cypress.initial': true,
|
||||
// },
|
||||
// headers: {
|
||||
// accept: ['text/html', 'application/xhtml+xml'],
|
||||
// },
|
||||
// },
|
||||
// res: {
|
||||
// setHeader: sinon.stub(),
|
||||
// },
|
||||
// getRemoteState: () => {
|
||||
// return {
|
||||
// strategy: 'http',
|
||||
// props: {
|
||||
// domain: 'proxy',
|
||||
// port: '80',
|
||||
// tld: 'com',
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
// getRenderedHTMLOrigins: () => {
|
||||
// return {}
|
||||
// },
|
||||
// }
|
||||
// })
|
||||
|
||||
Reference in New Issue
Block a user