Add (unimplemented) webview server
This commit is contained in:
parent
3a78c0964f
commit
6a35ab1dc0
14
entry.ts
14
entry.ts
@ -1,4 +1,12 @@
|
|||||||
import { Server } from "./server";
|
import { MainServer, WebviewServer } from "./server";
|
||||||
|
|
||||||
const server = new Server();
|
const webviewServer = new WebviewServer();
|
||||||
server.listen();
|
const server = new MainServer(webviewServer);
|
||||||
|
webviewServer.listen(8444).then(async () => {
|
||||||
|
await server.listen(8443);
|
||||||
|
console.log(`Main server serving ${server.address}`);
|
||||||
|
console.log(`Webview server serving ${webviewServer.address}`);
|
||||||
|
}).catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
169
server.ts
169
server.ts
@ -54,30 +54,24 @@ export class HttpError extends Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Server {
|
export abstract class Server {
|
||||||
// Used to notify the IPC server that there is a new client.
|
// The underlying web server.
|
||||||
public readonly _onDidClientConnect = new Emitter<ClientConnectionEvent>();
|
protected readonly server: http.Server;
|
||||||
public readonly onDidClientConnect = this._onDidClientConnect.event;
|
|
||||||
|
|
||||||
private readonly rootPath = path.resolve(__dirname, "../../..");
|
|
||||||
|
|
||||||
// This is separate instead of just extending this class since we can't
|
|
||||||
// use properties in the super call. This manages channels.
|
|
||||||
private readonly ipc = new IPCServer(this.onDidClientConnect);
|
|
||||||
|
|
||||||
// The web server.
|
|
||||||
private readonly server: http.Server;
|
|
||||||
|
|
||||||
private readonly environmentService: EnvironmentService;
|
|
||||||
private readonly logService: ILogService;
|
|
||||||
|
|
||||||
// Persistent connections. These can reconnect within a timeout.
|
|
||||||
private readonly connections = new Map<ConnectionType, Map<string, Connection>>();
|
|
||||||
|
|
||||||
public constructor() {
|
public constructor() {
|
||||||
this.server = http.createServer(async (request, response): Promise<void> => {
|
this.server = http.createServer(async (request, response): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const [content, headers] = await this.handleRequest(request);
|
if (request.method !== "GET") {
|
||||||
|
throw new HttpError(
|
||||||
|
`Unsupported method ${request.method}`,
|
||||||
|
HttpCode.BadRequest,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedUrl = url.parse(request.url || "", true);
|
||||||
|
const requestPath = parsedUrl.pathname || "/";
|
||||||
|
|
||||||
|
const [content, headers] = await this.handleRequest(request, parsedUrl, requestPath);
|
||||||
response.writeHead(HttpCode.Ok, {
|
response.writeHead(HttpCode.Ok, {
|
||||||
"Cache-Control": "max-age=86400",
|
"Cache-Control": "max-age=86400",
|
||||||
// TODO: ETag?
|
// TODO: ETag?
|
||||||
@ -89,6 +83,48 @@ export class Server {
|
|||||||
response.end(error.message);
|
response.end(error.message);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract handleRequest(
|
||||||
|
request: http.IncomingMessage,
|
||||||
|
parsedUrl: url.UrlWithParsedQuery,
|
||||||
|
requestPath: string,
|
||||||
|
): Promise<[string | Buffer, http.OutgoingHttpHeaders]>;
|
||||||
|
|
||||||
|
public listen(port: number): Promise<void> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.server.on("error", reject);
|
||||||
|
this.server.listen(port, resolve);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public get address(): string {
|
||||||
|
const address = this.server.address();
|
||||||
|
const endpoint = typeof address !== "string"
|
||||||
|
? ((address.address === "::" ? "localhost" : address.address) + ":" + address.port)
|
||||||
|
: address;
|
||||||
|
return `http://${endpoint}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MainServer extends Server {
|
||||||
|
// Used to notify the IPC server that there is a new client.
|
||||||
|
public readonly _onDidClientConnect = new Emitter<ClientConnectionEvent>();
|
||||||
|
public readonly onDidClientConnect = this._onDidClientConnect.event;
|
||||||
|
|
||||||
|
private readonly rootPath = path.resolve(__dirname, "../../..");
|
||||||
|
|
||||||
|
// This is separate instead of just extending this class since we can't
|
||||||
|
// use properties in the super call. This manages channels.
|
||||||
|
private readonly ipc = new IPCServer(this.onDidClientConnect);
|
||||||
|
|
||||||
|
// Persistent connections. These can reconnect within a timeout.
|
||||||
|
private readonly connections = new Map<ConnectionType, Map<string, Connection>>();
|
||||||
|
|
||||||
|
private readonly services = new ServiceCollection();
|
||||||
|
|
||||||
|
public constructor(private readonly webviewServer: WebviewServer) {
|
||||||
|
super();
|
||||||
|
|
||||||
this.server.on("upgrade", async (request, socket) => {
|
this.server.on("upgrade", async (request, socket) => {
|
||||||
const protocol = this.createProtocol(request, socket);
|
const protocol = this.createProtocol(request, socket);
|
||||||
@ -98,56 +134,44 @@ export class Server {
|
|||||||
protocol.dispose(error);
|
protocol.dispose(error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.server.on("error", (error) => {
|
public async listen(port: number): Promise<void> {
|
||||||
console.error(error);
|
const args = validatePaths(parseMainProcessArgv(process.argv));
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
let args: ParsedArgs;
|
const environmentService = new EnvironmentService(args, process.execPath);
|
||||||
try {
|
this.services.set(IEnvironmentService, environmentService);
|
||||||
args = parseMainProcessArgv(process.argv);
|
|
||||||
args = validatePaths(args);
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error.message);
|
|
||||||
return process.exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const services = new ServiceCollection();
|
const logService = new SpdLogService(
|
||||||
this.environmentService = new EnvironmentService(args, process.execPath);
|
|
||||||
services.set(IEnvironmentService, this.environmentService);
|
|
||||||
|
|
||||||
this.logService = new SpdLogService(
|
|
||||||
RemoteExtensionLogFileName,
|
RemoteExtensionLogFileName,
|
||||||
this.environmentService.logsPath,
|
environmentService.logsPath,
|
||||||
getLogLevel(this.environmentService),
|
getLogLevel(environmentService),
|
||||||
);
|
);
|
||||||
this.ipc.registerChannel("loglevel", new LogLevelSetterChannel(this.logService));
|
this.services.set(ILogService, logService);
|
||||||
|
|
||||||
const instantiationService = new InstantiationService(services);
|
this.ipc.registerChannel("loglevel", new LogLevelSetterChannel(logService));
|
||||||
|
|
||||||
|
const instantiationService = new InstantiationService(this.services);
|
||||||
instantiationService.invokeFunction(() => {
|
instantiationService.invokeFunction(() => {
|
||||||
instantiationService.createInstance(LogsDataCleaner);
|
instantiationService.createInstance(LogsDataCleaner);
|
||||||
this.ipc.registerChannel(
|
this.ipc.registerChannel(
|
||||||
REMOTE_FILE_SYSTEM_CHANNEL_NAME,
|
REMOTE_FILE_SYSTEM_CHANNEL_NAME,
|
||||||
new FileProviderChannel(this.logService),
|
new FileProviderChannel(logService),
|
||||||
);
|
);
|
||||||
this.ipc.registerChannel(
|
this.ipc.registerChannel(
|
||||||
"remoteextensionsenvironment",
|
"remoteextensionsenvironment",
|
||||||
new ExtensionEnvironmentChannel(this.environmentService, this.logService),
|
new ExtensionEnvironmentChannel(environmentService, logService),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await super.listen(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleRequest(request: http.IncomingMessage): Promise<[string | Buffer, http.OutgoingHttpHeaders]> {
|
protected async handleRequest(
|
||||||
if (request.method !== "GET") {
|
request: http.IncomingMessage,
|
||||||
throw new HttpError(
|
parsedUrl: url.UrlWithParsedQuery,
|
||||||
`Unsupported method ${request.method}`,
|
requestPath: string,
|
||||||
HttpCode.BadRequest,
|
): Promise<[string | Buffer, http.OutgoingHttpHeaders]> {
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const parsedUrl = url.parse(request.url || "", true);
|
|
||||||
const requestPath = parsedUrl.pathname || "/";
|
|
||||||
if (requestPath === "/") {
|
if (requestPath === "/") {
|
||||||
const htmlPath = path.join(
|
const htmlPath = path.join(
|
||||||
this.rootPath,
|
this.rootPath,
|
||||||
@ -159,7 +183,7 @@ export class Server {
|
|||||||
const remoteAuthority = request.headers.host as string;
|
const remoteAuthority = request.headers.host as string;
|
||||||
const transformer = getUriTransformer(remoteAuthority);
|
const transformer = getUriTransformer(remoteAuthority);
|
||||||
|
|
||||||
const webviewEndpoint = "";
|
const webviewEndpoint = this.webviewServer.address;
|
||||||
|
|
||||||
const cwd = process.env.VSCODE_CWD || process.cwd();
|
const cwd = process.env.VSCODE_CWD || process.cwd();
|
||||||
const workspacePath = parsedUrl.query.workspace as string | undefined;
|
const workspacePath = parsedUrl.query.workspace as string | undefined;
|
||||||
@ -167,13 +191,21 @@ export class Server {
|
|||||||
|
|
||||||
const options: Options = {
|
const options: Options = {
|
||||||
WORKBENCH_WEB_CONGIGURATION: {
|
WORKBENCH_WEB_CONGIGURATION: {
|
||||||
workspaceUri: workspacePath ? transformer.transformOutgoing(URI.file(sanitizeFilePath(workspacePath, cwd))) : undefined,
|
workspaceUri: workspacePath
|
||||||
folderUri: folderPath ? transformer.transformOutgoing(URI.file(sanitizeFilePath(folderPath, cwd))) : undefined,
|
? transformer.transformOutgoing(URI.file(sanitizeFilePath(workspacePath, cwd)))
|
||||||
|
: undefined,
|
||||||
|
folderUri: folderPath
|
||||||
|
? transformer.transformOutgoing(URI.file(sanitizeFilePath(folderPath, cwd)))
|
||||||
|
: undefined,
|
||||||
remoteAuthority,
|
remoteAuthority,
|
||||||
webviewEndpoint,
|
webviewEndpoint,
|
||||||
},
|
},
|
||||||
REMOTE_USER_DATA_URI: transformer.transformOutgoing(this.environmentService.webUserDataHome),
|
REMOTE_USER_DATA_URI: transformer.transformOutgoing(
|
||||||
PRODUCT_CONFIGURATION: require.__$__nodeRequire(path.resolve(getPathFromAmdModule(require, ""), "../product.json")),
|
(this.services.get(IEnvironmentService) as EnvironmentService).webUserDataHome,
|
||||||
|
),
|
||||||
|
PRODUCT_CONFIGURATION: require.__$__nodeRequire(
|
||||||
|
path.resolve(getPathFromAmdModule(require, ""), "../product.json"),
|
||||||
|
),
|
||||||
CONNECTION_AUTH_TOKEN: "",
|
CONNECTION_AUTH_TOKEN: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -239,17 +271,6 @@ export class Server {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public listen(port: number = 8443): void {
|
|
||||||
this.server.listen(port, () => {
|
|
||||||
const address = this.server.address();
|
|
||||||
const location = typeof address === "string"
|
|
||||||
? address
|
|
||||||
: `port ${address.port}`;
|
|
||||||
console.log(`Listening on ${location}`);
|
|
||||||
console.log(`Serving ${this.rootPath}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async connect(message: ConnectionTypeRequest, protocol: Protocol): Promise<void> {
|
private async connect(message: ConnectionTypeRequest, protocol: Protocol): Promise<void> {
|
||||||
switch (message.desiredConnectionType) {
|
switch (message.desiredConnectionType) {
|
||||||
case ConnectionType.ExtensionHost:
|
case ConnectionType.ExtensionHost:
|
||||||
@ -290,7 +311,9 @@ export class Server {
|
|||||||
onDidClientDisconnect: connection.onClose,
|
onDidClientDisconnect: connection.onClose,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
connection = new ExtensionHostConnection(protocol, this.logService);
|
connection = new ExtensionHostConnection(
|
||||||
|
protocol, this.services.get(ILogService) as ILogService,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
connections.set(protocol.options.reconnectionToken, connection);
|
connections.set(protocol.options.reconnectionToken, connection);
|
||||||
connection.onClose(() => {
|
connection.onClose(() => {
|
||||||
@ -309,3 +332,9 @@ export class Server {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class WebviewServer extends Server {
|
||||||
|
protected async handleRequest(): Promise<[string | Buffer, http.OutgoingHttpHeaders]> {
|
||||||
|
throw new Error("not implemented");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user