mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-24 12:19:20 +00:00
cli: reapply "code server-web when offline"
This commit is contained in:
@@ -81,4 +81,5 @@ codegen-units = 1
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
vsda = []
|
||||||
vscode-encrypt = []
|
vscode-encrypt = []
|
||||||
|
|||||||
@@ -723,7 +723,7 @@ impl Auth {
|
|||||||
|
|
||||||
match &init_code_json.message {
|
match &init_code_json.message {
|
||||||
Some(m) => self.log.result(m),
|
Some(m) => self.log.result(m),
|
||||||
None => self.log.result(&format!(
|
None => self.log.result(format!(
|
||||||
"To grant access to the server, please log into {} and use code {}",
|
"To grant access to the server, please log into {} and use code {}",
|
||||||
init_code_json.verification_uri, init_code_json.user_code
|
init_code_json.verification_uri, init_code_json.user_code
|
||||||
)),
|
)),
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use std::time::{Duration, Instant};
|
|||||||
use hyper::service::{make_service_fn, service_fn};
|
use hyper::service::{make_service_fn, service_fn};
|
||||||
use hyper::{Body, Request, Response, Server};
|
use hyper::{Body, Request, Response, Server};
|
||||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||||
use tokio::pin;
|
use tokio::{pin, time};
|
||||||
|
|
||||||
use crate::async_pipe::{
|
use crate::async_pipe::{
|
||||||
get_socket_name, get_socket_rw_stream, listen_socket_rw_stream, AsyncPipe,
|
get_socket_name, get_socket_rw_stream, listen_socket_rw_stream, AsyncPipe,
|
||||||
@@ -50,7 +50,7 @@ const SERVER_IDLE_TIMEOUT_SECS: u64 = 60 * 60;
|
|||||||
/// (should be large enough to basically never happen)
|
/// (should be large enough to basically never happen)
|
||||||
const SERVER_ACTIVE_TIMEOUT_SECS: u64 = SERVER_IDLE_TIMEOUT_SECS * 24 * 30 * 12;
|
const SERVER_ACTIVE_TIMEOUT_SECS: u64 = SERVER_IDLE_TIMEOUT_SECS * 24 * 30 * 12;
|
||||||
/// How long to cache the "latest" version we get from the update service.
|
/// How long to cache the "latest" version we get from the update service.
|
||||||
const RELEASE_CACHE_SECS: u64 = 60 * 60;
|
const RELEASE_CHECK_INTERVAL: u64 = 60 * 60;
|
||||||
|
|
||||||
/// Number of bytes for the secret keys. See workbench.ts for their usage.
|
/// Number of bytes for the secret keys. See workbench.ts for their usage.
|
||||||
const SECRET_KEY_BYTES: usize = 32;
|
const SECRET_KEY_BYTES: usize = 32;
|
||||||
@@ -86,7 +86,11 @@ pub async fn serve_web(ctx: CommandContext, mut args: ServeWebArgs) -> Result<i3
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let cm = ConnectionManager::new(&ctx, platform, args.clone());
|
let cm: Arc<ConnectionManager> = ConnectionManager::new(&ctx, platform, args.clone());
|
||||||
|
let update_check_interval = 3600;
|
||||||
|
cm.clone()
|
||||||
|
.start_update_checker(Duration::from_secs(update_check_interval));
|
||||||
|
|
||||||
let key = get_server_key_half(&ctx.paths);
|
let key = get_server_key_half(&ctx.paths);
|
||||||
let make_svc = move || {
|
let make_svc = move || {
|
||||||
let ctx = HandleContext {
|
let ctx = HandleContext {
|
||||||
@@ -175,7 +179,7 @@ async fn handle_proxied(ctx: &HandleContext, req: Request<Body>) -> Response<Bod
|
|||||||
let release = if let Some((r, _)) = get_release_from_path(req.uri().path(), ctx.cm.platform) {
|
let release = if let Some((r, _)) = get_release_from_path(req.uri().path(), ctx.cm.platform) {
|
||||||
r
|
r
|
||||||
} else {
|
} else {
|
||||||
match ctx.cm.get_latest_release().await {
|
match ctx.cm.get_release_from_cache().await {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(ctx.log, "error getting latest version: {}", e);
|
error!(ctx.log, "error getting latest version: {}", e);
|
||||||
@@ -538,21 +542,67 @@ impl ConnectionManager {
|
|||||||
pub fn new(ctx: &CommandContext, platform: Platform, args: ServeWebArgs) -> Arc<Self> {
|
pub fn new(ctx: &CommandContext, platform: Platform, args: ServeWebArgs) -> Arc<Self> {
|
||||||
let base_path = normalize_base_path(args.server_base_path.as_deref().unwrap_or_default());
|
let base_path = normalize_base_path(args.server_base_path.as_deref().unwrap_or_default());
|
||||||
|
|
||||||
|
let cache = DownloadCache::new(ctx.paths.web_server_storage());
|
||||||
|
let target_kind = TargetKind::Web;
|
||||||
|
|
||||||
|
let quality = VSCODE_CLI_QUALITY.map_or(Quality::Stable, |q| match Quality::try_from(q) {
|
||||||
|
Ok(q) => q,
|
||||||
|
Err(_) => Quality::Stable,
|
||||||
|
});
|
||||||
|
|
||||||
|
let latest_version = tokio::sync::Mutex::new(cache.get().first().map(|latest_commit| {
|
||||||
|
(
|
||||||
|
Instant::now() - Duration::from_secs(RELEASE_CHECK_INTERVAL),
|
||||||
|
Release {
|
||||||
|
name: String::from("0.0.0"), // Version information not stored on cache
|
||||||
|
commit: latest_commit.clone(),
|
||||||
|
platform,
|
||||||
|
target: target_kind,
|
||||||
|
quality,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}));
|
||||||
|
|
||||||
Arc::new(Self {
|
Arc::new(Self {
|
||||||
platform,
|
platform,
|
||||||
args,
|
args,
|
||||||
base_path,
|
base_path,
|
||||||
log: ctx.log.clone(),
|
log: ctx.log.clone(),
|
||||||
cache: DownloadCache::new(ctx.paths.web_server_storage()),
|
cache,
|
||||||
update_service: UpdateService::new(
|
update_service: UpdateService::new(
|
||||||
ctx.log.clone(),
|
ctx.log.clone(),
|
||||||
Arc::new(ReqwestSimpleHttp::with_client(ctx.http.clone())),
|
Arc::new(ReqwestSimpleHttp::with_client(ctx.http.clone())),
|
||||||
),
|
),
|
||||||
state: ConnectionStateMap::default(),
|
state: ConnectionStateMap::default(),
|
||||||
latest_version: tokio::sync::Mutex::default(),
|
latest_version,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// spawns a task that checks for updates every n seconds duration
|
||||||
|
pub fn start_update_checker(self: Arc<Self>, duration: Duration) {
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let mut interval = time::interval(duration);
|
||||||
|
loop {
|
||||||
|
interval.tick().await;
|
||||||
|
|
||||||
|
if let Err(e) = self.get_latest_release().await {
|
||||||
|
warning!(self.log, "error getting latest version: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the latest release from the cache, if one exists.
|
||||||
|
pub async fn get_release_from_cache(&self) -> Result<Release, CodeError> {
|
||||||
|
let latest = self.latest_version.lock().await;
|
||||||
|
if let Some((_, release)) = &*latest {
|
||||||
|
return Ok(release.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
drop(latest);
|
||||||
|
self.get_latest_release().await
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets a connection to a server version
|
/// Gets a connection to a server version
|
||||||
pub async fn get_connection(
|
pub async fn get_connection(
|
||||||
&self,
|
&self,
|
||||||
@@ -571,11 +621,7 @@ impl ConnectionManager {
|
|||||||
pub async fn get_latest_release(&self) -> Result<Release, CodeError> {
|
pub async fn get_latest_release(&self) -> Result<Release, CodeError> {
|
||||||
let mut latest = self.latest_version.lock().await;
|
let mut latest = self.latest_version.lock().await;
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
if let Some((checked_at, release)) = &*latest {
|
let target_kind = TargetKind::Web;
|
||||||
if checked_at.elapsed() < Duration::from_secs(RELEASE_CACHE_SECS) {
|
|
||||||
return Ok(release.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let quality = VSCODE_CLI_QUALITY
|
let quality = VSCODE_CLI_QUALITY
|
||||||
.ok_or_else(|| CodeError::UpdatesNotConfigured("no configured quality"))
|
.ok_or_else(|| CodeError::UpdatesNotConfigured("no configured quality"))
|
||||||
@@ -585,13 +631,14 @@ impl ConnectionManager {
|
|||||||
|
|
||||||
let release = self
|
let release = self
|
||||||
.update_service
|
.update_service
|
||||||
.get_latest_commit(self.platform, TargetKind::Web, quality)
|
.get_latest_commit(self.platform, target_kind, quality)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| CodeError::UpdateCheckFailed(e.to_string()));
|
.map_err(|e| CodeError::UpdateCheckFailed(e.to_string()));
|
||||||
|
|
||||||
// If the update service is unavailable and we have stale data, use that
|
// If the update service is unavailable and we have stale data, use that
|
||||||
if let (Err(e), Some((_, previous))) = (&release, &*latest) {
|
if let (Err(e), Some((_, previous))) = (&release, latest.clone()) {
|
||||||
warning!(self.log, "error getting latest release, using stale: {}", e);
|
warning!(self.log, "error getting latest release, using stale: {}", e);
|
||||||
|
*latest = Some((now, previous.clone()));
|
||||||
return Ok(previous.clone());
|
return Ok(previous.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ const KEEP_LRU: usize = 5;
|
|||||||
const STAGING_SUFFIX: &str = ".staging";
|
const STAGING_SUFFIX: &str = ".staging";
|
||||||
const RENAME_ATTEMPTS: u32 = 20;
|
const RENAME_ATTEMPTS: u32 = 20;
|
||||||
const RENAME_DELAY: std::time::Duration = std::time::Duration::from_millis(200);
|
const RENAME_DELAY: std::time::Duration = std::time::Duration::from_millis(200);
|
||||||
|
const PERSISTED_STATE_FILE_NAME: &str = "lru.json";
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DownloadCache {
|
pub struct DownloadCache {
|
||||||
@@ -30,11 +31,16 @@ pub struct DownloadCache {
|
|||||||
impl DownloadCache {
|
impl DownloadCache {
|
||||||
pub fn new(path: PathBuf) -> DownloadCache {
|
pub fn new(path: PathBuf) -> DownloadCache {
|
||||||
DownloadCache {
|
DownloadCache {
|
||||||
state: PersistedState::new(path.join("lru.json")),
|
state: PersistedState::new(path.join(PERSISTED_STATE_FILE_NAME)),
|
||||||
path,
|
path,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the value stored on the state
|
||||||
|
pub fn get(&self) -> Vec<String> {
|
||||||
|
self.state.load()
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the download cache path. Names of cache entries can be formed by
|
/// Gets the download cache path. Names of cache entries can be formed by
|
||||||
/// joining them to the path.
|
/// joining them to the path.
|
||||||
pub fn path(&self) -> &Path {
|
pub fn path(&self) -> &Path {
|
||||||
|
|||||||
@@ -674,7 +674,7 @@ where
|
|||||||
let write_line = |line: &str| -> std::io::Result<()> {
|
let write_line = |line: &str| -> std::io::Result<()> {
|
||||||
if let Some(mut f) = log_file.as_ref() {
|
if let Some(mut f) = log_file.as_ref() {
|
||||||
f.write_all(line.as_bytes())?;
|
f.write_all(line.as_bytes())?;
|
||||||
f.write_all(&[b'\n'])?;
|
f.write_all(b"\n")?;
|
||||||
}
|
}
|
||||||
if write_directly {
|
if write_directly {
|
||||||
println!("{}", line);
|
println!("{}", line);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "code-oss-dev",
|
"name": "code-oss-dev",
|
||||||
"version": "1.94.0",
|
"version": "1.94.0",
|
||||||
"distro": "36c6d77f96e54b1ad2233cd24fed8e8f08d5a388",
|
"distro": "fcaeb73de7ac6ff11a3e732c63987e01b88341e7",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "Microsoft Corporation"
|
"name": "Microsoft Corporation"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user