• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mongodb-js / devtools-shared / 14333619959

08 Apr 2025 12:33PM UTC coverage: 72.194% (-0.08%) from 72.278%
14333619959

push

github

web-flow
fix(mongodb-cloud-info): if the host's IP is not in a cloud's range, check if the CNAME resolves to AWS. COMPASS-8932 (#526)

* If the host's IP is not in a cloud's range, check if the CNAME resolves to AWS.

* Update packages/mongodb-cloud-info/src/index.ts

Co-authored-by: Anna Henningsen <anna.henningsen@mongodb.com>

* without the .only

* reformat unrelated code

---------

Co-authored-by: Anna Henningsen <anna.henningsen@mongodb.com>

1444 of 2269 branches covered (63.64%)

Branch coverage included in aggregate %.

9 of 9 new or added lines in 1 file covered. (100.0%)

109 existing lines in 5 files now uncovered.

3071 of 3985 relevant lines covered (77.06%)

636.4 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.9
/packages/devtools-proxy-support/src/proxy-agent.ts
1
// Copyright (c) 2013 Nathan Rajlich <nathan@tootallnate.net>
2
//
3
// Permission is hereby granted, free of charge, to any person obtaining
4
// a copy of this software and associated documentation files (the
5
// 'Software'), to deal in the Software without restriction, including
6
// without limitation the rights to use, copy, modify, merge, publish,
7
// distribute, sublicense, and/or sell copies of the Software, and to
8
// permit persons to whom the Software is furnished to do so, subject to
9
// the following conditions:
10
//
11
// The above copyright notice and this permission notice shall be
12
// included in all copies or substantial portions of the Software.
13
//
14
// THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
15
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21

22
// This file is closely adapted from
23
// https://github.com/TooTallNate/proxy-agents/blob/5555794b6d9e4b0a36fac80a2d3acea876a8f7dc/packages/proxy-agent/src/index.ts
24
// (hence the license notice above), with core differences being that
25
// this module uses a different `getProxyForUrl` signature for more flexibility
26
// and loads the individual agents it defers to lazily.
27
// Relevant pull requests have been linked in-line.
28

29
import * as http from 'http';
1✔
30
import * as https from 'https';
1✔
31
import { URL } from 'url';
1✔
32
import { LRUCache } from 'lru-cache';
1✔
33
import type { AgentConnectOpts } from 'agent-base';
34
import { Agent } from 'agent-base';
1✔
35
import createDebug from 'debug';
1✔
36
import type { PacProxyAgentOptions } from 'pac-proxy-agent';
37
import type { PacProxyAgent } from 'pac-proxy-agent';
38
import type { HttpProxyAgentOptions } from 'http-proxy-agent';
39
import type { HttpProxyAgent } from 'http-proxy-agent';
40
import type { HttpsProxyAgentOptions } from 'https-proxy-agent';
41
import type { HttpsProxyAgent } from 'https-proxy-agent';
42
import type { SocksProxyAgentOptions } from 'socks-proxy-agent';
43
import type { SocksProxyAgent } from 'socks-proxy-agent';
44
import { createRequire } from 'module';
1✔
45

46
const debug = createDebug('proxy-agent');
1✔
47

48
type ValidProtocol =
49
  | (typeof HttpProxyAgent.protocols)[number]
50
  | (typeof HttpsProxyAgent.protocols)[number]
51
  | (typeof SocksProxyAgent.protocols)[number]
52
  | (typeof PacProxyAgent.protocols)[number];
53

54
type AgentConstructor = new (
55
  proxy: string,
56
  proxyAgentOptions?: ProxyAgentOptions,
57
) => Agent;
58

59
type GetProxyForUrlCallback = (
60
  url: string,
61
  req: http.ClientRequest,
62
) => string | Promise<string>;
63

64
/**
65
 * Shorthands for built-in supported types.
66
 */
67
// https://github.com/TooTallNate/proxy-agents/pull/327
68
const wellKnownAgents = {
1✔
69
  http: async () => (await import('http-proxy-agent')).HttpProxyAgent,
4✔
70
  https: async () => (await import('https-proxy-agent')).HttpsProxyAgent,
12✔
71
  socks: async () => (await import('socks-proxy-agent')).SocksProxyAgent,
11✔
72
  pac: async () => (await import('pac-proxy-agent')).PacProxyAgent,
2✔
73
} as const;
74

75
/**
76
 * Supported proxy types.
77
 */
78
export const proxies: {
1✔
79
  [P in ValidProtocol]: [
80
    () => Promise<AgentConstructor>,
81
    () => Promise<AgentConstructor>,
82
  ];
83
} = {
84
  http: [wellKnownAgents.http, wellKnownAgents.https],
85
  https: [wellKnownAgents.http, wellKnownAgents.https],
86
  socks: [wellKnownAgents.socks, wellKnownAgents.socks],
87
  socks4: [wellKnownAgents.socks, wellKnownAgents.socks],
88
  socks4a: [wellKnownAgents.socks, wellKnownAgents.socks],
89
  socks5: [wellKnownAgents.socks, wellKnownAgents.socks],
90
  socks5h: [wellKnownAgents.socks, wellKnownAgents.socks],
91
  'pac+data': [wellKnownAgents.pac, wellKnownAgents.pac],
92
  'pac+file': [wellKnownAgents.pac, wellKnownAgents.pac],
93
  'pac+ftp': [wellKnownAgents.pac, wellKnownAgents.pac],
94
  'pac+http': [wellKnownAgents.pac, wellKnownAgents.pac],
95
  'pac+https': [wellKnownAgents.pac, wellKnownAgents.pac],
96
};
97

98
function isValidProtocol(v: string): v is ValidProtocol {
99
  return Object.keys(proxies).includes(v);
29✔
100
}
101

102
export type ProxyAgentOptions = HttpProxyAgentOptions<''> &
103
  HttpsProxyAgentOptions<''> &
104
  SocksProxyAgentOptions &
105
  PacProxyAgentOptions<''> & {
106
    /**
107
     * Default `http.Agent` instance to use when no proxy is
108
     * configured for a request. Defaults to a new `http.Agent()`
109
     * instance with the proxy agent options passed in.
110
     */
111
    httpAgent?: http.Agent;
112
    /**
113
     * Default `http.Agent` instance to use when no proxy is
114
     * configured for a request. Defaults to a new `https.Agent()`
115
     * instance with the proxy agent options passed in.
116
     */
117
    httpsAgent?: http.Agent;
118
    /**
119
     * A callback for dynamic provision of proxy for url.
120
     * Defaults to standard proxy environment variables,
121
     * see https://www.npmjs.com/package/proxy-from-env for details
122
     */
123
    // https://github.com/TooTallNate/proxy-agents/pull/326
124
    getProxyForUrl: GetProxyForUrlCallback;
125
  };
126

127
/**
128
 * Uses the appropriate `Agent` subclass based off of the "proxy"
129
 * environment variables that are currently set.
130
 *
131
 * An LRU cache is used, to prevent unnecessary creation of proxy
132
 * `http.Agent` instances.
133
 */
134
export class ProxyAgent extends Agent {
1✔
135
  /**
136
   * Cache for `Agent` instances.
137
   */
138
  // https://github.com/TooTallNate/proxy-agents/pull/325
139
  cache = new LRUCache<string, Agent>({
39✔
140
    max: 20,
141
    dispose: (agent) => agent.destroy(),
×
142
  });
143

144
  connectOpts?: ProxyAgentOptions;
145
  httpAgent: http.Agent;
146
  httpsAgent: http.Agent;
147
  getProxyForUrl: GetProxyForUrlCallback;
148

149
  constructor(opts: ProxyAgentOptions) {
150
    super(opts);
39✔
151
    debug('Creating new ProxyAgent instance: %o', opts);
39✔
152
    this.connectOpts = opts;
39✔
153
    this.httpAgent = opts?.httpAgent || new http.Agent(opts);
39✔
154
    this.httpsAgent =
39✔
155
      opts?.httpsAgent || new https.Agent(opts as https.AgentOptions);
78✔
156
    this.getProxyForUrl = opts.getProxyForUrl;
39✔
157
  }
158

159
  async connect(
160
    req: http.ClientRequest,
161
    opts: AgentConnectOpts,
162
  ): Promise<http.Agent> {
163
    const { secureEndpoint } = opts;
35✔
164
    const isWebSocket = req.getHeader('upgrade') === 'websocket';
35✔
165
    const protocol = secureEndpoint
35✔
166
      ? isWebSocket
10!
167
        ? 'wss:'
168
        : 'https:'
169
      : isWebSocket
25✔
170
        ? 'ws:'
171
        : 'http:';
172
    const host = req.getHeader('host');
35✔
173
    const url = new URL(req.path, `${protocol}//${String(host)}`).href;
35✔
174
    const proxy = await this.getProxyForUrl(url, req);
35✔
175

176
    if (!proxy) {
35✔
177
      debug('Proxy not enabled for URL: %o', url);
3✔
178
      return secureEndpoint ? this.httpsAgent : this.httpAgent;
3✔
179
    }
180

181
    debug('Request URL: %o', url);
32✔
182
    debug('Proxy URL: %o', proxy);
32✔
183

184
    if (proxy.startsWith('pac+')) {
32✔
185
      installPacHttpsHack();
3✔
186
    }
187

188
    // attempt to get a cached `http.Agent` instance first
189
    const cacheKey = `${protocol}+${proxy}`;
32✔
190
    let agent = this.cache.get(cacheKey);
32✔
191
    if (!agent) {
32✔
192
      const proxyUrl = new URL(proxy);
29✔
193
      const proxyProto = proxyUrl.protocol.replace(':', '');
29✔
194
      if (!isValidProtocol(proxyProto)) {
29!
195
        throw new Error(`Unsupported protocol for proxy URL: ${proxy}`);
×
196
      }
197
      const ctor =
198
        await proxies[proxyProto][secureEndpoint || isWebSocket ? 1 : 0]();
29✔
199
      agent = new ctor(proxy, this.connectOpts);
29✔
200
      this.cache.set(cacheKey, agent);
29✔
201
    } else {
202
      debug('Cache hit for proxy URL: %o', proxy);
3✔
203
    }
204

205
    return agent;
32✔
206
  }
207

208
  destroy(): void {
209
    for (const agent of this.cache.values()) {
31✔
210
      agent.destroy();
22✔
211
    }
212
    super.destroy();
31✔
213
  }
214
}
215

216
declare const __webpack_require__: unknown;
217

218
// Work around https://github.com/TooTallNate/proxy-agents/pull/329
219
// While the proxy-agent package implementation in this file,
220
// and in the original, properly check whether an 'upgrade' header
221
// is present and set to 'websocket', the pac-proxy-agent performs
222
// a similar 'CONNECT vs regular HTTP proxy' selection and doesn't
223
// account for this. We monkey-patch in this behavior ourselves.
224
function installPacHttpsHack() {
225
  // eslint-disable-next-line @typescript-eslint/consistent-type-imports
226
  let HttpProxyAgent: typeof import('http-proxy-agent').HttpProxyAgent;
227
  // eslint-disable-next-line @typescript-eslint/consistent-type-imports
228
  let HttpsProxyAgent: typeof import('https-proxy-agent').HttpsProxyAgent;
229
  if (typeof __webpack_require__ === 'undefined') {
3!
230
    const pacProxyAgentPath = require.resolve('pac-proxy-agent');
3✔
231
    const pacRequire = createRequire(pacProxyAgentPath);
3✔
232
    HttpProxyAgent = pacRequire('http-proxy-agent').HttpProxyAgent;
3✔
233
    HttpsProxyAgent = pacRequire('https-proxy-agent').HttpsProxyAgent;
3✔
234
  } else {
235
    // No such thing as require.resolve() in webpack, just need to assume
236
    // that everything is hoisted :(
237
    // eslint-disable-next-line @typescript-eslint/no-var-requires
UNCOV
238
    HttpProxyAgent = require('http-proxy-agent').HttpProxyAgent;
×
239
    // eslint-disable-next-line @typescript-eslint/no-var-requires
UNCOV
240
    HttpsProxyAgent = require('https-proxy-agent').HttpsProxyAgent;
×
241
  }
242

243
  const kCompanionHttpsProxyAgent = Symbol('kCompanionHttpsProxyAgent');
3✔
244
  // eslint-disable-next-line @typescript-eslint/unbound-method
245
  const originalConnect = HttpProxyAgent.prototype.connect;
3✔
246
  HttpProxyAgent.prototype.connect = function (req, ...args) {
3✔
247
    if (req.getHeader('upgrade') === 'websocket') {
1!
248
      let companionHttpsAgent: HttpsProxyAgent<string> = (this as any)[
1✔
249
        kCompanionHttpsProxyAgent
250
      ];
251
      if (!companionHttpsAgent) {
1!
252
        companionHttpsAgent = new HttpsProxyAgent(
1✔
253
          this.proxy.href,
254
          this.options,
255
        );
256
        (this as any)[kCompanionHttpsProxyAgent] = companionHttpsAgent;
1✔
257
      }
258
      return companionHttpsAgent.connect(req, ...args);
1✔
259
    }
UNCOV
260
    return originalConnect.call(this, req, ...args);
×
261
  };
262
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc