@mswjs/interceptors 0.22.2 → 0.22.4
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/lib/browser/interceptors/XMLHttpRequest/index.js +54 -26
- package/lib/browser/interceptors/XMLHttpRequest/index.mjs +54 -26
- package/lib/browser/interceptors/fetch/index.js +20 -9
- package/lib/browser/interceptors/fetch/index.mjs +20 -9
- package/lib/node/RemoteHttpInterceptor.js +4 -4
- package/lib/node/RemoteHttpInterceptor.mjs +2 -2
- package/lib/node/{chunk-SWJ33XIS.mjs → chunk-FGPCRIW6.mjs} +49 -24
- package/lib/node/{chunk-CYWTKHFI.mjs → chunk-P7NKDCFD.mjs} +54 -26
- package/lib/node/{chunk-GGD5JOGB.js → chunk-RVLLS44W.js} +54 -26
- package/lib/node/{chunk-PRX3F52M.js → chunk-XLZJAPVS.js} +49 -24
- package/lib/node/interceptors/ClientRequest/index.js +2 -2
- package/lib/node/interceptors/ClientRequest/index.mjs +1 -1
- package/lib/node/interceptors/XMLHttpRequest/index.js +2 -2
- package/lib/node/interceptors/XMLHttpRequest/index.mjs +1 -1
- package/lib/node/interceptors/fetch/index.js +20 -9
- package/lib/node/interceptors/fetch/index.mjs +20 -9
- package/package.json +2 -2
- package/src/interceptors/XMLHttpRequest/XMLHttpRequestController.ts +30 -29
- package/src/interceptors/XMLHttpRequest/XMLHttpRequestProxy.ts +20 -2
- package/src/interceptors/XMLHttpRequest/utils/createResponse.ts +1 -1
- package/src/interceptors/fetch/index.ts +22 -9
- package/src/utils/createProxy.test.ts +149 -0
- package/src/utils/createProxy.ts +27 -6
- package/src/utils/getUrlByRequestOptions.test.ts +92 -98
- package/src/utils/getUrlByRequestOptions.ts +78 -34
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { vi, it, expect } from 'vitest'
|
|
2
|
+
import { createProxy } from './createProxy'
|
|
3
|
+
|
|
4
|
+
it('does not interfere with default constructors', () => {
|
|
5
|
+
const ProxyClass = createProxy(
|
|
6
|
+
class {
|
|
7
|
+
constructor(public name: string) {}
|
|
8
|
+
},
|
|
9
|
+
{}
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
const instance = new ProxyClass('John')
|
|
13
|
+
expect(instance.name).toBe('John')
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('does not interfere with default getters', () => {
|
|
17
|
+
const proxy = createProxy({ foo: 'initial' }, {})
|
|
18
|
+
expect(proxy.foo).toBe('initial')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('does not interfere with default setters', () => {
|
|
22
|
+
const proxy = createProxy({ foo: 'initial' }, {})
|
|
23
|
+
proxy.foo = 'next'
|
|
24
|
+
|
|
25
|
+
expect(proxy.foo).toBe('next')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('does not interfere with default methods', () => {
|
|
29
|
+
const proxy = createProxy({ getValue: () => 'initial' }, {})
|
|
30
|
+
expect(proxy.getValue()).toBe('initial')
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('does not interfere with existing descriptors', () => {
|
|
34
|
+
const target = {} as { foo: string; bar: number }
|
|
35
|
+
let internalBar = 0
|
|
36
|
+
|
|
37
|
+
Object.defineProperties(target, {
|
|
38
|
+
foo: {
|
|
39
|
+
get: () => 'initial',
|
|
40
|
+
},
|
|
41
|
+
bar: {
|
|
42
|
+
set: (value) => {
|
|
43
|
+
internalBar = value + 10
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
const proxy = createProxy(target, {
|
|
49
|
+
getProperty(data, next) {
|
|
50
|
+
return next()
|
|
51
|
+
},
|
|
52
|
+
})
|
|
53
|
+
expect(proxy.foo).toBe('initial')
|
|
54
|
+
|
|
55
|
+
proxy.bar = 5
|
|
56
|
+
expect(proxy.bar).toBeUndefined()
|
|
57
|
+
expect(internalBar).toBe(15)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('infer prototype descriptors', () => {
|
|
61
|
+
class Child {
|
|
62
|
+
ok: boolean
|
|
63
|
+
|
|
64
|
+
set status(nextStatus: number) {
|
|
65
|
+
this.ok = nextStatus >= 200 && nextStatus < 300
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
Object.defineProperties(Child.prototype, {
|
|
70
|
+
status: { enumerable: true },
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
const scope = {} as { child: typeof Child }
|
|
74
|
+
|
|
75
|
+
Object.defineProperty(scope, 'child', {
|
|
76
|
+
enumerable: true,
|
|
77
|
+
value: Child,
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
const ProxyClass = createProxy(scope.child, {})
|
|
81
|
+
const instance = new ProxyClass()
|
|
82
|
+
|
|
83
|
+
instance.status = 201
|
|
84
|
+
expect(instance.ok).toBe(true)
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it('spies on the constructor', () => {
|
|
88
|
+
const OriginalClass = class {
|
|
89
|
+
constructor(public name: string, public age: number) {}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const constructorCall = vi.fn<
|
|
93
|
+
[ConstructorParameters<typeof OriginalClass>, Function],
|
|
94
|
+
typeof OriginalClass
|
|
95
|
+
>((args, next) => next())
|
|
96
|
+
|
|
97
|
+
const ProxyClass = createProxy(OriginalClass, {
|
|
98
|
+
constructorCall,
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
new ProxyClass('John', 32)
|
|
102
|
+
|
|
103
|
+
expect(constructorCall).toHaveBeenCalledTimes(1)
|
|
104
|
+
expect(constructorCall).toHaveBeenCalledWith(
|
|
105
|
+
['John', 32],
|
|
106
|
+
expect.any(Function)
|
|
107
|
+
)
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('spies on property getters', () => {
|
|
111
|
+
const getProperty = vi.fn((args, next) => next())
|
|
112
|
+
const proxy = createProxy({ foo: 'initial' }, { getProperty })
|
|
113
|
+
|
|
114
|
+
proxy.foo
|
|
115
|
+
|
|
116
|
+
expect(getProperty).toHaveBeenCalledTimes(1)
|
|
117
|
+
expect(getProperty).toHaveBeenCalledWith(['foo', proxy], expect.any(Function))
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('spies on property setters', () => {
|
|
121
|
+
const setProperty = vi.fn((args, next) => next())
|
|
122
|
+
const proxy = createProxy({ foo: 'initial' }, { setProperty })
|
|
123
|
+
|
|
124
|
+
proxy.foo = 'next'
|
|
125
|
+
|
|
126
|
+
expect(setProperty).toHaveBeenCalledTimes(1)
|
|
127
|
+
expect(setProperty).toHaveBeenCalledWith(
|
|
128
|
+
['foo', 'next'],
|
|
129
|
+
expect.any(Function)
|
|
130
|
+
)
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it('spies on method calls', () => {
|
|
134
|
+
const methodCall = vi.fn((args, next) => next())
|
|
135
|
+
const proxy = createProxy(
|
|
136
|
+
{
|
|
137
|
+
greet: (name: string) => `hello ${name}`,
|
|
138
|
+
},
|
|
139
|
+
{ methodCall }
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
proxy.greet('Clair')
|
|
143
|
+
|
|
144
|
+
expect(methodCall).toHaveBeenCalledTimes(1)
|
|
145
|
+
expect(methodCall).toHaveBeenCalledWith(
|
|
146
|
+
['greet', ['Clair']],
|
|
147
|
+
expect.any(Function)
|
|
148
|
+
)
|
|
149
|
+
})
|
package/src/utils/createProxy.ts
CHANGED
|
@@ -9,8 +9,8 @@ export interface ProxyOptions<Target extends Record<string, any>> {
|
|
|
9
9
|
|
|
10
10
|
setProperty?(
|
|
11
11
|
data: [propertyName: string | symbol, nextValue: unknown],
|
|
12
|
-
next: NextFunction<
|
|
13
|
-
):
|
|
12
|
+
next: NextFunction<boolean>
|
|
13
|
+
): boolean
|
|
14
14
|
|
|
15
15
|
getProperty?(
|
|
16
16
|
data: [propertyName: string | symbol, receiver: Target],
|
|
@@ -25,6 +25,7 @@ export function createProxy<Target extends object>(
|
|
|
25
25
|
options: ProxyOptions<Target>
|
|
26
26
|
): Target {
|
|
27
27
|
const proxy = new Proxy(target, optionsToProxyHandler(options))
|
|
28
|
+
|
|
28
29
|
return proxy
|
|
29
30
|
}
|
|
30
31
|
|
|
@@ -41,11 +42,31 @@ function optionsToProxyHandler<T extends Record<string, any>>(
|
|
|
41
42
|
}
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const
|
|
47
|
-
|
|
45
|
+
handler.set = function (target, propertyName, nextValue, receiver) {
|
|
46
|
+
const next = () => {
|
|
47
|
+
const ownDescriptors = Reflect.getOwnPropertyDescriptor(
|
|
48
|
+
target,
|
|
49
|
+
propertyName
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
if (typeof ownDescriptors?.set !== 'undefined') {
|
|
53
|
+
ownDescriptors.set.apply(target, [nextValue])
|
|
54
|
+
return true
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return Reflect.defineProperty(target, propertyName, {
|
|
58
|
+
writable: true,
|
|
59
|
+
enumerable: true,
|
|
60
|
+
configurable: true,
|
|
61
|
+
value: nextValue,
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (typeof setProperty !== 'undefined') {
|
|
66
|
+
return setProperty.call(target, [propertyName, nextValue], next)
|
|
48
67
|
}
|
|
68
|
+
|
|
69
|
+
return next()
|
|
49
70
|
}
|
|
50
71
|
|
|
51
72
|
handler.get = function (target, propertyName, receiver) {
|
|
@@ -4,138 +4,132 @@ import { RequestOptions, Agent as HttpsAgent } from 'https'
|
|
|
4
4
|
import { getUrlByRequestOptions } from './getUrlByRequestOptions'
|
|
5
5
|
|
|
6
6
|
it('returns a URL based on the basic RequestOptions', () => {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
expect(url).toBeInstanceOf(URL)
|
|
15
|
-
expect(url).toHaveProperty('port', '')
|
|
16
|
-
expect(url).toHaveProperty('href', 'https://127.0.0.1/resource')
|
|
7
|
+
expect(
|
|
8
|
+
getUrlByRequestOptions({
|
|
9
|
+
protocol: 'https:',
|
|
10
|
+
host: '127.0.0.1',
|
|
11
|
+
path: '/resource',
|
|
12
|
+
}).href
|
|
13
|
+
).toBe('https://127.0.0.1/resource')
|
|
17
14
|
})
|
|
18
15
|
|
|
19
16
|
it('inherits protocol and port from http.Agent, if set', () => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
expect(url).toBeInstanceOf(URL)
|
|
28
|
-
expect(url).toHaveProperty('protocol', 'http:')
|
|
29
|
-
expect(url).toHaveProperty('port', '')
|
|
30
|
-
expect(url).toHaveProperty('href', 'http://127.0.0.1/')
|
|
17
|
+
expect(
|
|
18
|
+
getUrlByRequestOptions({
|
|
19
|
+
host: '127.0.0.1',
|
|
20
|
+
path: '/',
|
|
21
|
+
agent: new HttpAgent(),
|
|
22
|
+
}).href
|
|
23
|
+
).toBe('http://127.0.0.1/')
|
|
31
24
|
})
|
|
32
25
|
|
|
33
26
|
it('inherits protocol and port from https.Agent, if set', () => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
expect(url).toBeInstanceOf(URL)
|
|
44
|
-
expect(url).toHaveProperty('protocol', 'https:')
|
|
45
|
-
expect(url).toHaveProperty('port', '3080')
|
|
46
|
-
expect(url).toHaveProperty('href', 'https://127.0.0.1:3080/')
|
|
27
|
+
expect(
|
|
28
|
+
getUrlByRequestOptions({
|
|
29
|
+
host: '127.0.0.1',
|
|
30
|
+
path: '/',
|
|
31
|
+
agent: new HttpsAgent({
|
|
32
|
+
port: 3080,
|
|
33
|
+
}),
|
|
34
|
+
}).href
|
|
35
|
+
).toBe('https://127.0.0.1:3080/')
|
|
47
36
|
})
|
|
48
37
|
|
|
49
38
|
it('resolves protocol to "http" given no explicit protocol and no certificate', () => {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
expect(url).toBeInstanceOf(URL)
|
|
57
|
-
expect(url).toHaveProperty('protocol', 'http:')
|
|
58
|
-
expect(url).toHaveProperty('port', '')
|
|
59
|
-
expect(url).toHaveProperty('href', 'http://127.0.0.1/')
|
|
39
|
+
expect(
|
|
40
|
+
getUrlByRequestOptions({
|
|
41
|
+
host: '127.0.0.1',
|
|
42
|
+
path: '/',
|
|
43
|
+
}).href
|
|
44
|
+
).toBe('http://127.0.0.1/')
|
|
60
45
|
})
|
|
61
46
|
|
|
62
47
|
it('resolves protocol to "https" given no explicit protocol, but certificate', () => {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
expect(url).toBeInstanceOf(URL)
|
|
71
|
-
expect(url).toHaveProperty('protocol', 'https:')
|
|
72
|
-
expect(url).toHaveProperty('port', '')
|
|
73
|
-
expect(url).toHaveProperty('href', 'https://127.0.0.1/secure')
|
|
48
|
+
expect(
|
|
49
|
+
getUrlByRequestOptions({
|
|
50
|
+
host: '127.0.0.1',
|
|
51
|
+
path: '/secure',
|
|
52
|
+
cert: '<!-- SSL certificate -->',
|
|
53
|
+
}).href
|
|
54
|
+
).toBe('https://127.0.0.1/secure')
|
|
74
55
|
})
|
|
75
56
|
|
|
76
57
|
it('resolves protocol to "https" given no explicit protocol, but port is 443', () => {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
expect(url).toBeInstanceOf(URL)
|
|
85
|
-
expect(url).toHaveProperty('port', '')
|
|
86
|
-
expect(url).toHaveProperty('href', 'https://127.0.0.1/resource')
|
|
58
|
+
expect(
|
|
59
|
+
getUrlByRequestOptions({
|
|
60
|
+
host: '127.0.0.1',
|
|
61
|
+
port: 443,
|
|
62
|
+
path: '/resource',
|
|
63
|
+
}).href
|
|
64
|
+
).toBe('https://127.0.0.1/resource')
|
|
87
65
|
})
|
|
88
66
|
|
|
89
67
|
it('resolves protocol to "https" given no explicit protocol, but agent port is 443', () => {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
expect(url).toBeInstanceOf(URL)
|
|
100
|
-
expect(url).toHaveProperty('port', '')
|
|
101
|
-
expect(url).toHaveProperty('href', 'https://127.0.0.1/resource')
|
|
68
|
+
expect(
|
|
69
|
+
getUrlByRequestOptions({
|
|
70
|
+
host: '127.0.0.1',
|
|
71
|
+
agent: new HttpsAgent({
|
|
72
|
+
port: 443,
|
|
73
|
+
}),
|
|
74
|
+
path: '/resource',
|
|
75
|
+
}).href
|
|
76
|
+
).toBe('https://127.0.0.1/resource')
|
|
102
77
|
})
|
|
103
78
|
|
|
104
|
-
it('
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
expect(url).toBeInstanceOf(URL)
|
|
114
|
-
expect(url).toHaveProperty('port', '4002')
|
|
115
|
-
expect(url).toHaveProperty('protocol', 'http:')
|
|
116
|
-
expect(url).toHaveProperty('href', 'http://127.0.0.1:4002/')
|
|
79
|
+
it('respects explicitly provided port', () => {
|
|
80
|
+
expect(
|
|
81
|
+
getUrlByRequestOptions({
|
|
82
|
+
protocol: 'http:',
|
|
83
|
+
host: '127.0.0.1',
|
|
84
|
+
port: 4002,
|
|
85
|
+
path: '/',
|
|
86
|
+
}).href
|
|
87
|
+
).toBe('http://127.0.0.1:4002/')
|
|
117
88
|
})
|
|
118
89
|
|
|
119
90
|
it('inherits "username" and "password"', () => {
|
|
120
|
-
const
|
|
91
|
+
const url = getUrlByRequestOptions({
|
|
121
92
|
protocol: 'https:',
|
|
122
93
|
host: '127.0.0.1',
|
|
123
94
|
path: '/user',
|
|
124
95
|
auth: 'admin:abc-123',
|
|
125
|
-
}
|
|
126
|
-
const url = getUrlByRequestOptions(options)
|
|
96
|
+
})
|
|
127
97
|
|
|
128
98
|
expect(url).toBeInstanceOf(URL)
|
|
129
99
|
expect(url).toHaveProperty('username', 'admin')
|
|
130
100
|
expect(url).toHaveProperty('password', 'abc-123')
|
|
131
|
-
expect(url).toHaveProperty('protocol', 'https:')
|
|
132
101
|
expect(url).toHaveProperty('href', 'https://admin:abc-123@127.0.0.1/user')
|
|
133
102
|
})
|
|
134
103
|
|
|
135
104
|
it('resolves hostname to localhost if none provided', () => {
|
|
136
|
-
|
|
105
|
+
expect(getUrlByRequestOptions({}).hostname).toBe('localhost')
|
|
106
|
+
})
|
|
137
107
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
108
|
+
it('supports "hostname" instead of "host" and "port"', () => {
|
|
109
|
+
const options: RequestOptions = {
|
|
110
|
+
protocol: 'https:',
|
|
111
|
+
hostname: '127.0.0.1:1234',
|
|
112
|
+
path: '/resource',
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
expect(getUrlByRequestOptions(options).href).toBe(
|
|
116
|
+
'https://127.0.0.1:1234/resource'
|
|
117
|
+
)
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('handles IPv6 hostnames', () => {
|
|
121
|
+
expect(
|
|
122
|
+
getUrlByRequestOptions({
|
|
123
|
+
host: '::1',
|
|
124
|
+
path: '/resource',
|
|
125
|
+
}).href
|
|
126
|
+
).toBe('http://[::1]/resource')
|
|
127
|
+
|
|
128
|
+
expect(
|
|
129
|
+
getUrlByRequestOptions({
|
|
130
|
+
host: '::1',
|
|
131
|
+
port: 3001,
|
|
132
|
+
path: '/resource',
|
|
133
|
+
}).href
|
|
134
|
+
).toBe('http://[::1]:3001/resource')
|
|
141
135
|
})
|
|
@@ -15,7 +15,6 @@ export type ResolvedRequestOptions = RequestOptions & RequestSelf
|
|
|
15
15
|
export const DEFAULT_PATH = '/'
|
|
16
16
|
const DEFAULT_PROTOCOL = 'http:'
|
|
17
17
|
const DEFAULT_HOST = 'localhost'
|
|
18
|
-
const DEFAULT_PORT = 80
|
|
19
18
|
const SSL_PORT = 443
|
|
20
19
|
|
|
21
20
|
function getAgent(
|
|
@@ -45,20 +44,45 @@ function getProtocolByRequestOptions(options: ResolvedRequestOptions): string {
|
|
|
45
44
|
function getPortByRequestOptions(
|
|
46
45
|
options: ResolvedRequestOptions
|
|
47
46
|
): number | undefined {
|
|
47
|
+
// Use the explicitly provided port.
|
|
48
|
+
if (options.port) {
|
|
49
|
+
return Number(options.port)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Extract the port from the hostname.
|
|
53
|
+
if (options.hostname != null) {
|
|
54
|
+
const [, extractedPort] = options.hostname.match(/:(\d+)$/) || []
|
|
55
|
+
|
|
56
|
+
if (extractedPort != null) {
|
|
57
|
+
return Number(extractedPort)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Otherwise, try to resolve port from the agent.
|
|
48
62
|
const agent = getAgent(options)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
(agent as
|
|
52
|
-
const optionsPort = options.port
|
|
53
|
-
|
|
54
|
-
if (optionsPort || agentPort) {
|
|
55
|
-
const explicitPort = optionsPort || agentPort || DEFAULT_PORT
|
|
56
|
-
return Number(explicitPort)
|
|
63
|
+
|
|
64
|
+
if ((agent as HttpsAgent)?.options.port) {
|
|
65
|
+
return Number((agent as HttpsAgent).options.port)
|
|
57
66
|
}
|
|
67
|
+
|
|
68
|
+
if ((agent as RequestOptions)?.defaultPort) {
|
|
69
|
+
return Number((agent as RequestOptions).defaultPort)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Lastly, return undefined indicating that the port
|
|
73
|
+
// must inferred from the protocol. Do not infer it here.
|
|
74
|
+
return undefined
|
|
58
75
|
}
|
|
59
76
|
|
|
60
77
|
function getHostByRequestOptions(options: ResolvedRequestOptions): string {
|
|
61
|
-
|
|
78
|
+
const { hostname, host } = options
|
|
79
|
+
|
|
80
|
+
// If the hostname is specified, resolve the host from the "host:port" string.
|
|
81
|
+
if (hostname != null) {
|
|
82
|
+
return hostname.replace(/:\d+$/, '')
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return host || DEFAULT_HOST
|
|
62
86
|
}
|
|
63
87
|
|
|
64
88
|
function getAuthByRequestOptions(options: ResolvedRequestOptions) {
|
|
@@ -77,45 +101,65 @@ function isRawIPv6Address(host: string): boolean {
|
|
|
77
101
|
return host.includes(':') && !host.startsWith('[') && !host.endsWith(']')
|
|
78
102
|
}
|
|
79
103
|
|
|
104
|
+
function getHostname(host: string, port?: number): string {
|
|
105
|
+
const portString = typeof port !== 'undefined' ? `:${port}` : ''
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @note As of Node >= 17, hosts (including "localhost") can resolve to IPv6
|
|
109
|
+
* addresses, so construct valid URL by surrounding the IPv6 host with brackets.
|
|
110
|
+
*/
|
|
111
|
+
if (isRawIPv6Address(host)) {
|
|
112
|
+
return `[${host}]${portString}`
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (typeof port === 'undefined') {
|
|
116
|
+
return host
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return `${host}${portString}`
|
|
120
|
+
}
|
|
121
|
+
|
|
80
122
|
/**
|
|
81
123
|
* Creates a `URL` instance from a given `RequestOptions` object.
|
|
82
124
|
*/
|
|
83
125
|
export function getUrlByRequestOptions(options: ResolvedRequestOptions): URL {
|
|
84
126
|
debug('request options', options)
|
|
85
127
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
128
|
+
if (options.uri) {
|
|
129
|
+
debug(
|
|
130
|
+
'constructing url from explicitly provided "options.uri": %s',
|
|
131
|
+
options.uri
|
|
132
|
+
)
|
|
133
|
+
return new URL(options.uri.href)
|
|
134
|
+
}
|
|
91
135
|
|
|
136
|
+
debug('figuring out url from request options...')
|
|
137
|
+
|
|
138
|
+
const protocol = getProtocolByRequestOptions(options)
|
|
92
139
|
debug('protocol', protocol)
|
|
140
|
+
|
|
141
|
+
const host = getHostByRequestOptions(options)
|
|
93
142
|
debug('host', host)
|
|
94
|
-
debug('port', port)
|
|
95
|
-
debug('path', path)
|
|
96
143
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
* addresses, so construct valid URL by surrounding the IPv6 host with brackets.
|
|
100
|
-
*/
|
|
101
|
-
const baseUrl = `${protocol}//${isRawIPv6Address(host) ? `[${host}]` : host}`
|
|
102
|
-
debug('base URL:', baseUrl)
|
|
144
|
+
const port = getPortByRequestOptions(options)
|
|
145
|
+
debug('port', port)
|
|
103
146
|
|
|
104
|
-
const
|
|
147
|
+
const hostname = getHostname(host, port)
|
|
148
|
+
debug('hostname', hostname)
|
|
105
149
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
url.port = port.toString()
|
|
109
|
-
}
|
|
150
|
+
const path = options.path || DEFAULT_PATH
|
|
151
|
+
debug('path', path)
|
|
110
152
|
|
|
111
|
-
|
|
112
|
-
|
|
153
|
+
const credentials = getAuthByRequestOptions(options)
|
|
154
|
+
debug('credentials', credentials)
|
|
113
155
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
156
|
+
const authString = credentials
|
|
157
|
+
? `${credentials.username}:${credentials.password}@`
|
|
158
|
+
: ''
|
|
159
|
+
debug('auth string:', authString)
|
|
117
160
|
|
|
118
|
-
|
|
161
|
+
const url = new URL(`${protocol}//${authString}${hostname}${path}`)
|
|
162
|
+
debug('created url:', url)
|
|
119
163
|
|
|
120
164
|
return url
|
|
121
165
|
}
|