diff --git a/Cargo.toml b/Cargo.toml index a250f00..0fdd42a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,6 +50,7 @@ serde = { version = "1.0.219", features = ["derive"] } thiserror = "2.0.12" tokio = { version = "1.45.0", features = ["macros", "rt", "rt-multi-thread"] } tonic = "0.14.0" +url = { version = "2.5.7", features = ["serde"] } uuid = { version = "1.17.0", features = ["v7"] } [profile.dev] diff --git a/bevy/src/peer.rs b/bevy/src/peer.rs index 5e1157c..181ad64 100644 --- a/bevy/src/peer.rs +++ b/bevy/src/peer.rs @@ -17,8 +17,8 @@ pub struct PeerAddress(String); #[tokio::main] async fn add_cached_peers(mut commands: Commands) { let config = CONFIG.get_unchecked(); - let path = String::from("unix://") + config.rpc.socket_path.as_os_str().to_str().expect("Invalid string"); - let mut client = caretta_sync_core::proto::cached_peer_service_client::CachedPeerServiceClient::connect(path).await.expect("Unix socket should be accessible"); + let url = config.rpc.endpoint_url.to_string(); + let mut client = caretta_sync_core::proto::cached_peer_service_client::CachedPeerServiceClient::connect(url).await.expect("Unix socket should be accessible"); let request = tonic::Request::new(CachedPeerListRequest {}); let response = client.list(request).await.expect("Faild to request/response"); let peers = response.into_inner().peers; diff --git a/cli/src/cli/peer/list.rs b/cli/src/cli/peer/list.rs index 4b551f5..079ff45 100644 --- a/cli/src/cli/peer/list.rs +++ b/cli/src/cli/peer/list.rs @@ -15,8 +15,8 @@ impl Runnable for PeerListCommandArgs { #[tokio::main] async fn run(self, app_name: &'static str) { let config = self.config.into_config(app_name).await; - let path = String::from("unix://") + config.rpc.socket_path.as_os_str().to_str().expect("Invalid string"); - let mut client = caretta_sync_core::proto::cached_peer_service_client::CachedPeerServiceClient::connect(path).await.expect("Unix socket should be accessible"); + let url = config.rpc.endpoint_url.to_string(); + let mut client = caretta_sync_core::proto::cached_peer_service_client::CachedPeerServiceClient::connect(url).await.expect("Target endpoint should be accessible"); let request = tonic::Request::new(CachedPeerListRequest {}); let response = client.list(request).await.expect("Faild to request/response"); println!("{:?}", response); diff --git a/core/Cargo.toml b/core/Cargo.toml index d023696..286b54c 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -23,6 +23,7 @@ libp2p.workspace = true libp2p-core = { version = "0.43.0", features = ["serde"] } libp2p-identity = { version = "0.2.11", features = ["ed25519", "peerid", "rand", "serde"] } prost = "0.14.1" +prost-types = "0.14.1" sea-orm.workspace = true sea-orm-migration.workspace = true serde.workspace = true @@ -35,7 +36,7 @@ tonic-prost = "0.14.0" tracing = "0.1.41" tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } uuid.workspace = true -prost-types = "0.14.1" +url.workspace = true sysinfo = "0.37.0" whoami = "1.6.1" diff --git a/core/src/config/error.rs b/core/src/config/error.rs index 1d59bf8..e2bd4d4 100644 --- a/core/src/config/error.rs +++ b/core/src/config/error.rs @@ -1,3 +1,5 @@ +use url::Url; + #[derive(thiserror::Error, Debug)] pub enum ConfigError { #[error("missing config: {0}")] @@ -7,5 +9,7 @@ pub enum ConfigError { #[error("Toml Deserialization Error")] TomlDerialization(#[from] toml::de::Error), #[error("Toml Serialization Error")] - TomlSerialization(#[from] toml::ser::Error), + TomlSerialization(#[from] toml::ser::Error), + #[error("Invalid url: {0}")] + InvalidUrl(Url) } \ No newline at end of file diff --git a/core/src/config/rpc.rs b/core/src/config/rpc.rs index 65eeac6..6cc3774 100644 --- a/core/src/config/rpc.rs +++ b/core/src/config/rpc.rs @@ -1,25 +1,28 @@ use std::{net::{IpAddr, Ipv4Addr, SocketAddr, TcpListener}, path::PathBuf, str::FromStr}; #[cfg(feature="cli")] use clap::Args; +use url::Url; use crate::{config::PartialConfig, utils::{emptiable::Emptiable, mergeable::Mergeable}}; use libp2p::mdns::Config; use serde::{Deserialize, Serialize}; use crate::config::error::ConfigError; + + #[cfg(unix)] -static DEFAULT_SOCKET_PATH: &str = "caretta.sock"; +static DEFAULT_PORT: u16 = 54321; #[derive(Clone, Debug)] pub struct RpcConfig { - pub socket_path: PathBuf, + pub endpoint_url: Url, } impl TryFrom for RpcConfig { type Error = ConfigError; fn try_from(config: PartialRpcConfig) -> Result { Ok(Self{ - socket_path: config.socket_path.ok_or(ConfigError::MissingConfig("port".to_string()))?, + endpoint_url: config.endpoint_url.ok_or(ConfigError::MissingConfig("endpoint".to_string()))?, }) } } @@ -27,21 +30,21 @@ impl TryFrom for RpcConfig { #[cfg_attr(feature="cli", derive(Args))] #[derive(Clone, Debug, Deserialize, Serialize, PartialEq)] pub struct PartialRpcConfig { - pub socket_path: Option, + pub endpoint_url: Option, } impl PartialRpcConfig { - #[cfg(not(target_os="ios"))] + #[cfg(not(any(all(target_os="ios", target_abi="sim"), target_os="windows")))] pub fn default(app_name: &'static str) -> Self { let username = whoami::username(); Self{ - socket_path: Some(std::env::temp_dir().join(username).join(String::from(app_name) + ".sock")), + endpoint_url: Some(Url::parse(&(String::from("unix://") + std::env::temp_dir().join(username).join(String::from(app_name) + ".sock").to_str().unwrap())).unwrap()), } } - #[cfg(target_os="ios")] + #[cfg(any(all(target_os="ios", target_abi="sim"), target_os="windows"))] pub fn default(app_name: &'static str) -> Self { Self{ - socket_path: Some(std::env::temp_dir().join(String::from(app_name) + ".sock")), + endpoint_url: Some(Url::parse("http://127.0.0.1:54321").unwrap()), } } } @@ -49,26 +52,26 @@ impl PartialRpcConfig { impl Emptiable for PartialRpcConfig { fn empty() -> Self { Self { - socket_path: None, + endpoint_url: None, } } fn is_empty(&self) -> bool { - self.socket_path.is_none() + self.endpoint_url.is_none() } } impl From for PartialRpcConfig { fn from(source: RpcConfig) -> Self { Self { - socket_path: Some(source.socket_path), + endpoint_url: Some(source.endpoint_url), } } } impl Mergeable for PartialRpcConfig { fn merge(&mut self, other: Self) { - if let Some(x) = other.socket_path { - self.socket_path = Some(x); + if let Some(x) = other.endpoint_url { + self.endpoint_url = Some(x); } } } diff --git a/core/src/tests.rs b/core/src/tests.rs index 78f44a4..f6b7b54 100644 --- a/core/src/tests.rs +++ b/core/src/tests.rs @@ -3,6 +3,7 @@ use std::{path::PathBuf, sync::LazyLock}; use sea_orm::{sea_query::{FromValueTuple, IntoValueTuple, ValueType}, ActiveModelBehavior, ActiveModelTrait, ColumnTrait, Condition, DatabaseConnection, EntityTrait, IntoActiveModel, ModelTrait, PrimaryKeyToColumn, PrimaryKeyTrait, Value}; use sea_orm::QueryFilter; use tempfile::TempDir; +use url::Url; use crate::{ config::{Config, PartialConfig, PartialP2pConfig, PartialRpcConfig, RpcConfig, StorageConfig}, message::Message}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; @@ -20,7 +21,7 @@ pub static TEST_CONFIG: LazyLock = LazyLock::new(|| { cache_directory: cache_dir, }, rpc: RpcConfig{ - socket_path: test_dir.join("socket.sock"), + endpoint_url: Url::parse(&(String::from("unix://") + test_dir.join("socket.sock").to_str().unwrap())).unwrap(), }, } }); diff --git a/examples/core/Cargo.toml b/examples/core/Cargo.toml index 7e93a70..10bec3f 100644 --- a/examples/core/Cargo.toml +++ b/examples/core/Cargo.toml @@ -13,3 +13,4 @@ libp2p.workspace = true tokio.workspace = true tokio-stream = { version = "0.1.17", features = ["net"] } tonic.workspace = true +url.workspace = true \ No newline at end of file diff --git a/examples/core/src/server.rs b/examples/core/src/server.rs index 66885f6..29548e1 100644 --- a/examples/core/src/server.rs +++ b/examples/core/src/server.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use caretta_sync::{ config::P2pConfig, proto::cached_peer_service_server::CachedPeerServiceServer, @@ -43,21 +45,35 @@ impl ServerTrait for Server { async fn serve_rpc(config: &T) -> Result<(), caretta_sync::error::Error> where T: AsRef { - let path = config.as_ref().socket_path.clone(); - if let Some(x) = path.parent() { - if !x.exists() { - std::fs::create_dir_all(x).expect("Failed to create directory for socket file!"); + let url = config.as_ref().endpoint_url.clone(); + let router = tonic::transport::Server::builder() + .add_service(CachedPeerServiceServer::new(CachedPeerService::default())); + match url.scheme() { + "unix" => { + let path = PathBuf::from(url.path()); + if let Some(x) = path.parent() { + if !x.exists() { + std::fs::create_dir_all(x).expect("Failed to create directory for socket file!"); + } + } + if path.exists() { + std::fs::remove_file(&path).expect("Failed to remove existing socket file!") + } + let uds = UnixListener::bind(path).unwrap(); + let uds_stream = UnixListenerStream::new(uds); + + router.serve_with_incoming(uds_stream) + .await.unwrap(); + }, + "http" => { + let host = url.socket_addrs(|| None).expect("http endpoint should have host address and port").pop().unwrap(); + + router.serve(host).await.unwrap(); + }, + _ => { + Err(caretta_sync::error::Error::Config(caretta_sync::config::ConfigError::InvalidUrl(url)))?; } } - if path.exists() { - std::fs::remove_file(&path).expect("Failed to remove existing socket file!") - } - let uds = UnixListener::bind(path).unwrap(); - let uds_stream = UnixListenerStream::new(uds); - tonic::transport::Server::builder() - .add_service(CachedPeerServiceServer::new(CachedPeerService::default())) - .serve_with_incoming(uds_stream) - .await.unwrap(); Ok(()) } } \ No newline at end of file diff --git a/examples/mobile/src/lib.rs b/examples/mobile/src/lib.rs index 68291d8..b6a21a4 100644 --- a/examples/mobile/src/lib.rs +++ b/examples/mobile/src/lib.rs @@ -30,16 +30,14 @@ pub async fn init_config() { let mut default = PartialConfig::default(APP_NAME); default.merge(config); let config2 : Config = default.try_into().unwrap(); - Server::serve_all(&config2).await; + Server::serve_all(&config2); } #[bevy_main] pub fn main() { - //init_config(); - - + init_config(); let mut app = App::new(); app.add_plugins(