Target a recent commit for VS Code
This is so we can try out the web worker extension host.
This commit is contained in:
parent
624a4c08b9
commit
b901043bfc
@ -8,14 +8,14 @@ matrix:
|
|||||||
- os: linux
|
- os: linux
|
||||||
dist: trusty
|
dist: trusty
|
||||||
env:
|
env:
|
||||||
- VSCODE_VERSION="1.37.0" MAJOR_VERSION="2" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER" TARGET="linux"
|
- VSCODE_VERSION="20dd80d91a76cbe80fb1d1979c3a9f02d94ba161" MAJOR_VERSION="2" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER" TARGET="linux"
|
||||||
- os: linux
|
- os: linux
|
||||||
dist: trusty
|
dist: trusty
|
||||||
env:
|
env:
|
||||||
- VSCODE_VERSION="1.37.0" MAJOR_VERSION="2" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER" TARGET="alpine"
|
- VSCODE_VERSION="20dd80d91a76cbe80fb1d1979c3a9f02d94ba161" MAJOR_VERSION="2" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER" TARGET="alpine"
|
||||||
- os: osx
|
- os: osx
|
||||||
env:
|
env:
|
||||||
- VSCODE_VERSION="1.37.0" MAJOR_VERSION="2" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER"
|
- VSCODE_VERSION="20dd80d91a76cbe80fb1d1979c3a9f02d94ba161" MAJOR_VERSION="2" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER"
|
||||||
before_install:
|
before_install:
|
||||||
- if [[ "$TRAVIS_BRANCH" == "master" ]]; then export MINIFY="true"; fi
|
- if [[ "$TRAVIS_BRANCH" == "master" ]]; then export MINIFY="true"; fi
|
||||||
- if [[ "$TRAVIS_BRANCH" == "master" ]]; then export PACKAGE="true"; fi
|
- if [[ "$TRAVIS_BRANCH" == "master" ]]; then export PACKAGE="true"; fi
|
||||||
|
26
README.md
26
README.md
@ -81,7 +81,7 @@ data collected to improve code-server.
|
|||||||
```shell
|
```shell
|
||||||
git clone https://github.com/microsoft/vscode
|
git clone https://github.com/microsoft/vscode
|
||||||
cd vscode
|
cd vscode
|
||||||
git checkout 1.37.0
|
git checkout <see travis.yml for the VS Code version to use here>
|
||||||
git clone https://github.com/cdr/code-server src/vs/server
|
git clone https://github.com/cdr/code-server src/vs/server
|
||||||
cd src/vs/server
|
cd src/vs/server
|
||||||
yarn patch:apply
|
yarn patch:apply
|
||||||
@ -98,30 +98,28 @@ If you run into issues about a different version of Node being used, try running
|
|||||||
`vscode-ripgrep`.
|
`vscode-ripgrep`.
|
||||||
|
|
||||||
### Upgrading VS Code
|
### Upgrading VS Code
|
||||||
We have to patch VS Code to provide and fix some functionality. As the web
|
We patch VS Code to provide and fix some functionality. As the web portion of VS
|
||||||
portion of VS Code matures, we'll be able to shrink and maybe even entirely
|
Code matures, we'll be able to shrink and maybe even entirely eliminate our
|
||||||
eliminate our patch. In the meantime, however, upgrading the VS Code version
|
patch. In the meantime, however, upgrading the VS Code version requires ensuring
|
||||||
requires ensuring that the patch still applies and has the intended effects.
|
that the patch still applies and has the intended effects.
|
||||||
|
|
||||||
To generate a new patch, **stage all the changes** you want to be included in
|
To generate a new patch, **stage all the changes** you want to be included in
|
||||||
the patch in the VS Code source, then run `yarn patch:generate` in this
|
the patch in the VS Code source, then run `yarn patch:generate` in this
|
||||||
directory.
|
directory.
|
||||||
|
|
||||||
Our changes include:
|
Our changes include:
|
||||||
- Add a `code-server` schema.
|
- Change the remote schema to `code-server`.
|
||||||
- Allow multiple extension directories (both user and built-in).
|
- Allow multiple extension directories (both user and built-in).
|
||||||
- Modify the loader, websocket, webview, service worker, and asset requests to
|
- Modify the loader, websocket, webview, service worker, and asset requests to
|
||||||
use the URL of the page as a base (and TLS if necessary for the websocket).
|
use the URL of the page as a base (and TLS if necessary for the websocket).
|
||||||
- Send client-side telemetry through the server.
|
- Send client-side telemetry through the server and get the initial log level
|
||||||
- Add a file prefix to ignore for temporary files created during upload.
|
from the server.
|
||||||
- Insert our upload service for use in editor windows and explorer.
|
- Add an upload service for use in editor windows and the explorer along with a
|
||||||
- Modify the log level to get its initial setting from the server.
|
file prefix to ignore for temporary files created during upload.
|
||||||
- Change a regular expression used for mnemonics so it works on Firefox.
|
- Make changing the display language work.
|
||||||
|
- Make hiding or toggling the menu bar possible.
|
||||||
- Make it possible for us to load code on the client.
|
- Make it possible for us to load code on the client.
|
||||||
- Modify the build process to include our code.
|
- Modify the build process to include our code.
|
||||||
- Fix a CSP issue within webviews.
|
|
||||||
- Fix an issue displaying extension contributions.
|
|
||||||
- Make changing the display language work.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
[MIT](LICENSE)
|
[MIT](LICENSE)
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -6,7 +6,7 @@ import { OS } from "vs/base/common/platform";
|
|||||||
import { URI, UriComponents } from "vs/base/common/uri";
|
import { URI, UriComponents } from "vs/base/common/uri";
|
||||||
import { transformOutgoingURIs } from "vs/base/common/uriIpc";
|
import { transformOutgoingURIs } from "vs/base/common/uriIpc";
|
||||||
import { IServerChannel } from "vs/base/parts/ipc/common/ipc";
|
import { IServerChannel } from "vs/base/parts/ipc/common/ipc";
|
||||||
import { IDiagnosticInfo } from "vs/platform/diagnostics/common/diagnosticsService";
|
import { IDiagnosticInfo } from "vs/platform/diagnostics/common/diagnostics";
|
||||||
import { IEnvironmentService } from "vs/platform/environment/common/environment";
|
import { IEnvironmentService } from "vs/platform/environment/common/environment";
|
||||||
import { ExtensionIdentifier, IExtensionDescription } from "vs/platform/extensions/common/extensions";
|
import { ExtensionIdentifier, IExtensionDescription } from "vs/platform/extensions/common/extensions";
|
||||||
import { FileDeleteOptions, FileOpenOptions, FileOverwriteOptions, FileType, IStat, IWatchOptions } from "vs/platform/files/common/files";
|
import { FileDeleteOptions, FileOpenOptions, FileOverwriteOptions, FileType, IStat, IWatchOptions } from "vs/platform/files/common/files";
|
||||||
@ -163,11 +163,10 @@ export class FileProviderChannel implements IServerChannel, IDisposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private transform(resource: UriComponents): URI {
|
private transform(resource: UriComponents): URI {
|
||||||
// HACK: for now assume /out is relative to the build (used for the
|
// Used for walkthrough content.
|
||||||
// walkthrough content).
|
if (resource.path.indexOf("/static") === 0) {
|
||||||
if (resource.path.indexOf("/out") === 0) {
|
return URI.file(this.environmentService.appRoot + resource.path.replace(/^\/static/, ""));
|
||||||
return URI.file(this.environmentService.appRoot + resource.path);
|
// Used by the webview service worker to load resources.
|
||||||
// This is used by the webview service worker to load resources.
|
|
||||||
} else if (resource.path === "/vscode-resource" && resource.query) {
|
} else if (resource.path === "/vscode-resource" && resource.query) {
|
||||||
try {
|
try {
|
||||||
const query = JSON.parse(resource.query);
|
const query = JSON.parse(resource.query);
|
||||||
@ -185,6 +184,7 @@ export class ExtensionEnvironmentChannel implements IServerChannel {
|
|||||||
private readonly environment: IEnvironmentService,
|
private readonly environment: IEnvironmentService,
|
||||||
private readonly log: ILogService,
|
private readonly log: ILogService,
|
||||||
private readonly telemetry: ITelemetryService,
|
private readonly telemetry: ITelemetryService,
|
||||||
|
private readonly connectionToken: string,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public listen(_: unknown, event: string): Event<any> {
|
public listen(_: unknown, event: string): Event<any> {
|
||||||
@ -207,6 +207,7 @@ export class ExtensionEnvironmentChannel implements IServerChannel {
|
|||||||
private async getEnvironmentData(locale: string): Promise<IRemoteAgentEnvironment> {
|
private async getEnvironmentData(locale: string): Promise<IRemoteAgentEnvironment> {
|
||||||
return {
|
return {
|
||||||
pid: process.pid,
|
pid: process.pid,
|
||||||
|
connectionToken: this.connectionToken,
|
||||||
appRoot: URI.file(this.environment.appRoot),
|
appRoot: URI.file(this.environment.appRoot),
|
||||||
appSettingsHome: this.environment.appSettingsHome,
|
appSettingsHome: this.environment.appSettingsHome,
|
||||||
settingsPath: this.environment.machineSettingsHome,
|
settingsPath: this.environment.machineSettingsHome,
|
||||||
|
@ -7,6 +7,7 @@ import { LocalizationsService } from "vs/platform/localizations/electron-browser
|
|||||||
import { IUpdateService } from "vs/platform/update/common/update";
|
import { IUpdateService } from "vs/platform/update/common/update";
|
||||||
import { UpdateService } from "vs/platform/update/electron-browser/updateService";
|
import { UpdateService } from "vs/platform/update/electron-browser/updateService";
|
||||||
import { TelemetryChannelClient } from "vs/server/src/telemetry";
|
import { TelemetryChannelClient } from "vs/server/src/telemetry";
|
||||||
|
import { IUploadService, UploadService } from 'vs/server/src/upload';
|
||||||
import { IRemoteAgentService } from "vs/workbench/services/remote/common/remoteAgentService";
|
import { IRemoteAgentService } from "vs/workbench/services/remote/common/remoteAgentService";
|
||||||
|
|
||||||
class TelemetryService extends TelemetryChannelClient {
|
class TelemetryService extends TelemetryChannelClient {
|
||||||
@ -18,12 +19,13 @@ class TelemetryService extends TelemetryChannelClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
registerSingleton(ILocalizationsService, LocalizationsService);
|
registerSingleton(ILocalizationsService, LocalizationsService);
|
||||||
registerSingleton(IUpdateService, UpdateService);
|
|
||||||
registerSingleton(ITelemetryService, TelemetryService);
|
registerSingleton(ITelemetryService, TelemetryService);
|
||||||
|
registerSingleton(IUpdateService, UpdateService);
|
||||||
|
registerSingleton(IUploadService, UploadService, true);
|
||||||
|
|
||||||
import "vs/workbench/contrib/update/electron-browser/update.contribution";
|
import "vs/workbench/contrib/update/electron-browser/update.contribution";
|
||||||
|
import 'vs/workbench/contrib/localizations/browser/localizations.contribution';
|
||||||
|
|
||||||
import "vs/css!./media/firefox";
|
|
||||||
import { coderApi, vscodeApi } from "vs/server/src/api";
|
import { coderApi, vscodeApi } from "vs/server/src/api";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,104 +1,12 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
|
||||||
|
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'self' 'unsafe-inline'; script-src 'unsafe-inline'; manifest-src 'self'; img-src 'self';">
|
||||||
<title>Authenticate: code-server</title>
|
<title>Authenticate: code-server</title>
|
||||||
<style>
|
<link rel="icon" href="./favicon.ico" type="image/x-icon" />
|
||||||
html {
|
<link rel="manifest" href="./manifest.json">
|
||||||
box-sizing: border-box;
|
<link href="./static/out/vs/server/src/media/login.css" rel="stylesheet">
|
||||||
}
|
|
||||||
|
|
||||||
*, *:before, *:after {
|
|
||||||
box-sizing: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
html, body {
|
|
||||||
background-color: #FFFFFF;
|
|
||||||
height: 100%;
|
|
||||||
min-height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
font-family: "monospace";
|
|
||||||
justify-content: center;
|
|
||||||
margin: 0;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form {
|
|
||||||
border-radius: 5px;
|
|
||||||
box-shadow: 0 18px 80px 10px rgba(69, 65, 78, 0.08);
|
|
||||||
color: #575962;
|
|
||||||
margin-top: -10%;
|
|
||||||
max-width: 328px;
|
|
||||||
padding: 40px;
|
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form > .title {
|
|
||||||
text-align: center;
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 500;
|
|
||||||
letter-spacing: 1.5px;
|
|
||||||
line-height: 15px;
|
|
||||||
margin-bottom: 0px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
margin-top: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form > .subtitle {
|
|
||||||
font-size: 19px;
|
|
||||||
font-weight: bold;
|
|
||||||
line-height: 25px;
|
|
||||||
margin-bottom: 45px;
|
|
||||||
margin: 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form > .field {
|
|
||||||
text-align: left;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #797E84;
|
|
||||||
margin: 16px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form > .field > .input {
|
|
||||||
background: none !important;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 2px;
|
|
||||||
padding: 5px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form > .button {
|
|
||||||
border: none;
|
|
||||||
border-radius: 24px;
|
|
||||||
box-shadow: 0 12px 17px 2px rgba(171,173,163,0.14), 0 5px 22px 4px rgba(171,173,163,0.12), 0 7px 8px -4px rgba(171,173,163,0.2);
|
|
||||||
cursor: pointer;
|
|
||||||
display: block;
|
|
||||||
padding: 15px 5px;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login-form > .button:hover {
|
|
||||||
background-color: rgb(0, 122, 204);
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.error-display {
|
|
||||||
box-sizing: border-box;
|
|
||||||
color: #bb2d0f;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: 400;
|
|
||||||
line-height: 12px;
|
|
||||||
padding: 20px 8px 0;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<form class="login-form" method="post">
|
<form class="login-form" method="post">
|
||||||
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
@ -1,14 +0,0 @@
|
|||||||
@supports (-moz-appearance:none) {
|
|
||||||
/* Firefox doesn't support -webkit-margin-{before/after} so use margin. */
|
|
||||||
/* These are the collapsible section headings in a sidebar panel. */
|
|
||||||
.monaco-panel-view .panel > .panel-header h3.title {
|
|
||||||
margin-bottom: 0;
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Firefox doesn't seem to support fit-content. */
|
|
||||||
/* These are the file tabs. */
|
|
||||||
.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit {
|
|
||||||
min-width: -moz-fit-content;
|
|
||||||
}
|
|
||||||
}
|
|
94
src/media/login.css
Normal file
94
src/media/login.css
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
html {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
*, *:before, *:after {
|
||||||
|
box-sizing: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
font-family: "monospace";
|
||||||
|
justify-content: center;
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form {
|
||||||
|
border-radius: 5px;
|
||||||
|
box-shadow: 0 18px 80px 10px rgba(69, 65, 78, 0.08);
|
||||||
|
color: #575962;
|
||||||
|
margin-top: -10%;
|
||||||
|
max-width: 328px;
|
||||||
|
padding: 40px;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form > .title {
|
||||||
|
text-align: center;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 1.5px;
|
||||||
|
line-height: 15px;
|
||||||
|
margin-bottom: 0px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
margin-top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form > .subtitle {
|
||||||
|
font-size: 19px;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 25px;
|
||||||
|
margin-bottom: 45px;
|
||||||
|
margin: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form > .field {
|
||||||
|
text-align: left;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #797E84;
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form > .field > .input {
|
||||||
|
background: none !important;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 5px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form > .button {
|
||||||
|
border: none;
|
||||||
|
border-radius: 24px;
|
||||||
|
box-shadow: 0 12px 17px 2px rgba(171,173,163,0.14), 0 5px 22px 4px rgba(171,173,163,0.12), 0 7px 8px -4px rgba(171,173,163,0.2);
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
padding: 15px 5px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-form > .button:hover {
|
||||||
|
background-color: rgb(0, 122, 204);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-display {
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: #bb2d0f;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 12px;
|
||||||
|
padding: 20px 8px 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
13
src/media/manifest.json
Normal file
13
src/media/manifest.json
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"name": "code-server",
|
||||||
|
"short_name": "code-server",
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"background-color": "#fff",
|
||||||
|
"description": "Run VS Code on a remote server.",
|
||||||
|
"icons": [{
|
||||||
|
"src": "static/code-server.png",
|
||||||
|
"sizes": "128x128",
|
||||||
|
"type": "image/png"
|
||||||
|
}]
|
||||||
|
}
|
104
src/server.ts
104
src/server.ts
@ -41,10 +41,9 @@ import { LocalizationsChannel } from "vs/platform/localizations/node/localizatio
|
|||||||
import { getLogLevel, ILogService } from "vs/platform/log/common/log";
|
import { getLogLevel, ILogService } from "vs/platform/log/common/log";
|
||||||
import { LogLevelSetterChannel } from "vs/platform/log/common/logIpc";
|
import { LogLevelSetterChannel } from "vs/platform/log/common/logIpc";
|
||||||
import { SpdLogService } from "vs/platform/log/node/spdlogService";
|
import { SpdLogService } from "vs/platform/log/node/spdlogService";
|
||||||
import { IProductConfiguration, IProductService } from "vs/platform/product/common/product";
|
import { IProductService } from "vs/platform/product/common/product";
|
||||||
import pkg from "vs/platform/product/node/package";
|
import pkg from "vs/platform/product/node/package";
|
||||||
import product from "vs/platform/product/node/product";
|
import product from "vs/platform/product/node/product";
|
||||||
import { ProductService } from "vs/platform/product/node/productService";
|
|
||||||
import { ConnectionType, ConnectionTypeRequest } from "vs/platform/remote/common/remoteAgentConnection";
|
import { ConnectionType, ConnectionTypeRequest } from "vs/platform/remote/common/remoteAgentConnection";
|
||||||
import { REMOTE_FILE_SYSTEM_CHANNEL_NAME } from "vs/platform/remote/common/remoteAgentFileSystemChannel";
|
import { REMOTE_FILE_SYSTEM_CHANNEL_NAME } from "vs/platform/remote/common/remoteAgentFileSystemChannel";
|
||||||
import { IRequestService } from "vs/platform/request/common/request";
|
import { IRequestService } from "vs/platform/request/common/request";
|
||||||
@ -81,8 +80,6 @@ export enum HttpCode {
|
|||||||
export interface Options {
|
export interface Options {
|
||||||
WORKBENCH_WEB_CONGIGURATION: IWorkbenchConstructionOptions;
|
WORKBENCH_WEB_CONGIGURATION: IWorkbenchConstructionOptions;
|
||||||
REMOTE_USER_DATA_URI: UriComponents | URI;
|
REMOTE_USER_DATA_URI: UriComponents | URI;
|
||||||
PRODUCT_CONFIGURATION: IProductConfiguration | null;
|
|
||||||
CONNECTION_AUTH_TOKEN: string;
|
|
||||||
NLS_CONFIGURATION: NLSConfiguration;
|
NLS_CONFIGURATION: NLSConfiguration;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,6 +107,7 @@ export class HttpError extends Error {
|
|||||||
export interface ServerOptions {
|
export interface ServerOptions {
|
||||||
readonly auth?: AuthType;
|
readonly auth?: AuthType;
|
||||||
readonly basePath?: string;
|
readonly basePath?: string;
|
||||||
|
readonly connectionToken?: string;
|
||||||
readonly cert?: string;
|
readonly cert?: string;
|
||||||
readonly certKey?: string;
|
readonly certKey?: string;
|
||||||
readonly folderUri?: string;
|
readonly folderUri?: string;
|
||||||
@ -122,6 +120,8 @@ export interface ServerOptions {
|
|||||||
export abstract class Server {
|
export abstract class Server {
|
||||||
protected readonly server: http.Server | https.Server;
|
protected readonly server: http.Server | https.Server;
|
||||||
protected rootPath = path.resolve(__dirname, "../../../..");
|
protected rootPath = path.resolve(__dirname, "../../../..");
|
||||||
|
protected serverRoot = path.join(this.rootPath, "/out/vs/server/src");
|
||||||
|
protected readonly allowedRequestPaths: string[] = [this.rootPath];
|
||||||
private listenPromise: Promise<string> | undefined;
|
private listenPromise: Promise<string> | undefined;
|
||||||
public readonly protocol: "http" | "https";
|
public readonly protocol: "http" | "https";
|
||||||
public readonly options: ServerOptions;
|
public readonly options: ServerOptions;
|
||||||
@ -185,6 +185,9 @@ export abstract class Server {
|
|||||||
|
|
||||||
protected async getResource(...parts: string[]): Promise<Response> {
|
protected async getResource(...parts: string[]): Promise<Response> {
|
||||||
const filePath = path.join(...parts);
|
const filePath = path.join(...parts);
|
||||||
|
if (!this.isAllowedRequestPath(filePath)) {
|
||||||
|
throw new HttpError("Unauthorized", HttpCode.Unauthorized);
|
||||||
|
}
|
||||||
return { content: await util.promisify(fs.readFile)(filePath), filePath };
|
return { content: await util.promisify(fs.readFile)(filePath), filePath };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,10 +196,20 @@ export abstract class Server {
|
|||||||
return `${this.protocol}://${request.headers.host}${this.options.basePath}${path}${split.length === 2 ? `?${split[1]}` : ""}`;
|
return `${this.protocol}://${request.headers.host}${this.options.basePath}${path}${split.length === 2 ? `?${split[1]}` : ""}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private isAllowedRequestPath(path: string): boolean {
|
||||||
|
for (let i = 0; i < this.allowedRequestPaths.length; ++i) {
|
||||||
|
if (path.indexOf(this.allowedRequestPaths[i]) === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private onRequest = async (request: http.IncomingMessage, response: http.ServerResponse): Promise<void> => {
|
private onRequest = async (request: http.IncomingMessage, response: http.ServerResponse): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const payload = await this.preHandleRequest(request);
|
const payload = await this.preHandleRequest(request);
|
||||||
response.writeHead(payload.redirect ? HttpCode.Redirect : payload.code || HttpCode.Ok, {
|
response.writeHead(payload.redirect ? HttpCode.Redirect : payload.code || HttpCode.Ok, {
|
||||||
|
// "Cache-Control": "public, max-age=31536000",
|
||||||
"Content-Type": getMediaMime(payload.filePath),
|
"Content-Type": 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 || "/" } : {}),
|
||||||
@ -233,25 +246,29 @@ export abstract class Server {
|
|||||||
base = path.normalize(base);
|
base = path.normalize(base);
|
||||||
requestPath = path.normalize(requestPath || "/index.html");
|
requestPath = path.normalize(requestPath || "/index.html");
|
||||||
|
|
||||||
|
if (base !== "/login" || !this.options.auth || requestPath !== "/index.html") {
|
||||||
|
this.ensureGet(request);
|
||||||
|
}
|
||||||
|
|
||||||
switch (base) {
|
switch (base) {
|
||||||
case "/":
|
case "/":
|
||||||
this.ensureGet(request);
|
switch (requestPath) {
|
||||||
if (requestPath === "/favicon.ico") {
|
case "/favicon.ico":
|
||||||
return this.getResource(this.rootPath, "/out/vs/server/src/favicon", requestPath);
|
case "/manifest.json":
|
||||||
} else if (!this.authenticate(request)) {
|
return this.getResource(this.serverRoot, "media", requestPath);
|
||||||
|
}
|
||||||
|
if (!this.authenticate(request)) {
|
||||||
return { redirect: "/login" };
|
return { redirect: "/login" };
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "/static":
|
||||||
|
return this.getResource(this.rootPath, requestPath);
|
||||||
case "/login":
|
case "/login":
|
||||||
if (!this.options.auth) {
|
if (!this.options.auth || requestPath !== "/index.html") {
|
||||||
throw new HttpError("Not found", HttpCode.NotFound);
|
throw new HttpError("Not found", HttpCode.NotFound);
|
||||||
} else if (requestPath === "/index.html") {
|
|
||||||
return this.tryLogin(request);
|
|
||||||
}
|
}
|
||||||
this.ensureGet(request);
|
return this.tryLogin(request);
|
||||||
return this.getResource(this.rootPath, "/out/vs/server/src/login", requestPath);
|
|
||||||
default:
|
default:
|
||||||
this.ensureGet(request);
|
|
||||||
if (!this.authenticate(request)) {
|
if (!this.authenticate(request)) {
|
||||||
throw new HttpError("Unauthorized", HttpCode.Unauthorized);
|
throw new HttpError("Unauthorized", HttpCode.Unauthorized);
|
||||||
}
|
}
|
||||||
@ -274,6 +291,7 @@ export abstract class Server {
|
|||||||
socket.on("error", () => socket.destroy());
|
socket.on("error", () => socket.destroy());
|
||||||
socket.on("end", () => socket.destroy());
|
socket.on("end", () => socket.destroy());
|
||||||
|
|
||||||
|
this.ensureGet(request);
|
||||||
if (!this.authenticate(request)) {
|
if (!this.authenticate(request)) {
|
||||||
throw new HttpError("Unauthorized", HttpCode.Unauthorized);
|
throw new HttpError("Unauthorized", HttpCode.Unauthorized);
|
||||||
} else if (request.headers.upgrade !== "websocket") {
|
} else if (request.headers.upgrade !== "websocket") {
|
||||||
@ -297,8 +315,7 @@ export abstract class Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async tryLogin(request: http.IncomingMessage): Promise<Response> {
|
private async tryLogin(request: http.IncomingMessage): Promise<Response> {
|
||||||
if (this.authenticate(request)) {
|
if (this.authenticate(request) && (request.method === "GET" || request.method === "POST")) {
|
||||||
this.ensureGet(request);
|
|
||||||
return { redirect: "/" };
|
return { redirect: "/" };
|
||||||
}
|
}
|
||||||
if (request.method === "POST") {
|
if (request.method === "POST") {
|
||||||
@ -322,7 +339,7 @@ export abstract class Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async getLogin(error: string = "", payload?: LoginPayload): Promise<Response> {
|
private async getLogin(error: string = "", payload?: LoginPayload): Promise<Response> {
|
||||||
const filePath = path.join(this.rootPath, "out/vs/server/src/login/index.html");
|
const filePath = path.join(this.serverRoot, "login/index.html");
|
||||||
const content = (await util.promisify(fs.readFile)(filePath, "utf8"))
|
const content = (await util.promisify(fs.readFile)(filePath, "utf8"))
|
||||||
.replace("{{ERROR}}", error)
|
.replace("{{ERROR}}", error)
|
||||||
.replace("display:none", error ? "display:block" : "display:none")
|
.replace("display:none", error ? "display:block" : "display:none")
|
||||||
@ -446,24 +463,12 @@ export class MainServer extends Server {
|
|||||||
): Promise<Response> {
|
): Promise<Response> {
|
||||||
switch (base) {
|
switch (base) {
|
||||||
case "/": return this.getRoot(request, parsedUrl);
|
case "/": return this.getRoot(request, parsedUrl);
|
||||||
case "/resources":
|
case "/resource":
|
||||||
case "/vscode-resources":
|
case "/vscode-remote-resource":
|
||||||
if (requestPath === "/fetch") {
|
if (typeof parsedUrl.query.path === "string") {
|
||||||
if (typeof parsedUrl.query.u === "string") {
|
return this.getResource(parsedUrl.query.path);
|
||||||
return this.getResource(JSON.parse(parsedUrl.query.u).path);
|
|
||||||
}
|
}
|
||||||
// For some reason VS Code encodes the = so the query doesn't parse
|
break;
|
||||||
// correctly. We'll look through what's available and try to find it.
|
|
||||||
for (let value in parsedUrl.query) {
|
|
||||||
if (value && typeof value === "string") {
|
|
||||||
const query = querystring.parse(value);
|
|
||||||
if (typeof query.u === "string") {
|
|
||||||
return this.getResource(JSON.parse(query.u).path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new HttpError("Not found", HttpCode.NotFound);
|
|
||||||
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/, ""));
|
||||||
@ -473,9 +478,8 @@ export class MainServer extends Server {
|
|||||||
"out/vs/workbench/contrib/webview/browser/pre",
|
"out/vs/workbench/contrib/webview/browser/pre",
|
||||||
requestPath
|
requestPath
|
||||||
);
|
);
|
||||||
default:
|
|
||||||
return this.getResource(this.rootPath, base, requestPath);
|
|
||||||
}
|
}
|
||||||
|
throw new HttpError("Not found", HttpCode.NotFound);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getRoot(request: http.IncomingMessage, parsedUrl: url.UrlWithParsedQuery): Promise<Response> {
|
private async getRoot(request: http.IncomingMessage, parsedUrl: url.UrlWithParsedQuery): Promise<Response> {
|
||||||
@ -502,16 +506,11 @@ export class MainServer extends Server {
|
|||||||
? transformer.transformOutgoing(URI.file(sanitizeFilePath(folderPath, cwd)))
|
? transformer.transformOutgoing(URI.file(sanitizeFilePath(folderPath, cwd)))
|
||||||
: undefined,
|
: undefined,
|
||||||
remoteAuthority,
|
remoteAuthority,
|
||||||
|
productConfiguration: product,
|
||||||
},
|
},
|
||||||
REMOTE_USER_DATA_URI: transformer.transformOutgoing(
|
REMOTE_USER_DATA_URI: transformer.transformOutgoing(
|
||||||
(this.services.get(IEnvironmentService) as EnvironmentService).webUserDataHome,
|
(this.services.get(IEnvironmentService) as EnvironmentService).webUserDataHome,
|
||||||
),
|
),
|
||||||
PRODUCT_CONFIGURATION: {
|
|
||||||
...product,
|
|
||||||
// @ts-ignore workaround for getting the VS Code version to the browser.
|
|
||||||
version: pkg.version,
|
|
||||||
},
|
|
||||||
CONNECTION_AUTH_TOKEN: "",
|
|
||||||
NLS_CONFIGURATION: await getNlsConfiguration(locale, environment.userDataPath),
|
NLS_CONFIGURATION: await getNlsConfiguration(locale, environment.userDataPath),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -586,6 +585,14 @@ export class MainServer extends Server {
|
|||||||
const fileService = new FileService(logService);
|
const fileService = new FileService(logService);
|
||||||
fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(logService));
|
fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(logService));
|
||||||
|
|
||||||
|
this.allowedRequestPaths.push(
|
||||||
|
path.join(environmentService.userDataPath, "clp"), // Language packs.
|
||||||
|
environmentService.extensionsPath,
|
||||||
|
environmentService.builtinExtensionsPath,
|
||||||
|
...environmentService.extraExtensionPaths,
|
||||||
|
...environmentService.extraBuiltinExtensionPaths,
|
||||||
|
);
|
||||||
|
|
||||||
this.ipc.registerChannel("loglevel", new LogLevelSetterChannel(logService));
|
this.ipc.registerChannel("loglevel", new LogLevelSetterChannel(logService));
|
||||||
this.ipc.registerChannel(ExtensionHostDebugBroadcastChannel.ChannelName, new ExtensionHostDebugBroadcastChannel());
|
this.ipc.registerChannel(ExtensionHostDebugBroadcastChannel.ChannelName, new ExtensionHostDebugBroadcastChannel());
|
||||||
|
|
||||||
@ -595,7 +602,7 @@ export class MainServer extends Server {
|
|||||||
this.services.set(IConfigurationService, new SyncDescriptor(ConfigurationService, [environmentService.machineSettingsResource]));
|
this.services.set(IConfigurationService, new SyncDescriptor(ConfigurationService, [environmentService.machineSettingsResource]));
|
||||||
this.services.set(IRequestService, new SyncDescriptor(RequestService));
|
this.services.set(IRequestService, new SyncDescriptor(RequestService));
|
||||||
this.services.set(IFileService, fileService);
|
this.services.set(IFileService, fileService);
|
||||||
this.services.set(IProductService, new SyncDescriptor(ProductService));
|
this.services.set(IProductService, { _serviceBrand: undefined, ...product });
|
||||||
this.services.set(IDialogService, new DialogChannelClient(this.ipc.getChannel("dialog", router)));
|
this.services.set(IDialogService, new DialogChannelClient(this.ipc.getChannel("dialog", router)));
|
||||||
this.services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService));
|
this.services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService));
|
||||||
this.services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService));
|
this.services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService));
|
||||||
@ -608,14 +615,9 @@ export class MainServer extends Server {
|
|||||||
),
|
),
|
||||||
commonProperties: resolveCommonProperties(
|
commonProperties: resolveCommonProperties(
|
||||||
product.commit, pkg.codeServerVersion, await getMachineId(),
|
product.commit, pkg.codeServerVersion, await getMachineId(),
|
||||||
environmentService.installSourcePath, "code-server",
|
[], environmentService.installSourcePath, "code-server",
|
||||||
),
|
),
|
||||||
piiPaths: [
|
piiPaths: this.allowedRequestPaths,
|
||||||
environmentService.appRoot,
|
|
||||||
environmentService.extensionsPath,
|
|
||||||
...environmentService.extraExtensionPaths,
|
|
||||||
...environmentService.extraBuiltinExtensionPaths,
|
|
||||||
],
|
|
||||||
} as ITelemetryServiceConfig]));
|
} as ITelemetryServiceConfig]));
|
||||||
} else {
|
} else {
|
||||||
this.services.set(ITelemetryService, NullTelemetryService);
|
this.services.set(ITelemetryService, NullTelemetryService);
|
||||||
@ -633,7 +635,7 @@ export class MainServer extends Server {
|
|||||||
const telemetryService = this.services.get(ITelemetryService) as ITelemetryService;
|
const telemetryService = this.services.get(ITelemetryService) as ITelemetryService;
|
||||||
|
|
||||||
const extensionsChannel = new ExtensionManagementChannel(extensionsService, (context) => getUriTransformer(context.remoteAuthority));
|
const extensionsChannel = new ExtensionManagementChannel(extensionsService, (context) => getUriTransformer(context.remoteAuthority));
|
||||||
const extensionsEnvironmentChannel = new ExtensionEnvironmentChannel(environmentService, logService, telemetryService);
|
const extensionsEnvironmentChannel = new ExtensionEnvironmentChannel(environmentService, logService, telemetryService, this.options.connectionToken || "");
|
||||||
const fileChannel = new FileProviderChannel(environmentService, logService);
|
const fileChannel = new FileProviderChannel(environmentService, logService);
|
||||||
const requestChannel = new RequestChannel(this.services.get(IRequestService) as IRequestService);
|
const requestChannel = new RequestChannel(this.services.get(IRequestService) as IRequestService);
|
||||||
const telemetryChannel = new TelemetryChannel(telemetryService);
|
const telemetryChannel = new TelemetryChannel(telemetryService);
|
||||||
|
Reference in New Issue
Block a user