@@ -5,6 +5,7 @@ import * as https from "https";
55import * as net from "net" ;
66import * as path from "path" ;
77import * as querystring from "querystring" ;
8+ import { Readable } from "stream" ;
89import * as tls from "tls" ;
910import * as url from "url" ;
1011import * as util from "util" ;
@@ -67,6 +68,8 @@ import { AuthType, getMediaMime, getUriTransformer, localRequire, tmpdir } from
6768import { RemoteExtensionLogFileName } from "vs/workbench/services/remote/common/remoteAgentService" ;
6869import { IWorkbenchConstructionOptions } from "vs/workbench/workbench.web.api" ;
6970
71+ const tarFs = localRequire < typeof import ( "tar-fs" ) > ( "tar-fs/index" ) ;
72+
7073export enum HttpCode {
7174 Ok = 200 ,
7275 Redirect = 302 ,
@@ -89,7 +92,9 @@ export interface Response {
8992 content ?: string | Buffer ;
9093 filePath ?: string ;
9194 headers ?: http . OutgoingHttpHeaders ;
95+ mime ?: string ;
9296 redirect ?: string ;
97+ stream ?: Readable ;
9398}
9499
95100export interface LoginPayload {
@@ -185,11 +190,21 @@ export abstract class Server {
185190 ) : Promise < Response > ;
186191
187192 protected async getResource ( ...parts : string [ ] ) : Promise < Response > {
193+ const filePath = this . ensureAuthorizedFilePath ( ...parts ) ;
194+ return { content : await util . promisify ( fs . readFile ) ( filePath ) , filePath } ;
195+ }
196+
197+ protected async getTarredResource ( ...parts : string [ ] ) : Promise < Response > {
198+ const filePath = this . ensureAuthorizedFilePath ( ...parts ) ;
199+ return { stream : tarFs . pack ( filePath ) , filePath, mime : "application/tar" } ;
200+ }
201+
202+ protected ensureAuthorizedFilePath ( ...parts : string [ ] ) : string {
188203 const filePath = path . join ( ...parts ) ;
189204 if ( ! this . isAllowedRequestPath ( filePath ) ) {
190205 throw new HttpError ( "Unauthorized" , HttpCode . Unauthorized ) ;
191206 }
192- return { content : await util . promisify ( fs . readFile ) ( filePath ) , filePath } ;
207+ return filePath ;
193208 }
194209
195210 protected withBase ( request : http . IncomingMessage , path : string ) : string {
@@ -211,13 +226,21 @@ export abstract class Server {
211226 const parsedUrl = request . url ? url . parse ( request . url , true ) : { query : { } } ;
212227 const payload = await this . preHandleRequest ( request , parsedUrl ) ;
213228 response . writeHead ( payload . redirect ? HttpCode . Redirect : payload . code || HttpCode . Ok , {
214- "Content-Type" : getMediaMime ( payload . filePath ) ,
229+ "Content-Type" : payload . mime || getMediaMime ( payload . filePath ) ,
215230 ...( payload . redirect ? { Location : this . withBase ( request , payload . redirect ) } : { } ) ,
216231 ...( request . headers [ "service-worker" ] ? { "Service-Worker-Allowed" : this . options . basePath || "/" } : { } ) ,
217232 ...( payload . cache ? { "Cache-Control" : "public, max-age=31536000" } : { } ) ,
218233 ...payload . headers ,
219234 } ) ;
220- response . end ( payload . content ) ;
235+ if ( payload . stream ) {
236+ payload . stream . on ( "error" , ( error : NodeJS . ErrnoException ) => {
237+ response . writeHead ( error . code === "ENOENT" ? HttpCode . NotFound : HttpCode . ServerError ) ;
238+ response . end ( error . message ) ;
239+ } ) ;
240+ payload . stream . pipe ( response ) ;
241+ } else {
242+ response . end ( payload . content ) ;
243+ }
221244 } catch ( error ) {
222245 if ( error . code === "ENOENT" || error . code === "EISDIR" ) {
223246 error = new HttpError ( "Not found" , HttpCode . NotFound ) ;
@@ -484,6 +507,11 @@ export class MainServer extends Server {
484507 return this . getResource ( parsedUrl . query . path ) ;
485508 }
486509 break ;
510+ case "/tar" :
511+ if ( typeof parsedUrl . query . path === "string" ) {
512+ return this . getTarredResource ( parsedUrl . query . path ) ;
513+ }
514+ break ;
487515 case "/webview" :
488516 if ( requestPath . indexOf ( "/vscode-resource" ) === 0 ) {
489517 return this . getResource ( requestPath . replace ( / ^ \/ v s c o d e - r e s o u r c e / , "" ) ) ;
0 commit comments