Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/api/node/proxyResolver.ts
5240 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { IExtHostWorkspaceProvider } from '../common/extHostWorkspace.js';
7
import { ConfigurationInspect, ExtHostConfigProvider } from '../common/extHostConfiguration.js';
8
import { MainThreadTelemetryShape } from '../common/extHost.protocol.js';
9
import { IExtensionHostInitData } from '../../services/extensions/common/extensionHostProtocol.js';
10
import { ExtHostExtensionService } from './extHostExtensionService.js';
11
import { URI } from '../../../base/common/uri.js';
12
import { ILogService, LogLevel as LogServiceLevel } from '../../../platform/log/common/log.js';
13
import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js';
14
import { LogLevel, createHttpPatch, createProxyResolver, createTlsPatch, ProxySupportSetting, ProxyAgentParams, createNetPatch, loadSystemCertificates, ResolveProxyWithRequest } from '@vscode/proxy-agent';
15
import { AuthInfo, systemCertificatesNodeDefault } from '../../../platform/request/common/request.js';
16
import { DisposableStore } from '../../../base/common/lifecycle.js';
17
import { createRequire } from 'node:module';
18
import type * as undiciType from 'undici-types';
19
import type * as tlsType from 'tls';
20
import { lookupKerberosAuthorization } from '../../../platform/request/node/requestService.js';
21
import * as proxyAgent from '@vscode/proxy-agent';
22
23
const require = createRequire(import.meta.url);
24
const http = require('http');
25
const https = require('https');
26
const tls: typeof tlsType = require('tls');
27
const net = require('net');
28
29
const systemCertificatesV2Default = false;
30
const useElectronFetchDefault = false;
31
32
export function connectProxyResolver(
33
extHostWorkspace: IExtHostWorkspaceProvider,
34
configProvider: ExtHostConfigProvider,
35
extensionService: ExtHostExtensionService,
36
extHostLogService: ILogService,
37
mainThreadTelemetry: MainThreadTelemetryShape,
38
initData: IExtensionHostInitData,
39
disposables: DisposableStore,
40
) {
41
42
const isRemote = initData.remote.isRemote;
43
const useHostProxyDefault = initData.environment.useHostProxy ?? !isRemote;
44
const fallbackToLocalKerberos = useHostProxyDefault;
45
const loadLocalCertificates = useHostProxyDefault;
46
const isUseHostProxyEnabled = () => !isRemote || configProvider.getConfiguration('http').get<boolean>('useLocalProxyConfiguration', useHostProxyDefault);
47
const timedResolveProxy = createTimedResolveProxy(extHostWorkspace, mainThreadTelemetry);
48
const params: ProxyAgentParams = {
49
resolveProxy: timedResolveProxy,
50
lookupProxyAuthorization: lookupProxyAuthorization.bind(undefined, extHostWorkspace, extHostLogService, mainThreadTelemetry, configProvider, {}, {}, initData.remote.isRemote, fallbackToLocalKerberos),
51
getProxyURL: () => getExtHostConfigValue<string>(configProvider, isRemote, 'http.proxy'),
52
getProxySupport: () => getExtHostConfigValue<ProxySupportSetting>(configProvider, isRemote, 'http.proxySupport') || 'off',
53
getNoProxyConfig: () => getExtHostConfigValue<string[]>(configProvider, isRemote, 'http.noProxy') || [],
54
isAdditionalFetchSupportEnabled: () => getExtHostConfigValue<boolean>(configProvider, isRemote, 'http.fetchAdditionalSupport', true),
55
addCertificatesV1: () => certSettingV1(configProvider, isRemote),
56
addCertificatesV2: () => certSettingV2(configProvider, isRemote),
57
loadSystemCertificatesFromNode: () => getExtHostConfigValue<boolean>(configProvider, isRemote, 'http.systemCertificatesNode', systemCertificatesNodeDefault),
58
log: extHostLogService,
59
getLogLevel: () => {
60
const level = extHostLogService.getLevel();
61
switch (level) {
62
case LogServiceLevel.Trace: return LogLevel.Trace;
63
case LogServiceLevel.Debug: return LogLevel.Debug;
64
case LogServiceLevel.Info: return LogLevel.Info;
65
case LogServiceLevel.Warning: return LogLevel.Warning;
66
case LogServiceLevel.Error: return LogLevel.Error;
67
case LogServiceLevel.Off: return LogLevel.Off;
68
default: return never(level);
69
}
70
function never(level: never) {
71
extHostLogService.error('Unknown log level', level);
72
return LogLevel.Debug;
73
}
74
},
75
proxyResolveTelemetry: () => { },
76
isUseHostProxyEnabled,
77
getNetworkInterfaceCheckInterval: () => {
78
const intervalSeconds = getExtHostConfigValue<number>(configProvider, isRemote, 'http.experimental.networkInterfaceCheckInterval', 300);
79
return intervalSeconds * 1000;
80
},
81
loadAdditionalCertificates: async () => {
82
const useNodeSystemCerts = getExtHostConfigValue<boolean>(configProvider, isRemote, 'http.systemCertificatesNode', systemCertificatesNodeDefault);
83
const promises: Promise<string[]>[] = [];
84
if (isRemote) {
85
promises.push(loadSystemCertificates({
86
loadSystemCertificatesFromNode: () => useNodeSystemCerts,
87
log: extHostLogService,
88
}));
89
}
90
if (loadLocalCertificates) {
91
if (!isRemote && useNodeSystemCerts) {
92
promises.push(loadSystemCertificates({
93
loadSystemCertificatesFromNode: () => useNodeSystemCerts,
94
log: extHostLogService,
95
}));
96
} else {
97
extHostLogService.trace('ProxyResolver#loadAdditionalCertificates: Loading certificates from main process');
98
const certs = extHostWorkspace.loadCertificates(); // Loading from main process to share cache.
99
certs.then(certs => extHostLogService.trace('ProxyResolver#loadAdditionalCertificates: Loaded certificates from main process', certs.length));
100
promises.push(certs);
101
}
102
}
103
// Using https.globalAgent because it is shared with proxy.test.ts and mutable.
104
if (initData.environment.extensionTestsLocationURI && https.globalAgent.testCertificates?.length) {
105
extHostLogService.trace('ProxyResolver#loadAdditionalCertificates: Loading test certificates');
106
promises.push(Promise.resolve(https.globalAgent.testCertificates as string[]));
107
}
108
const result = (await Promise.all(promises)).flat();
109
mainThreadTelemetry.$publicLog2<AdditionalCertificatesEvent, AdditionalCertificatesClassification>('additionalCertificates', {
110
count: result.length,
111
isRemote,
112
loadLocalCertificates,
113
useNodeSystemCerts,
114
});
115
return result;
116
},
117
env: process.env,
118
};
119
const { resolveProxyWithRequest, resolveProxyURL } = createProxyResolver(params);
120
// eslint-disable-next-line local/code-no-any-casts
121
const target = (proxyAgent as any).default || proxyAgent;
122
target.resolveProxyURL = resolveProxyURL;
123
124
patchGlobalFetch(params, configProvider, mainThreadTelemetry, initData, resolveProxyURL, disposables);
125
126
const lookup = createPatchedModules(params, resolveProxyWithRequest);
127
return configureModuleLoading(extensionService, lookup);
128
}
129
130
const unsafeHeaders = [
131
'content-length',
132
'host',
133
'trailer',
134
'te',
135
'upgrade',
136
'cookie2',
137
'keep-alive',
138
'transfer-encoding',
139
'set-cookie',
140
];
141
142
function patchGlobalFetch(params: ProxyAgentParams, configProvider: ExtHostConfigProvider, mainThreadTelemetry: MainThreadTelemetryShape, initData: IExtensionHostInitData, resolveProxyURL: (url: string) => Promise<string | undefined>, disposables: DisposableStore) {
143
// eslint-disable-next-line local/code-no-any-casts
144
if (!(globalThis as any).__vscodeOriginalFetch) {
145
const originalFetch = globalThis.fetch;
146
// eslint-disable-next-line local/code-no-any-casts
147
(globalThis as any).__vscodeOriginalFetch = originalFetch;
148
const patchedFetch = proxyAgent.createFetchPatch(params, originalFetch, resolveProxyURL);
149
// eslint-disable-next-line local/code-no-any-casts
150
(globalThis as any).__vscodePatchedFetch = patchedFetch;
151
let useElectronFetch = false;
152
if (!initData.remote.isRemote) {
153
useElectronFetch = configProvider.getConfiguration('http').get<boolean>('electronFetch', useElectronFetchDefault);
154
disposables.add(configProvider.onDidChangeConfiguration(e => {
155
if (e.affectsConfiguration('http.electronFetch')) {
156
useElectronFetch = configProvider.getConfiguration('http').get<boolean>('electronFetch', useElectronFetchDefault);
157
}
158
}));
159
}
160
// https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
161
globalThis.fetch = async function fetch(input: string | URL | Request, init?: RequestInit) {
162
function getRequestProperty(name: keyof Request & keyof RequestInit) {
163
return init && name in init ? init[name] : typeof input === 'object' && 'cache' in input ? input[name] : undefined;
164
}
165
// Limitations: https://github.com/electron/electron/pull/36733#issuecomment-1405615494
166
// net.fetch fails on manual redirect: https://github.com/electron/electron/issues/43715
167
const urlString = typeof input === 'string' ? input : 'cache' in input ? input.url : input.toString();
168
const isDataUrl = urlString.startsWith('data:');
169
if (isDataUrl) {
170
recordFetchFeatureUse(mainThreadTelemetry, 'data');
171
}
172
const isBlobUrl = urlString.startsWith('blob:');
173
if (isBlobUrl) {
174
recordFetchFeatureUse(mainThreadTelemetry, 'blob');
175
}
176
const isManualRedirect = getRequestProperty('redirect') === 'manual';
177
if (isManualRedirect) {
178
recordFetchFeatureUse(mainThreadTelemetry, 'manualRedirect');
179
}
180
const integrity = getRequestProperty('integrity');
181
if (integrity) {
182
recordFetchFeatureUse(mainThreadTelemetry, 'integrity');
183
}
184
if (!useElectronFetch || isDataUrl || isBlobUrl || isManualRedirect || integrity) {
185
const response = await patchedFetch(input, init);
186
monitorResponseProperties(mainThreadTelemetry, response, urlString);
187
return response;
188
}
189
// Unsupported headers: https://source.chromium.org/chromium/chromium/src/+/main:services/network/public/cpp/header_util.cc;l=32;drc=ee7299f8961a1b05a3554efcc496b6daa0d7f6e1
190
if (init?.headers) {
191
const headers = new Headers(init.headers);
192
for (const header of unsafeHeaders) {
193
headers.delete(header);
194
}
195
init = { ...init, headers };
196
}
197
// Support for URL: https://github.com/electron/electron/issues/43712
198
const electronInput = input instanceof URL ? input.toString() : input;
199
const electron = require('electron');
200
const response = await electron.net.fetch(electronInput, init);
201
monitorResponseProperties(mainThreadTelemetry, response, urlString);
202
return response;
203
};
204
}
205
}
206
207
function monitorResponseProperties(mainThreadTelemetry: MainThreadTelemetryShape, response: Response, urlString: string) {
208
const originalUrl = response.url;
209
Object.defineProperty(response, 'url', {
210
get() {
211
recordFetchFeatureUse(mainThreadTelemetry, 'url');
212
return originalUrl || urlString;
213
}
214
});
215
const originalType = response.type;
216
Object.defineProperty(response, 'type', {
217
get() {
218
recordFetchFeatureUse(mainThreadTelemetry, 'typeProperty');
219
return originalType !== 'default' ? originalType : 'basic';
220
}
221
});
222
}
223
224
type FetchFeatureUseClassification = {
225
owner: 'chrmarti';
226
comment: 'Data about fetch API use';
227
url: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the url property was used.' };
228
typeProperty: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the type property was used.' };
229
data: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether a data URL was used.' };
230
blob: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether a blob URL was used.' };
231
integrity: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the integrity property was used.' };
232
manualRedirect: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether a manual redirect was used.' };
233
};
234
235
type FetchFeatureUseEvent = {
236
url: number;
237
typeProperty: number;
238
data: number;
239
blob: number;
240
integrity: number;
241
manualRedirect: number;
242
};
243
244
const fetchFeatureUse: FetchFeatureUseEvent = {
245
url: 0,
246
typeProperty: 0,
247
data: 0,
248
blob: 0,
249
integrity: 0,
250
manualRedirect: 0,
251
};
252
253
let timer: Timeout | undefined;
254
const enableFeatureUseTelemetry = false;
255
function recordFetchFeatureUse(mainThreadTelemetry: MainThreadTelemetryShape, feature: keyof typeof fetchFeatureUse) {
256
if (enableFeatureUseTelemetry && !fetchFeatureUse[feature]++) {
257
if (timer) {
258
clearTimeout(timer);
259
}
260
timer = setTimeout(() => {
261
mainThreadTelemetry.$publicLog2<FetchFeatureUseEvent, FetchFeatureUseClassification>('fetchFeatureUse', fetchFeatureUse);
262
}, 10000); // collect additional features for 10 seconds
263
(timer as unknown as NodeJS.Timeout).unref?.();
264
}
265
}
266
267
type AdditionalCertificatesClassification = {
268
owner: 'chrmarti';
269
comment: 'Tracks the number of additional certificates loaded for TLS connections';
270
count: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Number of additional certificates loaded' };
271
isRemote: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Whether this is a remote extension host' };
272
loadLocalCertificates: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Whether local certificates are loaded' };
273
useNodeSystemCerts: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'Whether Node.js system certificates are used' };
274
};
275
276
type AdditionalCertificatesEvent = {
277
count: number;
278
isRemote: boolean;
279
loadLocalCertificates: boolean;
280
useNodeSystemCerts: boolean;
281
};
282
283
type ProxyResolveStatsClassification = {
284
owner: 'chrmarti';
285
comment: 'Performance statistics for proxy resolution';
286
count: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Number of proxy resolution calls' };
287
totalDuration: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Total time spent in proxy resolution (ms)' };
288
minDuration: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Minimum resolution time (ms)' };
289
maxDuration: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Maximum resolution time (ms)' };
290
avgDuration: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Average resolution time (ms)' };
291
};
292
293
type ProxyResolveStatsEvent = {
294
count: number;
295
totalDuration: number;
296
minDuration: number;
297
maxDuration: number;
298
avgDuration: number;
299
};
300
301
const proxyResolveStats = {
302
count: 0,
303
totalDuration: 0,
304
minDuration: Number.MAX_SAFE_INTEGER,
305
maxDuration: 0,
306
lastSentTime: 0,
307
};
308
309
const telemetryInterval = 60 * 60 * 1000; // 1 hour
310
311
function sendProxyResolveStats(mainThreadTelemetry: MainThreadTelemetryShape) {
312
if (proxyResolveStats.count > 0) {
313
const avgDuration = proxyResolveStats.totalDuration / proxyResolveStats.count;
314
mainThreadTelemetry.$publicLog2<ProxyResolveStatsEvent, ProxyResolveStatsClassification>('proxyResolveStats', {
315
count: proxyResolveStats.count,
316
totalDuration: proxyResolveStats.totalDuration,
317
minDuration: proxyResolveStats.minDuration,
318
maxDuration: proxyResolveStats.maxDuration,
319
avgDuration,
320
});
321
// Reset stats after sending
322
proxyResolveStats.count = 0;
323
proxyResolveStats.totalDuration = 0;
324
proxyResolveStats.minDuration = Number.MAX_SAFE_INTEGER;
325
proxyResolveStats.maxDuration = 0;
326
}
327
proxyResolveStats.lastSentTime = Date.now();
328
}
329
330
function createTimedResolveProxy(extHostWorkspace: IExtHostWorkspaceProvider, mainThreadTelemetry: MainThreadTelemetryShape) {
331
return async (url: string): Promise<string | undefined> => {
332
const startTime = performance.now();
333
try {
334
return await extHostWorkspace.resolveProxy(url);
335
} finally {
336
const duration = performance.now() - startTime;
337
proxyResolveStats.count++;
338
proxyResolveStats.totalDuration += duration;
339
proxyResolveStats.minDuration = Math.min(proxyResolveStats.minDuration, duration);
340
proxyResolveStats.maxDuration = Math.max(proxyResolveStats.maxDuration, duration);
341
342
// Send telemetry if at least an hour has passed since last send
343
const now = Date.now();
344
if (now - proxyResolveStats.lastSentTime >= telemetryInterval) {
345
sendProxyResolveStats(mainThreadTelemetry);
346
}
347
}
348
};
349
}
350
351
function createPatchedModules(params: ProxyAgentParams, resolveProxy: ResolveProxyWithRequest) {
352
353
function mergeModules(module: any, patch: any) {
354
const target = module.default || module;
355
target.__vscodeOriginal = Object.assign({}, target);
356
return Object.assign(target, patch);
357
}
358
359
return {
360
http: mergeModules(http, createHttpPatch(params, http, resolveProxy)),
361
https: mergeModules(https, createHttpPatch(params, https, resolveProxy)),
362
net: mergeModules(net, createNetPatch(params, net)),
363
tls: mergeModules(tls, createTlsPatch(params, tls))
364
};
365
}
366
367
function certSettingV1(configProvider: ExtHostConfigProvider, isRemote: boolean) {
368
return !getExtHostConfigValue<boolean>(configProvider, isRemote, 'http.experimental.systemCertificatesV2', systemCertificatesV2Default) && !!getExtHostConfigValue<boolean>(configProvider, isRemote, 'http.systemCertificates');
369
}
370
371
function certSettingV2(configProvider: ExtHostConfigProvider, isRemote: boolean) {
372
return !!getExtHostConfigValue<boolean>(configProvider, isRemote, 'http.experimental.systemCertificatesV2', systemCertificatesV2Default) && !!getExtHostConfigValue<boolean>(configProvider, isRemote, 'http.systemCertificates');
373
}
374
375
const modulesCache = new Map<IExtensionDescription | undefined, { http?: typeof http; https?: typeof https; undici?: typeof undiciType }>();
376
function configureModuleLoading(extensionService: ExtHostExtensionService, lookup: ReturnType<typeof createPatchedModules>): Promise<void> {
377
return extensionService.getExtensionPathIndex()
378
.then(extensionPaths => {
379
const node_module = require('module');
380
const original = node_module._load;
381
node_module._load = function load(request: string, parent: { filename: string }, isMain: boolean) {
382
if (request === 'net') {
383
return lookup.net;
384
}
385
386
if (request === 'tls') {
387
return lookup.tls;
388
}
389
390
if (request !== 'http' && request !== 'https' && request !== 'undici') {
391
return original.apply(this, arguments);
392
}
393
394
const ext = extensionPaths.findSubstr(URI.file(parent.filename));
395
let cache = modulesCache.get(ext);
396
if (!cache) {
397
modulesCache.set(ext, cache = {});
398
}
399
if (!cache[request]) {
400
if (request === 'undici') {
401
const undici = original.apply(this, arguments);
402
proxyAgent.patchUndici(undici);
403
cache[request] = undici;
404
} else {
405
const mod = lookup[request];
406
cache[request] = { ...mod }; // Copy to work around #93167.
407
}
408
}
409
return cache[request];
410
};
411
});
412
}
413
414
async function lookupProxyAuthorization(
415
extHostWorkspace: IExtHostWorkspaceProvider,
416
extHostLogService: ILogService,
417
mainThreadTelemetry: MainThreadTelemetryShape,
418
configProvider: ExtHostConfigProvider,
419
proxyAuthenticateCache: Record<string, string | string[] | undefined>,
420
basicAuthCache: Record<string, string | undefined>,
421
isRemote: boolean,
422
fallbackToLocalKerberos: boolean,
423
proxyURL: string,
424
proxyAuthenticate: string | string[] | undefined,
425
state: { kerberosRequested?: boolean; basicAuthCacheUsed?: boolean; basicAuthAttempt?: number }
426
): Promise<string | undefined> {
427
proxyURL = proxyURL.replace(/\/+$/, '');
428
const cached = proxyAuthenticateCache[proxyURL];
429
if (proxyAuthenticate) {
430
proxyAuthenticateCache[proxyURL] = proxyAuthenticate;
431
}
432
extHostLogService.trace('ProxyResolver#lookupProxyAuthorization callback', `proxyURL:${proxyURL}`, `proxyAuthenticate:${proxyAuthenticate}`, `proxyAuthenticateCache:${cached}`);
433
const header = proxyAuthenticate || cached;
434
const authenticate = Array.isArray(header) ? header : typeof header === 'string' ? [header] : [];
435
sendTelemetry(mainThreadTelemetry, authenticate, isRemote);
436
if (authenticate.some(a => /^(Negotiate|Kerberos)( |$)/i.test(a)) && !state.kerberosRequested) {
437
state.kerberosRequested = true;
438
439
try {
440
const spnConfig = getExtHostConfigValue<string>(configProvider, isRemote, 'http.proxyKerberosServicePrincipal');
441
const response = await lookupKerberosAuthorization(proxyURL, spnConfig, extHostLogService, 'ProxyResolver#lookupProxyAuthorization');
442
return 'Negotiate ' + response;
443
} catch (err) {
444
extHostLogService.debug('ProxyResolver#lookupProxyAuthorization Kerberos authentication failed', err);
445
}
446
447
if (isRemote && fallbackToLocalKerberos) {
448
extHostLogService.debug('ProxyResolver#lookupProxyAuthorization Kerberos authentication lookup on host', `proxyURL:${proxyURL}`);
449
const auth = await extHostWorkspace.lookupKerberosAuthorization(proxyURL);
450
if (auth) {
451
return 'Negotiate ' + auth;
452
}
453
}
454
}
455
const basicAuthHeader = authenticate.find(a => /^Basic( |$)/i.test(a));
456
if (basicAuthHeader) {
457
try {
458
const cachedAuth = basicAuthCache[proxyURL];
459
if (cachedAuth) {
460
if (state.basicAuthCacheUsed) {
461
extHostLogService.debug('ProxyResolver#lookupProxyAuthorization Basic authentication deleting cached credentials', `proxyURL:${proxyURL}`);
462
delete basicAuthCache[proxyURL];
463
} else {
464
extHostLogService.debug('ProxyResolver#lookupProxyAuthorization Basic authentication using cached credentials', `proxyURL:${proxyURL}`);
465
state.basicAuthCacheUsed = true;
466
return cachedAuth;
467
}
468
}
469
state.basicAuthAttempt = (state.basicAuthAttempt || 0) + 1;
470
const realm = / realm="([^"]+)"/i.exec(basicAuthHeader)?.[1];
471
extHostLogService.debug('ProxyResolver#lookupProxyAuthorization Basic authentication lookup', `proxyURL:${proxyURL}`, `realm:${realm}`);
472
const url = new URL(proxyURL);
473
const authInfo: AuthInfo = {
474
scheme: 'basic',
475
host: url.hostname,
476
port: Number(url.port),
477
realm: realm || '',
478
isProxy: true,
479
attempt: state.basicAuthAttempt,
480
};
481
const credentials = await extHostWorkspace.lookupAuthorization(authInfo);
482
if (credentials) {
483
extHostLogService.debug('ProxyResolver#lookupProxyAuthorization Basic authentication received credentials', `proxyURL:${proxyURL}`, `realm:${realm}`);
484
const auth = 'Basic ' + Buffer.from(`${credentials.username}:${credentials.password}`).toString('base64');
485
basicAuthCache[proxyURL] = auth;
486
return auth;
487
} else {
488
extHostLogService.debug('ProxyResolver#lookupProxyAuthorization Basic authentication received no credentials', `proxyURL:${proxyURL}`, `realm:${realm}`);
489
}
490
} catch (err) {
491
extHostLogService.error('ProxyResolver#lookupProxyAuthorization Basic authentication failed', err);
492
}
493
}
494
return undefined;
495
}
496
497
type ProxyAuthenticationClassification = {
498
owner: 'chrmarti';
499
comment: 'Data about proxy authentication requests';
500
authenticationType: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'Type of the authentication requested' };
501
extensionHostType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Type of the extension host' };
502
};
503
504
type ProxyAuthenticationEvent = {
505
authenticationType: string;
506
extensionHostType: string;
507
};
508
509
let telemetrySent = false;
510
const enableProxyAuthenticationTelemetry = false;
511
function sendTelemetry(mainThreadTelemetry: MainThreadTelemetryShape, authenticate: string[], isRemote: boolean) {
512
if (!enableProxyAuthenticationTelemetry || telemetrySent || !authenticate.length) {
513
return;
514
}
515
telemetrySent = true;
516
517
mainThreadTelemetry.$publicLog2<ProxyAuthenticationEvent, ProxyAuthenticationClassification>('proxyAuthenticationRequest', {
518
authenticationType: authenticate.map(a => a.split(' ')[0]).join(','),
519
extensionHostType: isRemote ? 'remote' : 'local',
520
});
521
}
522
523
function getExtHostConfigValue<T>(configProvider: ExtHostConfigProvider, isRemote: boolean, key: string, fallback: T): T;
524
function getExtHostConfigValue<T>(configProvider: ExtHostConfigProvider, isRemote: boolean, key: string): T | undefined;
525
function getExtHostConfigValue<T>(configProvider: ExtHostConfigProvider, isRemote: boolean, key: string, fallback?: T): T | undefined {
526
if (isRemote) {
527
return configProvider.getConfiguration().get<T>(key) ?? fallback;
528
}
529
const values: ConfigurationInspect<T> | undefined = configProvider.getConfiguration().inspect<T>(key);
530
return values?.globalLocalValue ?? values?.defaultValue ?? fallback;
531
}
532
533