Files
vscode/cli/src/tunnels/wsl_server.rs
Connor Peet 1b5fd140fb Run tunnels as singleton process (for a --cli-data-dir) (#177002)
* wip on singleton

* wip

* windows support

* wip

* wip

* fix clippy
2023-03-14 08:09:47 -07:00

167 lines
4.4 KiB
Rust

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
use tokio::sync::mpsc;
use crate::{
log,
msgpack_rpc::{new_msgpack_rpc, start_msgpack_rpc, MsgPackCaller},
state::LauncherPaths,
tunnels::code_server::ServerBuilder,
update_service::{Platform, Release, TargetKind},
util::{
errors::{
wrap, AnyError, InvalidRpcDataError, MismatchedLaunchModeError, NoAttachedServerError,
},
http::ReqwestSimpleHttp,
sync::Barrier,
},
};
use super::{
code_server::{AnyCodeServer, CodeServerArgs, ResolvedServerParams},
protocol::{EmptyObject, InstallFromLocalFolderParams, ServerMessageParams, VersionParams},
server_bridge::ServerBridge,
server_multiplexer::ServerMultiplexer,
shutdown_signal::ShutdownSignal,
socket_signal::{ClientMessageDecoder, ServerMessageDestination, ServerMessageSink},
};
struct HandlerContext {
log: log::Logger,
code_server_args: CodeServerArgs,
launcher_paths: LauncherPaths,
platform: Platform,
http: ReqwestSimpleHttp,
caller: MsgPackCaller,
multiplexer: ServerMultiplexer,
}
#[derive(Clone)]
struct RpcLogSink(MsgPackCaller);
impl RpcLogSink {
fn write_json(&self, level: String, message: &str) {
self.0.notify(
"log",
serde_json::json!({
"level": level,
"message": message,
}),
);
}
}
impl log::LogSink for RpcLogSink {
fn write_log(&self, level: log::Level, _prefix: &str, message: &str) {
self.write_json(level.to_string(), message);
}
fn write_result(&self, message: &str) {
self.write_json("result".to_string(), message);
}
}
pub async fn serve_wsl(
log: log::Logger,
launcher_paths: LauncherPaths,
code_server_args: CodeServerArgs,
platform: Platform,
http: reqwest::Client,
shutdown_rx: Barrier<ShutdownSignal>,
) -> Result<i32, AnyError> {
let (caller_tx, caller_rx) = mpsc::unbounded_channel();
let mut rpc = new_msgpack_rpc();
let caller = rpc.get_caller(caller_tx);
// notify the incoming client about the server version
caller.notify("version", VersionParams::default());
let log = log.with_sink(RpcLogSink(caller.clone()));
let mut rpc = rpc.methods(HandlerContext {
log: log.clone(),
caller,
code_server_args,
launcher_paths,
platform,
multiplexer: ServerMultiplexer::new(),
http: ReqwestSimpleHttp::with_client(http),
});
rpc.register_async(
"serve",
move |m: InstallFromLocalFolderParams, c| async move { handle_serve(&c, m).await },
);
rpc.register_sync("servermsg", move |m: ServerMessageParams, c| {
if c.multiplexer.write_message(&c.log, m.i, m.body) {
Ok(EmptyObject {})
} else {
Err(NoAttachedServerError().into())
}
});
start_msgpack_rpc(
rpc.build(log),
tokio::io::stdin(),
tokio::io::stderr(),
caller_rx,
shutdown_rx,
)
.await
.map_err(|e| wrap(e, "error handling server stdio"))?;
Ok(0)
}
async fn handle_serve(
c: &HandlerContext,
params: InstallFromLocalFolderParams,
) -> Result<EmptyObject, AnyError> {
// fill params.extensions into code_server_args.install_extensions
let mut csa = c.code_server_args.clone();
csa.connection_token = params.inner.connection_token.or(csa.connection_token);
csa.install_extensions
.extend(params.inner.extensions.into_iter());
let resolved = ResolvedServerParams {
code_server_args: csa,
release: Release {
name: String::new(),
commit: params
.inner
.commit_id
.ok_or_else(|| InvalidRpcDataError("commit_id is required".to_string()))?,
platform: c.platform,
target: TargetKind::Server,
quality: params.inner.quality,
},
};
let sb = ServerBuilder::new(&c.log, &resolved, &c.launcher_paths, c.http.clone());
let code_server = match sb.get_running().await? {
Some(AnyCodeServer::Socket(s)) => s,
Some(_) => return Err(MismatchedLaunchModeError().into()),
None => {
sb.setup(Some(params.archive_path.into())).await?;
sb.listen_on_default_socket().await?
}
};
let bridge = ServerBridge::new(
&code_server.socket,
ServerMessageSink::new_plain(
c.multiplexer.clone(),
params.inner.socket_id,
ServerMessageDestination::Rpc(c.caller.clone()),
),
ClientMessageDecoder::new_plain(),
)
.await?;
c.multiplexer.register(params.inner.socket_id, bridge);
trace!(c.log, "Attached to server");
Ok(EmptyObject {})
}