/
request-middleware.ts
158 lines (121 loc) · 4.5 KB
/
request-middleware.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
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 { blocklistHosts } = this.config
if (blocklistHosts) {
const matches = blacklist.matches(this.req.proxiedUrl, blocklistHosts)
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,
}