mirror of
https://github.com/cypress-io/cypress.git
synced 2026-01-31 03:29:43 -06:00
* force websockets transport * wip: ensure ws connections take place thru a known socket * clean up yesterday's work * remove dead code (request is undefined) * update websocket tests * add websocket tests * update socket + server specs * add token auth for file_server * Fix cy.visit file_server * restrict non-proxied URLs, serve error on runner URL non-proxied * add e2e test for server splash page * fix types * use clientRoute, fix tests * only run 6_non_proxied in electron * use browser.path * improve empty options type * add ws assertions in e2e tests * fix server_spec * refactor socket whitelisting logic * update server_spec * respond to PR feedback - added tests for non-clientRoute redirecting to clientRoute when not behind proxy - cleaned up comments - cleaned up logic in server.coffee - moved error html to own file - added unit test for socket whitelist + fixed removal bug
159 lines
4.5 KiB
TypeScript
159 lines
4.5 KiB
TypeScript
import _ from 'lodash'
|
|
import debugModule from 'debug'
|
|
import { blacklist, cors } from '@packages/network'
|
|
import { HttpMiddleware } from './'
|
|
|
|
export type RequestMiddleware = HttpMiddleware<{
|
|
outgoingReq: any
|
|
}>
|
|
|
|
const debug = debugModule('cypress:proxy:http:request-middleware')
|
|
|
|
const LogRequest: RequestMiddleware = function () {
|
|
debug('proxying request %o', {
|
|
req: _.pick(this.req, 'method', 'proxiedUrl', 'headers'),
|
|
})
|
|
|
|
this.next()
|
|
}
|
|
|
|
const RedirectToClientRouteIfUnloaded: RequestMiddleware = function () {
|
|
// if we have an unload header it means our parent app has been navigated away
|
|
// directly and we need to automatically redirect to the clientRoute
|
|
if (this.req.cookies['__cypress.unload']) {
|
|
this.res.redirect(this.config.clientRoute)
|
|
|
|
return this.end()
|
|
}
|
|
|
|
this.next()
|
|
}
|
|
|
|
// TODO: is this necessary? it seems to be for requesting Cypress w/o the proxy,
|
|
// which isn't currently supported
|
|
const RedirectToClientRouteIfNotProxied: RequestMiddleware = function () {
|
|
// when you access cypress from a browser which has not had its proxy setup then
|
|
// req.url will match req.proxiedUrl and we'll know to instantly redirect them
|
|
// to the correct client route
|
|
if (this.req.url === this.req.proxiedUrl && !this.getRemoteState().visiting) {
|
|
// if we dont have a remoteState.origin that means we're initially requesting
|
|
// the cypress app and we need to redirect to the root path that serves the app
|
|
this.res.redirect(this.config.clientRoute)
|
|
|
|
return this.end()
|
|
}
|
|
|
|
this.next()
|
|
}
|
|
|
|
const EndRequestsToBlacklistedHosts: RequestMiddleware = function () {
|
|
const { blacklistHosts } = this.config
|
|
|
|
if (blacklistHosts) {
|
|
const matches = blacklist.matches(this.req.proxiedUrl, blacklistHosts)
|
|
|
|
if (matches) {
|
|
this.res.set('x-cypress-matched-blacklisted-host', matches)
|
|
debug('blacklisting request %o', {
|
|
url: this.req.proxiedUrl,
|
|
matches,
|
|
})
|
|
|
|
this.res.status(503).end()
|
|
|
|
return this.end()
|
|
}
|
|
}
|
|
|
|
this.next()
|
|
}
|
|
|
|
const MaybeEndRequestWithBufferedResponse: RequestMiddleware = function () {
|
|
const buffer = this.buffers.take(this.req.proxiedUrl)
|
|
|
|
if (buffer) {
|
|
debug('got a buffer %o', _.pick(buffer, 'url'))
|
|
this.res.wantsInjection = 'full'
|
|
|
|
return this.onResponse(buffer.response, buffer.stream)
|
|
}
|
|
|
|
this.next()
|
|
}
|
|
|
|
const StripUnsupportedAcceptEncoding: RequestMiddleware = function () {
|
|
// Cypress can only support plaintext or gzip, so make sure we don't request anything else
|
|
const acceptEncoding = this.req.headers['accept-encoding']
|
|
|
|
if (acceptEncoding) {
|
|
if (acceptEncoding.includes('gzip')) {
|
|
this.req.headers['accept-encoding'] = 'gzip'
|
|
} else {
|
|
delete this.req.headers['accept-encoding']
|
|
}
|
|
}
|
|
|
|
this.next()
|
|
}
|
|
|
|
function reqNeedsBasicAuthHeaders (req, { auth, origin }) {
|
|
//if we have auth headers, this request matches our origin, protection space, and the user has not supplied auth headers
|
|
return auth && !req.headers['authorization'] && cors.urlMatchesOriginProtectionSpace(req.proxiedUrl, origin)
|
|
}
|
|
|
|
const MaybeSetBasicAuthHeaders: RequestMiddleware = function () {
|
|
const remoteState = this.getRemoteState()
|
|
|
|
if (reqNeedsBasicAuthHeaders(this.req, remoteState)) {
|
|
const { auth } = remoteState
|
|
const base64 = Buffer.from(`${auth.username}:${auth.password}`).toString('base64')
|
|
|
|
this.req.headers['authorization'] = `Basic ${base64}`
|
|
}
|
|
|
|
this.next()
|
|
}
|
|
|
|
const SendRequestOutgoing: RequestMiddleware = function () {
|
|
const requestOptions = {
|
|
timeout: this.config.responseTimeout,
|
|
strictSSL: false,
|
|
followRedirect: false,
|
|
retryIntervals: [0, 100, 200, 200],
|
|
url: this.req.proxiedUrl,
|
|
}
|
|
|
|
const { strategy, origin, fileServer } = this.getRemoteState()
|
|
|
|
if (strategy === 'file' && requestOptions.url.startsWith(origin)) {
|
|
this.req.headers['x-cypress-authorization'] = this.getFileServerToken()
|
|
|
|
requestOptions.url = requestOptions.url.replace(origin, fileServer)
|
|
}
|
|
|
|
const req = this.request.create(requestOptions)
|
|
|
|
req.on('error', this.onError)
|
|
req.on('response', (incomingRes) => this.onResponse(incomingRes, req))
|
|
this.req.on('aborted', () => {
|
|
debug('request aborted')
|
|
req.abort()
|
|
})
|
|
|
|
// pipe incoming request body, headers to new request
|
|
this.req.pipe(req)
|
|
|
|
this.outgoingReq = req
|
|
}
|
|
|
|
export default {
|
|
LogRequest,
|
|
RedirectToClientRouteIfUnloaded,
|
|
RedirectToClientRouteIfNotProxied,
|
|
EndRequestsToBlacklistedHosts,
|
|
MaybeEndRequestWithBufferedResponse,
|
|
StripUnsupportedAcceptEncoding,
|
|
MaybeSetBasicAuthHeaders,
|
|
SendRequestOutgoing,
|
|
}
|