Skip to content

Commit 59c23e7

Browse files
committed
PERP-4586 | Refactor CosmosInterceptor and parse_cosmos_grpc
1 parent 1dc7370 commit 59c23e7

File tree

3 files changed

+57
-29
lines changed

3 files changed

+57
-29
lines changed

packages/cosmos/src/client.rs

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ pub(crate) mod query;
55

66
use std::{
77
collections::HashMap,
8-
str::FromStr,
98
sync::{Arc, Weak},
109
};
1110

@@ -32,7 +31,7 @@ use cosmos_sdk_proto::{
3231
use parking_lot::{Mutex, RwLock};
3332
use tokio::{sync::mpsc::Receiver, task::JoinSet, time::Instant};
3433
use tonic::{
35-
metadata::{MetadataKey, MetadataValue},
34+
metadata::{Ascii, MetadataKey, MetadataValue},
3635
service::Interceptor,
3736
Status,
3837
};
@@ -743,19 +742,13 @@ impl Cosmos {
743742
}
744743

745744
#[derive(Clone)]
746-
pub struct CosmosInterceptor(pub HashMap<String, String>);
745+
pub struct CosmosInterceptor(Arc<[(MetadataKey<Ascii>, MetadataValue<Ascii>)]>);
747746

748747
impl Interceptor for CosmosInterceptor {
749748
fn call(&mut self, mut request: tonic::Request<()>) -> Result<tonic::Request<()>, Status> {
750-
let req = request.metadata_mut();
751-
for (key, value) in &self.0 {
752-
let key = MetadataKey::from_bytes(key.as_bytes())
753-
.map_err(|_| Status::internal(format!("Invalid header key: '{}'", key)))?;
754-
755-
let value = MetadataValue::from_str(value)
756-
.map_err(|_| Status::internal(format!("Invalid header value for '{}'", key)))?;
757-
758-
req.insert(key, value);
749+
let meta = request.metadata_mut();
750+
for (key, value) in self.0.iter() {
751+
meta.insert(key, value.clone());
759752
}
760753
Ok(request)
761754
}

packages/cosmos/src/client/node.rs

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::{
2-
collections::HashMap,
32
ops::Deref,
3+
str::FromStr,
44
sync::Arc,
55
time::{Duration, Instant},
66
};
@@ -9,7 +9,9 @@ use chrono::{DateTime, Utc};
99
use parking_lot::RwLock;
1010
use tonic::{
1111
codegen::InterceptedService,
12+
metadata::{Ascii, MetadataKey, MetadataValue},
1213
transport::{Channel, ClientTlsConfig, Endpoint, Uri},
14+
Status,
1315
};
1416

1517
use crate::{
@@ -91,23 +93,47 @@ impl LastError {
9193
}
9294
}
9395

94-
fn parse_cosmos_grpc(value: &str) -> (String, HashMap<String, String>) {
95-
let parts: Vec<&str> = value.splitn(2, '#').collect();
96-
97-
let endpoint = parts[0].trim().to_string();
98-
let headers = if parts.len() == 2 {
99-
parts[1]
100-
.split(';')
101-
.filter_map(|pair| {
102-
let mut kv = pair.splitn(2, '=');
103-
Some((kv.next()?.to_string(), kv.next()?.to_string()))
104-
})
105-
.collect()
106-
} else {
107-
HashMap::new()
96+
type ParseCosmosGrpcResult =
97+
Result<(String, Arc<[(MetadataKey<Ascii>, MetadataValue<Ascii>)]>), Status>;
98+
99+
pub fn parse_cosmos_grpc(value: &str) -> ParseCosmosGrpcResult {
100+
let (endpoint, raw_headers) = match value.split_once('#') {
101+
Some((endpoint, headers)) => (endpoint.trim().to_string(), Some(headers)),
102+
None => (value.trim().to_string(), None),
103+
};
104+
105+
let headers = {
106+
let mut parsed = Vec::new();
107+
if let Some(hdrs) = raw_headers {
108+
for pair in hdrs.split(';').filter(|s| !s.trim().is_empty()) {
109+
let (key, val) = pair.split_once('=').ok_or_else(|| {
110+
Status::invalid_argument(format!("Malformed header: '{}'", pair))
111+
})?;
112+
113+
let key = MetadataKey::from_bytes(key.trim().as_bytes()).map_err(|_| {
114+
Status::invalid_argument(format!("Invalid header key '{}'", key))
115+
})?;
116+
117+
let val_str = val.trim();
118+
119+
if val_str.is_empty() {
120+
return Err(Status::invalid_argument(format!(
121+
"Header '{}' has empty value",
122+
key
123+
)));
124+
}
125+
126+
let val = MetadataValue::from_str(val_str).map_err(|_| {
127+
Status::invalid_argument(format!("Invalid header value for '{}'", key))
128+
})?;
129+
130+
parsed.push((key, val));
131+
}
132+
}
133+
Arc::from(parsed.into_boxed_slice())
108134
};
109135

110-
(endpoint, headers)
136+
Ok((endpoint, headers))
111137
}
112138

113139
impl CosmosBuilder {
@@ -116,7 +142,11 @@ impl CosmosBuilder {
116142
grpc_url: &Arc<String>,
117143
is_fallback: bool,
118144
) -> Result<Node, BuilderError> {
119-
let (url, headers) = parse_cosmos_grpc(grpc_url.as_str());
145+
let (url, headers) =
146+
parse_cosmos_grpc(grpc_url.as_str()).map_err(|e| BuilderError::InvalidGrpcHeaders {
147+
grpc_url: grpc_url.clone(),
148+
source: e,
149+
})?;
120150
let grpc_url = Arc::<String>::new(url);
121151

122152
let grpc_endpoint =

packages/cosmos/src/error.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ pub enum BuilderError {
102102
SanityQueryFailed { source: QueryError },
103103
#[error("Could not find Sei gas config for chain ID {chain_id} at {url}")]
104104
SeiGasConfigNotFound { chain_id: String, url: String },
105+
#[error("Invalid gRPC headers in {grpc_url}: {source}")]
106+
InvalidGrpcHeaders {
107+
grpc_url: Arc<String>,
108+
source: tonic::Status,
109+
},
105110
}
106111

107112
/// Parse errors while interacting with chain data.

0 commit comments

Comments
 (0)