Skip to content

Commit

Permalink
fix(ClientRequest): ensure "options._defaultAgent" is always set base…
Browse files Browse the repository at this point in the history
…d on the protocol (#221)

* chore(http.request): improve the log

* fix(ClientRequest): ensure "options._defaultAgent" is always set based on the protocol
  • Loading branch information
kettanaito authored Mar 8, 2022
1 parent 97a04bc commit ca5cc32
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 10 deletions.
2 changes: 1 addition & 1 deletion src/interceptors/ClientRequest/http.request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export function request(
observer: Observer
) {
return (...args: ClientRequestArgs): ClientRequest => {
log('intercepted request:', args)
log('request call (protocol "%s"):', protocol, args)

const clientRequestArgs = normalizeClientRequestArgs(
`${protocol}:`,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { parse } from 'url'
import { Agent as HttpsAgent } from 'https'
import { globalAgent as httpGlobalAgent, RequestOptions } from 'http'
import { Agent as HttpsAgent, globalAgent as httpsGlobalAgent } from 'https'
import { getUrlByRequestOptions } from '../../../utils/getUrlByRequestOptions'
import { normalizeClientRequestArgs } from './normalizeClientRequestArgs'

Expand Down Expand Up @@ -192,8 +193,9 @@ test('handles [URL, RequestOptions, callback] input', () => {
expect(url.href).toEqual('https://mswjs.io/resource')

// Options must be preserved.
expect(options).toEqual({
expect(options).toEqual<RequestOptions>({
agent: false,
_defaultAgent: httpsGlobalAgent,
protocol: url.protocol,
method: 'GET',
headers: {
Expand Down Expand Up @@ -294,12 +296,11 @@ test('sets fallback Agent based on the URL protocol', () => {
'https:',
'https://github.com'
)
const agent = options.agent as HttpsAgent

expect(options.agent).toBeInstanceOf(HttpsAgent)
expect(
// @ts-expect-error Protocol is an internal property.
options.agent.protocol
).toEqual(url.protocol)
expect(agent).toBeInstanceOf(HttpsAgent)
expect(agent).toHaveProperty('defaultPort', 443)
expect(agent).toHaveProperty('protocol', url.protocol)
})

test('does not set any fallback Agent given "agent: false" option', () => {
Expand All @@ -312,6 +313,41 @@ test('does not set any fallback Agent given "agent: false" option', () => {
expect(options.agent).toEqual(false)
})

test('sets the default Agent for HTTP request', () => {
const [, options] = normalizeClientRequestArgs(
'http:',
'http://github.com',
{}
)

expect(options._defaultAgent).toEqual(httpGlobalAgent)
})

test('sets the default Agent for HTTPS request', () => {
const [, options] = normalizeClientRequestArgs(
'https:',
'https://github.com',
{}
)

expect(options._defaultAgent).toEqual(httpsGlobalAgent)
})

test('preserves a custom default Agent when set', () => {
const [, options] = normalizeClientRequestArgs(
'https:',
'https://github.com',
{
/**
* @note Intentionally incorrect Agent for HTTPS request.
*/
_defaultAgent: httpGlobalAgent,
}
)

expect(options._defaultAgent).toEqual(httpGlobalAgent)
})

test('merges URL-based RequestOptions with the custom RequestOptions', () => {
const [url, options] = normalizeClientRequestArgs(
'https:',
Expand Down
31 changes: 29 additions & 2 deletions src/interceptors/ClientRequest/utils/normalizeClientRequestArgs.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { debug } from 'debug'
import { Agent as HttpAgent, IncomingMessage } from 'http'
import { RequestOptions, Agent as HttpsAgent } from 'https'
import {
Agent as HttpAgent,
globalAgent as httpGlobalAgent,
IncomingMessage,
} from 'http'
import {
RequestOptions,
Agent as HttpsAgent,
globalAgent as httpsGlobalAgent,
} from 'https'
import { Url as LegacyURL } from 'url'
import { getRequestOptionsByUrl } from '../../../utils/getRequestOptionsByUrl'
import {
Expand Down Expand Up @@ -198,8 +206,27 @@ export function normalizeClientRequestArgs(
log('resolved fallback agent:', agent)
}

/**
* Ensure that the default Agent is always set.
* This prevents the protocol mismatch for requests with { agent: false },
* where the global Agent is inferred.
* @see https://github.com/mswjs/msw/issues/1150
* @see https://github.com/nodejs/node/blob/418ff70b810f0e7112d48baaa72932a56cfa213b/lib/_http_client.js#L130
* @see https://github.com/nodejs/node/blob/418ff70b810f0e7112d48baaa72932a56cfa213b/lib/_http_client.js#L157-L159
*/
if (!options._defaultAgent) {
log(
'has no default agent, setting the default agent for "%s"',
options.protocol
)

options._defaultAgent =
options.protocol === 'https:' ? httpsGlobalAgent : httpGlobalAgent
}

log('successfully resolved url:', url.href)
log('successfully resolved options:', options)
log('successfully resolved callback:', callback)

return [url, options, callback]
}

0 comments on commit ca5cc32

Please sign in to comment.