Skip to content

Commit 4c4a179

Browse files
committed
TLS socket still doesn't work
1 parent a4f21fb commit 4c4a179

9 files changed

Lines changed: 113 additions & 73 deletions

File tree

scripts/ci.bash

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ function docker-build() {
1717
if [[ "${image}" == "codercom/nbin-alpine" ]] ; then
1818
docker exec "${containerId}" apk add libxkbfile-dev libsecret-dev
1919
else
20-
# TODO: at some point git existing but now it seems to have disappeared.
20+
# TODO: at some point git existed but it seems to have disappeared.
2121
docker exec "${containerId}" yum install -y libxkbfile-devel libsecret-devel git
2222
fi
2323

scripts/vscode.patch

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -371,15 +371,15 @@ index 9f68b645b6..f0cae7111d 100644
371371
this.channel.call('setLevel', level);
372372
}
373373
diff --git a/src/vs/platform/remote/browser/browserWebSocketFactory.ts b/src/vs/platform/remote/browser/browserWebSocketFactory.ts
374-
index 6d9ecbcf5a..1ebd5a4b84 100644
374+
index 6d9ecbcf5a..1b3499dddf 100644
375375
--- a/src/vs/platform/remote/browser/browserWebSocketFactory.ts
376376
+++ b/src/vs/platform/remote/browser/browserWebSocketFactory.ts
377377
@@ -79,7 +79,7 @@ class BrowserSocket implements ISocket {
378378
export const browserWebSocketFactory = new class implements IWebSocketFactory {
379379
connect(host: string, port: number, query: string, callback: IConnectCallback): void {
380380
const errorListener = (err: any) => callback(err, undefined);
381381
- const socket = new WebSocket(`ws://${host}:${port}/?${query}&skipWebSocketFrames=false`);
382-
+ const socket = new WebSocket(`ws://${host}:${port}${window.location.pathname.replace(/\/+$/, '')}/?${query}&skipWebSocketFrames=false`);
382+
+ const socket = new WebSocket(`${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${host}:${port}${window.location.pathname.replace(/\/+$/, '')}/?${query}&skipWebSocketFrames=false`);
383383
socket.onopen = function (event) {
384384
socket.removeEventListener('error', errorListener);
385385
callback(undefined, new BrowserSocket(socket));
@@ -1397,7 +1397,7 @@ index 306d58f915..58c603ad3d 100644
13971397
if (definition.fontCharacter || definition.fontColor) {
13981398
let body = '';
13991399
diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts
1400-
index c28adc0ad9..4517c308da 100644
1400+
index c28adc0ad9..3d1adba3d9 100644
14011401
--- a/src/vs/workbench/workbench.web.main.ts
14021402
+++ b/src/vs/workbench/workbench.web.main.ts
14031403
@@ -128,7 +128,7 @@ import 'vs/workbench/services/extensions/browser/extensionService';
@@ -1422,3 +1422,9 @@ index c28adc0ad9..4517c308da 100644
14221422

14231423
// Output Panel
14241424
import 'vs/workbench/contrib/output/browser/output.contribution';
1425+
@@ -356,3 +356,5 @@ import 'vs/workbench/contrib/outline/browser/outline.contribution';
1426+
// import 'vs/workbench/contrib/issue/electron-browser/issue.contribution';
1427+
1428+
//#endregion
1429+
+
1430+
+import 'vs/server/src/client';

src/cli.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ import { buildHelpMessage, buildVersionMessage, options } from "vs/platform/envi
77
import pkg from "vs/platform/product/node/package";
88
import product from "vs/platform/product/node/product";
99

10-
import { AuthType, MainServer } from "vs/server/src/server";
10+
import { MainServer } from "vs/server/src/server";
1111
import "vs/server/src/tar";
12-
import { buildAllowedMessage, generateCertificate, generatePassword, open, unpackExecutables } from "vs/server/src/util";
12+
import { AuthType, buildAllowedMessage, generateCertificate, generatePassword, open, unpackExecutables } from "vs/server/src/util";
1313

1414
interface Args extends ParsedArgs {
1515
auth?: AuthType;

src/client.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import 'vs/css!./media/firefox';

src/connection.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as cp from "child_process";
2+
import * as tls from "tls";
23

34
import { getPathFromAmdModule } from "vs/base/common/amd";
45
import { VSBuffer } from "vs/base/common/buffer";
@@ -88,7 +89,7 @@ export class ExtensionHostConnection extends Connection {
8889
type: "VSCODE_EXTHOST_IPC_SOCKET",
8990
initialDataChunk: (buffer.buffer as Buffer).toString("base64"),
9091
skipWebSocketFrames: this.protocol.getSocket() instanceof NodeSocket,
91-
}, socket);
92+
}, socket instanceof tls.TLSSocket ? (<any>socket)._parent : socket);
9293
}
9394

9495
private spawn(buffer: VSBuffer): cp.ChildProcess {

src/media/firefox.css

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
@supports (-moz-appearance:none) {
2+
/*
3+
.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container .monaco-button.monaco-text-button {
4+
max-width: 100%;
5+
width: auto;
6+
}
7+
8+
.monaco-shell .screen-reader-detected-explanation .buttons a,
9+
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink,
10+
.monaco-workbench .notifications-list-container .notification-list-item .notification-list-item-buttons-container .monaco-button {
11+
max-width: -moz-fit-content;
12+
}
13+
14+
.monaco-workbench > .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit,
15+
.explorer-viewlet .panel-header .count,
16+
.extensions-viewlet > .extensions .extension > .details > .header-container > .header > .version,
17+
.debug-viewlet .debug-call-stack .stack-frame .label {
18+
min-width: -moz-fit-content;
19+
}
20+
*/
21+
}

src/protocol.ts

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import * as crypto from "crypto";
21
import * as net from "net";
32

43
import { VSBuffer } from "vs/base/common/buffer";
@@ -13,30 +12,12 @@ export interface SocketOptions {
1312
}
1413

1514
export class Protocol extends PersistentProtocol {
16-
public constructor(
17-
secWebsocketKey: string,
18-
socket: net.Socket,
19-
public readonly options: SocketOptions,
20-
) {
15+
public constructor(socket: net.Socket, public readonly options: SocketOptions) {
2116
super(
2217
options.skipWebSocketFrames
2318
? new NodeSocket(socket)
2419
: new WebSocketNodeSocket(new NodeSocket(socket)),
2520
);
26-
socket.on("error", () => socket.destroy());
27-
socket.on("end", () => socket.destroy());
28-
29-
// This magic value is specified by the websocket spec.
30-
const magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
31-
const reply = crypto.createHash("sha1")
32-
.update(secWebsocketKey + magic)
33-
.digest("base64");
34-
socket.write([
35-
"HTTP/1.1 101 Switching Protocols",
36-
"Upgrade: websocket",
37-
"Connection: Upgrade",
38-
`Sec-WebSocket-Accept: ${reply}`,
39-
].join("\r\n") + "\r\n\r\n");
4021
}
4122

4223
public getUnderlyingSocket(): net.Socket {

src/server.ts

Lines changed: 73 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import * as crypto from "crypto";
12
import * as fs from "fs";
23
import * as http from "http";
34
import * as https from "https";
@@ -55,7 +56,7 @@ import { Connection, ManagementConnection, ExtensionHostConnection } from "vs/se
5556
import { ExtensionEnvironmentChannel, FileProviderChannel , } from "vs/server/src/channel";
5657
import { TelemetryClient } from "vs/server/src/insights";
5758
import { Protocol } from "vs/server/src/protocol";
58-
import { getMediaMime, getUriTransformer } from "vs/server/src/util";
59+
import { AuthType, getMediaMime, getUriTransformer } from "vs/server/src/util";
5960

6061
export enum HttpCode {
6162
Ok = 200,
@@ -95,10 +96,6 @@ export class HttpError extends Error {
9596
}
9697
}
9798

98-
export enum AuthType {
99-
Password = "password",
100-
}
101-
10299
export interface ServerOptions {
103100
readonly auth?: AuthType;
104101
readonly basePath?: string;
@@ -140,6 +137,7 @@ export abstract class Server {
140137
if (!this.listenPromise) {
141138
this.listenPromise = new Promise((resolve, reject) => {
142139
this.server.on("error", reject);
140+
this.server.on("upgrade", this.onUpgrade);
143141
const onListen = () => resolve(this.address());
144142
if (this.options.socket) {
145143
this.server.listen(this.options.socket, onListen);
@@ -167,14 +165,20 @@ export abstract class Server {
167165
return `${this.protocol}://${endpoint}`;
168166
}
169167

168+
protected abstract handleWebSocket(
169+
socket: net.Socket,
170+
parsedUrl: url.UrlWithParsedQuery
171+
): Promise<void>;
172+
170173
protected abstract handleRequest(
171174
base: string,
172175
requestPath: string,
173176
parsedUrl: url.UrlWithParsedQuery,
174177
request: http.IncomingMessage,
175178
): Promise<Response>;
176179

177-
protected async getResource(filePath: string): Promise<Response> {
180+
protected async getResource(...parts: string[]): Promise<Response> {
181+
const filePath = path.join(...parts);
178182
return { content: await util.promisify(fs.readFile)(filePath), filePath };
179183
}
180184

@@ -205,7 +209,7 @@ export abstract class Server {
205209
return { redirect: request.url };
206210
}
207211

208-
const parsedUrl = request.url ? url.parse(request.url, true) : {} as url.UrlWithParsedQuery;
212+
const parsedUrl = request.url ? url.parse(request.url, true) : { query: {}};
209213
const fullPath = decodeURIComponent(parsedUrl.pathname || "/");
210214
const match = fullPath.match(/^(\/?[^/]*)(.*)$/);
211215
let [, base, requestPath] = match
@@ -218,15 +222,13 @@ export abstract class Server {
218222
base = "/";
219223
}
220224
base = path.normalize(base);
221-
if (requestPath !== "") { // "" will become "." with normalize.
222-
requestPath = path.normalize(requestPath);
223-
}
225+
requestPath = path.normalize(requestPath || "/index.html");
224226

225227
switch (base) {
226228
case "/":
227229
this.ensureGet(request);
228230
if (requestPath === "/favicon.ico") {
229-
return this.getResource(path.join(this.rootPath, "/out/vs/server/src/favicon", requestPath));
231+
return this.getResource(this.rootPath, "/out/vs/server/src/favicon", requestPath);
230232
} else if (!this.authenticate(request)) {
231233
return { redirect: "/login" };
232234
}
@@ -238,18 +240,53 @@ export abstract class Server {
238240
return this.tryLogin(request);
239241
}
240242
this.ensureGet(request);
241-
return this.getResource(path.join(this.rootPath, "/out/vs/server/src/login", requestPath));
243+
return this.getResource(this.rootPath, "/out/vs/server/src/login", requestPath);
242244
default:
243245
this.ensureGet(request);
244246
if (!this.authenticate(request)) {
245-
throw new HttpError(`Unauthorized`, HttpCode.Unauthorized);
247+
throw new HttpError("Unauthorized", HttpCode.Unauthorized);
246248
}
247249
break;
248250
}
249251

250252
return this.handleRequest(base, requestPath, parsedUrl, request);
251253
}
252254

255+
private onUpgrade = async (request: http.IncomingMessage, socket: net.Socket): Promise<void> => {
256+
try {
257+
await this.preHandleWebSocket(request, socket);
258+
} catch (error) {
259+
socket.destroy();
260+
console.error(error);
261+
}
262+
}
263+
264+
private preHandleWebSocket(request: http.IncomingMessage, socket: net.Socket): Promise<void> {
265+
socket.on("error", () => socket.destroy());
266+
socket.on("end", () => socket.destroy());
267+
268+
if (!this.authenticate(request)) {
269+
throw new HttpError("Unauthorized", HttpCode.Unauthorized);
270+
} else if (request.headers.upgrade !== "websocket") {
271+
throw new Error("HTTP/1.1 400 Bad Request");
272+
}
273+
274+
// This magic value is specified by the websocket spec.
275+
const magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
276+
const reply = crypto.createHash("sha1")
277+
.update(<string>request.headers["sec-websocket-key"] + magic)
278+
.digest("base64");
279+
socket.write([
280+
"HTTP/1.1 101 Switching Protocols",
281+
"Upgrade: websocket",
282+
"Connection: Upgrade",
283+
`Sec-WebSocket-Accept: ${reply}`,
284+
].join("\r\n") + "\r\n\r\n");
285+
286+
const parsedUrl = request.url ? url.parse(request.url, true) : { query: {}};
287+
return this.handleWebSocket(socket, parsedUrl);
288+
}
289+
253290
private async tryLogin(request: http.IncomingMessage): Promise<Response> {
254291
if (this.authenticate(request)) {
255292
this.ensureGet(request);
@@ -305,10 +342,7 @@ export abstract class Server {
305342
const onData = (d: Buffer): void => {
306343
body += d;
307344
if (body.length > 1e6) {
308-
onError(new HttpError(
309-
"Payload is too large",
310-
HttpCode.LargePayload,
311-
));
345+
onError(new HttpError("Payload is too large", HttpCode.LargePayload));
312346
request.connection.destroy();
313347
}
314348
};
@@ -359,16 +393,6 @@ export class MainServer extends Server {
359393

360394
public constructor(options: ServerOptions, args: ParsedArgs) {
361395
super(options);
362-
this.server.on("upgrade", async (request, socket) => {
363-
const protocol = this.createProtocol(request, socket);
364-
try {
365-
await this.connect(await protocol.handshake(), protocol);
366-
} catch (error) {
367-
protocol.sendMessage({ type: "error", reason: error.message });
368-
protocol.dispose();
369-
protocol.getSocket().dispose();
370-
}
371-
});
372396
this.servicesPromise = this.initializeServices(args);
373397
}
374398

@@ -382,6 +406,21 @@ export class MainServer extends Server {
382406
return address;
383407
}
384408

409+
protected async handleWebSocket(socket: net.Socket, parsedUrl: url.UrlWithParsedQuery): Promise<void> {
410+
const protocol = new Protocol(socket, {
411+
reconnectionToken: <string>parsedUrl.query.reconnectionToken || "",
412+
reconnection: parsedUrl.query.reconnection === "true",
413+
skipWebSocketFrames: parsedUrl.query.skipWebSocketFrames === "true",
414+
});
415+
try {
416+
await this.connect(await protocol.handshake(), protocol);
417+
} catch (error) {
418+
protocol.sendMessage({ type: "error", reason: error.message });
419+
protocol.dispose();
420+
protocol.getSocket().dispose();
421+
}
422+
}
423+
385424
protected async handleRequest(
386425
base: string,
387426
requestPath: string,
@@ -390,14 +429,15 @@ export class MainServer extends Server {
390429
): Promise<Response> {
391430
switch (base) {
392431
case "/": return this.getRoot(request, parsedUrl);
393-
case "/node_modules":
394-
case "/out":
395-
return this.getResource(path.join(this.rootPath, base, requestPath));
396432
case "/resources": return this.getResource(requestPath);
397433
case "/webview":
398-
const webviewPath = path.join(this.rootPath, "out/vs/workbench/contrib/webview/browser/pre");
399-
return this.getResource(path.join(webviewPath, requestPath || "/index.html"));
400-
default: throw new HttpError("Not found", HttpCode.NotFound);
434+
return this.getResource(
435+
this.rootPath,
436+
"out/vs/workbench/contrib/webview/browser/pre",
437+
requestPath
438+
);
439+
default:
440+
return this.getResource(this.rootPath, base, requestPath);
401441
}
402442
}
403443

@@ -440,18 +480,6 @@ export class MainServer extends Server {
440480
return { content, filePath };
441481
}
442482

443-
private createProtocol(request: http.IncomingMessage, socket: net.Socket): Protocol {
444-
if (request.headers.upgrade !== "websocket") {
445-
throw new Error("HTTP/1.1 400 Bad Request");
446-
}
447-
const query = request.url ? url.parse(request.url, true).query : {};
448-
return new Protocol(<string>request.headers["sec-websocket-key"], socket, {
449-
reconnectionToken: <string>query.reconnectionToken || "",
450-
reconnection: query.reconnection === "true",
451-
skipWebSocketFrames: query.skipWebSocketFrames === "true",
452-
});
453-
}
454-
455483
private async connect(message: ConnectionTypeRequest, protocol: Protocol): Promise<void> {
456484
switch (message.desiredConnectionType) {
457485
case ConnectionType.ExtensionHost:

src/util.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import { extname } from "vs/base/common/path";
1212
import { URITransformer, IRawURITransformer } from "vs/base/common/uriIpc";
1313
import { mkdirp } from "vs/base/node/pfs";
1414

15-
import { AuthType } from "vs/server/src/server";
15+
export enum AuthType {
16+
Password = "password",
17+
}
1618

1719
export const tmpdir = path.join(os.tmpdir(), "code-server");
1820

0 commit comments

Comments
 (0)