iota_grpc_client/
client.rs1use std::time::Duration;
6
7use iota_grpc_types::v1::{
8 ledger_service::ledger_service_client::LedgerServiceClient,
9 move_package_service::move_package_service_client::MovePackageServiceClient,
10 state_service::state_service_client::StateServiceClient,
11 transaction_execution_service::transaction_execution_service_client::TransactionExecutionServiceClient,
12};
13use tonic::codec::CompressionEncoding;
14
15use crate::{api::Result, interceptors::HeadersInterceptor};
16
17type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
18
19pub type InterceptedChannel =
20 tonic::service::interceptor::InterceptedService<tonic::transport::Channel, HeadersInterceptor>;
21
22#[derive(Clone)]
24pub struct Client {
25 uri: http::Uri,
27 channel: tonic::transport::Channel,
29 headers: HeadersInterceptor,
31 max_decoding_message_size: Option<usize>,
33}
34
35impl Client {
36 #[allow(clippy::result_large_err)]
38 pub async fn connect<T>(uri: T) -> Result<Self>
39 where
40 T: TryInto<http::Uri>,
41 T::Error: Into<BoxError>,
42 {
43 let uri = uri
44 .try_into()
45 .map_err(Into::into)
46 .map_err(tonic::Status::from_error)?;
47
48 let mut endpoint = tonic::transport::Endpoint::from(uri.clone());
49 if uri.scheme() == Some(&http::uri::Scheme::HTTPS) {
50 #[cfg(not(feature = "tls-ring"))]
51 return Err(tonic::Status::failed_precondition(
52 "the `tls-ring` feature must be enabled for HTTPS",
53 )
54 .into());
55
56 #[cfg(not(any(feature = "tls-native-roots", feature = "tls-webpki-roots")))]
57 return Err(tonic::Status::failed_precondition(
58 "the `tls-native-roots` or `tls-webpki-roots` feature must be enabled for HTTPS",
59 )
60 .into());
61
62 #[cfg(all(
63 feature = "tls-ring",
64 any(feature = "tls-native-roots", feature = "tls-webpki-roots")
65 ))]
66 {
67 endpoint = endpoint
68 .tls_config(
69 tonic::transport::channel::ClientTlsConfig::new().with_enabled_roots(),
70 )
71 .map_err(Into::into)
72 .map_err(tonic::Status::from_error)?;
73 }
74 }
75
76 let channel = endpoint
77 .connect_timeout(Duration::from_secs(5))
78 .http2_keep_alive_interval(Duration::from_secs(5))
79 .connect_lazy();
80
81 Ok(Self {
82 uri,
83 channel,
84 headers: Default::default(),
85 max_decoding_message_size: None,
86 })
87 }
88
89 pub fn uri(&self) -> &http::Uri {
90 &self.uri
91 }
92
93 pub fn channel(&self) -> &tonic::transport::Channel {
98 &self.channel
99 }
100
101 pub fn headers(&self) -> &HeadersInterceptor {
102 &self.headers
103 }
104
105 pub fn max_decoding_message_size(&self) -> Option<usize> {
106 self.max_decoding_message_size
107 }
108
109 pub fn with_headers(mut self, headers: HeadersInterceptor) -> Self {
110 self.headers = headers;
111 self
112 }
113
114 pub fn with_max_decoding_message_size(mut self, limit: usize) -> Self {
115 self.max_decoding_message_size = Some(limit);
116 self
117 }
118
119 pub fn ledger_service_client(&self) -> LedgerServiceClient<InterceptedChannel> {
121 self.configure_client(LedgerServiceClient::with_interceptor(
122 self.channel.clone(),
123 self.headers.clone(),
124 ))
125 }
126
127 pub fn execution_service_client(
129 &self,
130 ) -> TransactionExecutionServiceClient<InterceptedChannel> {
131 self.configure_client(TransactionExecutionServiceClient::with_interceptor(
132 self.channel.clone(),
133 self.headers.clone(),
134 ))
135 }
136
137 pub fn state_service_client(&self) -> StateServiceClient<InterceptedChannel> {
139 self.configure_client(StateServiceClient::with_interceptor(
140 self.channel.clone(),
141 self.headers.clone(),
142 ))
143 }
144
145 pub fn move_package_service_client(&self) -> MovePackageServiceClient<InterceptedChannel> {
147 self.configure_client(MovePackageServiceClient::with_interceptor(
148 self.channel.clone(),
149 self.headers.clone(),
150 ))
151 }
152
153 fn configure_client<C: GrpcClientConfig>(&self, client: C) -> C {
155 let client = client.accept_compressed(CompressionEncoding::Zstd);
156 if let Some(limit) = self.max_decoding_message_size {
157 client.max_decoding_message_size(limit)
158 } else {
159 client
160 }
161 }
162}
163
164trait GrpcClientConfig: Sized {
170 fn accept_compressed(self, encoding: CompressionEncoding) -> Self;
171 fn max_decoding_message_size(self, limit: usize) -> Self;
172}
173
174macro_rules! impl_grpc_client_config {
176 ($($client:ty),* $(,)?) => {
177 $(
178 impl GrpcClientConfig for $client {
179 fn accept_compressed(self, encoding: CompressionEncoding) -> Self {
180 self.accept_compressed(encoding)
181 }
182 fn max_decoding_message_size(self, limit: usize) -> Self {
183 self.max_decoding_message_size(limit)
184 }
185 }
186 )*
187 };
188}
189
190impl_grpc_client_config!(
191 LedgerServiceClient<InterceptedChannel>,
192 TransactionExecutionServiceClient<InterceptedChannel>,
193 StateServiceClient<InterceptedChannel>,
194 MovePackageServiceClient<InterceptedChannel>,
195);