提交 fe3e0cdc 编写于 作者: S Sandeep Somavarapu

Support request service in web

上级 d57ddfa0
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { isBoolean, isNumber } from 'vs/base/common/types';
import * as https from 'https';
import * as http from 'http';
import { Stream } from 'stream';
import { parse as parseUrl } from 'url';
import { createWriteStream } from 'fs';
import { assign } from 'vs/base/common/objects';
import { createGunzip } from 'zlib';
import { CancellationToken } from 'vs/base/common/cancellation';
import { canceled } from 'vs/base/common/errors';
export type Agent = any;
export interface IRawRequestFunction {
(options: http.RequestOptions, callback?: (res: http.IncomingMessage) => void): http.ClientRequest;
}
export interface IRequestOptions {
type?: string;
url?: string;
user?: string;
password?: string;
headers?: any;
timeout?: number;
data?: string | Stream;
agent?: Agent;
followRedirects?: number;
strictSSL?: boolean;
getRawRequest?(options: IRequestOptions): IRawRequestFunction;
}
export interface IRequestContext {
// req: http.ClientRequest;
// res: http.ClientResponse;
res: {
headers: { [n: string]: string };
statusCode?: number;
};
stream: Stream;
}
export interface IRequestFunction {
(options: IRequestOptions, token: CancellationToken): Promise<IRequestContext>;
}
async function getNodeRequest(options: IRequestOptions): Promise<IRawRequestFunction> {
const endpoint = parseUrl(options.url!);
const module = endpoint.protocol === 'https:' ? await import('https') : await import('http');
return module.request;
}
export function request(options: IRequestOptions, token: CancellationToken): Promise<IRequestContext> {
let req: http.ClientRequest;
const rawRequestPromise = options.getRawRequest
? Promise.resolve(options.getRawRequest(options))
: Promise.resolve(getNodeRequest(options));
return rawRequestPromise.then(rawRequest => {
return new Promise<IRequestContext>((c, e) => {
const endpoint = parseUrl(options.url!);
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']) {
request(assign({}, options, {
url: res.headers['location'],
followRedirects: followRedirects - 1
}), token).then(c, e);
} else {
let stream: Stream = res;
if (res.headers['content-encoding'] === 'gzip') {
stream = stream.pipe(createGunzip());
}
c({ res, 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);
} else {
options.data.pipe(req);
return;
}
}
req.end();
token.onCancellationRequested(() => {
req.abort();
e(canceled());
});
});
});
}
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 function download(filePath: string, context: IRequestContext): Promise<void> {
return new Promise<void>((c, e) => {
const out = createWriteStream(filePath);
out.once('finish', () => c(undefined));
context.stream.once('error', e);
context.stream.pipe(out);
});
}
export function asText(context: IRequestContext): Promise<string | null> {
return new Promise((c, e) => {
if (!isSuccess(context)) {
return e('Server returned ' + context.res.statusCode);
}
if (hasNoContent(context)) {
return c(null);
}
const buffer: string[] = [];
context.stream.on('data', (d: string) => buffer.push(d));
context.stream.on('end', () => c(buffer.join('')));
context.stream.on('error', e);
});
}
export function asJson<T = {}>(context: IRequestContext): Promise<T | null> {
return new Promise((c, e) => {
if (!isSuccess(context)) {
return e('Server returned ' + context.res.statusCode);
}
if (hasNoContent(context)) {
return c(null);
}
const buffer: string[] = [];
context.stream.on('data', (d: string) => buffer.push(d));
context.stream.on('end', () => {
try {
c(JSON.parse(buffer.join('')));
} catch (err) {
e(err);
}
});
context.stream.on('error', e);
});
}
......@@ -19,8 +19,8 @@ import { ExtensionManagementService } from 'vs/platform/extensionManagement/node
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
import { IRequestService } from 'vs/platform/request/node/request';
import { RequestService } from 'vs/platform/request/electron-browser/requestService';
import { IRequestService } from 'vs/platform/request/common/request';
import { RequestService } from 'vs/platform/request/browser/requestService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { combinedAppender, NullTelemetryService, ITelemetryAppender, NullAppender, LogAppender } from 'vs/platform/telemetry/common/telemetryUtils';
import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties';
......@@ -51,6 +51,10 @@ import { SpdLogService } from 'vs/platform/log/node/spdlogService';
import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService';
import { IDiagnosticsService } from 'vs/platform/diagnostics/common/diagnosticsService';
import { DiagnosticsChannel } from 'vs/platform/diagnostics/node/diagnosticsIpc';
import { FileService } from 'vs/platform/files/common/fileService';
import { IFileService } from 'vs/platform/files/common/files';
import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider';
import { Schemas } from 'vs/base/common/network';
export interface ISharedProcessConfiguration {
readonly machineId: string;
......@@ -122,6 +126,15 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
const dialogChannel = server.getChannel('dialog', activeWindowRouter);
services.set(IDialogService, new DialogChannelClient(dialogChannel));
// Files
const fileService = new FileService(logService);
services.set(IFileService, fileService);
disposables.add(fileService);
const diskFileSystemProvider = new DiskFileSystemProvider(logService);
disposables.add(diskFileSystemProvider);
fileService.registerProvider(Schemas.file, diskFileSystemProvider);
const instantiationService = new InstantiationService(services);
let telemetryService: ITelemetryService;
......
......@@ -83,6 +83,9 @@ import { statSync } from 'fs';
import { ISignService } from 'vs/platform/sign/common/sign';
import { IDiagnosticsService } from 'vs/platform/diagnostics/common/diagnosticsService';
import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsIpc';
import { FileService } from 'vs/platform/files/common/fileService';
import { IFileService } from 'vs/platform/files/common/files';
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
export class CodeApplication extends Disposable {
......@@ -418,6 +421,13 @@ export class CodeApplication extends Disposable {
private async createServices(machineId: string, sharedProcess: SharedProcess, sharedProcessClient: Promise<Client<string>>): Promise<IInstantiationService> {
const services = new ServiceCollection();
// Files
const fileService = this._register(new FileService(this.logService));
services.set(IFileService, fileService);
const diskFileSystemProvider = this._register(new DiskFileSystemProvider(this.logService));
fileService.registerProvider(Schemas.file, diskFileSystemProvider);
switch (process.platform) {
case 'win32':
services.set(IUpdateService, new SyncDescriptor(Win32UpdateService));
......
......@@ -26,7 +26,7 @@ import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
import { IRequestService } from 'vs/platform/request/node/request';
import { IRequestService } from 'vs/platform/request/common/request';
import { RequestService } from 'vs/platform/request/electron-main/requestService';
import * as fs from 'fs';
import { CodeApplication } from 'vs/code/electron-main/app';
......
......@@ -22,7 +22,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { combinedAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService';
import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties';
import { IRequestService } from 'vs/platform/request/node/request';
import { IRequestService } from 'vs/platform/request/common/request';
import { RequestService } from 'vs/platform/request/node/requestService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
......
......@@ -7,19 +7,20 @@ import { IDownloadService } from 'vs/platform/download/common/download';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { copy } from 'vs/base/node/pfs';
import { IRequestService } from 'vs/platform/request/node/request';
import { asText, download } from 'vs/base/node/request';
import { IRequestService, asText } from 'vs/platform/request/common/request';
import { CancellationToken } from 'vs/base/common/cancellation';
import { join } from 'vs/base/common/path';
import { tmpdir } from 'os';
import { generateUuid } from 'vs/base/common/uuid';
import { IFileService } from 'vs/platform/files/common/files';
export class DownloadService implements IDownloadService {
_serviceBrand: any;
constructor(
@IRequestService private readonly requestService: IRequestService
@IRequestService private readonly requestService: IRequestService,
@IFileService private readonly fileService: IFileService
) { }
download(uri: URI, target: string = join(tmpdir(), generateUuid()), cancellationToken: CancellationToken = CancellationToken.None): Promise<string> {
......@@ -30,7 +31,7 @@ export class DownloadService implements IDownloadService {
return this.requestService.request(options, cancellationToken)
.then(context => {
if (context.res.statusCode === 200) {
return download(target, context).then(() => target);
return this.fileService.writeFile(URI.file(target), context.stream).then(() => target);
}
return asText(context)
.then(message => Promise.reject(new Error(`Expected 200, got back ${context.res.statusCode} instead.\n\n${message}`)));
......
......@@ -9,10 +9,9 @@ import { getErrorMessage, isPromiseCanceledError, canceled } from 'vs/base/commo
import { StatisticType, IGalleryExtension, IExtensionGalleryService, IGalleryExtensionAsset, IQueryOptions, SortBy, SortOrder, IExtensionIdentifier, IReportedExtension, InstallOperation, ITranslation, IGalleryExtensionVersion, IGalleryExtensionAssets, isIExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement';
import { getGalleryExtensionId, getGalleryExtensionTelemetryData, adoptToGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { assign, getOrDefault } from 'vs/base/common/objects';
import { IRequestService } from 'vs/platform/request/node/request';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IPager } from 'vs/base/common/paging';
import { IRequestOptions, IRequestContext, download, asJson, asText } from 'vs/base/node/request';
import { IRequestService, IRequestOptions, IRequestContext, asJson, asText } from 'vs/platform/request/common/request';
import pkg from 'vs/platform/product/node/package';
import product from 'vs/platform/product/node/product';
import { isEngineValid } from 'vs/platform/extensions/node/extensionValidator';
......@@ -23,6 +22,8 @@ import { values } from 'vs/base/common/map';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ILogService } from 'vs/platform/log/common/log';
import { IExtensionManifest } from 'vs/platform/extensions/common/extensions';
import { IFileService } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
interface IRawGalleryExtensionFile {
assetType: string;
......@@ -336,7 +337,8 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
@IRequestService private readonly requestService: IRequestService,
@ILogService private readonly logService: ILogService,
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@ITelemetryService private readonly telemetryService: ITelemetryService
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IFileService private readonly fileService: IFileService,
) {
const config = product.extensionsGallery;
this.extensionsGalleryUrl = config && config.serviceUrl;
......@@ -555,7 +557,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
} : extension.assets.download;
return this.getAsset(downloadAsset)
.then(context => download(zipPath, context))
.then(context => this.fileService.writeFile(URI.file(zipPath), context.stream))
.then(() => log(new Date().getTime() - startTime))
.then(() => zipPath);
}
......
......@@ -105,7 +105,7 @@ export interface IFileService {
/**
* Updates the content replacing its previous value.
*/
writeFile(resource: URI, bufferOrReadable: VSBuffer | VSBufferReadable, options?: IWriteFileOptions): Promise<IFileStatWithMetadata>;
writeFile(resource: URI, bufferOrReadable: VSBuffer | VSBufferReadable | VSBufferReadableStream, options?: IWriteFileOptions): Promise<IFileStatWithMetadata>;
/**
* Moves the file/folder to a new path identified by the resource.
......
......@@ -9,14 +9,9 @@ import { FileDeleteOptions, FileSystemProviderCapabilities } from 'vs/platform/f
import { isWindows } from 'vs/base/common/platform';
import { localize } from 'vs/nls';
import { basename } from 'vs/base/common/path';
import { ILogService } from 'vs/platform/log/common/log';
export class DiskFileSystemProvider extends NodeDiskFileSystemProvider {
constructor(logService: ILogService) {
super(logService);
}
get capabilities(): FileSystemProviderCapabilities {
if (!this._capabilities) {
this._capabilities = super.capabilities | FileSystemProviderCapabilities.Trash;
......
/*---------------------------------------------------------------------------------------------
* 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/platform/request/common/request';
import { CancellationToken } from 'vs/base/common/cancellation';
import { canceled } from 'vs/base/common/errors';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILogService } from 'vs/platform/log/common/log';
import { assign } from 'vs/base/common/objects';
import { VSBuffer, bufferToStream } from 'vs/base/common/buffer';
/**
* This service exposes the `request` API, while using the global
* or configured proxy settings.
*/
export class RequestService {
_serviceBrand: any;
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);
const authorization = this.configurationService.getValue<string>('http.proxyAuthorization');
if (authorization) {
options.headers = assign(options.headers || {}, { 'Proxy-Authorization': authorization });
}
const xhr = new XMLHttpRequest();
return new Promise<IRequestContext>((resolve, reject) => {
xhr.open(options.type || 'GET', options.url || '', true, options.user, options.password);
this.setRequestHeaders(xhr, options);
xhr.responseType = 'arraybuffer';
xhr.onerror = e => reject(new Error(xhr.statusText && ('XHR failed: ' + xhr.statusText)));
xhr.onload = (e) => {
resolve({
res: {
statusCode: xhr.status,
headers: this.getResponseHeaders(xhr)
},
stream: bufferToStream(VSBuffer.wrap(new Uint8Array(xhr.response)))
});
};
xhr.ontimeout = e => reject(new Error(`XHR timeout: ${options.timeout}ms`));
if (options.timeout) {
xhr.timeout = options.timeout;
}
// TODO: remove any
xhr.send(options.data as any);
// cancel
token.onCancellationRequested(() => {
xhr.abort();
reject(canceled());
});
});
}
private setRequestHeaders(xhr: XMLHttpRequest, options: IRequestOptions): void {
if (options.headers) {
outer: for (let k in options.headers) {
switch (k) {
case 'User-Agent':
case 'Accept-Encoding':
case 'Content-Length':
// unsafe headers
continue outer;
}
xhr.setRequestHeader(k, options.headers[k]);
}
}
}
private getResponseHeaders(xhr: XMLHttpRequest): { [name: string]: string } {
const headers: { [name: string]: string } = Object.create(null);
for (const line of xhr.getAllResponseHeaders().split(/\r\n|\n|\r/g)) {
if (line) {
const idx = line.indexOf(':');
headers[line.substr(0, idx).trim().toLowerCase()] = line.substr(idx + 1).trim();
}
}
return headers;
}
}
\ No newline at end of file
......@@ -4,20 +4,72 @@
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { CancellationToken } from 'vs/base/common/cancellation';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IRequestOptions, IRequestContext } from 'vs/base/node/request';
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
import { Registry } from 'vs/platform/registry/common/platform';
import { CancellationToken } from 'vs/base/common/cancellation';
import { VSBufferReadableStream, streamToBuffer } from 'vs/base/common/buffer';
export const IRequestService = createDecorator<IRequestService>('requestService');
export interface IRequestOptions {
type?: string;
url?: string;
user?: string;
password?: string;
headers?: any;
timeout?: number;
data?: string;
followRedirects?: number;
}
export interface IRequestContext {
// req: http.ClientRequest;
// res: http.ClientResponse;
res: {
headers: { [n: string]: string };
statusCode?: number;
};
stream: VSBufferReadableStream;
}
export interface IRequestService {
_serviceBrand: any;
request(options: IRequestOptions, token: CancellationToken): Promise<IRequestContext>;
}
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);
return JSON.parse(buffer.toString());
}
export interface IHTTPConfiguration {
http?: {
proxy?: string;
......
/*---------------------------------------------------------------------------------------------
* 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, IRequestFunction } from 'vs/base/node/request';
import { Readable } from 'stream';
import { RequestService as NodeRequestService } from 'vs/platform/request/node/requestService';
import { CancellationToken } from 'vs/base/common/cancellation';
import { canceled } from 'vs/base/common/errors';
/**
* This service exposes the `request` API, while using the global
* or configured proxy settings.
*/
export class RequestService extends NodeRequestService {
request(options: IRequestOptions, token: CancellationToken): Promise<IRequestContext> {
return super.request(options, token, xhrRequest);
}
}
export const xhrRequest: IRequestFunction = (options: IRequestOptions, token: CancellationToken): Promise<IRequestContext> => {
const xhr = new XMLHttpRequest();
return new Promise<IRequestContext>((resolve, reject) => {
xhr.open(options.type || 'GET', options.url || '', true, options.user, options.password);
setRequestHeaders(xhr, options);
xhr.responseType = 'arraybuffer';
xhr.onerror = e => reject(new Error(xhr.statusText && ('XHR failed: ' + xhr.statusText)));
xhr.onload = (e) => {
resolve({
res: {
statusCode: xhr.status,
headers: getResponseHeaders(xhr)
},
stream: new class ArrayBufferStream extends Readable {
private _buffer: Buffer;
private _offset: number;
private _length: number;
constructor(arraybuffer: ArrayBuffer) {
super();
this._buffer = Buffer.from(new Uint8Array(arraybuffer));
this._offset = 0;
this._length = this._buffer.length;
}
_read(size: number) {
if (this._offset < this._length) {
this.push(this._buffer.slice(this._offset, (this._offset + size)));
this._offset += size;
} else {
this.push(null);
}
}
}(xhr.response)
});
};
xhr.ontimeout = e => reject(new Error(`XHR timeout: ${options.timeout}ms`));
if (options.timeout) {
xhr.timeout = options.timeout;
}
// TODO: remove any
xhr.send(options.data as any);
// cancel
token.onCancellationRequested(() => {
xhr.abort();
reject(canceled());
});
});
};
function setRequestHeaders(xhr: XMLHttpRequest, options: IRequestOptions): void {
if (options.headers) {
outer: for (let k in options.headers) {
switch (k) {
case 'User-Agent':
case 'Accept-Encoding':
case 'Content-Length':
// unsafe headers
continue outer;
}
xhr.setRequestHeader(k, options.headers[k]);
}
}
}
function getResponseHeaders(xhr: XMLHttpRequest): { [name: string]: string } {
const headers: { [name: string]: string } = Object.create(null);
for (const line of xhr.getAllResponseHeaders().split(/\r\n|\n|\r/g)) {
if (line) {
const idx = line.indexOf(':');
headers[line.substr(0, idx).trim().toLowerCase()] = line.substr(idx + 1).trim();
}
}
return headers;
}
......@@ -3,8 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IRequestOptions, IRequestContext, request, IRawRequestFunction } from 'vs/base/node/request';
import { RequestService as NodeRequestService } from 'vs/platform/request/node/requestService';
import { IRequestOptions, IRequestContext } from 'vs/platform/request/common/request';
import { RequestService as NodeRequestService, IRawRequestFunction } from 'vs/platform/request/node/requestService';
import { assign } from 'vs/base/common/objects';
import { net } from 'electron';
import { CancellationToken } from 'vs/base/common/cancellation';
......@@ -16,6 +16,6 @@ function getRawRequest(options: IRequestOptions): IRawRequestFunction {
export class RequestService extends NodeRequestService {
request(options: IRequestOptions, token: CancellationToken): Promise<IRequestContext> {
return super.request(options, token, options => request(assign({}, options || {}, { getRawRequest }), token));
return super.request(assign({}, options || {}, { getRawRequest }), token);
}
}
......@@ -5,7 +5,8 @@
import { Url, parse as parseUrl } from 'url';
import { isBoolean } from 'vs/base/common/types';
import { Agent } from './request';
export type Agent = any;
function getSystemProxyURI(requestURL: Url): string | null {
if (requestURL.protocol === 'http:') {
......
......@@ -3,14 +3,31 @@
* 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 { Stream } from 'stream';
import { createGunzip } from 'zlib';
import { parse as parseUrl } from 'url';
import { Disposable } from 'vs/base/common/lifecycle';
import { assign } from 'vs/base/common/objects';
import { IRequestOptions, IRequestContext, IRequestFunction, request } from 'vs/base/node/request';
import { getProxyAgent } from 'vs/base/node/proxy';
import { IRequestService, IHTTPConfiguration } from 'vs/platform/request/node/request';
import { isBoolean, isNumber } from 'vs/base/common/types';
import { canceled } from 'vs/base/common/errors';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IRequestOptions, IRequestContext, IRequestService, IHTTPConfiguration } from 'vs/platform/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 { CancellationToken } from 'vs/base/common/cancellation';
import { VSBuffer, VSBufferWriteableStream, writeableBufferStream } 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
......@@ -20,9 +37,9 @@ export class RequestService extends Disposable implements IRequestService {
_serviceBrand: any;
private proxyUrl?: string;
private strictSSL: boolean;
private authorization?: string;
protected proxyUrl?: string;
protected strictSSL: boolean;
protected authorization?: string;
constructor(
@IConfigurationService configurationService: IConfigurationService,
......@@ -39,21 +56,96 @@ export class RequestService extends Disposable implements IRequestService {
this.authorization = config.http && config.http.proxyAuthorization;
}
request(options: IRequestOptions, token: CancellationToken, requestFn: IRequestFunction = request): Promise<IRequestContext> {
async request(options: NodeRequestOptions, token: CancellationToken): Promise<IRequestContext> {
this.logService.trace('RequestService#request', options.url);
const { proxyUrl, strictSSL } = this;
const agentPromise = options.agent ? Promise.resolve(options.agent) : Promise.resolve(getProxyAgent(options.url || '', { proxyUrl, strictSSL }));
const agent = options.agent ? options.agent : await getProxyAgent(options.url || '', { proxyUrl, strictSSL });
options.agent = agent;
options.strictSSL = strictSSL;
if (this.authorization) {
options.headers = assign(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;
return agentPromise.then(agent => {
options.agent = agent;
options.strictSSL = strictSSL;
const endpoint = parseUrl(options.url!);
const rawRequest = options.getRawRequest
? options.getRawRequest(options)
: await this.getNodeRequest(options);
if (this.authorization) {
options.headers = assign(options.headers || {}, { 'Proxy-Authorization': this.authorization });
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(assign({}, options, {
url: res.headers['location'],
followRedirects: followRedirects - 1
}), token).then(c, e);
} else {
let _stream: Stream = res;
if (res.headers['content-encoding'] === 'gzip') {
_stream = _stream.pipe(createGunzip());
}
const stream: VSBufferWriteableStream = writeableBufferStream();
_stream.on('data', (d: string) => stream.write(VSBuffer.fromString(d)));
_stream.on('error', e => stream.end(e));
_stream.on('end', e => stream.end());
c({ res, 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);
}
}
return requestFn(options, token);
req.end();
token.onCancellationRequested(() => {
req.abort();
e(canceled());
});
});
}
}
}
\ No newline at end of file
......@@ -11,7 +11,7 @@ import product from 'vs/platform/product/node/product';
import { IUpdateService, State, StateType, AvailableForDownload, UpdateType } from 'vs/platform/update/common/update';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ILogService } from 'vs/platform/log/common/log';
import { IRequestService } from 'vs/platform/request/node/request';
import { IRequestService } from 'vs/platform/request/common/request';
import { CancellationToken } from 'vs/base/common/cancellation';
export function createUpdateURL(platform: string, quality: string): string {
......
......@@ -14,7 +14,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ILogService } from 'vs/platform/log/common/log';
import { AbstractUpdateService, createUpdateURL } from 'vs/platform/update/electron-main/abstractUpdateService';
import { IRequestService } from 'vs/platform/request/node/request';
import { IRequestService } from 'vs/platform/request/common/request';
export class DarwinUpdateService extends AbstractUpdateService {
......
......@@ -6,13 +6,12 @@
import product from 'vs/platform/product/node/product';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { IRequestService } from 'vs/platform/request/node/request';
import { State, IUpdate, AvailableForDownload, UpdateType } from 'vs/platform/update/common/update';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ILogService } from 'vs/platform/log/common/log';
import { createUpdateURL, AbstractUpdateService } from 'vs/platform/update/electron-main/abstractUpdateService';
import { asJson } from 'vs/base/node/request';
import { IRequestService, asJson } from 'vs/platform/request/common/request';
import { shell } from 'electron';
import { CancellationToken } from 'vs/base/common/cancellation';
......
......@@ -9,20 +9,21 @@ import * as pfs from 'vs/base/node/pfs';
import { memoize } from 'vs/base/common/decorators';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { IRequestService } from 'vs/platform/request/node/request';
import product from 'vs/platform/product/node/product';
import { State, IUpdate, StateType, AvailableForDownload, UpdateType } from 'vs/platform/update/common/update';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ILogService } from 'vs/platform/log/common/log';
import { createUpdateURL, AbstractUpdateService } from 'vs/platform/update/electron-main/abstractUpdateService';
import { download, asJson } from 'vs/base/node/request';
import { IRequestService, asJson } from 'vs/platform/request/common/request';
import { checksum } from 'vs/base/node/crypto';
import { tmpdir } from 'os';
import { spawn } from 'child_process';
import { shell } from 'electron';
import { CancellationToken } from 'vs/base/common/cancellation';
import { timeout } from 'vs/base/common/async';
import { IFileService } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
async function pollUntil(fn: () => boolean, millis = 1000): Promise<void> {
while (!fn()) {
......@@ -64,7 +65,8 @@ export class Win32UpdateService extends AbstractUpdateService {
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IEnvironmentService environmentService: IEnvironmentService,
@IRequestService requestService: IRequestService,
@ILogService logService: ILogService
@ILogService logService: ILogService,
@IFileService private readonly fileService: IFileService
) {
super(lifecycleService, configurationService, environmentService, requestService, logService);
......@@ -142,7 +144,7 @@ export class Win32UpdateService extends AbstractUpdateService {
const downloadPath = `${updatePackagePath}.tmp`;
return this.requestService.request({ url }, CancellationToken.None)
.then(context => download(downloadPath, context))
.then(context => this.fileService.writeFile(URI.file(downloadPath), context.stream))
.then(hash ? () => checksum(downloadPath, update.hash) : () => undefined)
.then(() => pfs.rename(downloadPath, updatePackagePath))
.then(() => updatePackagePath);
......
......@@ -455,27 +455,6 @@ export class SimpleMultiExtensionsManagementService implements IExtensionManagem
//#endregion
//#region Request
export const IRequestService = createDecorator<IRequestService>('requestService');
export interface IRequestService {
_serviceBrand: any;
request(options: any, token: CancellationToken): Promise<object>;
}
export class SimpleRequestService implements IRequestService {
_serviceBrand: any;
request(options: any, token: CancellationToken): Promise<object> {
return Promise.resolve(Object.create(null));
}
}
//#endregion
//#region Telemetry
export class SimpleTelemetryService implements ITelemetryService {
......
......@@ -12,11 +12,10 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IRequestService } from 'vs/platform/request/node/request';
import { language } from 'vs/base/common/platform';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { match } from 'vs/base/common/glob';
import { asJson } from 'vs/base/node/request';
import { IRequestService, asJson } from 'vs/platform/request/common/request';
import { Emitter, Event } from 'vs/base/common/event';
import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/common/textfiles';
import { WorkspaceStats } from 'vs/workbench/contrib/stats/electron-browser/workspaceStats';
......
......@@ -32,8 +32,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { guessMimeTypes, MIME_UNKNOWN } from 'vs/base/common/mime';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { getHashedRemotesFromUri } from 'vs/workbench/contrib/stats/electron-browser/workspaceStats';
import { IRequestService } from 'vs/platform/request/node/request';
import { asJson } from 'vs/base/node/request';
import { IRequestService, asJson } from 'vs/platform/request/common/request';
import { isNumber } from 'vs/base/common/types';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { INotificationService } from 'vs/platform/notification/common/notification';
......
......@@ -11,9 +11,8 @@ import { URI } from 'vs/base/common/uri';
import { IExtensionHostProfile } from 'vs/workbench/services/extensions/common/extensions';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { localize } from 'vs/nls';
import { IRequestService } from 'vs/platform/request/node/request';
import { CancellationToken } from 'vs/base/common/cancellation';
import { asText } from 'vs/base/node/request';
import { IRequestService, asText } from 'vs/platform/request/common/request';
import { join } from 'vs/base/common/path';
import { onUnexpectedError } from 'vs/base/common/errors';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
......
......@@ -8,8 +8,7 @@ import { top } from 'vs/base/common/arrays';
import * as strings from 'vs/base/common/strings';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IRequestService } from 'vs/platform/request/node/request';
import { asJson } from 'vs/base/node/request';
import { IRequestService, asJson } from 'vs/platform/request/common/request';
import { IExtensionManagementService, ILocalExtension, IExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ILogService } from 'vs/platform/log/common/log';
import { IPreferencesSearchService, ISearchProvider, IWorkbenchSettingsConfiguration } from 'vs/workbench/contrib/preferences/common/preferences';
......
......@@ -7,7 +7,6 @@ import { onUnexpectedError } from 'vs/base/common/errors';
import * as marked from 'vs/base/common/marked/marked';
import { OS } from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
import { asText } from 'vs/base/node/request';
import { TokenizationRegistry, ITokenizationSupport } from 'vs/editor/common/modes';
import { generateTokensCSSForColorMap } from 'vs/editor/common/modes/supports/tokenization';
import { tokenizeToString } from 'vs/editor/common/modes/textToHtmlTokenizer';
......@@ -17,7 +16,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IRequestService } from 'vs/platform/request/node/request';
import { IRequestService, asText } from 'vs/platform/request/common/request';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { addGAParameters } from 'vs/platform/telemetry/node/telemetryNodeUtils';
import { IWebviewEditorService } from 'vs/workbench/contrib/webview/browser/webviewEditorService';
......
......@@ -12,12 +12,15 @@ import { debugExceptionWidgetBackground } from 'vs/workbench/contrib/debug/brows
import { debugToolBarBackground } from 'vs/workbench/contrib/debug/browser/debugToolBar';
import { buttonBackground } from 'vs/workbench/contrib/welcome/page/browser/welcomePage';
import { embeddedEditorBackground } from 'vs/workbench/contrib/welcome/walkThrough/browser/walkThroughPart';
import { request, asText } from 'vs/base/node/request';
import { asText } from 'vs/platform/request/common/request';
import * as pfs from 'vs/base/node/pfs';
import * as path from 'vs/base/common/path';
import * as assert from 'assert';
import { getPathFromAmdModule } from 'vs/base/common/amd';
import { CancellationToken } from 'vs/base/common/cancellation';
import { RequestService } from 'vs/platform/request/node/requestService';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { NullLogService } from 'vs/platform/log/common/log';
interface ColorInfo {
......@@ -40,7 +43,7 @@ export const experimental: string[] = []; // 'settings.modifiedItemForeground',
suite('Color Registry', function () {
test('all colors documented', async function () {
const reqContext = await request({ url: 'https://raw.githubusercontent.com/Microsoft/vscode-docs/vnext/docs/getstarted/theme-color-reference.md' }, CancellationToken.None);
const reqContext = await new RequestService(new TestConfigurationService(), new NullLogService()).request({ url: 'https://raw.githubusercontent.com/Microsoft/vscode-docs/vnext/docs/getstarted/theme-color-reference.md' }, CancellationToken.None);
const content = (await asText(reqContext))!;
const expression = /\-\s*\`([\w\.]+)\`: (.*)/g;
......
......@@ -68,8 +68,8 @@ import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService';
import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
import { IRequestService } from 'vs/platform/request/node/request';
import { RequestService } from 'vs/platform/request/electron-browser/requestService';
import { IRequestService } from 'vs/platform/request/common/request';
import { RequestService } from 'vs/platform/request/browser/requestService';
import { LifecycleService } from 'vs/platform/lifecycle/electron-browser/lifecycleService';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { ILocalizationsService } from 'vs/platform/localizations/common/localizations';
......
......@@ -66,8 +66,8 @@ import { BrowserAccessibilityService } from 'vs/platform/accessibility/common/ac
// import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ContextViewService } from 'vs/platform/contextview/browser/contextViewService';
// import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService';
// import { IRequestService } from 'vs/platform/request/node/request';
// import { RequestService } from 'vs/platform/request/electron-browser/requestService';
import { IRequestService } from 'vs/platform/request/common/request';
import { RequestService } from 'vs/platform/request/browser/requestService';
import { BrowserLifecycleService } from 'vs/platform/lifecycle/browser/lifecycleService';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
......@@ -156,7 +156,7 @@ registerSingleton(ITextResourceConfigurationService, TextResourceConfigurationSe
registerSingleton(IAccessibilityService, BrowserAccessibilityService, true);
registerSingleton(IContextViewService, ContextViewService, true);
// registerSingleton(IExtensionGalleryService, ExtensionGalleryService, true);
// registerSingleton(IRequestService, RequestService, true);
registerSingleton(IRequestService, RequestService, true);
registerSingleton(ILifecycleService, BrowserLifecycleService);
// registerSingleton(ILocalizationsService, LocalizationsService);
// registerSingleton(ISharedProcessService, SharedProcessService, true);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册