requestService.ts 5.2 KB
Newer Older
J
Joao Moreno 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

6 7
import * as https from 'https';
import * as http from 'http';
8
import * as streams from 'vs/base/common/stream';
9 10
import { createGunzip } from 'zlib';
import { parse as parseUrl } from 'url';
11
import { Disposable } from 'vs/base/common/lifecycle';
12 13 14
import { isBoolean, isNumber } from 'vs/base/common/types';
import { canceled } from 'vs/base/common/errors';
import { CancellationToken } from 'vs/base/common/cancellation';
15 16
import { IRequestService, IHTTPConfiguration } from 'vs/platform/request/common/request';
import { IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request';
17
import { getProxyAgent, Agent } from 'vs/platform/request/node/proxy';
18
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
19
import { ILogService } from 'vs/platform/log/common/log';
20
import { streamToBufferReadableStream } from 'vs/base/common/buffer';
21 22 23 24 25 26 27 28 29 30

export interface IRawRequestFunction {
	(options: http.RequestOptions, callback?: (res: http.IncomingMessage) => void): http.ClientRequest;
}

export interface NodeRequestOptions extends IRequestOptions {
	agent?: Agent;
	strictSSL?: boolean;
	getRawRequest?(options: IRequestOptions): IRawRequestFunction;
}
J
Joao Moreno 已提交
31 32 33 34 35

/**
 * This service exposes the `request` API, while using the global
 * or configured proxy settings.
 */
36
export class RequestService extends Disposable implements IRequestService {
J
Joao Moreno 已提交
37

38
	declare readonly _serviceBrand: undefined;
J
Joao Moreno 已提交
39

S
Sandeep Somavarapu 已提交
40
	private proxyUrl?: string;
B
Benjamin Pasero 已提交
41
	private strictSSL: boolean | undefined;
S
Sandeep Somavarapu 已提交
42
	private authorization?: string;
J
Joao Moreno 已提交
43 44

	constructor(
J
Joao Moreno 已提交
45
		@IConfigurationService configurationService: IConfigurationService,
46
		@ILogService private readonly logService: ILogService
J
Joao Moreno 已提交
47
	) {
48
		super();
49
		this.configure(configurationService.getValue<IHTTPConfiguration>());
50
		this._register(configurationService.onDidChangeConfiguration(() => this.configure(configurationService.getValue()), this));
J
Joao Moreno 已提交
51 52
	}

53
	private configure(config: IHTTPConfiguration) {
J
Joao Moreno 已提交
54
		this.proxyUrl = config.http && config.http.proxy;
M
Matt Bierner 已提交
55
		this.strictSSL = !!(config.http && config.http.proxyStrictSSL);
J
Joao Moreno 已提交
56
		this.authorization = config.http && config.http.proxyAuthorization;
J
Joao Moreno 已提交
57 58
	}

59
	async request(options: NodeRequestOptions, token: CancellationToken): Promise<IRequestContext> {
J
Joao Moreno 已提交
60 61
		this.logService.trace('RequestService#request', options.url);

J
Joao Moreno 已提交
62
		const { proxyUrl, strictSSL } = this;
63 64 65 66 67 68
		const agent = options.agent ? options.agent : await getProxyAgent(options.url || '', { proxyUrl, strictSSL });

		options.agent = agent;
		options.strictSSL = strictSSL;

		if (this.authorization) {
69 70 71 72
			options.headers = {
				...(options.headers || {}),
				'Proxy-Authorization': this.authorization
			};
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
		}

		return this._request(options, token);
	}

	private async getNodeRequest(options: IRequestOptions): Promise<IRawRequestFunction> {
		const endpoint = parseUrl(options.url!);
		const module = endpoint.protocol === 'https:' ? await import('https') : await import('http');
		return module.request;
	}

	private _request(options: NodeRequestOptions, token: CancellationToken): Promise<IRequestContext> {

		return new Promise<IRequestContext>(async (c, e) => {
			let req: http.ClientRequest;
J
Joao Moreno 已提交
88

89 90 91 92
			const endpoint = parseUrl(options.url!);
			const rawRequest = options.getRawRequest
				? options.getRawRequest(options)
				: await this.getNodeRequest(options);
J
Joao Moreno 已提交
93

94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
			const opts: https.RequestOptions = {
				hostname: endpoint.hostname,
				port: endpoint.port ? parseInt(endpoint.port) : (endpoint.protocol === 'https:' ? 443 : 80),
				protocol: endpoint.protocol,
				path: endpoint.path,
				method: options.type || 'GET',
				headers: options.headers,
				agent: options.agent,
				rejectUnauthorized: isBoolean(options.strictSSL) ? options.strictSSL : true
			};

			if (options.user && options.password) {
				opts.auth = options.user + ':' + options.password;
			}

			req = rawRequest(opts, (res: http.IncomingMessage) => {
				const followRedirects: number = isNumber(options.followRedirects) ? options.followRedirects : 3;
				if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && followRedirects > 0 && res.headers['location']) {
112 113
					this._request({
						...options,
114 115
						url: res.headers['location'],
						followRedirects: followRedirects - 1
116
					}, token).then(c, e);
117
				} else {
118
					let stream: streams.ReadableStreamEvents<Uint8Array> = res;
119 120

					if (res.headers['content-encoding'] === 'gzip') {
121
						stream = res.pipe(createGunzip());
122 123
					}

124
					c({ res, stream: streamToBufferReadableStream(stream) } as IRequestContext);
125 126 127 128 129 130 131 132 133 134 135 136 137
				}
			});

			req.on('error', e);

			if (options.timeout) {
				req.setTimeout(options.timeout);
			}

			if (options.data) {
				if (typeof options.data === 'string') {
					req.write(options.data);
				}
J
Joao Moreno 已提交
138
			}
J
Joao Moreno 已提交
139

140 141 142 143 144 145
			req.end();

			token.onCancellationRequested(() => {
				req.abort();
				e(canceled());
			});
J
Joao Moreno 已提交
146
		});
J
Joao Moreno 已提交
147
	}
148

149 150 151
	async resolveProxy(url: string): Promise<string | undefined> {
		return undefined; // currently not implemented in node
	}
B
Benjamin Pasero 已提交
152
}