diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index cd56b13c900..af44ce6abc5 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -43,9 +43,8 @@ import { LocalizationsChannel } from 'vs/platform/localizations/node/localizatio import { DialogChannelClient } from 'vs/platform/dialogs/node/dialogIpc'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { DownloadService } from 'vs/platform/download/node/downloadService'; import { IDownloadService } from 'vs/platform/download/common/download'; -import { DownloadServiceChannelClient } from 'vs/platform/download/node/downloadIpc'; -import { DefaultURITransformer } from 'vs/base/common/uriIpc'; export interface ISharedProcessConfiguration { readonly machineId: string; @@ -80,6 +79,7 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I services.set(ILogService, logService); services.set(IConfigurationService, new SyncDescriptor(ConfigurationService)); services.set(IRequestService, new SyncDescriptor(RequestService)); + services.set(IDownloadService, new SyncDescriptor(DownloadService)); const windowsChannel = server.getChannel('windows', { routeCall: mainRoute, routeEvent: mainRoute }); const windowsService = new WindowsChannelClient(windowsChannel); @@ -91,9 +91,6 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I const dialogChannel = server.getChannel('dialog', { routeCall: route, routeEvent: route }); services.set(IDialogService, new DialogChannelClient(dialogChannel)); - const downloadChannel = server.getChannel('download', { routeCall: route, routeEvent: route }); - services.set(IDownloadService, new DownloadServiceChannelClient(downloadChannel, DefaultURITransformer)); - const instantiationService = new InstantiationService(services); instantiationService.invokeFunction(accessor => { diff --git a/src/vs/platform/download/common/download.ts b/src/vs/platform/download/common/download.ts index 1ff68352bb5..e8c59d7c516 100644 --- a/src/vs/platform/download/common/download.ts +++ b/src/vs/platform/download/common/download.ts @@ -8,6 +8,7 @@ import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { TPromise } from 'vs/base/common/winjs.base'; +import { CancellationToken } from 'vs/base/common/cancellation'; export const IDownloadService = createDecorator('downloadService'); @@ -15,6 +16,6 @@ export interface IDownloadService { _serviceBrand: any; - download(location: URI, file: string): TPromise; + download(uri: URI, to: string, cancellationToken?: CancellationToken): TPromise; } \ No newline at end of file diff --git a/src/vs/platform/download/node/downloadIpc.ts b/src/vs/platform/download/node/downloadIpc.ts deleted file mode 100644 index 675a725b3b1..00000000000 --- a/src/vs/platform/download/node/downloadIpc.ts +++ /dev/null @@ -1,83 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -'use strict'; - -import { URI } from 'vs/base/common/uri'; -import * as path from 'path'; -import * as fs from 'fs'; -import { TPromise } from 'vs/base/common/winjs.base'; -import { IChannel } from 'vs/base/parts/ipc/node/ipc'; -import { Event, Emitter, buffer } from 'vs/base/common/event'; -import { IDownloadService } from 'vs/platform/download/common/download'; -import { mkdirp } from 'vs/base/node/pfs'; -import { IURITransformer } from 'vs/base/common/uriIpc'; - -export type UploadResponse = Buffer | string | undefined; - -export function upload(uri: URI): Event { - const stream = new Emitter(); - const readstream = fs.createReadStream(uri.fsPath); - readstream.on('data', data => stream.fire(data)); - readstream.on('error', error => stream.fire(error.toString())); - readstream.on('close', () => stream.fire()); - return stream.event; -} - -export interface IDownloadServiceChannel extends IChannel { - listen(event: 'upload', uri: URI): Event; - listen(event: string, arg?: any): Event; -} - -export class DownloadServiceChannel implements IDownloadServiceChannel { - - constructor() { } - - listen(event: string, arg?: any): Event { - switch (event) { - case 'upload': return buffer(upload(URI.revive(arg))); - } - return undefined; - } - - call(command: string, arg?: any): TPromise { - throw new Error('No calls'); - } -} - -export class DownloadServiceChannelClient implements IDownloadService { - - _serviceBrand: any; - - constructor(private channel: IDownloadServiceChannel, private uriTransformer: IURITransformer) { } - - download(from: URI, to: string): TPromise { - from = this.uriTransformer.transformOutgoing(from); - const dirName = path.dirname(to); - let out: fs.WriteStream; - return new TPromise((c, e) => { - return mkdirp(dirName) - .then(() => { - out = fs.createWriteStream(to); - out.once('close', () => c(null)); - out.once('error', e); - const uploadStream = this.channel.listen('upload', from); - const disposable = uploadStream((result: UploadResponse) => { - if (result === void 0) { - out.end(); - disposable.dispose(); - c(null); - } else if (Buffer.isBuffer(result)) { - out.write(result); - } else if (typeof result === 'string') { - out.close(); - disposable.dispose(); - e(result); - } - }); - }); - }); - } -} \ No newline at end of file diff --git a/src/vs/platform/download/node/downloadService.ts b/src/vs/platform/download/node/downloadService.ts new file mode 100644 index 00000000000..8834f7c7d29 --- /dev/null +++ b/src/vs/platform/download/node/downloadService.ts @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +'use strict'; + +import { IDownloadService } from 'vs/platform/download/common/download'; +import { TPromise } from 'vs/base/common/winjs.base'; +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 { CancellationToken } from 'vs/base/common/cancellation'; + +export class DownloadService implements IDownloadService { + + _serviceBrand: any; + + constructor( + @IRequestService private requestService: IRequestService + ) { } + + download(uri: URI, target: string, cancellationToken: CancellationToken = CancellationToken.None): TPromise { + if (uri.scheme === Schemas.file) { + return copy(uri.fsPath, target); + } + const options = { type: 'GET', url: uri.toString() }; + return this.requestService.request(options, cancellationToken) + .then(context => { + if (context.res.statusCode === 200) { + return download(target, context); + } + return asText(context) + .then(message => TPromise.wrapError(new Error(`Expected 200, got back ${context.res.statusCode} instead.\n\n${message}`))); + }); + } +} \ No newline at end of file diff --git a/src/vs/workbench/electron-browser/commands.ts b/src/vs/workbench/electron-browser/commands.ts index fe2277003fd..670179785ae 100644 --- a/src/vs/workbench/electron-browser/commands.ts +++ b/src/vs/workbench/electron-browser/commands.ts @@ -20,6 +20,7 @@ import { ITree } from 'vs/base/parts/tree/browser/tree'; import { InEditorZenModeContext, NoEditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench/common/editor'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { URI } from 'vs/base/common/uri'; +import { IDownloadService } from 'vs/platform/download/common/download'; // --- List Commands @@ -555,4 +556,10 @@ export function registerCommands(): void { return windowsService.removeFromRecentlyOpened([path]).then(() => void 0); }); + + CommandsRegistry.registerCommand('_workbench.downloadResource', function (accessor: ServicesAccessor, resource: URI, to: string) { + const downloadService = accessor.get(IDownloadService); + + return downloadService.download(resource, to); + }); } diff --git a/src/vs/workbench/electron-browser/shell.ts b/src/vs/workbench/electron-browser/shell.ts index 10d3358ffc3..3fe5d68a219 100644 --- a/src/vs/workbench/electron-browser/shell.ts +++ b/src/vs/workbench/electron-browser/shell.ts @@ -95,11 +95,12 @@ import { OpenerService } from 'vs/editor/browser/services/openerService'; import { SearchHistoryService } from 'vs/workbench/services/search/node/searchHistoryService'; import { MulitExtensionManagementService } from 'vs/platform/extensionManagement/node/multiExtensionManagement'; import { ExtensionManagementServerService } from 'vs/workbench/services/extensions/node/extensionManagementServerService'; -import { DownloadServiceChannel } from 'vs/platform/download/node/downloadIpc'; import { DefaultURITransformer } from 'vs/base/common/uriIpc'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/extensionGalleryService'; import { ILabelService } from 'vs/platform/label/common/label'; import { runWhenIdle } from 'vs/base/common/async'; +import { IDownloadService } from 'vs/platform/download/common/download'; +import { DownloadService } from 'vs/platform/download/node/downloadService'; /** * Services that we require for the Shell @@ -345,7 +346,6 @@ export class WorkbenchShell extends Disposable { .then(() => connectNet(this.environmentService.sharedIPCHandle, `window:${this.configuration.windowId}`)); sharedProcess.then(client => { - client.registerChannel('download', new DownloadServiceChannel()); client.registerChannel('dialog', instantiationService.createInstance(DialogChannel)); }); @@ -389,6 +389,7 @@ export class WorkbenchShell extends Disposable { this.lifecycleService = lifecycleService; serviceCollection.set(IRequestService, new SyncDescriptor(RequestService)); + serviceCollection.set(IDownloadService, new SyncDescriptor(DownloadService)); serviceCollection.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); const extensionManagementChannel = getDelayedChannel(sharedProcess.then(c => c.getChannel('extensions')));