Skip to content

Commit a5c35af

Browse files
committed
Fix encoding issues with folder and workspace params
The raw value is now passed back to VS Code so it can do the parsing with its own URI class rather than trying to parse using Node's url module first since that has no guarantee of working the same way. It also lets us keep the vscode-remote bit internal to VS Code. Removed the logic that keeps trying paths until it finds a valid one because it seems confusing to open a path and silently get some other path instead of an error for the one you tried to open. Now it'll just use exactly what you specified or fail trying. Fixes #1488. The problem here was that url.parse was encoding the spaces then the validation failed looking for a literal %20.
1 parent b78bdaf commit a5c35af

File tree

2 files changed

+60
-51
lines changed

2 files changed

+60
-51
lines changed

ci/vscode.patch

+46-10
Original file line numberDiff line numberDiff line change
@@ -261,16 +261,32 @@ index 2c64061da7..c0ef8faedd 100644
261261
// Do nothing. If we can't read the file we have no
262262
// language pack config.
263263
diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts
264-
index 45f6f17ce0..4d1a590a7c 100644
264+
index 45f6f17ce0..102289c147 100644
265265
--- a/src/vs/code/browser/workbench/workbench.ts
266266
+++ b/src/vs/code/browser/workbench/workbench.ts
267+
@@ -16,6 +16,7 @@ import product from 'vs/platform/product/common/product';
268+
import { Schemas } from 'vs/base/common/network';
269+
import { posix } from 'vs/base/common/path';
270+
import { localize } from 'vs/nls';
271+
+import { encodePath } from 'vs/server/node/util';
272+
273+
interface ICredential {
274+
service: string;
275+
@@ -237,7 +238,6 @@ class WorkspaceProvider implements IWorkspaceProvider {
276+
}
277+
278+
private createTargetUrl(workspace: IWorkspace, options?: { reuse?: boolean, payload?: object }): string | undefined {
279+
-
280+
// Empty
281+
let targetHref: string | undefined = undefined;
282+
if (!workspace) {
267283
@@ -246,12 +246,18 @@ class WorkspaceProvider implements IWorkspaceProvider {
268284

269285
// Folder
270286
else if (isFolderToOpen(workspace)) {
271287
- targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_FOLDER}=${encodeURIComponent(workspace.folderUri.toString())}`;
272288
+ const target = workspace.folderUri.scheme === Schemas.vscodeRemote
273-
+ ? encodeURIComponent(workspace.folderUri.path).replace(/%2F/g, "/")
289+
+ ? encodePath(workspace.folderUri.path)
274290
+ : encodeURIComponent(workspace.folderUri.toString());
275291
+ targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_FOLDER}=${target}`;
276292
}
@@ -279,7 +295,7 @@ index 45f6f17ce0..4d1a590a7c 100644
279295
else if (isWorkspaceToOpen(workspace)) {
280296
- targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_WORKSPACE}=${encodeURIComponent(workspace.workspaceUri.toString())}`;
281297
+ const target = workspace.workspaceUri.scheme === Schemas.vscodeRemote
282-
+ ? encodeURIComponent(workspace.workspaceUri.path).replace(/%2F/g, "/")
298+
+ ? encodePath(workspace.workspaceUri.path)
283299
+ : encodeURIComponent(workspace.workspaceUri.toString());
284300
+ targetHref = `${document.location.origin}${document.location.pathname}?${WorkspaceProvider.QUERY_PARAM_WORKSPACE}=${target}`;
285301
}
@@ -290,7 +306,7 @@ index 45f6f17ce0..4d1a590a7c 100644
290306
const config: IWorkbenchConstructionOptions & { folderUri?: UriComponents, workspaceUri?: UriComponents } = JSON.parse(configElementAttribute);
291307

292308
+ // Strip the protocol from the authority if it exists.
293-
+ const normalizeAuthority = (authority: string): string => authority.replace(/^https?:\/\//, "");
309+
+ const normalizeAuthority = (authority?: string): string => authority && authority.replace(/^https?:\/\//, "");
294310
+ if (config.remoteAuthority) {
295311
+ (config as any).remoteAuthority = normalizeAuthority(config.remoteAuthority);
296312
+ }
@@ -2346,10 +2362,10 @@ index 0000000000..3c74512192
23462362
+}
23472363
diff --git a/src/vs/server/node/server.ts b/src/vs/server/node/server.ts
23482364
new file mode 100644
2349-
index 0000000000..d6dcfe1fe7
2365+
index 0000000000..52311bf756
23502366
--- /dev/null
23512367
+++ b/src/vs/server/node/server.ts
2352-
@@ -0,0 +1,257 @@
2368+
@@ -0,0 +1,269 @@
23532369
+import * as net from 'net';
23542370
+import * as path from 'path';
23552371
+import { Emitter } from 'vs/base/common/event';
@@ -2429,10 +2445,22 @@ index 0000000000..d6dcfe1fe7
24292445
+ await this.servicesPromise;
24302446
+ const environment = this.services.get(IEnvironmentService) as IEnvironmentService;
24312447
+ const startPath = options.startPath;
2448+
+ const parseUrl = (url: string): URI => {
2449+
+ // This might be a fully-specified URL or just a path.
2450+
+ try {
2451+
+ return URI.parse(url, true);
2452+
+ } catch (error) {
2453+
+ return URI.from({
2454+
+ scheme: Schemas.vscodeRemote,
2455+
+ authority: options.remoteAuthority,
2456+
+ path: url,
2457+
+ });
2458+
+ }
2459+
+ };
24322460
+ return {
24332461
+ workbenchWebConfiguration: {
2434-
+ workspaceUri: startPath && startPath.workspace ? URI.parse(startPath.url) : undefined,
2435-
+ folderUri: startPath && !startPath.workspace ? URI.parse(startPath.url) : undefined,
2462+
+ workspaceUri: startPath && startPath.workspace ? parseUrl(startPath.url) : undefined,
2463+
+ folderUri: startPath && !startPath.workspace ? parseUrl(startPath.url) : undefined,
24362464
+ remoteAuthority: options.remoteAuthority,
24372465
+ logLevel: getLogLevel(environment),
24382466
+ },
@@ -2639,10 +2667,10 @@ index 0000000000..fc69441cf0
26392667
+};
26402668
diff --git a/src/vs/server/node/util.ts b/src/vs/server/node/util.ts
26412669
new file mode 100644
2642-
index 0000000000..06b080044c
2670+
index 0000000000..dd7fdf7b58
26432671
--- /dev/null
26442672
+++ b/src/vs/server/node/util.ts
2645-
@@ -0,0 +1,9 @@
2673+
@@ -0,0 +1,17 @@
26462674
+import { getPathFromAmdModule } from 'vs/base/common/amd';
26472675
+import { URITransformer, IRawURITransformer } from 'vs/base/common/uriIpc';
26482676
+
@@ -2652,6 +2680,14 @@ index 0000000000..06b080044c
26522680
+ const rawURITransformer = <IRawURITransformer>rawURITransformerFactory(remoteAuthority);
26532681
+ return new URITransformer(rawURITransformer);
26542682
+};
2683+
+
2684+
+/**
2685+
+ * Encode a path for opening via the folder or workspace query parameter. This
2686+
+ * preserves slashes so it can be edited by hand more easily.
2687+
+ */
2688+
+export const encodePath = (path: string): string => {
2689+
+ return path.split("/").map((p) => encodeURIComponent(p)).join("/");
2690+
+};
26552691
diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts
26562692
index e69aa80159..71a899d37b 100644
26572693
--- a/src/vs/workbench/api/browser/extensionHost.contribution.ts

src/node/app/vscode.ts

+14-41
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
import { field, logger } from "@coder/logger"
22
import * as cp from "child_process"
33
import * as crypto from "crypto"
4-
import * as fs from "fs-extra"
54
import * as http from "http"
65
import * as net from "net"
76
import * as path from "path"
8-
import * as url from "url"
97
import {
108
CodeServerMessage,
119
Options,
@@ -168,15 +166,12 @@ export class VscodeHttpProvider extends HttpProvider {
168166
private async getRoot(request: http.IncomingMessage, route: Route): Promise<HttpResponse> {
169167
const remoteAuthority = request.headers.host as string
170168
const { lastVisited } = await settings.read()
171-
const startPath = await this.getFirstValidPath(
172-
[
173-
{ url: route.query.workspace, workspace: true },
174-
{ url: route.query.folder, workspace: false },
175-
this.args._ && this.args._.length > 0 ? { url: path.resolve(this.args._[this.args._.length - 1]) } : undefined,
176-
lastVisited,
177-
],
178-
remoteAuthority,
179-
)
169+
const startPath = await this.getFirstPath([
170+
{ url: route.query.workspace, workspace: true },
171+
{ url: route.query.folder, workspace: false },
172+
this.args._ && this.args._.length > 0 ? { url: path.resolve(this.args._[this.args._.length - 1]) } : undefined,
173+
lastVisited,
174+
])
180175
const [response, options] = await Promise.all([
181176
await this.getUtf8Resource(this.rootPath, "src/browser/pages/vscode.html"),
182177
this.initialize({
@@ -209,41 +204,19 @@ export class VscodeHttpProvider extends HttpProvider {
209204
}
210205

211206
/**
212-
* Choose the first valid path. If `workspace` is undefined then either a
213-
* workspace or a directory are acceptable. Otherwise it must be a file if a
214-
* workspace or a directory otherwise.
207+
* Choose the first non-empty path.
215208
*/
216-
private async getFirstValidPath(
209+
private async getFirstPath(
217210
startPaths: Array<{ url?: string | string[]; workspace?: boolean } | undefined>,
218-
remoteAuthority: string,
219211
): Promise<StartPath | undefined> {
220212
for (let i = 0; i < startPaths.length; ++i) {
221213
const startPath = startPaths[i]
222-
if (!startPath) {
223-
continue
224-
}
225-
const paths = typeof startPath.url === "string" ? [startPath.url] : startPath.url || []
226-
for (let j = 0; j < paths.length; ++j) {
227-
const uri = url.parse(paths[j])
228-
try {
229-
if (!uri.pathname) {
230-
throw new Error(`${paths[j]} is not a valid URL`)
231-
}
232-
const stat = await fs.stat(uri.pathname)
233-
if (typeof startPath.workspace === "undefined" || startPath.workspace !== stat.isDirectory()) {
234-
return {
235-
url: url.format({
236-
protocol: uri.protocol || "vscode-remote",
237-
hostname: remoteAuthority.split(":")[0],
238-
port: remoteAuthority.split(":")[1],
239-
pathname: uri.pathname,
240-
slashes: true,
241-
}),
242-
workspace: !stat.isDirectory(),
243-
}
244-
}
245-
} catch (error) {
246-
logger.warn(error.message)
214+
const url =
215+
startPath && (typeof startPath.url === "string" ? [startPath.url] : startPath.url || []).find((p) => !!p)
216+
if (startPath && url) {
217+
return {
218+
url,
219+
workspace: !!startPath.workspace,
247220
}
248221
}
249222
}

0 commit comments

Comments
 (0)