Add tar endpoint
This will be used to load extensions into the browser using requirefs.
This commit is contained in:
parent
c3c50e9a6a
commit
f8635a124f
@ -17,6 +17,7 @@
|
|||||||
"@coder/nbin": "^1.2.0",
|
"@coder/nbin": "^1.2.0",
|
||||||
"@types/pem": "^1.9.5",
|
"@types/pem": "^1.9.5",
|
||||||
"@types/safe-compare": "^1.1.0",
|
"@types/safe-compare": "^1.1.0",
|
||||||
|
"@types/tar-fs": "^1.16.1",
|
||||||
"@types/tar-stream": "^1.6.1",
|
"@types/tar-stream": "^1.6.1",
|
||||||
"nodemon": "^1.19.1"
|
"nodemon": "^1.19.1"
|
||||||
},
|
},
|
||||||
@ -29,6 +30,7 @@
|
|||||||
"httpolyglot": "^0.1.2",
|
"httpolyglot": "^0.1.2",
|
||||||
"pem": "^1.14.2",
|
"pem": "^1.14.2",
|
||||||
"safe-compare": "^1.1.4",
|
"safe-compare": "^1.1.4",
|
||||||
|
"tar-fs": "^2.0.0",
|
||||||
"tar-stream": "^2.1.0"
|
"tar-stream": "^2.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import * as https from "https";
|
|||||||
import * as net from "net";
|
import * as net from "net";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
import * as querystring from "querystring";
|
import * as querystring from "querystring";
|
||||||
|
import { Readable } from "stream";
|
||||||
import * as tls from "tls";
|
import * as tls from "tls";
|
||||||
import * as url from "url";
|
import * as url from "url";
|
||||||
import * as util from "util";
|
import * as util from "util";
|
||||||
@ -67,6 +68,8 @@ import { AuthType, getMediaMime, getUriTransformer, localRequire, tmpdir } from
|
|||||||
import { RemoteExtensionLogFileName } from "vs/workbench/services/remote/common/remoteAgentService";
|
import { RemoteExtensionLogFileName } from "vs/workbench/services/remote/common/remoteAgentService";
|
||||||
import { IWorkbenchConstructionOptions } from "vs/workbench/workbench.web.api";
|
import { IWorkbenchConstructionOptions } from "vs/workbench/workbench.web.api";
|
||||||
|
|
||||||
|
const tarFs = localRequire<typeof import("tar-fs")>("tar-fs/index");
|
||||||
|
|
||||||
export enum HttpCode {
|
export enum HttpCode {
|
||||||
Ok = 200,
|
Ok = 200,
|
||||||
Redirect = 302,
|
Redirect = 302,
|
||||||
@ -89,7 +92,9 @@ export interface Response {
|
|||||||
content?: string | Buffer;
|
content?: string | Buffer;
|
||||||
filePath?: string;
|
filePath?: string;
|
||||||
headers?: http.OutgoingHttpHeaders;
|
headers?: http.OutgoingHttpHeaders;
|
||||||
|
mime?: string;
|
||||||
redirect?: string;
|
redirect?: string;
|
||||||
|
stream?: Readable;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LoginPayload {
|
export interface LoginPayload {
|
||||||
@ -185,11 +190,21 @@ export abstract class Server {
|
|||||||
): Promise<Response>;
|
): Promise<Response>;
|
||||||
|
|
||||||
protected async getResource(...parts: string[]): Promise<Response> {
|
protected async getResource(...parts: string[]): Promise<Response> {
|
||||||
|
const filePath = this.ensureAuthorizedFilePath(...parts);
|
||||||
|
return { content: await util.promisify(fs.readFile)(filePath), filePath };
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async getTarredResource(...parts: string[]): Promise<Response> {
|
||||||
|
const filePath = this.ensureAuthorizedFilePath(...parts);
|
||||||
|
return { stream: tarFs.pack(filePath), filePath, mime: "application/tar" };
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ensureAuthorizedFilePath(...parts: string[]): string {
|
||||||
const filePath = path.join(...parts);
|
const filePath = path.join(...parts);
|
||||||
if (!this.isAllowedRequestPath(filePath)) {
|
if (!this.isAllowedRequestPath(filePath)) {
|
||||||
throw new HttpError("Unauthorized", HttpCode.Unauthorized);
|
throw new HttpError("Unauthorized", HttpCode.Unauthorized);
|
||||||
}
|
}
|
||||||
return { content: await util.promisify(fs.readFile)(filePath), filePath };
|
return filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected withBase(request: http.IncomingMessage, path: string): string {
|
protected withBase(request: http.IncomingMessage, path: string): string {
|
||||||
@ -211,13 +226,21 @@ export abstract class Server {
|
|||||||
const parsedUrl = request.url ? url.parse(request.url, true) : { query: {}};
|
const parsedUrl = request.url ? url.parse(request.url, true) : { query: {}};
|
||||||
const payload = await this.preHandleRequest(request, parsedUrl);
|
const payload = await this.preHandleRequest(request, parsedUrl);
|
||||||
response.writeHead(payload.redirect ? HttpCode.Redirect : payload.code || HttpCode.Ok, {
|
response.writeHead(payload.redirect ? HttpCode.Redirect : payload.code || HttpCode.Ok, {
|
||||||
"Content-Type": getMediaMime(payload.filePath),
|
"Content-Type": payload.mime || getMediaMime(payload.filePath),
|
||||||
...(payload.redirect ? { Location: this.withBase(request, payload.redirect) } : {}),
|
...(payload.redirect ? { Location: this.withBase(request, payload.redirect) } : {}),
|
||||||
...(request.headers["service-worker"] ? { "Service-Worker-Allowed": this.options.basePath || "/" } : {}),
|
...(request.headers["service-worker"] ? { "Service-Worker-Allowed": this.options.basePath || "/" } : {}),
|
||||||
...(payload.cache ? { "Cache-Control": "public, max-age=31536000" } : {}),
|
...(payload.cache ? { "Cache-Control": "public, max-age=31536000" } : {}),
|
||||||
...payload.headers,
|
...payload.headers,
|
||||||
});
|
});
|
||||||
|
if (payload.stream) {
|
||||||
|
payload.stream.on("error", (error: NodeJS.ErrnoException) => {
|
||||||
|
response.writeHead(error.code === "ENOENT" ? HttpCode.NotFound : HttpCode.ServerError);
|
||||||
|
response.end(error.message);
|
||||||
|
});
|
||||||
|
payload.stream.pipe(response);
|
||||||
|
} else {
|
||||||
response.end(payload.content);
|
response.end(payload.content);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code === "ENOENT" || error.code === "EISDIR") {
|
if (error.code === "ENOENT" || error.code === "EISDIR") {
|
||||||
error = new HttpError("Not found", HttpCode.NotFound);
|
error = new HttpError("Not found", HttpCode.NotFound);
|
||||||
@ -484,6 +507,11 @@ export class MainServer extends Server {
|
|||||||
return this.getResource(parsedUrl.query.path);
|
return this.getResource(parsedUrl.query.path);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "/tar":
|
||||||
|
if (typeof parsedUrl.query.path === "string") {
|
||||||
|
return this.getTarredResource(parsedUrl.query.path);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case "/webview":
|
case "/webview":
|
||||||
if (requestPath.indexOf("/vscode-resource") === 0) {
|
if (requestPath.indexOf("/vscode-resource") === 0) {
|
||||||
return this.getResource(requestPath.replace(/^\/vscode-resource/, ""));
|
return this.getResource(requestPath.replace(/^\/vscode-resource/, ""));
|
||||||
|
31
yarn.lock
31
yarn.lock
@ -35,6 +35,13 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/safe-compare/-/safe-compare-1.1.0.tgz#47ed9b9ca51a3a791b431cd59b28f47fa9bf1224"
|
resolved "https://registry.yarnpkg.com/@types/safe-compare/-/safe-compare-1.1.0.tgz#47ed9b9ca51a3a791b431cd59b28f47fa9bf1224"
|
||||||
integrity sha512-1ri+LJhh0gRxIa37IpGytdaW7yDEHeJniBSMD1BmitS07R1j63brcYCzry+l0WJvGdEKQNQ7DYXO2epgborWPw==
|
integrity sha512-1ri+LJhh0gRxIa37IpGytdaW7yDEHeJniBSMD1BmitS07R1j63brcYCzry+l0WJvGdEKQNQ7DYXO2epgborWPw==
|
||||||
|
|
||||||
|
"@types/tar-fs@^1.16.1":
|
||||||
|
version "1.16.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/tar-fs/-/tar-fs-1.16.1.tgz#6e3fba276c173e365ae91e55f7b797a0e64298e5"
|
||||||
|
integrity sha512-uQQIaa8ukcKf/1yy2kzfP1PF+7jEZghFDKpDvgtsYo/mbqM1g4Qza1Y5oAw6kJMa7eLA/HkmxUsDqb2sWKVF9g==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/tar-stream@^1.6.1":
|
"@types/tar-stream@^1.6.1":
|
||||||
version "1.6.1"
|
version "1.6.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/tar-stream/-/tar-stream-1.6.1.tgz#67d759068ff781d976cad978893bb7a334ec8809"
|
resolved "https://registry.yarnpkg.com/@types/tar-stream/-/tar-stream-1.6.1.tgz#67d759068ff781d976cad978893bb7a334ec8809"
|
||||||
@ -480,7 +487,7 @@ duplexer3@^0.1.4:
|
|||||||
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
|
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
|
||||||
integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
|
integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
|
||||||
|
|
||||||
end-of-stream@^1.4.1:
|
end-of-stream@^1.1.0, end-of-stream@^1.4.1:
|
||||||
version "1.4.1"
|
version "1.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
|
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
|
||||||
integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==
|
integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==
|
||||||
@ -1293,7 +1300,7 @@ object.pick@^1.3.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
isobject "^3.0.1"
|
isobject "^3.0.1"
|
||||||
|
|
||||||
once@^1.3.0, once@^1.4.0:
|
once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||||
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
|
||||||
@ -1417,6 +1424,14 @@ pstree.remy@^1.1.6:
|
|||||||
resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.7.tgz#c76963a28047ed61542dc361aa26ee55a7fa15f3"
|
resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.7.tgz#c76963a28047ed61542dc361aa26ee55a7fa15f3"
|
||||||
integrity sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A==
|
integrity sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A==
|
||||||
|
|
||||||
|
pump@^3.0.0:
|
||||||
|
version "3.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
|
||||||
|
integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
|
||||||
|
dependencies:
|
||||||
|
end-of-stream "^1.1.0"
|
||||||
|
once "^1.3.1"
|
||||||
|
|
||||||
rc@^1.0.1, rc@^1.1.6, rc@^1.2.7:
|
rc@^1.0.1, rc@^1.1.6, rc@^1.2.7:
|
||||||
version "1.2.8"
|
version "1.2.8"
|
||||||
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
|
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
|
||||||
@ -1744,7 +1759,17 @@ supports-color@^5.2.0, supports-color@^5.3.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
has-flag "^3.0.0"
|
has-flag "^3.0.0"
|
||||||
|
|
||||||
tar-stream@^2.1.0:
|
tar-fs@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.0.tgz#677700fc0c8b337a78bee3623fdc235f21d7afad"
|
||||||
|
integrity sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA==
|
||||||
|
dependencies:
|
||||||
|
chownr "^1.1.1"
|
||||||
|
mkdirp "^0.5.1"
|
||||||
|
pump "^3.0.0"
|
||||||
|
tar-stream "^2.0.0"
|
||||||
|
|
||||||
|
tar-stream@^2.0.0, tar-stream@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.0.tgz#d1aaa3661f05b38b5acc9b7020efdca5179a2cc3"
|
resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.0.tgz#d1aaa3661f05b38b5acc9b7020efdca5179a2cc3"
|
||||||
integrity sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw==
|
integrity sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw==
|
||||||
|
Reference in New Issue
Block a user