1use std::{collections::BTreeSet, future::Future, marker::PhantomData};
3
4use prost::Message;
5
6use super::MethodDescriptor;
7
8pub trait Rpc {
9 type Request: Message;
10 type Response: Message + Default;
11 const METHOD: &'static MethodDescriptor;
12}
13pub trait UnaryRpc: Rpc {}
14pub trait ServerStreamingRpc: Rpc {}
15pub trait ClientStreamingRpc: Rpc {}
16pub trait BidirectionalRpc: Rpc {}
17
18pub trait MessageReader: Send {
21 type Error: std::error::Error + Send + Sync + 'static;
22 fn next(&mut self) -> impl Future<Output = Result<Option<Vec<u8>>, Self::Error>> + Send;
23 fn cancel(&mut self);
25}
26
27pub trait MessageWriter: Send {
28 type Error: std::error::Error + Send + Sync + 'static;
29 fn send(&mut self, message: Vec<u8>) -> impl Future<Output = Result<(), Self::Error>> + Send;
30 fn finish(&mut self) -> impl Future<Output = Result<(), Self::Error>> + Send;
31 fn abort(&mut self);
32}
33
34pub trait RpcTransport: Send + Sync {
38 type Error: std::error::Error + Send + Sync + 'static;
39 type Reader: MessageReader<Error = Self::Error>;
40 type Writer: MessageWriter<Error = Self::Error>;
41 fn unary(
42 &self,
43 method: &'static MethodDescriptor,
44 request: Vec<u8>,
45 ) -> impl Future<Output = Result<Vec<u8>, Self::Error>> + Send;
46 fn observe(
47 &self,
48 method: &'static MethodDescriptor,
49 request: Vec<u8>,
50 ) -> impl Future<Output = Result<Self::Reader, Self::Error>> + Send;
51 fn exchange(
52 &self,
53 method: &'static MethodDescriptor,
54 opening: Vec<u8>,
55 ) -> impl Future<Output = Result<(Self::Writer, Self::Reader), Self::Error>> + Send;
56}
57
58#[derive(Debug, thiserror::Error)]
59pub enum ClientError<E: std::error::Error> {
60 #[error("endpoint does not implement {0}")]
61 NotImplemented(&'static str),
62 #[error("incompatible peer for {0}")]
63 Protocol(&'static str),
64 #[error("{0} requires a stable client operation ID")]
65 MissingOperationId(&'static str),
66 #[error("invalid request metadata: {0}")]
67 Metadata(#[from] crate::RequestMetadataError),
68 #[error("transport failure: {0}")]
69 Transport(E),
70 #[error("invalid request variant: {0}")]
71 RequestFeature(#[from] crate::hybrid_codec::Reject),
72 #[error("invalid protobuf response: {0}")]
73 Decode(#[from] prost::DecodeError),
74}
75
76pub struct Client<T> {
77 transport: T,
78 implemented: BTreeSet<String>,
79 protocol: Option<crate::heddle::api::common::ProtocolCompatibility>,
80}
81
82impl<T: RpcTransport> Client<T> {
83 pub fn new(transport: T, implemented: impl IntoIterator<Item = String>) -> Self {
86 Self {
87 transport,
88 implemented: implemented.into_iter().collect(),
89 protocol: None,
90 }
91 }
92
93 pub fn with_protocol(
97 mut self,
98 protocol: crate::heddle::api::common::ProtocolCompatibility,
99 ) -> Self {
100 self.protocol = Some(protocol);
101 self
102 }
103
104 fn encode<M: Rpc>(&self, request: &M::Request) -> Result<Vec<u8>, ClientError<T::Error>> {
105 let method = M::METHOD;
106 if !self.implemented.contains(method.path) {
107 return Err(ClientError::NotImplemented(method.path));
108 }
109 if !method.mandatory_features.is_empty() {
110 crate::import_authority::require_hybrid_peer(self.protocol.as_ref())
111 .map_err(|_| ClientError::Protocol(method.path))?;
112 }
113 let bytes = request.encode_to_vec();
114 super::protocol::validate_request_features(method.path, &bytes, self.protocol.as_ref())?;
115 validate_stream_protocol(method, &bytes, true, true)
116 .map_err(|_| ClientError::Protocol(method.path))?;
117 if method.client_operation_id_required {
118 let Some(field) = method.client_operation_id_field_number else {
119 return Err(ClientError::MissingOperationId(method.path));
120 };
121 let id = crate::transport::protobuf_string_field(&bytes, field)?;
122 if id.is_none_or(|value| value.trim().is_empty()) {
123 return Err(ClientError::MissingOperationId(method.path));
124 }
125 }
126 Ok(bytes)
127 }
128
129 pub async fn call<M: UnaryRpc>(
134 &self,
135 request: &M::Request,
136 ) -> Result<M::Response, ClientError<T::Error>> {
137 let bytes = self
138 .transport
139 .unary(M::METHOD, self.encode::<M>(request)?)
140 .await
141 .map_err(ClientError::Transport)?;
142 Ok(M::Response::decode(bytes.as_slice())?)
143 }
144
145 pub async fn observe<M: ServerStreamingRpc>(
146 &self,
147 request: &M::Request,
148 ) -> Result<Messages<T::Reader, M::Response>, ClientError<T::Error>> {
149 let reader = self
150 .transport
151 .observe(M::METHOD, self.encode::<M>(request)?)
152 .await
153 .map_err(ClientError::Transport)?;
154 Ok(Messages {
155 reader,
156 method: M::METHOD,
157 first: true,
158 done: false,
159 message: PhantomData,
160 })
161 }
162
163 pub async fn exchange<M: BidirectionalRpc>(
165 &self,
166 opening: &M::Request,
167 ) -> Result<
168 (
169 Sender<T::Writer, M::Request>,
170 Messages<T::Reader, M::Response>,
171 ),
172 ClientError<T::Error>,
173 > {
174 let (writer, reader) = self
175 .transport
176 .exchange(M::METHOD, self.encode::<M>(opening)?)
177 .await
178 .map_err(ClientError::Transport)?;
179 Ok((
180 Sender {
181 writer,
182 method: M::METHOD,
183 finished: false,
184 message: PhantomData,
185 },
186 Messages {
187 reader,
188 method: M::METHOD,
189 first: true,
190 done: false,
191 message: PhantomData,
192 },
193 ))
194 }
195}
196
197pub struct Messages<R: MessageReader, O> {
198 reader: R,
199 method: &'static MethodDescriptor,
200 first: bool,
201 done: bool,
202 message: PhantomData<O>,
203}
204
205impl<R: MessageReader, O> Messages<R, O> {
206 pub fn cancel(&mut self) {
209 if !self.done {
210 self.reader.cancel();
211 self.done = true;
212 }
213 }
214}
215
216impl<R: MessageReader, O: Message + Default> Messages<R, O> {
217 pub async fn next(&mut self) -> Result<Option<O>, ClientError<R::Error>> {
218 if self.done {
219 return Ok(None);
220 }
221 let decoded = match self.reader.next().await {
222 Ok(Some(bytes)) => {
223 if validate_stream_protocol(self.method, &bytes, false, self.first).is_err() {
224 Err(ClientError::Protocol(self.method.path))
225 } else {
226 self.first = false;
227 O::decode(bytes.as_slice())
228 .map(Some)
229 .map_err(ClientError::Decode)
230 }
231 }
232 Ok(None) => {
233 if self.first && is_hybrid_stream(self.method) {
234 Err(ClientError::Protocol(self.method.path))
235 } else {
236 self.done = true;
237 Ok(None)
238 }
239 }
240 Err(error) => Err(ClientError::Transport(error)),
241 };
242 if decoded.is_err() {
243 self.reader.cancel();
244 self.done = true;
245 }
246 decoded
247 }
248}
249
250impl<R: MessageReader, O> Drop for Messages<R, O> {
251 fn drop(&mut self) {
252 self.cancel();
253 }
254}
255
256pub struct Sender<W: MessageWriter, I> {
257 writer: W,
258 method: &'static MethodDescriptor,
259 finished: bool,
260 message: PhantomData<I>,
261}
262
263impl<W: MessageWriter, I: Message> Sender<W, I> {
264 pub async fn send(&mut self, message: &I) -> Result<(), ClientError<W::Error>> {
265 let bytes = message.encode_to_vec();
266 validate_stream_protocol(self.method, &bytes, true, false)
267 .map_err(|_| ClientError::Protocol(self.method.path))?;
268 self.writer
269 .send(bytes)
270 .await
271 .map_err(ClientError::Transport)
272 }
273 pub async fn finish(mut self) -> Result<(), W::Error> {
274 self.writer.finish().await?;
275 self.finished = true;
276 Ok(())
277 }
278}
279
280impl<W: MessageWriter, I> Drop for Sender<W, I> {
281 fn drop(&mut self) {
282 if !self.finished {
283 self.writer.abort();
284 }
285 }
286}
287
288fn is_hybrid_stream(method: &MethodDescriptor) -> bool {
291 method.path.starts_with("/heddle.api.v1alpha2.SyncService/")
292 && !method.mandatory_features.is_empty()
293}
294fn validate_stream_protocol(
296 method: &MethodDescriptor,
297 bytes: &[u8],
298 request: bool,
299 first: bool,
300) -> Result<(), crate::hybrid_codec::Reject> {
301 use crate::heddle::api::v1alpha2::*;
302 use crate::hybrid_codec::Reject;
303 use crate::import_authority::require_hybrid_peer;
304 if !is_hybrid_stream(method) {
305 return Ok(());
306 }
307 macro_rules! frame {
308 ($ty:ty, $variant:path) => {{
309 let value = <$ty>::decode(bytes).map_err(|_| Reject::Protocol)?;
310 match value.body {
311 Some($variant(open)) => require_hybrid_peer(open.protocol.as_ref()),
312 _ if first => Err(Reject::Protocol),
313 _ => Ok(()),
314 }
315 }};
316 }
317 match (method.path.rsplit('/').next(), request) {
318 (Some("Fetch"), true) => {
319 frame!(FetchClientFrame, fetch_client_frame::Body::Open)
320 }
321 (Some("Fetch"), false) => frame!(FetchServerFrame, fetch_server_frame::Body::Ready),
322 (Some("PublishContent"), true) => frame!(
323 PublishContentClientFrame,
324 publish_content_client_frame::Body::Open
325 ),
326 (Some("PublishContent"), false) => frame!(
327 PublishContentServerFrame,
328 publish_content_server_frame::Body::Ready
329 ),
330 (Some("ReplicateThread"), true) => {
331 frame!(ReplicateThreadRequest, replicate_thread_request::Body::Open)
332 }
333 (Some("ReplicateThread"), false) => frame!(
334 ReplicateThreadResponse,
335 replicate_thread_response::Body::Ready
336 ),
337 _ => Err(Reject::Protocol),
338 }
339}