Change:
It doesn't look like this new CLI supports serving on a UNIX domain socket. Could we please get that in too, like in We're going to need it in order to handle auth in a proxy. (I'm happy to open a new issue for that if you need) Originally posted by in
Nice, thanks!
I can do this what now?
Also is it an issue that the socket file wasn't release/deleted after a ctrl+C? or do I clean that up manually?
We should probably automatically delete the file if it exists You can do a to make sure it's actually working . Without extra stuff you won't be able to open it in the browser, but verifying the connection should be enough.

Discussion:
use crate::{constants::APPLICATION_NAME, util::errors::CodeError}; use async_trait::async_trait; use std::path::{Path, PathBuf}; <ins> use std::pin::Pin; use std::task::{Context, Poll}; </ins> use tokio::io::{AsyncRead, AsyncWrite}; use tokio::net::TcpListener; use uuid::Uuid;

} else { use tokio::{time::sleep, io::ReadBuf}; use tokio::net::windows::named_pipe::{ClientOptions, ServerOptions, NamedPipeClient, NamedPipeServer}; <del> use std::{time::Duration, pin::Pin, task::{Context, Poll}, io}; </del> <ins> use std::{time::Duration, io}; </ins> use pin_project::pin_project; #[pin_project(project = AsyncPipeProj)]

} } <ins> impl AsyncPipeListener { pub fn into_pollable(self) -> PollableAsyncListener { PollableAsyncListener { listener: Some(self), write_fut: tokio_util::sync::ReusableBoxFuture::new(make_accept_fut(None)), } } } pub struct PollableAsyncListener { listener: Option<AsyncPipeListener>, write_fut: tokio_util::sync::ReusableBoxFuture< 'static, (AsyncPipeListener, Result<AsyncPipe, CodeError>), >, } async fn make_accept_fut( data: Option<AsyncPipeListener>, ) -> (AsyncPipeListener, Result<AsyncPipe, CodeError>) { match data { Some(mut l) => { let c = l.accept().await; (l, c) } None => unreachable!("this future should not be pollable in this state"), } } impl hyper::server::accept::Accept for PollableAsyncListener { type Conn = AsyncPipe; type Error = CodeError; fn poll_accept( mut self: Pin<&mut Self>, cx: &mut Context<'_>, ) -> Poll<Option<Result<Self::Conn, Self::Error>>> { if let Some(l) = self.listener.take() { self.write_fut.set(make_accept_fut(Some(l))) } match self.write_fut.poll(cx) { Poll::Ready((l, cnx)) => { self.listener = Some(l); Poll::Ready(Some(cnx)) } Poll::Pending => Poll::Pending, } } } </ins> /// Gets a random name for a pipe/socket on the paltform pub fn get_socket_name() -> PathBuf { cfg_if::cfg_if! {

/// Host to listen on, defaults to 'localhost' #[clap(long)] pub host: Option<String>, <ins> // The path to a socket file for the server to listen to. #[clap(long)] pub socket_path: Option<String>, </ins> /// Port to listen on. If 0 is passed a random free port is picked. #[clap(long, default_value_t = 8000)] pub port: u16,

use tokio::pin; use tokio::process::Command; <del> use crate::async_pipe::{get_socket_name, get_socket_rw_stream, AsyncPipe}; </del> <ins> use crate::async_pipe::{ get_socket_name, get_socket_rw_stream, listen_socket_rw_stream, AsyncPipe, }; </ins> use crate::constants::VSCODE_CLI_QUALITY; use crate::download_cache::DownloadCache; use crate::log;

/// while new clients get new VS Code Server versions. pub async fn serve_web(ctx: CommandContext, mut args: ServeWebArgs) -> Result<i32, AnyError> { legal::require_consent(&ctx.paths, args.accept_server_license_terms)?; <del> let mut addr: SocketAddr = match &args.host { Some(h) => h.parse().map_err(CodeError::InvalidHostAddress)?, None => SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0), }; addr.set_port(args.port); </del> let platform: crate::update_service::Platform = PreReqChecker::new().verify().await?; if !args.without_connection_token { // Ensure there's a defined connection token, since if multiple server versions // are excuted, they will need to have a single shared token. <del> let connection_token = args .connection_token .clone() .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()); ctx.log.result(format!( "Web UI available at http://{}?tkn={}", addr, connection_token, )); args.connection_token = Some(connection_token); } else { ctx.log .result(format!("Web UI available at http://{}", addr)); args.connection_token = None; </del> <ins> args.connection_token = Some( args.connection_token .clone() .unwrap_or_else(|| uuid::Uuid::new_v4().to_string()), ); </ins> } <del> let cm = ConnectionManager::new(&ctx, platform, args); let make_svc = make_service_fn(move |_conn| { </del> <ins> let cm = ConnectionManager::new(&ctx, platform, args.clone()); let make_svc = move || { </ins> let cm = cm.clone(); <del> let log = ctx.log.clone(); </del> <ins> let log = cm.log.clone(); </ins> let service = service_fn(move |req| handle(cm.clone(), log.clone(), req)); async move { Ok::<_, Infallible>(service) } <del> }); </del> <ins> }; let r = if let Some(s) = args.socket_path { let socket = listen_socket_rw_stream(&PathBuf::from(&s)).await?; ctx.log.result(format!("Web UI available on {}", s)); Server::builder(socket.into_pollable()) .serve(make_service_fn(|_| make_svc())) .await } else { let addr: SocketAddr = match &args.host { Some(h) => { SocketAddr::new(h.parse().map_err(CodeError::InvalidHostAddress)?, args.port) } None => SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), args.port), }; </ins> <del> let server = Server::bind(&addr).serve(make_svc); </del> <ins> let mut listening = format!("Web UI available at http://{}", addr); if let Some(ct) = args.connection_token { listening.push_str(&format!("?tkn={}", ct)); } ctx.log.result(listening); Server::bind(&addr) .serve(make_service_fn(|_| make_svc())) .await }; </ins> <del> server.await.map_err(CodeError::CouldNotListenOnInterface)?; </del> <ins> r.map_err(CodeError::CouldNotListenOnInterface)?; </ins> Ok(0) }