mirror of
https://github.com/microsoft/vscode.git
synced 2026-09-13 13:52:33 +01:00
Merge remote-tracking branch 'RitamM16/cli-serve-web-offline-use-cache' into connor4312/cli-offline-serve
This commit is contained in:
@@ -222,6 +222,10 @@ pub struct ServeWebArgs {
|
||||
/// Set the root path for extensions.
|
||||
#[clap(long)]
|
||||
pub extensions_dir: Option<String>,
|
||||
/// Set update check interval in seconds, defaults to 3600 seconds. Set to 0 to disable update checks
|
||||
#[clap(long)]
|
||||
pub update_check_interval: Option<u64>,
|
||||
|
||||
}
|
||||
|
||||
#[derive(Args, Debug, Clone)]
|
||||
|
||||
@@ -15,7 +15,7 @@ use std::time::{Duration, Instant};
|
||||
use hyper::service::{make_service_fn, service_fn};
|
||||
use hyper::{Body, Request, Response, Server};
|
||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||
use tokio::pin;
|
||||
use tokio::{pin,time};
|
||||
|
||||
use crate::async_pipe::{
|
||||
get_socket_name, get_socket_rw_stream, listen_socket_rw_stream, AsyncPipe,
|
||||
@@ -86,7 +86,15 @@ 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 = args.update_check_interval.unwrap_or(3600);
|
||||
|
||||
if update_check_interval > 0 {
|
||||
// Start the update checker
|
||||
cm.clone().start_update_checker(Duration::from_secs(update_check_interval));
|
||||
}
|
||||
|
||||
let key = get_server_key_half(&ctx.paths);
|
||||
let make_svc = move || {
|
||||
let ctx = HandleContext {
|
||||
@@ -175,7 +183,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) {
|
||||
r
|
||||
} else {
|
||||
match ctx.cm.get_latest_release().await {
|
||||
match ctx.cm.get_release_from_cache().await {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
error!(ctx.log, "error getting latest version: {}", e);
|
||||
@@ -538,21 +546,77 @@ impl ConnectionManager {
|
||||
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 cache = DownloadCache::load(ctx.paths.web_server_storage());
|
||||
let latest_version: tokio::sync::Mutex<Option<(Instant, Release)>>;
|
||||
let target_kind = TargetKind::Web;
|
||||
|
||||
//Set the instant to now minus the RELEASE_CACHE_SECS
|
||||
//This allows the service to skip use of the cache for the first run
|
||||
let instant = Instant::now() - Duration::from_secs(RELEASE_CACHE_SECS);
|
||||
|
||||
let quality = VSCODE_CLI_QUALITY
|
||||
.map_or(Quality::Stable, |q| {
|
||||
match Quality::try_from(q) {
|
||||
Ok(q) => q,
|
||||
Err(_) => Quality::Stable
|
||||
}
|
||||
});
|
||||
|
||||
if let Some(latest_commit) = cache.get().first() {
|
||||
let release = Release {
|
||||
name: String::from("0.0.0"), // Version information not stored on cache
|
||||
commit: latest_commit.clone(),
|
||||
platform,
|
||||
target: target_kind,
|
||||
quality
|
||||
};
|
||||
latest_version = tokio::sync::Mutex::new(Some((instant, release)));
|
||||
} else {
|
||||
latest_version = tokio::sync::Mutex::default();
|
||||
}
|
||||
|
||||
Arc::new(Self {
|
||||
platform,
|
||||
args,
|
||||
base_path,
|
||||
log: ctx.log.clone(),
|
||||
cache: DownloadCache::new(ctx.paths.web_server_storage()),
|
||||
cache,
|
||||
update_service: UpdateService::new(
|
||||
ctx.log.clone(),
|
||||
Arc::new(ReqwestSimpleHttp::with_client(ctx.http.clone())),
|
||||
),
|
||||
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) {
|
||||
debug!(self.log, "starting update checker");
|
||||
tokio::spawn(async move {
|
||||
let mut interval = time::interval(duration);
|
||||
loop {
|
||||
interval.tick().await;
|
||||
debug!(self.log, "checking for updates");
|
||||
match self.get_latest_release().await {
|
||||
Ok(_) => {},
|
||||
Err(e) => {
|
||||
error!(self.log, "error getting latest version: {}", e);
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Returns the latest release, available on the cache
|
||||
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());
|
||||
}
|
||||
Err(CodeError::ServerNotYetDownloaded)
|
||||
}
|
||||
|
||||
/// Gets a connection to a server version
|
||||
pub async fn get_connection(
|
||||
&self,
|
||||
@@ -571,11 +635,7 @@ impl ConnectionManager {
|
||||
pub async fn get_latest_release(&self) -> Result<Release, CodeError> {
|
||||
let mut latest = self.latest_version.lock().await;
|
||||
let now = Instant::now();
|
||||
if let Some((checked_at, release)) = &*latest {
|
||||
if checked_at.elapsed() < Duration::from_secs(RELEASE_CACHE_SECS) {
|
||||
return Ok(release.clone());
|
||||
}
|
||||
}
|
||||
let target_kind = TargetKind::Web;
|
||||
|
||||
let quality = VSCODE_CLI_QUALITY
|
||||
.ok_or_else(|| CodeError::UpdatesNotConfigured("no configured quality"))
|
||||
@@ -585,16 +645,43 @@ impl ConnectionManager {
|
||||
|
||||
let release = self
|
||||
.update_service
|
||||
.get_latest_commit(self.platform, TargetKind::Web, quality)
|
||||
.get_latest_commit(self.platform, target_kind, quality)
|
||||
.await
|
||||
.map_err(|e| CodeError::UpdateCheckFailed(e.to_string()));
|
||||
|
||||
// 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);
|
||||
*latest = Some((now, previous.clone()));
|
||||
return Ok(previous.clone());
|
||||
}
|
||||
|
||||
// If the new release and previous release are different, download the new version
|
||||
if let Ok(new_release) = &release {
|
||||
let (_, previous_release) = latest.clone().unwrap_or((Instant::now(), Release {
|
||||
name: String::from("0.0.0"),
|
||||
commit: String::from("0.0.0"),
|
||||
platform:self.platform,
|
||||
target: target_kind,
|
||||
quality
|
||||
}));
|
||||
if new_release.commit != previous_release.commit {
|
||||
match self.get_version_data_inner(new_release.clone()) {
|
||||
Ok(mut r) => {
|
||||
match r.wait().await {
|
||||
Ok(_) => {},
|
||||
Err(e) => {
|
||||
info!(self.log, "{}", e);
|
||||
}
|
||||
};
|
||||
},
|
||||
Err(e) => {
|
||||
info!(self.log, "{}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let release = release?;
|
||||
debug!(self.log, "refreshed latest release: {}", release);
|
||||
*latest = Some((now, release.clone()));
|
||||
|
||||
@@ -20,6 +20,7 @@ const KEEP_LRU: usize = 5;
|
||||
const STAGING_SUFFIX: &str = ".staging";
|
||||
const RENAME_ATTEMPTS: u32 = 20;
|
||||
const RENAME_DELAY: std::time::Duration = std::time::Duration::from_millis(200);
|
||||
const PERSISTED_STATE_FILE_NAME: &str = "lru.json";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DownloadCache {
|
||||
@@ -30,11 +31,27 @@ pub struct DownloadCache {
|
||||
impl DownloadCache {
|
||||
pub fn new(path: PathBuf) -> DownloadCache {
|
||||
DownloadCache {
|
||||
state: PersistedState::new(path.join("lru.json")),
|
||||
state: PersistedState::new(path.join(PERSISTED_STATE_FILE_NAME)),
|
||||
path,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets an DownloadCache with previously persisted value if it exists
|
||||
/// on the persistant storage, else returns a new DownloadCache.
|
||||
pub fn load(path: PathBuf) -> DownloadCache {
|
||||
let state = PersistedState::<Vec::<String>>::new(path.join(PERSISTED_STATE_FILE_NAME));
|
||||
state.load();
|
||||
DownloadCache {
|
||||
state,
|
||||
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
|
||||
/// joining them to the path.
|
||||
pub fn path(&self) -> &Path {
|
||||
|
||||
Reference in New Issue
Block a user