@mswjs/interceptors 0.32.2 → 0.33.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -7
- package/lib/browser/{chunk-732REFPX.mjs → chunk-5ETVT6GU.mjs} +28 -79
- package/lib/browser/chunk-5ETVT6GU.mjs.map +1 -0
- package/lib/browser/chunk-6MBJUL74.js +142 -0
- package/lib/browser/chunk-6MBJUL74.js.map +1 -0
- package/lib/browser/chunk-7A4UJNSW.mjs +196 -0
- package/lib/browser/chunk-7A4UJNSW.mjs.map +1 -0
- package/lib/browser/{chunk-PSX5J3RF.js → chunk-7GVJEW45.js} +30 -81
- package/lib/browser/chunk-7GVJEW45.js.map +1 -0
- package/lib/browser/{chunk-2CRB3JAQ.js → chunk-FXSPMSSQ.js} +1 -1
- package/lib/browser/chunk-FXSPMSSQ.js.map +1 -0
- package/lib/browser/{chunk-OMISYKWR.mjs → chunk-GGUENBDN.mjs} +1 -1
- package/lib/browser/chunk-GGUENBDN.mjs.map +1 -0
- package/lib/browser/chunk-NU2MPFD6.mjs +142 -0
- package/lib/browser/chunk-NU2MPFD6.mjs.map +1 -0
- package/lib/browser/chunk-VRKVKT62.js +196 -0
- package/lib/browser/chunk-VRKVKT62.js.map +1 -0
- package/lib/browser/glossary-7d7adb4b.d.ts +66 -0
- package/lib/browser/index.d.ts +1 -1
- package/lib/browser/index.js +2 -2
- package/lib/browser/index.mjs +1 -1
- package/lib/browser/interceptors/XMLHttpRequest/index.d.ts +2 -6
- package/lib/browser/interceptors/XMLHttpRequest/index.js +4 -4
- package/lib/browser/interceptors/XMLHttpRequest/index.mjs +3 -3
- package/lib/browser/interceptors/fetch/index.d.ts +1 -1
- package/lib/browser/interceptors/fetch/index.js +4 -4
- package/lib/browser/interceptors/fetch/index.mjs +3 -3
- package/lib/browser/presets/browser.d.ts +1 -1
- package/lib/browser/presets/browser.js +6 -6
- package/lib/browser/presets/browser.mjs +4 -4
- package/lib/node/{BatchInterceptor-2badedde.d.ts → BatchInterceptor-13d40c95.d.ts} +1 -1
- package/lib/node/{Interceptor-88ee47c0.d.ts → Interceptor-a31b1217.d.ts} +35 -13
- package/lib/node/RemoteHttpInterceptor.d.ts +2 -2
- package/lib/node/RemoteHttpInterceptor.js +55 -52
- package/lib/node/RemoteHttpInterceptor.js.map +1 -1
- package/lib/node/RemoteHttpInterceptor.mjs +53 -50
- package/lib/node/RemoteHttpInterceptor.mjs.map +1 -1
- package/lib/node/{chunk-2COJKQQB.js → chunk-42632LKH.js} +3 -3
- package/lib/node/chunk-5WWNCLB3.js +196 -0
- package/lib/node/chunk-5WWNCLB3.js.map +1 -0
- package/lib/node/{chunk-TGTPXCLF.mjs → chunk-BUCULLYM.mjs} +1 -1
- package/lib/node/{chunk-TGTPXCLF.mjs.map → chunk-BUCULLYM.mjs.map} +1 -1
- package/lib/node/{chunk-OJ6O4LSC.mjs → chunk-BZ3Y7YV5.mjs} +1 -1
- package/lib/node/chunk-BZ3Y7YV5.mjs.map +1 -0
- package/lib/node/{chunk-PNWPIDEL.mjs → chunk-HGQLG7KE.mjs} +2 -2
- package/lib/node/{chunk-EIBTX65O.js → chunk-IDEEMJ3F.js} +1 -1
- package/lib/node/chunk-IDEEMJ3F.js.map +1 -0
- package/lib/node/chunk-KY3RJ2M3.mjs +196 -0
- package/lib/node/chunk-KY3RJ2M3.mjs.map +1 -0
- package/lib/node/{chunk-CFRXZJO4.js → chunk-MCSMMYNC.js} +81 -89
- package/lib/node/chunk-MCSMMYNC.js.map +1 -0
- package/lib/node/{chunk-CMVICWQS.mjs → chunk-MII7WYYZ.mjs} +73 -81
- package/lib/node/chunk-MII7WYYZ.mjs.map +1 -0
- package/lib/node/{chunk-PYD4E2EJ.js → chunk-P6QG76R3.js} +34 -85
- package/lib/node/chunk-P6QG76R3.js.map +1 -0
- package/lib/node/{chunk-DV4PBH4D.mjs → chunk-TOV4TYIX.mjs} +29 -80
- package/lib/node/chunk-TOV4TYIX.mjs.map +1 -0
- package/lib/node/{chunk-BFLYGQ6D.js → chunk-YGM3BCJU.js} +1 -1
- package/lib/node/chunk-YGM3BCJU.js.map +1 -0
- package/lib/node/index.d.ts +2 -2
- package/lib/node/index.js +4 -4
- package/lib/node/index.mjs +3 -3
- package/lib/node/interceptors/ClientRequest/index.d.ts +2 -2
- package/lib/node/interceptors/ClientRequest/index.js +4 -4
- package/lib/node/interceptors/ClientRequest/index.mjs +3 -3
- package/lib/node/interceptors/XMLHttpRequest/index.d.ts +2 -6
- package/lib/node/interceptors/XMLHttpRequest/index.js +5 -5
- package/lib/node/interceptors/XMLHttpRequest/index.mjs +4 -4
- package/lib/node/interceptors/fetch/index.d.ts +1 -1
- package/lib/node/interceptors/fetch/index.js +52 -123
- package/lib/node/interceptors/fetch/index.js.map +1 -1
- package/lib/node/interceptors/fetch/index.mjs +50 -121
- package/lib/node/interceptors/fetch/index.mjs.map +1 -1
- package/lib/node/presets/node.d.ts +1 -1
- package/lib/node/presets/node.js +7 -7
- package/lib/node/presets/node.mjs +5 -5
- package/package.json +2 -2
- package/src/InterceptorError.ts +7 -0
- package/src/RemoteHttpInterceptor.ts +62 -57
- package/src/RequestController.test.ts +49 -0
- package/src/RequestController.ts +81 -0
- package/src/glossary.ts +4 -6
- package/src/interceptors/ClientRequest/MockHttpSocket.ts +1 -1
- package/src/interceptors/ClientRequest/index.test.ts +23 -34
- package/src/interceptors/ClientRequest/index.ts +21 -82
- package/src/interceptors/ClientRequest/utils/recordRawHeaders.test.ts +159 -0
- package/src/interceptors/ClientRequest/utils/recordRawHeaders.ts +80 -27
- package/src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts +1 -1
- package/src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts +27 -108
- package/src/interceptors/XMLHttpRequest/index.ts +0 -6
- package/src/interceptors/fetch/index.ts +52 -169
- package/src/utils/handleRequest.ts +213 -0
- package/src/utils/responseUtils.ts +4 -4
- package/lib/browser/chunk-2CRB3JAQ.js.map +0 -1
- package/lib/browser/chunk-732REFPX.mjs.map +0 -1
- package/lib/browser/chunk-MAEPOYB6.mjs +0 -213
- package/lib/browser/chunk-MAEPOYB6.mjs.map +0 -1
- package/lib/browser/chunk-MQJ3JOOK.js +0 -49
- package/lib/browser/chunk-MQJ3JOOK.js.map +0 -1
- package/lib/browser/chunk-OMISYKWR.mjs.map +0 -1
- package/lib/browser/chunk-OUWBQF3Z.mjs +0 -49
- package/lib/browser/chunk-OUWBQF3Z.mjs.map +0 -1
- package/lib/browser/chunk-PSX5J3RF.js.map +0 -1
- package/lib/browser/chunk-WBHIW62P.js +0 -213
- package/lib/browser/chunk-WBHIW62P.js.map +0 -1
- package/lib/browser/glossary-1c204f45.d.ts +0 -44
- package/lib/node/chunk-BFLYGQ6D.js.map +0 -1
- package/lib/node/chunk-CFRXZJO4.js.map +0 -1
- package/lib/node/chunk-CMVICWQS.mjs.map +0 -1
- package/lib/node/chunk-DV4PBH4D.mjs.map +0 -1
- package/lib/node/chunk-EIBTX65O.js.map +0 -1
- package/lib/node/chunk-KWV3JXSI.mjs +0 -49
- package/lib/node/chunk-KWV3JXSI.mjs.map +0 -1
- package/lib/node/chunk-OJ6O4LSC.mjs.map +0 -1
- package/lib/node/chunk-PYD4E2EJ.js.map +0 -1
- package/lib/node/chunk-UXCYRE4F.js +0 -49
- package/lib/node/chunk-UXCYRE4F.js.map +0 -1
- package/src/utils/toInteractiveRequest.ts +0 -23
- /package/lib/node/{chunk-2COJKQQB.js.map → chunk-42632LKH.js.map} +0 -0
- /package/lib/node/{chunk-PNWPIDEL.mjs.map → chunk-HGQLG7KE.mjs.map} +0 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
import { it, expect, afterEach } from 'vitest'
|
|
3
|
+
import {
|
|
4
|
+
recordRawFetchHeaders,
|
|
5
|
+
restoreHeadersPrototype,
|
|
6
|
+
getRawFetchHeaders,
|
|
7
|
+
} from './recordRawHeaders'
|
|
8
|
+
|
|
9
|
+
const url = 'http://localhost'
|
|
10
|
+
|
|
11
|
+
afterEach(() => {
|
|
12
|
+
restoreHeadersPrototype()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
it('returns an empty list if no headers were set', () => {
|
|
16
|
+
expect(getRawFetchHeaders(new Headers())).toEqual([])
|
|
17
|
+
expect(getRawFetchHeaders(new Headers(undefined))).toEqual([])
|
|
18
|
+
expect(getRawFetchHeaders(new Headers({}))).toEqual([])
|
|
19
|
+
expect(getRawFetchHeaders(new Headers([]))).toEqual([])
|
|
20
|
+
expect(getRawFetchHeaders(new Request(url).headers)).toEqual([])
|
|
21
|
+
expect(getRawFetchHeaders(new Response().headers)).toEqual([])
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('records raw headers (Headers / object as init)', () => {
|
|
25
|
+
recordRawFetchHeaders()
|
|
26
|
+
const headers = new Headers({
|
|
27
|
+
'Content-Type': 'application/json',
|
|
28
|
+
'X-My-Header': '1',
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
expect(getRawFetchHeaders(headers)).toEqual([
|
|
32
|
+
['Content-Type', 'application/json'],
|
|
33
|
+
['X-My-Header', '1'],
|
|
34
|
+
])
|
|
35
|
+
expect(Object.fromEntries(headers)).toEqual({
|
|
36
|
+
'content-type': 'application/json',
|
|
37
|
+
'x-my-header': '1',
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
it('records raw headers (Headers / array as init)', () => {
|
|
42
|
+
recordRawFetchHeaders()
|
|
43
|
+
const headers = new Headers([['X-My-Header', '1']])
|
|
44
|
+
|
|
45
|
+
expect(getRawFetchHeaders(headers)).toEqual([['X-My-Header', '1']])
|
|
46
|
+
expect(Object.fromEntries(headers)).toEqual({
|
|
47
|
+
'x-my-header': '1',
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
it('records raw headers (Headers / Headers as init', () => {
|
|
52
|
+
recordRawFetchHeaders()
|
|
53
|
+
const headers = new Headers([['X-My-Header', '1']])
|
|
54
|
+
|
|
55
|
+
expect(getRawFetchHeaders(new Headers(headers))).toEqual([
|
|
56
|
+
['X-My-Header', '1'],
|
|
57
|
+
])
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('records raw headers added via ".set()"', () => {
|
|
61
|
+
recordRawFetchHeaders()
|
|
62
|
+
const headers = new Headers([['X-My-Header', '1']])
|
|
63
|
+
headers.set('X-Another-Header', '2')
|
|
64
|
+
|
|
65
|
+
expect(getRawFetchHeaders(headers)).toEqual([
|
|
66
|
+
['X-My-Header', '1'],
|
|
67
|
+
['X-Another-Header', '2'],
|
|
68
|
+
])
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('records raw headers added via ".append()"', () => {
|
|
72
|
+
recordRawFetchHeaders()
|
|
73
|
+
const headers = new Headers([['X-My-Header', '1']])
|
|
74
|
+
headers.append('X-My-Header', '2')
|
|
75
|
+
|
|
76
|
+
expect(getRawFetchHeaders(headers)).toEqual([
|
|
77
|
+
['X-My-Header', '1'],
|
|
78
|
+
['X-My-Header', '2'],
|
|
79
|
+
])
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('deletes the header when called ".delete()"', () => {
|
|
83
|
+
const headers = new Headers([['X-My-Header', '1']])
|
|
84
|
+
headers.delete('X-My-Header')
|
|
85
|
+
|
|
86
|
+
expect(getRawFetchHeaders(headers)).toEqual([])
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('records raw headers (Request / object as init)', () => {
|
|
90
|
+
recordRawFetchHeaders()
|
|
91
|
+
const request = new Request(url, {
|
|
92
|
+
headers: {
|
|
93
|
+
'Content-Type': 'application/json',
|
|
94
|
+
'X-My-Header': '1',
|
|
95
|
+
},
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
expect(getRawFetchHeaders(request.headers)).toEqual([
|
|
99
|
+
['Content-Type', 'application/json'],
|
|
100
|
+
['X-My-Header', '1'],
|
|
101
|
+
])
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('records raw headers (Request / array as init)', () => {
|
|
105
|
+
recordRawFetchHeaders()
|
|
106
|
+
const request = new Request(url, {
|
|
107
|
+
headers: [['X-My-Header', '1']],
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
expect(getRawFetchHeaders(request.headers)).toEqual([['X-My-Header', '1']])
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('records raw headers (Request / Headers as init)', () => {
|
|
114
|
+
recordRawFetchHeaders()
|
|
115
|
+
const headers = new Headers([['X-My-Header', '1']])
|
|
116
|
+
const request = new Request(url, { headers })
|
|
117
|
+
|
|
118
|
+
expect(getRawFetchHeaders(request.headers)).toEqual([['X-My-Header', '1']])
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('records raw headers (Response / object as init)', () => {
|
|
122
|
+
recordRawFetchHeaders()
|
|
123
|
+
const response = new Response(null, {
|
|
124
|
+
headers: {
|
|
125
|
+
'Content-Type': 'application/json',
|
|
126
|
+
'X-My-Header': '1',
|
|
127
|
+
},
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
expect(getRawFetchHeaders(response.headers)).toEqual([
|
|
131
|
+
['Content-Type', 'application/json'],
|
|
132
|
+
['X-My-Header', '1'],
|
|
133
|
+
])
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('records raw headers (Response / array as init)', () => {
|
|
137
|
+
recordRawFetchHeaders()
|
|
138
|
+
const response = new Response(null, {
|
|
139
|
+
headers: [['X-My-Header', '1']],
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
expect(getRawFetchHeaders(response.headers)).toEqual([['X-My-Header', '1']])
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it('records raw headers (Response / Headers as init)', () => {
|
|
146
|
+
recordRawFetchHeaders()
|
|
147
|
+
const headers = new Headers([['X-My-Header', '1']])
|
|
148
|
+
const response = new Response(null, { headers })
|
|
149
|
+
|
|
150
|
+
expect(getRawFetchHeaders(response.headers)).toEqual([['X-My-Header', '1']])
|
|
151
|
+
})
|
|
152
|
+
|
|
153
|
+
it('stops recording once the patches are restored', () => {
|
|
154
|
+
restoreHeadersPrototype()
|
|
155
|
+
|
|
156
|
+
const headers = new Headers({ 'X-My-Header': '1' })
|
|
157
|
+
// Must return the normalized headers (no access to raw headers).
|
|
158
|
+
expect(getRawFetchHeaders(headers)).toEqual([['x-my-header', '1']])
|
|
159
|
+
})
|
|
@@ -5,16 +5,24 @@ const kRawHeaders = Symbol('kRawHeaders')
|
|
|
5
5
|
const kRestorePatches = Symbol('kRestorePatches')
|
|
6
6
|
|
|
7
7
|
function recordRawHeader(headers: Headers, args: HeaderTuple) {
|
|
8
|
-
if (Reflect.
|
|
9
|
-
|
|
10
|
-
value: [],
|
|
11
|
-
enumerable: false,
|
|
12
|
-
})
|
|
8
|
+
if (!Reflect.has(headers, kRawHeaders)) {
|
|
9
|
+
defineRawHeaders(headers, [])
|
|
13
10
|
}
|
|
14
11
|
const rawHeaders = Reflect.get(headers, kRawHeaders) as RawHeaders
|
|
15
12
|
rawHeaders.push(args)
|
|
16
13
|
}
|
|
17
14
|
|
|
15
|
+
function defineRawHeaders(headers: Headers, rawHeaders: RawHeaders): void {
|
|
16
|
+
if (Reflect.has(headers, kRawHeaders)) {
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
Object.defineProperty(headers, kRawHeaders, {
|
|
21
|
+
value: rawHeaders,
|
|
22
|
+
enumerable: false,
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
18
26
|
/**
|
|
19
27
|
* Patch the global `Headers` class to store raw headers.
|
|
20
28
|
* This is for compatibility with `IncomingMessage.prototype.rawHeaders`.
|
|
@@ -35,7 +43,11 @@ export function recordRawFetchHeaders() {
|
|
|
35
43
|
return Reflect.get(Headers, kRestorePatches)
|
|
36
44
|
}
|
|
37
45
|
|
|
38
|
-
const {
|
|
46
|
+
const {
|
|
47
|
+
Headers: OriginalHeaders,
|
|
48
|
+
Request: OriginalRequest,
|
|
49
|
+
Response: OriginalResponse,
|
|
50
|
+
} = globalThis
|
|
39
51
|
const { set, append, delete: headersDeleteMethod } = Headers.prototype
|
|
40
52
|
|
|
41
53
|
Object.defineProperty(Headers, kRestorePatches, {
|
|
@@ -43,30 +55,49 @@ export function recordRawFetchHeaders() {
|
|
|
43
55
|
Headers.prototype.set = set
|
|
44
56
|
Headers.prototype.append = append
|
|
45
57
|
Headers.prototype.delete = headersDeleteMethod
|
|
58
|
+
globalThis.Headers = OriginalHeaders
|
|
46
59
|
|
|
47
60
|
globalThis.Request = OriginalRequest
|
|
48
61
|
globalThis.Response = OriginalResponse
|
|
62
|
+
|
|
63
|
+
Reflect.deleteProperty(Headers, kRestorePatches)
|
|
49
64
|
},
|
|
50
65
|
enumerable: false,
|
|
66
|
+
/**
|
|
67
|
+
* @note Mark this property as configurable
|
|
68
|
+
* so we can delete it using `Reflect.delete` during cleanup.
|
|
69
|
+
*/
|
|
70
|
+
configurable: true,
|
|
51
71
|
})
|
|
52
72
|
|
|
53
73
|
Headers = new Proxy(Headers, {
|
|
54
74
|
construct(target, args, newTarget) {
|
|
75
|
+
const headersInit = args[0] || []
|
|
76
|
+
|
|
77
|
+
if (
|
|
78
|
+
headersInit instanceof Headers &&
|
|
79
|
+
Reflect.has(headersInit, kRawHeaders)
|
|
80
|
+
) {
|
|
81
|
+
const headers = Reflect.construct(
|
|
82
|
+
target,
|
|
83
|
+
[Reflect.get(headersInit, kRawHeaders)],
|
|
84
|
+
newTarget
|
|
85
|
+
)
|
|
86
|
+
defineRawHeaders(headers, Reflect.get(headersInit, kRawHeaders))
|
|
87
|
+
return headers
|
|
88
|
+
}
|
|
89
|
+
|
|
55
90
|
const headers = Reflect.construct(target, args, newTarget)
|
|
56
|
-
const initialHeaders = args[0] || []
|
|
57
|
-
const initialRawHeaders = Array.isArray(initialHeaders)
|
|
58
|
-
? initialHeaders
|
|
59
|
-
: Object.entries(initialHeaders)
|
|
60
91
|
|
|
61
92
|
// Request/Response constructors will set the symbol
|
|
62
93
|
// upon creating a new instance, using the raw developer
|
|
63
94
|
// input as the raw headers. Skip the symbol altogether
|
|
64
95
|
// in those cases because the input to Headers will be normalized.
|
|
65
96
|
if (!Reflect.has(headers, kRawHeaders)) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
97
|
+
const rawHeadersInit = Array.isArray(headersInit)
|
|
98
|
+
? headersInit
|
|
99
|
+
: Object.entries(headersInit)
|
|
100
|
+
defineRawHeaders(headers, rawHeadersInit)
|
|
70
101
|
}
|
|
71
102
|
|
|
72
103
|
return headers
|
|
@@ -105,14 +136,27 @@ export function recordRawFetchHeaders() {
|
|
|
105
136
|
|
|
106
137
|
Request = new Proxy(Request, {
|
|
107
138
|
construct(target, args, newTarget) {
|
|
108
|
-
|
|
109
|
-
|
|
139
|
+
/**
|
|
140
|
+
* @note If the headers init argument of Request
|
|
141
|
+
* is existing Headers instance, use its raw headers
|
|
142
|
+
* as the headers init instead.
|
|
143
|
+
* This is needed because the Headers constructor copies
|
|
144
|
+
* all normalized headers from the given Headers instance
|
|
145
|
+
* and uses ".append()" to add it to the new instance.
|
|
146
|
+
*/
|
|
110
147
|
if (
|
|
111
148
|
typeof args[1] === 'object' &&
|
|
112
149
|
args[1].headers != null &&
|
|
113
|
-
|
|
150
|
+
args[1].headers instanceof Headers &&
|
|
151
|
+
Reflect.has(args[1].headers, kRawHeaders)
|
|
114
152
|
) {
|
|
115
|
-
|
|
153
|
+
args[1].headers = args[1].headers[kRawHeaders]
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const request = Reflect.construct(target, args, newTarget)
|
|
157
|
+
|
|
158
|
+
if (typeof args[1] === 'object' && args[1].headers != null) {
|
|
159
|
+
defineRawHeaders(request.headers, inferRawHeaders(args[1].headers))
|
|
116
160
|
}
|
|
117
161
|
|
|
118
162
|
return request
|
|
@@ -121,15 +165,19 @@ export function recordRawFetchHeaders() {
|
|
|
121
165
|
|
|
122
166
|
Response = new Proxy(Response, {
|
|
123
167
|
construct(target, args, newTarget) {
|
|
168
|
+
if (
|
|
169
|
+
typeof args[1] === 'object' &&
|
|
170
|
+
args[1].headers != null &&
|
|
171
|
+
args[1].headers instanceof Headers &&
|
|
172
|
+
Reflect.has(args[1].headers, kRawHeaders)
|
|
173
|
+
) {
|
|
174
|
+
args[1].headers = args[1].headers[kRawHeaders]
|
|
175
|
+
}
|
|
176
|
+
|
|
124
177
|
const response = Reflect.construct(target, args, newTarget)
|
|
125
178
|
|
|
126
179
|
if (typeof args[1] === 'object' && args[1].headers != null) {
|
|
127
|
-
|
|
128
|
-
* @note Pass the init argument directly because it gets
|
|
129
|
-
* transformed into a normalized Headers instance once it
|
|
130
|
-
* passes the Response constructor.
|
|
131
|
-
*/
|
|
132
|
-
response.headers[kRawHeaders] = inferRawHeaders(args[1].headers)
|
|
180
|
+
defineRawHeaders(response.headers, inferRawHeaders(args[1].headers))
|
|
133
181
|
}
|
|
134
182
|
|
|
135
183
|
return response
|
|
@@ -146,9 +194,14 @@ export function restoreHeadersPrototype() {
|
|
|
146
194
|
}
|
|
147
195
|
|
|
148
196
|
export function getRawFetchHeaders(headers: Headers): RawHeaders {
|
|
149
|
-
//
|
|
150
|
-
//
|
|
151
|
-
|
|
197
|
+
// If the raw headers recording failed for some reason,
|
|
198
|
+
// use the normalized header entries instead.
|
|
199
|
+
if (!Reflect.has(headers, kRawHeaders)) {
|
|
200
|
+
return Array.from(headers.entries())
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const rawHeaders = Reflect.get(headers, kRawHeaders) as RawHeaders
|
|
204
|
+
return rawHeaders.length > 0 ? rawHeaders : Array.from(headers.entries())
|
|
152
205
|
}
|
|
153
206
|
|
|
154
207
|
/**
|
|
@@ -1,13 +1,8 @@
|
|
|
1
|
-
import { until } from '@open-draft/until'
|
|
2
1
|
import type { Logger } from '@open-draft/logger'
|
|
3
2
|
import { XMLHttpRequestEmitter } from '.'
|
|
4
|
-
import {
|
|
5
|
-
import { emitAsync } from '../../utils/emitAsync'
|
|
3
|
+
import { RequestController } from '../../RequestController'
|
|
6
4
|
import { XMLHttpRequestController } from './XMLHttpRequestController'
|
|
7
|
-
import {
|
|
8
|
-
createServerErrorResponse,
|
|
9
|
-
isResponseError,
|
|
10
|
-
} from '../../utils/responseUtils'
|
|
5
|
+
import { handleRequest } from '../../utils/handleRequest'
|
|
11
6
|
|
|
12
7
|
export interface XMLHttpRequestProxyOptions {
|
|
13
8
|
emitter: XMLHttpRequestEmitter
|
|
@@ -57,116 +52,40 @@ export function createXMLHttpRequestProxy({
|
|
|
57
52
|
)
|
|
58
53
|
|
|
59
54
|
xhrRequestController.onRequest = async function ({ request, requestId }) {
|
|
60
|
-
const
|
|
61
|
-
toInteractiveRequest(request)
|
|
55
|
+
const controller = new RequestController(request)
|
|
62
56
|
|
|
63
57
|
this.logger.info('awaiting mocked response...')
|
|
64
58
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (requestController.responsePromise.state === 'pending') {
|
|
71
|
-
requestController.respondWith(undefined)
|
|
72
|
-
}
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
const resolverResult = await until(async () => {
|
|
76
|
-
this.logger.info(
|
|
77
|
-
'emitting the "request" event for %s listener(s)...',
|
|
78
|
-
emitter.listenerCount('request')
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
await emitAsync(emitter, 'request', {
|
|
82
|
-
request: interactiveRequest,
|
|
83
|
-
requestId,
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
this.logger.info('all "request" listeners settled!')
|
|
87
|
-
|
|
88
|
-
const mockedResponse = await requestController.responsePromise
|
|
89
|
-
|
|
90
|
-
this.logger.info('event.respondWith called with:', mockedResponse)
|
|
91
|
-
|
|
92
|
-
return mockedResponse
|
|
93
|
-
})
|
|
94
|
-
|
|
95
|
-
if (resolverResult.error) {
|
|
96
|
-
this.logger.info(
|
|
97
|
-
'request listener threw an exception, aborting request...',
|
|
98
|
-
resolverResult.error
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
// Treat thrown Responses as mocked responses.
|
|
102
|
-
if (resolverResult.error instanceof Response) {
|
|
103
|
-
if (isResponseError(resolverResult.error)) {
|
|
104
|
-
xhrRequestController.errorWith(new TypeError('Network error'))
|
|
105
|
-
} else {
|
|
106
|
-
this.respondWith(resolverResult.error)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
if (emitter.listenerCount('unhandledException') > 0) {
|
|
113
|
-
// Emit the "unhandledException" event so the client can opt-out
|
|
114
|
-
// from the default exception handling (producing 500 error responses).
|
|
115
|
-
await emitAsync(emitter, 'unhandledException', {
|
|
116
|
-
error: resolverResult.error,
|
|
117
|
-
request,
|
|
118
|
-
requestId,
|
|
119
|
-
controller: {
|
|
120
|
-
respondWith:
|
|
121
|
-
xhrRequestController.respondWith.bind(xhrRequestController),
|
|
122
|
-
errorWith:
|
|
123
|
-
xhrRequestController.errorWith.bind(xhrRequestController),
|
|
124
|
-
},
|
|
125
|
-
})
|
|
59
|
+
this.logger.info(
|
|
60
|
+
'emitting the "request" event for %s listener(s)...',
|
|
61
|
+
emitter.listenerCount('request')
|
|
62
|
+
)
|
|
126
63
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
64
|
+
const isRequestHandled = await handleRequest({
|
|
65
|
+
request,
|
|
66
|
+
requestId,
|
|
67
|
+
controller,
|
|
68
|
+
emitter,
|
|
69
|
+
onResponse: (response) => {
|
|
70
|
+
this.respondWith(response)
|
|
71
|
+
},
|
|
72
|
+
onRequestError: () => {
|
|
73
|
+
this.errorWith(new TypeError('Network error'))
|
|
74
|
+
},
|
|
75
|
+
onError: (error) => {
|
|
76
|
+
this.logger.info('request errored!', { error })
|
|
77
|
+
|
|
78
|
+
if (error instanceof Error) {
|
|
79
|
+
this.errorWith(error)
|
|
133
80
|
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// Unhandled exceptions in the request listeners are
|
|
137
|
-
// synonymous to unhandled exceptions on the server.
|
|
138
|
-
// Those are represented as 500 error responses.
|
|
139
|
-
xhrRequestController.respondWith(
|
|
140
|
-
createServerErrorResponse(resolverResult.error)
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
return
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const mockedResponse = resolverResult.data
|
|
81
|
+
},
|
|
82
|
+
})
|
|
147
83
|
|
|
148
|
-
if (
|
|
84
|
+
if (!isRequestHandled) {
|
|
149
85
|
this.logger.info(
|
|
150
|
-
'
|
|
151
|
-
mockedResponse.status,
|
|
152
|
-
mockedResponse.statusText
|
|
86
|
+
'no mocked response received, performing request as-is...'
|
|
153
87
|
)
|
|
154
|
-
|
|
155
|
-
if (isResponseError(mockedResponse)) {
|
|
156
|
-
this.logger.info(
|
|
157
|
-
'received a network error response, rejecting the request promise...'
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
xhrRequestController.errorWith(new TypeError('Network error'))
|
|
161
|
-
return
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return xhrRequestController.respondWith(mockedResponse)
|
|
165
88
|
}
|
|
166
|
-
|
|
167
|
-
this.logger.info(
|
|
168
|
-
'no mocked response received, performing request as-is...'
|
|
169
|
-
)
|
|
170
89
|
}
|
|
171
90
|
|
|
172
91
|
xhrRequestController.onResponse = async function ({
|
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
import { invariant } from 'outvariant'
|
|
2
2
|
import { Emitter } from 'strict-event-emitter'
|
|
3
3
|
import { HttpRequestEventMap, IS_PATCHED_MODULE } from '../../glossary'
|
|
4
|
-
import { InteractiveRequest } from '../../utils/toInteractiveRequest'
|
|
5
4
|
import { Interceptor } from '../../Interceptor'
|
|
6
5
|
import { createXMLHttpRequestProxy } from './XMLHttpRequestProxy'
|
|
7
6
|
|
|
8
|
-
export type XMLHttpRequestEventListener = (args: {
|
|
9
|
-
request: InteractiveRequest
|
|
10
|
-
requestId: string
|
|
11
|
-
}) => Promise<void> | void
|
|
12
|
-
|
|
13
7
|
export type XMLHttpRequestEmitter = Emitter<HttpRequestEventMap>
|
|
14
8
|
|
|
15
9
|
export class XMLHttpRequestInterceptor extends Interceptor<HttpRequestEventMap> {
|