diff --git a/crates/auto_update/src/auto_update.rs b/crates/auto_update/src/auto_update.rs index c2a0b0c27d..926a9a8519 100644 --- a/crates/auto_update/src/auto_update.rs +++ b/crates/auto_update/src/auto_update.rs @@ -29,7 +29,7 @@ use std::{ }; use update_notification::UpdateNotification; use util::{ - http::{HttpClient, ZedHttpClient}, + http::{HttpClient, HttpClientWithUrl}, ResultExt, }; use workspace::Workspace; @@ -67,7 +67,7 @@ pub enum AutoUpdateStatus { pub struct AutoUpdater { status: AutoUpdateStatus, current_version: SemanticVersion, - http_client: Arc, + http_client: Arc, pending_poll: Option>>, } @@ -115,7 +115,7 @@ struct ReleaseNotesBody { release_notes: String, } -pub fn init(http_client: Arc, cx: &mut AppContext) { +pub fn init(http_client: Arc, cx: &mut AppContext) { AutoUpdateSetting::register(cx); cx.observe_new_views(|workspace: &mut Workspace, _cx| { @@ -181,7 +181,7 @@ pub fn view_release_notes(_: &ViewReleaseNotes, cx: &mut AppContext) -> Option<( let current_version = auto_updater.current_version; let url = &auto_updater .http_client - .zed_url(&format!("/releases/{release_channel}/{current_version}")); + .build_url(&format!("/releases/{release_channel}/{current_version}")); cx.open_url(&url); } @@ -193,7 +193,7 @@ fn view_release_notes_locally(workspace: &mut Workspace, cx: &mut ViewContext().0.clone() } - fn new(current_version: SemanticVersion, http_client: Arc) -> Self { + fn new(current_version: SemanticVersion, http_client: Arc) -> Self { Self { status: AutoUpdateStatus::Idle, current_version, @@ -337,7 +337,7 @@ impl AutoUpdater { (this.http_client.clone(), this.current_version) })?; - let mut url_string = client.zed_url(&format!( + let mut url_string = client.build_url(&format!( "/api/releases/latest?asset=Zed.dmg&os={}&arch={}", OS, ARCH )); diff --git a/crates/client/src/client.rs b/crates/client/src/client.rs index a50d96eed8..57d59fa041 100644 --- a/crates/client/src/client.rs +++ b/crates/client/src/client.rs @@ -42,7 +42,7 @@ use std::{ use telemetry::Telemetry; use thiserror::Error; use url::Url; -use util::http::{HttpClient, ZedHttpClient}; +use util::http::{HttpClient, HttpClientWithUrl}; use util::{ResultExt, TryFutureExt}; pub use rpc::*; @@ -153,7 +153,7 @@ impl Global for GlobalClient {} pub struct Client { id: AtomicU64, peer: Arc, - http: Arc, + http: Arc, telemetry: Arc, state: RwLock, @@ -424,7 +424,7 @@ impl settings::Settings for TelemetrySettings { impl Client { pub fn new( clock: Arc, - http: Arc, + http: Arc, cx: &mut AppContext, ) -> Arc { let client = Arc::new(Self { @@ -447,7 +447,7 @@ impl Client { self.id.load(std::sync::atomic::Ordering::SeqCst) } - pub fn http_client(&self) -> Arc { + pub fn http_client(&self) -> Arc { self.http.clone() } @@ -970,14 +970,14 @@ impl Client { } async fn get_rpc_url( - http: Arc, + http: Arc, release_channel: Option, ) -> Result { if let Some(url) = &*ZED_RPC_URL { return Url::parse(url).context("invalid rpc url"); } - let mut url = http.zed_url("/rpc"); + let mut url = http.build_url("/rpc"); if let Some(preview_param) = release_channel.and_then(|channel| channel.release_query_param()) { @@ -1110,7 +1110,7 @@ impl Client { // Open the Zed sign-in page in the user's browser, with query parameters that indicate // that the user is signing in from a Zed app running on the same device. - let mut url = http.zed_url(&format!( + let mut url = http.build_url(&format!( "/native_app_signin?native_app_port={}&native_app_public_key={}", port, public_key_string )); @@ -1145,7 +1145,7 @@ impl Client { } let post_auth_url = - http.zed_url("/native_app_signin_succeeded"); + http.build_url("/native_app_signin_succeeded"); req.respond( tiny_http::Response::empty(302).with_header( tiny_http::Header::from_bytes( @@ -1187,7 +1187,7 @@ impl Client { } async fn authenticate_as_admin( - http: Arc, + http: Arc, login: String, mut api_token: String, ) -> Result { diff --git a/crates/client/src/telemetry.rs b/crates/client/src/telemetry.rs index 7d1e248ec2..0520f52988 100644 --- a/crates/client/src/telemetry.rs +++ b/crates/client/src/telemetry.rs @@ -20,7 +20,7 @@ use telemetry_events::{ EditEvent, EditorEvent, Event, EventRequestBody, EventWrapper, MemoryEvent, SettingEvent, }; use tempfile::NamedTempFile; -use util::http::{self, HttpClient, Method, ZedHttpClient}; +use util::http::{self, HttpClient, HttpClientWithUrl, Method}; #[cfg(not(debug_assertions))] use util::ResultExt; use util::TryFutureExt; @@ -29,7 +29,7 @@ use self::event_coalescer::EventCoalescer; pub struct Telemetry { clock: Arc, - http_client: Arc, + http_client: Arc, executor: BackgroundExecutor, state: Arc>, } @@ -75,7 +75,7 @@ static ZED_CLIENT_CHECKSUM_SEED: Lazy>> = Lazy::new(|| { impl Telemetry { pub fn new( clock: Arc, - client: Arc, + client: Arc, cx: &mut AppContext, ) -> Arc { let release_channel = @@ -474,7 +474,7 @@ impl Telemetry { let request = http::Request::builder() .method(Method::POST) - .uri(this.http_client.zed_api_url("/telemetry/events")) + .uri(this.http_client.build_zed_api_url("/telemetry/events")) .header("Content-Type", "text/plain") .header("x-zed-checksum", checksum) .body(json_bytes.into()); diff --git a/crates/extension/src/extension_store.rs b/crates/extension/src/extension_store.rs index c87a38d695..d8434ea895 100644 --- a/crates/extension/src/extension_store.rs +++ b/crates/extension/src/extension_store.rs @@ -20,7 +20,7 @@ use std::{ time::Duration, }; use theme::{ThemeRegistry, ThemeSettings}; -use util::http::{AsyncBody, ZedHttpClient}; +use util::http::{AsyncBody, HttpClientWithUrl}; use util::TryFutureExt; use util::{http::HttpClient, paths::EXTENSIONS_DIR, ResultExt}; @@ -69,7 +69,7 @@ impl ExtensionStatus { pub struct ExtensionStore { manifest: Arc>, fs: Arc, - http_client: Arc, + http_client: Arc, extensions_dir: PathBuf, extensions_being_installed: HashSet>, extensions_being_uninstalled: HashSet>, @@ -125,7 +125,7 @@ actions!(zed, [ReloadExtensions]); pub fn init( fs: Arc, - http_client: Arc, + http_client: Arc, language_registry: Arc, theme_registry: Arc, cx: &mut AppContext, @@ -157,7 +157,7 @@ impl ExtensionStore { pub fn new( extensions_dir: PathBuf, fs: Arc, - http_client: Arc, + http_client: Arc, language_registry: Arc, theme_registry: Arc, cx: &mut ModelContext, @@ -236,7 +236,7 @@ impl ExtensionStore { search: Option<&str>, cx: &mut ModelContext, ) -> Task>> { - let url = self.http_client.zed_api_url(&format!( + let url = self.http_client.build_zed_api_url(&format!( "/extensions{query}", query = search .map(|search| format!("?filter={search}")) @@ -276,7 +276,7 @@ impl ExtensionStore { log::info!("installing extension {extension_id} {version}"); let url = self .http_client - .zed_api_url(&format!("/extensions/{extension_id}/{version}/download")); + .build_zed_api_url(&format!("/extensions/{extension_id}/{version}/download")); let extensions_dir = self.extensions_dir(); let http_client = self.http_client.clone(); diff --git a/crates/feedback/src/feedback_modal.rs b/crates/feedback/src/feedback_modal.rs index f06fe8ba05..9b76ea69a6 100644 --- a/crates/feedback/src/feedback_modal.rs +++ b/crates/feedback/src/feedback_modal.rs @@ -299,7 +299,7 @@ impl FeedbackModal { let installation_id = telemetry.installation_id(); let is_staff = telemetry.is_staff(); let http_client = zed_client.http_client(); - let feedback_endpoint = http_client.zed_url("/api/feedback"); + let feedback_endpoint = http_client.build_url("/api/feedback"); let request = FeedbackRequestBody { feedback_text: &feedback_text, email, diff --git a/crates/util/src/http.rs b/crates/util/src/http.rs index 89b1b482e9..62cf2d1239 100644 --- a/crates/util/src/http.rs +++ b/crates/util/src/http.rs @@ -14,56 +14,62 @@ use std::fmt; use std::{sync::Arc, time::Duration}; pub use url::Url; -pub struct ZedHttpClient { - pub zed_host: Mutex, - client: Box, +/// An [`HttpClient`] that has a base URL. +pub struct HttpClientWithUrl { + base_url: Mutex, + client: Arc, } -impl ZedHttpClient { - pub fn zed_url(&self, path: &str) -> String { - format!("{}{}", self.zed_host.lock(), path) +impl HttpClientWithUrl { + /// Returns a new [`HttpClientWithUrl`] with the given base URL. + pub fn new(base_url: impl Into) -> Self { + Self { + base_url: Mutex::new(base_url.into()), + client: client(), + } } - pub fn zed_api_url(&self, path: &str) -> String { - let zed_host = self.zed_host.lock().clone(); + /// Returns the base URL. + pub fn base_url(&self) -> String { + self.base_url.lock().clone() + } - let host = match zed_host.as_ref() { + /// Sets the base URL. + pub fn set_base_url(&self, base_url: impl Into) { + *self.base_url.lock() = base_url.into(); + } + + /// Builds a URL using the given path. + pub fn build_url(&self, path: &str) -> String { + format!("{}{}", self.base_url.lock(), path) + } + + /// Builds a Zed API URL using the given path. + pub fn build_zed_api_url(&self, path: &str) -> String { + let base_url = self.base_url.lock().clone(); + let base_api_url = match base_url.as_ref() { "https://zed.dev" => "https://api.zed.dev", "https://staging.zed.dev" => "https://api-staging.zed.dev", "http://localhost:3000" => "http://localhost:8080", other => other, }; - format!("{}{}", host, path) + format!("{}{}", base_api_url, path) } } -impl HttpClient for Arc { +impl HttpClient for Arc { fn send(&self, req: Request) -> BoxFuture, Error>> { self.client.send(req) } } -impl HttpClient for ZedHttpClient { +impl HttpClient for HttpClientWithUrl { fn send(&self, req: Request) -> BoxFuture, Error>> { self.client.send(req) } } -pub fn zed_client(zed_host: &str) -> Arc { - Arc::new(ZedHttpClient { - zed_host: Mutex::new(zed_host.to_string()), - client: Box::new( - isahc::HttpClient::builder() - .connect_timeout(Duration::from_secs(5)) - .low_speed_timeout(100, Duration::from_secs(5)) - .proxy(http_proxy_from_env()) - .build() - .unwrap(), - ), - }) -} - pub trait HttpClient: Send + Sync { fn send(&self, req: Request) -> BoxFuture, Error>>; @@ -134,20 +140,20 @@ pub struct FakeHttpClient { #[cfg(feature = "test-support")] impl FakeHttpClient { - pub fn create(handler: F) -> Arc + pub fn create(handler: F) -> Arc where Fut: 'static + Send + futures::Future, Error>>, F: 'static + Send + Sync + Fn(Request) -> Fut, { - Arc::new(ZedHttpClient { - zed_host: Mutex::new("http://test.example".into()), - client: Box::new(Self { + Arc::new(HttpClientWithUrl { + base_url: Mutex::new("http://test.example".into()), + client: Arc::new(Self { handler: Box::new(move |req| Box::pin(handler(req))), }), }) } - pub fn with_404_response() -> Arc { + pub fn with_404_response() -> Arc { Self::create(|_| async move { Ok(Response::builder() .status(404) @@ -156,7 +162,7 @@ impl FakeHttpClient { }) } - pub fn with_200_response() -> Arc { + pub fn with_200_response() -> Arc { Self::create(|_| async move { Ok(Response::builder() .status(200) diff --git a/crates/zed/src/main.rs b/crates/zed/src/main.rs index 509ba772ab..96714368af 100644 --- a/crates/zed/src/main.rs +++ b/crates/zed/src/main.rs @@ -46,7 +46,7 @@ use std::{ use theme::{ActiveTheme, SystemAppearance, ThemeRegistry, ThemeSettings}; use util::{ async_maybe, - http::{self, HttpClient, ZedHttpClient}, + http::{HttpClient, HttpClientWithUrl}, paths::{self, CRASHES_DIR, CRASHES_RETIRED_DIR}, ResultExt, }; @@ -140,7 +140,9 @@ fn main() { client::init_settings(cx); let clock = Arc::new(clock::RealSystemClock); - let http = http::zed_client(&client::ClientSettings::get_global(cx).server_url); + let http = Arc::new(HttpClientWithUrl::new( + &client::ClientSettings::get_global(cx).server_url, + )); let client = client::Client::new(clock, http.clone(), cx); let mut languages = LanguageRegistry::new(login_shell_env_loaded); @@ -198,9 +200,8 @@ fn main() { move |cx| { languages.set_theme(cx.theme().clone()); let new_host = &client::ClientSettings::get_global(cx).server_url; - let mut host = http.zed_host.lock(); - if &*host != new_host { - *host = new_host.clone(); + if &http.base_url() != new_host { + http.set_base_url(new_host); if client.status().borrow().is_connected() { client.reconnect(&cx.to_async()); } @@ -677,7 +678,7 @@ fn init_panic_hook(app: &App, installation_id: Option, session_id: Strin })); } -fn upload_panics_and_crashes(http: Arc, cx: &mut AppContext) { +fn upload_panics_and_crashes(http: Arc, cx: &mut AppContext) { let telemetry_settings = *client::TelemetrySettings::get_global(cx); cx.background_executor() .spawn(async move { @@ -692,12 +693,12 @@ fn upload_panics_and_crashes(http: Arc, cx: &mut AppContext) { .detach() } -/// upload panics to us (via zed.dev) +/// Uploads panics via `zed.dev`. async fn upload_previous_panics( - http: Arc, + http: Arc, telemetry_settings: client::TelemetrySettings, ) -> Result> { - let panic_report_url = http.zed_url("/api/panic"); + let panic_report_url = http.build_url("/api/panic"); let mut children = smol::fs::read_dir(&*paths::LOGS_DIR).await?; let mut most_recent_panic = None; @@ -766,7 +767,7 @@ static LAST_CRASH_UPLOADED: &'static str = "LAST_CRASH_UPLOADED"; /// upload crashes from apple's diagnostic reports to our server. /// (only if telemetry is enabled) async fn upload_previous_crashes( - http: Arc, + http: Arc, most_recent_panic: Option<(i64, String)>, telemetry_settings: client::TelemetrySettings, ) -> Result<()> { @@ -778,7 +779,7 @@ async fn upload_previous_crashes( .unwrap_or("zed-2024-01-17-221900.ips".to_string()); // don't upload old crash reports from before we had this. let mut uploaded = last_uploaded.clone(); - let crash_report_url = http.zed_url("/api/crash"); + let crash_report_url = http.build_url("/api/crash"); for dir in [&*CRASHES_DIR, &*CRASHES_RETIRED_DIR] { let mut children = smol::fs::read_dir(&dir).await?;