diff --git a/src/config.rs b/src/config.rs index 26c848a..283ece6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -309,20 +309,22 @@ impl Config { pub fn get_brain_info() -> (String, String) { match Self::init_config().network.as_str() { - "staging" => ("https://184.107.169.199:49092".to_string(), "staging-brain".to_string()), + "staging" => ("https://10.254.254.8:31337".to_string(), "staging-brain".to_string()), "localhost" => ("https://localhost:31337".to_string(), "staging-brain".to_string()), _ => ("https://173.234.17.2:39477".to_string(), "testnet-brain".to_string()), } } - pub async fn get_brain_channel() -> Result { - let (brain_url, brain_san) = Self::get_brain_info(); - + pub async fn connect_brain_channel( + brain_url: String, + ) -> Result { use hyper_rustls::HttpsConnectorBuilder; use rustls::pki_types::pem::PemObject; use rustls::pki_types::CertificateDer; use rustls::{ClientConfig, RootCertStore}; + let brain_san = Config::get_brain_info().1; + let mut detee_root_ca_store = RootCertStore::empty(); detee_root_ca_store .add(CertificateDer::from_pem_file(Config::get_root_ca_path()?).map_err(|e| { diff --git a/src/constants.rs b/src/constants.rs index e39d4fd..dd466f0 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -1 +1,2 @@ pub const HRATLS_APP_PORT: u32 = 34500; +pub const MAX_REDIRECTS: u16 = 3; diff --git a/src/general/grpc.rs b/src/general/grpc.rs index 18b39fb..014fa50 100644 --- a/src/general/grpc.rs +++ b/src/general/grpc.rs @@ -35,7 +35,8 @@ pub enum Error { } async fn client() -> Result, Error> { - Ok(BrainGeneralCliClient::new(Config::get_brain_channel().await?)) + let default_brain_url = Config::get_brain_info().0; + Ok(BrainGeneralCliClient::new(Config::connect_brain_channel(default_brain_url).await?)) } pub async fn get_balance(account: &str) -> Result { diff --git a/src/sgx/grpc_brain.rs b/src/sgx/grpc_brain.rs index c2b4678..f5e07f8 100644 --- a/src/sgx/grpc_brain.rs +++ b/src/sgx/grpc_brain.rs @@ -66,7 +66,8 @@ impl crate::HumanOutput for AppContract { } async fn client() -> Result> { - Ok(BrainAppCliClient::new(Config::get_brain_channel().await?)) + let default_brain_url = Config::get_brain_info().0; + Ok(BrainAppCliClient::new(Config::connect_brain_channel(default_brain_url).await?)) } pub async fn new_app(app_deploy_config: AppDeployConfig) -> Result { diff --git a/src/snp/grpc.rs b/src/snp/grpc.rs index a2fc231..27b9ee4 100644 --- a/src/snp/grpc.rs +++ b/src/snp/grpc.rs @@ -4,6 +4,7 @@ pub mod proto { } use crate::config::Config; +use crate::constants::MAX_REDIRECTS; use lazy_static::lazy_static; use log::{debug, info, warn}; use proto::{ @@ -41,6 +42,8 @@ pub enum Error { CorruptedRootCa(#[from] std::io::Error), #[error("Internal app error: could not parse Brain URL")] CorruptedBrainUrl, + #[error("Max redirects exceeded: {0}")] + MaxRedirectsExceeded(String), } impl crate::HumanOutput for VmContract { @@ -84,7 +87,14 @@ impl crate::HumanOutput for VmNodeListResp { } async fn client() -> Result, Error> { - Ok(BrainVmCliClient::new(Config::get_brain_channel().await?)) + let default_brain_url = Config::get_brain_info().0; + Ok(BrainVmCliClient::new(Config::connect_brain_channel(default_brain_url).await?)) +} + +async fn client_from_endpoint( + reconnect_endpoint: String, +) -> Result, Error> { + Ok(BrainVmCliClient::new(Config::connect_brain_channel(reconnect_endpoint).await?)) } fn sign_request(req: T) -> Result, Error> { @@ -130,10 +140,28 @@ pub async fn get_one_node(req: VmNodeFilters) -> Result { pub async fn create_vm(req: NewVmReq) -> Result { let mut client = client().await?; debug!("Sending NewVmReq to brain: {req:?}"); - match client.new_vm(sign_request(req)?).await { - Ok(resp) => Ok(resp.into_inner()), - Err(e) => Err(e.into()), + for attempt in 0..MAX_REDIRECTS { + match client.new_vm(sign_request(req.clone())?).await { + Ok(resp) => return Ok(resp.into_inner()), + Err(status) + if status.code() == tonic::Code::Unavailable + && status.message() == "moved" + && status.metadata().contains_key("location") => + { + let redirect_url = status + .metadata() + .get("location") + .and_then(|v| v.to_str().ok().map(|s| format!("https://{s}"))) + .unwrap_or_default(); + // TODO: change this println to log::info!() + println!("{attempt}) server moved to a different URL, trying to reconnect... ({redirect_url})"); + client = client_from_endpoint(redirect_url).await?; + continue; + } + Err(e) => return Err(e.into()), + }; } + Err(Error::MaxRedirectsExceeded(MAX_REDIRECTS.to_string())) } pub async fn list_contracts(req: ListVmContractsReq) -> Result, Error> {