Merge commit 'be3e8236086165e5e45a5a10783823874b3f3ebd' as 'lib/vscode'

This commit is contained in:
Joe Previte
2020-12-15 15:52:33 -07:00
4649 changed files with 1311795 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILogService } from 'vs/platform/log/common/log';
import { request } from 'vs/base/parts/request/browser/request';
import { IRequestService } from 'vs/platform/request/common/request';
/**
* This service exposes the `request` API, while using the global
* or configured proxy settings.
*/
export class RequestService implements IRequestService {
declare readonly _serviceBrand: undefined;
constructor(
@IConfigurationService private readonly configurationService: IConfigurationService,
@ILogService private readonly logService: ILogService
) {
}
request(options: IRequestOptions, token: CancellationToken): Promise<IRequestContext> {
this.logService.trace('RequestService#request', options.url);
if (!options.proxyAuthorization) {
options.proxyAuthorization = this.configurationService.getValue<string>('http.proxyAuthorization');
}
return request(options, token);
}
async resolveProxy(url: string): Promise<string | undefined> {
return undefined; // not implemented in the web
}
}

View File

@@ -0,0 +1,108 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { CancellationToken } from 'vs/base/common/cancellation';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
import { Registry } from 'vs/platform/registry/common/platform';
import { streamToBuffer } from 'vs/base/common/buffer';
import { IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request';
export const IRequestService = createDecorator<IRequestService>('requestService');
export interface IRequestService {
readonly _serviceBrand: undefined;
request(options: IRequestOptions, token: CancellationToken): Promise<IRequestContext>;
resolveProxy(url: string): Promise<string | undefined>;
}
export function isSuccess(context: IRequestContext): boolean {
return (context.res.statusCode && context.res.statusCode >= 200 && context.res.statusCode < 300) || context.res.statusCode === 1223;
}
function hasNoContent(context: IRequestContext): boolean {
return context.res.statusCode === 204;
}
export async function asText(context: IRequestContext): Promise<string | null> {
if (!isSuccess(context)) {
throw new Error('Server returned ' + context.res.statusCode);
}
if (hasNoContent(context)) {
return null;
}
const buffer = await streamToBuffer(context.stream);
return buffer.toString();
}
export async function asJson<T = {}>(context: IRequestContext): Promise<T | null> {
if (!isSuccess(context)) {
throw new Error('Server returned ' + context.res.statusCode);
}
if (hasNoContent(context)) {
return null;
}
const buffer = await streamToBuffer(context.stream);
const str = buffer.toString();
try {
return JSON.parse(str);
} catch (err) {
err.message += ':\n' + str;
throw err;
}
}
export interface IHTTPConfiguration {
http?: {
proxy?: string;
proxyStrictSSL?: boolean;
proxyAuthorization?: string;
};
}
Registry.as<IConfigurationRegistry>(Extensions.Configuration)
.registerConfiguration({
id: 'http',
order: 15,
title: localize('httpConfigurationTitle', "HTTP"),
type: 'object',
properties: {
'http.proxy': {
type: 'string',
pattern: '^https?://([^:]*(:[^@]*)?@)?([^:]+|\\[[:0-9a-fA-F]+\\])(:\\d+)?/?$|^$',
markdownDescription: localize('proxy', "The proxy setting to use. If not set, will be inherited from the `http_proxy` and `https_proxy` environment variables.")
},
'http.proxyStrictSSL': {
type: 'boolean',
default: true,
description: localize('strictSSL', "Controls whether the proxy server certificate should be verified against the list of supplied CAs.")
},
'http.proxyAuthorization': {
type: ['null', 'string'],
default: null,
markdownDescription: localize('proxyAuthorization', "The value to send as the `Proxy-Authorization` header for every network request.")
},
'http.proxySupport': {
type: 'string',
enum: ['off', 'on', 'override'],
enumDescriptions: [
localize('proxySupportOff', "Disable proxy support for extensions."),
localize('proxySupportOn', "Enable proxy support for extensions."),
localize('proxySupportOverride', "Enable proxy support for extensions, override request options."),
],
default: 'override',
description: localize('proxySupport', "Use the proxy support for extensions.")
},
'http.systemCertificates': {
type: 'boolean',
default: true,
description: localize('systemCertificates', "Controls whether CA certificates should be loaded from the OS. (On Windows and macOS a reload of the window is required after turning this off.)")
}
}
});

View File

@@ -0,0 +1,56 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
import { Event } from 'vs/base/common/event';
import { IRequestService } from 'vs/platform/request/common/request';
import { IRequestOptions, IRequestContext, IHeaders } from 'vs/base/parts/request/common/request';
import { CancellationToken } from 'vs/base/common/cancellation';
import { VSBuffer, bufferToStream, streamToBuffer } from 'vs/base/common/buffer';
type RequestResponse = [
{
headers: IHeaders;
statusCode?: number;
},
VSBuffer
];
export class RequestChannel implements IServerChannel {
constructor(private readonly service: IRequestService) { }
listen(context: any, event: string): Event<any> {
throw new Error('Invalid listen');
}
call(context: any, command: string, args?: any): Promise<any> {
switch (command) {
case 'request': return this.service.request(args[0], CancellationToken.None)
.then(async ({ res, stream }) => {
const buffer = await streamToBuffer(stream);
return <RequestResponse>[{ statusCode: res.statusCode, headers: res.headers }, buffer];
});
}
throw new Error('Invalid call');
}
}
export class RequestChannelClient {
declare readonly _serviceBrand: undefined;
constructor(private readonly channel: IChannel) { }
async request(options: IRequestOptions, token: CancellationToken): Promise<IRequestContext> {
return RequestChannelClient.request(this.channel, options, token);
}
static async request(channel: IChannel, options: IRequestOptions, token: CancellationToken): Promise<IRequestContext> {
const [res, buffer] = await channel.call<RequestResponse>('request', [options]);
return { res, stream: bufferToStream(buffer) };
}
}

View File

@@ -0,0 +1,20 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request';
import { RequestService as NodeRequestService, IRawRequestFunction } from 'vs/platform/request/node/requestService';
import { net } from 'electron';
import { CancellationToken } from 'vs/base/common/cancellation';
function getRawRequest(options: IRequestOptions): IRawRequestFunction {
return net.request as any as IRawRequestFunction;
}
export class RequestMainService extends NodeRequestService {
request(options: IRequestOptions, token: CancellationToken): Promise<IRequestContext> {
return super.request({ ...(options || {}), getRawRequest }, token);
}
}

View File

@@ -0,0 +1,50 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Url, parse as parseUrl } from 'url';
import { isBoolean } from 'vs/base/common/types';
export type Agent = any;
function getSystemProxyURI(requestURL: Url): string | null {
if (requestURL.protocol === 'http:') {
return process.env.HTTP_PROXY || process.env.http_proxy || null;
} else if (requestURL.protocol === 'https:') {
return process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy || null;
}
return null;
}
export interface IOptions {
proxyUrl?: string;
strictSSL?: boolean;
}
export async function getProxyAgent(rawRequestURL: string, options: IOptions = {}): Promise<Agent> {
const requestURL = parseUrl(rawRequestURL);
const proxyURL = options.proxyUrl || getSystemProxyURI(requestURL);
if (!proxyURL) {
return null;
}
const proxyEndpoint = parseUrl(proxyURL);
if (!/^https?:$/.test(proxyEndpoint.protocol || '')) {
return null;
}
const opts = {
host: proxyEndpoint.hostname || '',
port: proxyEndpoint.port || (proxyEndpoint.protocol === 'https' ? '443' : '80'),
auth: proxyEndpoint.auth,
rejectUnauthorized: isBoolean(options.strictSSL) ? options.strictSSL : true,
};
return requestURL.protocol === 'http:'
? new (await import('http-proxy-agent'))(opts as any as Url)
: new (await import('https-proxy-agent'))(opts);
}

View File

@@ -0,0 +1,152 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as https from 'https';
import * as http from 'http';
import * as streams from 'vs/base/common/stream';
import { createGunzip } from 'zlib';
import { parse as parseUrl } from 'url';
import { Disposable } from 'vs/base/common/lifecycle';
import { isBoolean, isNumber } from 'vs/base/common/types';
import { canceled } from 'vs/base/common/errors';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IRequestService, IHTTPConfiguration } from 'vs/platform/request/common/request';
import { IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request';
import { getProxyAgent, Agent } from 'vs/platform/request/node/proxy';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILogService } from 'vs/platform/log/common/log';
import { streamToBufferReadableStream } from 'vs/base/common/buffer';
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;
}
/**
* This service exposes the `request` API, while using the global
* or configured proxy settings.
*/
export class RequestService extends Disposable implements IRequestService {
declare readonly _serviceBrand: undefined;
private proxyUrl?: string;
private strictSSL: boolean | undefined;
private authorization?: string;
constructor(
@IConfigurationService configurationService: IConfigurationService,
@ILogService private readonly logService: ILogService
) {
super();
this.configure(configurationService.getValue<IHTTPConfiguration>());
this._register(configurationService.onDidChangeConfiguration(() => this.configure(configurationService.getValue()), this));
}
private configure(config: IHTTPConfiguration) {
this.proxyUrl = config.http && config.http.proxy;
this.strictSSL = !!(config.http && config.http.proxyStrictSSL);
this.authorization = config.http && config.http.proxyAuthorization;
}
async request(options: NodeRequestOptions, token: CancellationToken): Promise<IRequestContext> {
this.logService.trace('RequestService#request', options.url);
const { proxyUrl, strictSSL } = this;
const agent = options.agent ? options.agent : await getProxyAgent(options.url || '', { proxyUrl, strictSSL });
options.agent = agent;
options.strictSSL = strictSSL;
if (this.authorization) {
options.headers = {
...(options.headers || {}),
'Proxy-Authorization': this.authorization
};
}
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;
const endpoint = parseUrl(options.url!);
const rawRequest = options.getRawRequest
? options.getRawRequest(options)
: await this.getNodeRequest(options);
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']) {
this._request({
...options,
url: res.headers['location'],
followRedirects: followRedirects - 1
}, token).then(c, e);
} else {
let stream: streams.ReadableStreamEvents<Uint8Array> = res;
if (res.headers['content-encoding'] === 'gzip') {
stream = res.pipe(createGunzip());
}
c({ res, stream: streamToBufferReadableStream(stream) } as IRequestContext);
}
});
req.on('error', e);
if (options.timeout) {
req.setTimeout(options.timeout);
}
if (options.data) {
if (typeof options.data === 'string') {
req.write(options.data);
}
}
req.end();
token.onCancellationRequested(() => {
req.abort();
e(canceled());
});
});
}
async resolveProxy(url: string): Promise<string | undefined> {
return undefined; // currently not implemented in node
}
}