Compare commits
7 Commits
2.1650-vsc
...
2.1655-vsc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e22964915a | ||
|
|
197d0b6ca9 | ||
|
|
422503ef98 | ||
|
|
ea36345d2c | ||
|
|
a89d83cbba | ||
|
|
83ff31b620 | ||
|
|
3a9b032c72 |
@@ -50,6 +50,21 @@ index a657f4a4d9..66bd13dffa 100644
|
||||
} else if (typeof process === 'object') {
|
||||
_isWindows = (process.platform === 'win32');
|
||||
_isMacintosh = (process.platform === 'darwin');
|
||||
diff --git a/src/vs/base/common/processes.ts b/src/vs/base/common/processes.ts
|
||||
index c52f7b3774..5635cfac8a 100644
|
||||
--- a/src/vs/base/common/processes.ts
|
||||
+++ b/src/vs/base/common/processes.ts
|
||||
@@ -110,7 +110,9 @@ export function sanitizeProcessEnvironment(env: IProcessEnvironment, ...preserve
|
||||
/^ELECTRON_.+$/,
|
||||
/^GOOGLE_API_KEY$/,
|
||||
/^VSCODE_.+$/,
|
||||
- /^SNAP(|_.*)$/
|
||||
+ /^SNAP(|_.*)$/,
|
||||
+ /^NBIN_BYPASS$/,
|
||||
+ /^LAUNCH_VSCODE$/
|
||||
];
|
||||
const envKeys = Object.keys(env);
|
||||
envKeys
|
||||
diff --git a/src/vs/base/node/languagePacks.js b/src/vs/base/node/languagePacks.js
|
||||
index 3ae24454cb..fac8679290 100644
|
||||
--- a/src/vs/base/node/languagePacks.js
|
||||
|
||||
@@ -3,15 +3,16 @@ import { URI } from "vs/base/common/uri";
|
||||
import { registerSingleton } from "vs/platform/instantiation/common/extensions";
|
||||
import { ServiceCollection } from "vs/platform/instantiation/common/serviceCollection";
|
||||
import { ILocalizationsService } from "vs/platform/localizations/common/localizations";
|
||||
import { LocalizationsService } from "vs/workbench/services/localizations/electron-browser/localizationsService";
|
||||
import { PersistentConnectionEventType } from "vs/platform/remote/common/remoteAgentConnection";
|
||||
import { ITelemetryService } from "vs/platform/telemetry/common/telemetry";
|
||||
import { coderApi, vscodeApi } from "vs/server/src/browser/api";
|
||||
import { IUploadService, UploadService } from "vs/server/src/browser/upload";
|
||||
import { INodeProxyService, NodeProxyChannelClient } from "vs/server/src/common/nodeProxy";
|
||||
import { TelemetryChannelClient } from "vs/server/src/common/telemetry";
|
||||
import { split } from "vs/server/src/common/util";
|
||||
import "vs/workbench/contrib/localizations/browser/localizations.contribution";
|
||||
import { LocalizationsService } from "vs/workbench/services/localizations/electron-browser/localizationsService";
|
||||
import { IRemoteAgentService } from "vs/workbench/services/remote/common/remoteAgentService";
|
||||
import { PersistentConnectionEventType } from "vs/platform/remote/common/remoteAgentConnection";
|
||||
|
||||
class TelemetryService extends TelemetryChannelClient {
|
||||
public constructor(
|
||||
@@ -79,7 +80,7 @@ export const withQuery = (url: string, replace: Query): string => {
|
||||
const uri = URI.parse(url);
|
||||
const query = { ...replace };
|
||||
uri.query.split("&").forEach((kv) => {
|
||||
const [key, value] = kv.split("=", 2);
|
||||
const [key, value] = split(kv, "=");
|
||||
if (!(key in query)) {
|
||||
query[key] = value;
|
||||
}
|
||||
|
||||
10
src/common/util.ts
Normal file
10
src/common/util.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Split a string up to the delimiter. If the delimiter doesn't exist the first
|
||||
* item will have all the text and the second item will be an empty string.
|
||||
*/
|
||||
export const split = (str: string, delimiter: string): [string, string] => {
|
||||
const index = str.indexOf(delimiter);
|
||||
return index !== -1
|
||||
? [str.substring(0, index).trim(), str.substring(index + 1)]
|
||||
: [str, ""];
|
||||
};
|
||||
@@ -90,7 +90,7 @@ const startVscode = async (): Promise<void | void[]> => {
|
||||
basePath: args["base-path"],
|
||||
cert: args.cert,
|
||||
certKey: args["cert-key"],
|
||||
folderUri: extra.length > 1 ? extra[extra.length - 1] : undefined,
|
||||
openUri: extra.length > 1 ? extra[extra.length - 1] : undefined,
|
||||
host: args.host,
|
||||
password: process.env.PASSWORD,
|
||||
};
|
||||
@@ -204,6 +204,7 @@ export class WrapperProcess {
|
||||
logger.info("Relaunching...");
|
||||
this.started = undefined;
|
||||
if (this.process) {
|
||||
this.process.removeAllListeners();
|
||||
this.process.kill();
|
||||
}
|
||||
try {
|
||||
@@ -223,7 +224,9 @@ export class WrapperProcess {
|
||||
public start(): Promise<void> {
|
||||
if (!this.started) {
|
||||
const child = this.spawn();
|
||||
this.started = ipcMain.handshake(child);
|
||||
this.started = ipcMain.handshake(child).then(() => {
|
||||
child.once("exit", (code) => exit(code!));
|
||||
});
|
||||
this.process = child;
|
||||
}
|
||||
return this.started;
|
||||
|
||||
@@ -56,6 +56,7 @@ import { resolveCommonProperties } from "vs/platform/telemetry/node/commonProper
|
||||
import { UpdateChannel } from "vs/platform/update/electron-main/updateIpc";
|
||||
import { INodeProxyService, NodeProxyChannel } from "vs/server/src/common/nodeProxy";
|
||||
import { TelemetryChannel } from "vs/server/src/common/telemetry";
|
||||
import { split } from "vs/server/src/common/util";
|
||||
import { ExtensionEnvironmentChannel, FileProviderChannel, NodeProxyService } from "vs/server/src/node/channel";
|
||||
import { Connection, ExtensionHostConnection, ManagementConnection } from "vs/server/src/node/connection";
|
||||
import { TelemetryClient } from "vs/server/src/node/insights";
|
||||
@@ -115,7 +116,7 @@ export interface ServerOptions {
|
||||
readonly connectionToken?: string;
|
||||
readonly cert?: string;
|
||||
readonly certKey?: string;
|
||||
readonly folderUri?: string;
|
||||
readonly openUri?: string;
|
||||
readonly host?: string;
|
||||
readonly password?: string;
|
||||
readonly port?: number;
|
||||
@@ -212,8 +213,8 @@ export abstract class Server {
|
||||
}
|
||||
|
||||
protected withBase(request: http.IncomingMessage, path: string): string {
|
||||
const split = request.url ? request.url.split("?", 2) : [];
|
||||
return `${this.protocol}://${request.headers.host}${this.options.basePath}${path}${split.length === 2 ? `?${split[1]}` : ""}`;
|
||||
const [, query] = request.url ? split(request.url, "?") : [];
|
||||
return `${this.protocol}://${request.headers.host}${this.options.basePath}${path}${query ? `?${query}` : ""}`;
|
||||
}
|
||||
|
||||
private isAllowedRequestPath(path: string): boolean {
|
||||
@@ -440,8 +441,8 @@ export abstract class Server {
|
||||
const cookies: { [key: string]: string } = {};
|
||||
if (request.headers.cookie) {
|
||||
request.headers.cookie.split(";").forEach((keyValue) => {
|
||||
const [key, value] = keyValue.split("=", 2);
|
||||
cookies[key.trim()] = decodeURI(value);
|
||||
const [key, value] = split(keyValue, "=");
|
||||
cookies[key] = decodeURI(value);
|
||||
});
|
||||
}
|
||||
return cookies as T;
|
||||
@@ -474,6 +475,9 @@ export class MainServer extends Server {
|
||||
private readonly proxyTimeout = 5000;
|
||||
|
||||
private settings: Settings = {};
|
||||
private heartbeatTimer?: NodeJS.Timeout;
|
||||
private heartbeatInterval = 60000;
|
||||
private lastHeartbeat = 0;
|
||||
|
||||
public constructor(options: ServerOptions, args: ParsedArgs) {
|
||||
super(options);
|
||||
@@ -491,6 +495,7 @@ export class MainServer extends Server {
|
||||
}
|
||||
|
||||
protected async handleWebSocket(socket: net.Socket, parsedUrl: url.UrlWithParsedQuery): Promise<void> {
|
||||
this.heartbeat();
|
||||
if (!parsedUrl.query.reconnectionToken) {
|
||||
throw new Error("Reconnection token is missing from query parameters");
|
||||
}
|
||||
@@ -514,12 +519,13 @@ export class MainServer extends Server {
|
||||
parsedUrl: url.UrlWithParsedQuery,
|
||||
request: http.IncomingMessage,
|
||||
): Promise<Response> {
|
||||
this.heartbeat();
|
||||
switch (base) {
|
||||
case "/": return this.getRoot(request, parsedUrl);
|
||||
case "/resource":
|
||||
case "/vscode-remote-resource":
|
||||
if (typeof parsedUrl.query.path === "string") {
|
||||
return this.getResource(parsedUrl.query.path);
|
||||
return this.getAnyResource(parsedUrl.query.path);
|
||||
}
|
||||
break;
|
||||
case "/tar":
|
||||
@@ -546,9 +552,9 @@ export class MainServer extends Server {
|
||||
util.promisify(fs.readFile)(filePath, "utf8"),
|
||||
this.getFirstValidPath([
|
||||
{ path: parsedUrl.query.workspace, workspace: true },
|
||||
{ path: parsedUrl.query.folder },
|
||||
{ path: parsedUrl.query.folder, workspace: false },
|
||||
(await this.readSettings()).lastVisited,
|
||||
{ path: this.options.folderUri }
|
||||
{ path: this.options.openUri }
|
||||
]),
|
||||
this.servicesPromise,
|
||||
]);
|
||||
@@ -592,7 +598,9 @@ export class MainServer extends Server {
|
||||
}
|
||||
|
||||
/**
|
||||
* Choose the first valid path.
|
||||
* Choose the first valid path. If `workspace` is undefined then either a
|
||||
* workspace or a directory are acceptable. Otherwise it must be a file if a
|
||||
* workspace or a directory otherwise.
|
||||
*/
|
||||
private async getFirstValidPath(startPaths: Array<StartPath | undefined>): Promise<{ uri: URI, workspace?: boolean} | undefined> {
|
||||
const logger = this.services.get(ILogService) as ILogService;
|
||||
@@ -607,9 +615,8 @@ export class MainServer extends Server {
|
||||
const uri = URI.file(sanitizeFilePath(paths[j], cwd));
|
||||
try {
|
||||
const stat = await util.promisify(fs.stat)(uri.fsPath);
|
||||
// Workspace must be a file.
|
||||
if (!!startPath.workspace !== stat.isDirectory()) {
|
||||
return { uri, workspace: startPath.workspace };
|
||||
if (typeof startPath.workspace === "undefined" || startPath.workspace !== stat.isDirectory()) {
|
||||
return { uri, workspace: !stat.isDirectory() };
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn(error.message);
|
||||
@@ -876,4 +883,48 @@ export class MainServer extends Server {
|
||||
(this.services.get(ILogService) as ILogService).warn(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the file path for the heartbeat file.
|
||||
*/
|
||||
private get heartbeatPath(): string {
|
||||
const environment = this.services.get(IEnvironmentService) as IEnvironmentService;
|
||||
return path.join(environment.userDataPath, "heartbeat");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all online connections regardless of type.
|
||||
*/
|
||||
private get onlineConnections(): Connection[] {
|
||||
const online = <Connection[]>[];
|
||||
this.connections.forEach((connections) => {
|
||||
connections.forEach((connection) => {
|
||||
if (typeof connection.offline === "undefined") {
|
||||
online.push(connection);
|
||||
}
|
||||
});
|
||||
});
|
||||
return online;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write to the heartbeat file if we haven't already done so within the
|
||||
* timeout and start or reset a timer that keeps running as long as there are
|
||||
* active connections. Failures are logged as warnings.
|
||||
*/
|
||||
private heartbeat(): void {
|
||||
const now = Date.now();
|
||||
if (now - this.lastHeartbeat >= this.heartbeatInterval) {
|
||||
util.promisify(fs.writeFile)(this.heartbeatPath, "").catch((error) => {
|
||||
(this.services.get(ILogService) as ILogService).warn(error.message);
|
||||
});
|
||||
this.lastHeartbeat = now;
|
||||
clearTimeout(this.heartbeatTimer!); // We can clear undefined so ! is fine.
|
||||
this.heartbeatTimer = setTimeout(() => {
|
||||
if (this.onlineConnections.length > 0) {
|
||||
this.heartbeat();
|
||||
}
|
||||
}, this.heartbeatInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user