11use 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};
99use parking_lot:: RwLock ;
1010use tonic:: {
1111 codegen:: InterceptedService ,
12+ metadata:: { Ascii , MetadataKey , MetadataValue } ,
1213 transport:: { Channel , ClientTlsConfig , Endpoint , Uri } ,
14+ Status ,
1315} ;
1416
1517use 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
113139impl 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 =
0 commit comments