proxyResolver.ts 3.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import { ExtHostWorkspace } from 'vs/workbench/api/node/extHostWorkspace';
import { ExtHostConfiguration } from 'vs/workbench/api/node/extHostConfiguration';
import * as http from 'http';
import * as https from 'https';
import ElectronProxyAgent = require('electron-proxy-agent');
import { MainThreadTelemetryShape } from 'vs/workbench/api/node/extHost.protocol';
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
import { toErrorMessage } from 'vs/base/common/errorMessage';

export function connectProxyResolver(extHostWorkspace: ExtHostWorkspace, extHostConfiguration: ExtHostConfiguration, extHostLogService: ExtHostLogService, mainThreadTelemetry: MainThreadTelemetryShape) {
	let timeout: NodeJS.Timer | undefined;
	let count = 0;
	let duration = 0;
	let errorCount = 0;
	function logEvent() {
		timeout = undefined;
		/* __GDPR__
			"resolveProxy" : {
				"count": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
				"duration": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
				"errorCount": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "isMeasurement": true },
				"${include}": [
					"${TypeScriptCommonProperties}"
				]
			}
		*/
		mainThreadTelemetry.$publicLog('resolveProxy', { count, duration, errorCount });
		count = duration = errorCount = 0;
	}

	function resolveProxy(url: string, callback: (proxy?: string) => void) {
		if (!timeout) {
			timeout = setTimeout(logEvent, 10 * 60 * 1000);
		}

		const start = Date.now();
		extHostWorkspace.resolveProxy(url)
			.then(proxy => {
				callback(proxy);
			}).then(() => {
				count++;
				duration = Date.now() - start + duration;
			}, err => {
				errorCount++;
				extHostLogService.error('resolveProxy', toErrorMessage(err));
				callback();
			});
	}

	const agent = new ElectronProxyAgent({ resolveProxy });

	let config = extHostConfiguration.getConfiguration('http').get('systemProxy') || 'off';
	extHostConfiguration.onDidChangeConfiguration(e => {
		config = extHostConfiguration.getConfiguration('http').get('systemProxy') || 'off';
	});

	function patch(original: typeof http.get) {
		function patched(url: string | URL, options?: http.RequestOptions, callback?: (res: http.IncomingMessage) => void): http.ClientRequest {
			if (config === 'off') {
				return original.apply(null, arguments);
			}

			if (typeof url !== 'string' && !(url && (<any>url).searchParams)) {
				callback = <any>options;
				options = url;
				url = null;
			}
			if (typeof options === 'function') {
				callback = options;
				options = null;
			}
			options = options || {};

			if (config === 'force' || config === 'on' && !options.agent) {
				if (url) {
					const parsed = typeof url === 'string' ? new URL(url) : url;
					options = {
						protocol: parsed.protocol,
						hostname: parsed.hostname,
						port: parsed.port,
						path: parsed.pathname,
						...options
					};
				}
				options.agent = agent;
				return original(options, callback);
			}

			return original.apply(null, arguments);
		}
		return patched;
	}

	(<any>http).get = patch(http.get);
	(<any>http).request = patch(http.request);
	(<any>https).get = patch(https.get);
	(<any>https).request = patch(https.request);
}