1use std::{
3 cell::RefCell,
4 collections::BTreeSet,
5 io,
6 rc::Rc,
7 time::{Duration, Instant},
8};
9
10use grpc_core::{
11 Code, Codec, CompressionAlgorithm, GrpcClientSession, Metadata, SessionConfig, Status, Timeout,
12 transport::{SyncTransport, TcpTransport},
13};
14
15use crate::stream::{
16 BidiStreamingCall, ClientStreamingCall, ServerStreamingCall, StreamDriver, StreamError,
17};
18
19#[derive(Clone, Debug)]
21pub struct ChannelConfig {
22 pub authority: String,
23 pub scheme: String,
24 pub default_timeout: Option<Timeout>,
25 pub default_compression: CompressionAlgorithm,
26 pub session_config: SessionConfig,
27}
28impl Default for ChannelConfig {
29 fn default() -> Self {
30 Self {
31 authority: "localhost".into(),
32 scheme: "http".into(),
33 default_timeout: None,
34 default_compression: CompressionAlgorithm::Identity,
35 session_config: SessionConfig::default(),
36 }
37 }
38}
39impl ChannelConfig {
40 pub fn with_compression(mut self, compression: CompressionAlgorithm) -> Self {
42 self.default_compression = compression;
43 self
44 }
45}
46#[derive(Clone, Debug, Default)]
48pub struct CallOptions {
49 pub metadata: Metadata,
50 pub timeout: Option<Timeout>,
51 pub compression: Option<CompressionAlgorithm>,
52}
53struct ChannelInner<T: SyncTransport> {
54 session: GrpcClientSession,
55 transport: T,
56 buffer: Vec<u8>,
57 config: ChannelConfig,
58 cleanup_error: Option<Status>,
59 pending_reap: BTreeSet<i32>,
60 pending_cancellations: Rc<RefCell<Vec<i32>>>,
61}
62impl<T: SyncTransport> ChannelInner<T> {
63 fn pump(&mut self, wait: bool, deadline: Option<Instant>) -> Result<(), Status> {
64 check_deadline(deadline)?;
65 self.transport.set_nonblocking(true).map_err(io_status)?;
66 self.flush()?;
67 let mut read = loop {
68 match self.transport.read(&mut self.buffer) {
69 Err(error) if error.kind() == io::ErrorKind::Interrupted => {
70 check_deadline(deadline)?;
71 }
72 result => break result,
73 }
74 };
75 if matches!(&read, Err(error) if error.kind() == io::ErrorKind::WouldBlock) && wait {
76 let remaining = deadline.map(|at| at.saturating_duration_since(Instant::now()));
77 check_deadline(deadline)?;
78 let timeout = if self.session.want_write() {
79 Some(remaining.map_or(Duration::from_millis(1), |left| {
80 left.min(Duration::from_millis(1))
81 }))
82 } else {
83 remaining
84 };
85 self.transport
86 .set_read_timeout(timeout)
87 .map_err(io_status)?;
88 self.transport.set_nonblocking(false).map_err(io_status)?;
89 read = self.transport.read(&mut self.buffer);
90 let restored = self.transport.set_nonblocking(true).map_err(io_status);
91 restored?;
92 }
93 let count = match read {
94 Ok(0) => return Err(Status::unavailable("transport EOF")),
95 Ok(count) => count,
96 Err(error)
97 if matches!(
98 error.kind(),
99 io::ErrorKind::WouldBlock
100 | io::ErrorKind::TimedOut
101 | io::ErrorKind::Interrupted
102 ) =>
103 {
104 check_deadline(deadline)?;
105 return self.reap();
106 }
107 Err(error) => return Err(io_status(error)),
108 };
109 let mut offset = 0;
110 while offset < count {
111 let used = self.session.mem_recv(&self.buffer[offset..count])?;
112 if used == 0 {
113 return Err(Status::internal("HTTP/2 receive made no progress"));
114 }
115 offset += used;
116 }
117 self.flush()?;
118 check_deadline(deadline)
119 }
120 fn drain_cancellations(&mut self) -> Result<(), Status> {
121 let ids = std::mem::take(&mut *self.pending_cancellations.borrow_mut());
122 for id in ids {
123 let reset = self.session.cancel_stream(id, 8);
124 self.pending_reap.insert(id);
125 if let Err(reset_error) = reset {
126 match self.session.remove_closed_stream(id) {
127 Ok(()) => {
128 self.pending_reap.remove(&id);
129 }
130 Err(error) if error.code() == Code::NotFound => {
131 self.pending_reap.remove(&id);
132 }
133 Err(_) => {
134 self.cleanup_error = Some(reset_error.clone());
135 return Err(reset_error);
136 }
137 }
138 }
139 }
140 Ok(())
141 }
142 fn reap(&mut self) -> Result<(), Status> {
143 let ids: Vec<_> = self.pending_reap.iter().copied().collect();
144 for id in ids {
145 match self.session.remove_closed_stream(id) {
146 Ok(()) => {
147 self.pending_reap.remove(&id);
148 }
149 Err(error) if error.code() == Code::NotFound => {
150 self.pending_reap.remove(&id);
151 }
152 Err(error) if error.code() == Code::FailedPrecondition => {}
153 Err(error) => {
154 self.cleanup_error = Some(error.clone());
155 return Err(error);
156 }
157 }
158 }
159 Ok(())
160 }
161 fn flush(&mut self) -> Result<(), Status> {
162 self.drain_cancellations()?;
163 self.session.flush_to_transport(&mut self.transport)?;
164 self.reap()
165 }
166}
167fn io_status(error: io::Error) -> Status {
168 Status::unavailable(error.to_string())
169}
170fn codec_status(error: impl std::fmt::Display) -> Status {
171 Status::internal(format!("codec failure: {error}"))
172}
173
174pub struct SyncChannel<T: SyncTransport> {
188 inner: Rc<RefCell<ChannelInner<T>>>,
189}
190impl<T: SyncTransport> Clone for SyncChannel<T> {
191 fn clone(&self) -> Self {
192 Self {
193 inner: Rc::clone(&self.inner),
194 }
195 }
196}
197impl<T: SyncTransport + 'static> SyncChannel<T> {
198 pub fn new(transport: T) -> Result<Self, Status> {
202 Self::with_config(transport, ChannelConfig::default())
203 }
204 pub fn with_config(mut transport: T, mut config: ChannelConfig) -> Result<Self, Status> {
208 if config.authority.is_empty()
209 || config
210 .authority
211 .bytes()
212 .any(|byte| byte.is_ascii_control() || byte.is_ascii_whitespace())
213 {
214 return Err(Status::invalid_argument("invalid channel authority"));
215 }
216 config.session_config.scheme.clone_from(&config.scheme);
217 let session = GrpcClientSession::new(config.session_config.clone())?;
218 transport.set_nonblocking(true).map_err(io_status)?;
219 Ok(Self {
220 inner: Rc::new(RefCell::new(ChannelInner {
221 session,
222 transport,
223 buffer: vec![0; 16 * 1024],
224 config,
225 cleanup_error: None,
226 pending_reap: BTreeSet::new(),
227 pending_cancellations: Rc::new(RefCell::new(Vec::new())),
228 })),
229 })
230 }
231 fn deadline(&self, options: &CallOptions) -> Result<Option<Instant>, Status> {
232 let inner = self
233 .inner
234 .try_borrow()
235 .map_err(|_| Status::failed_precondition("reentrant channel access"))?;
236 options
237 .timeout
238 .or(inner.config.default_timeout)
239 .map(|duration| {
240 Instant::now()
241 .checked_add(duration)
242 .ok_or_else(|| Status::invalid_argument("RPC deadline overflow"))
243 })
244 .transpose()
245 }
246 fn start<C: Codec>(
247 &mut self,
248 path: &str,
249 codec: C,
250 options: CallOptions,
251 content_type: &str,
252 absolute_deadline: Option<Option<Instant>>,
253 ) -> Result<ChannelStreamDriver<T, C>, Status> {
254 let mut inner = self
255 .inner
256 .try_borrow_mut()
257 .map_err(|_| Status::failed_precondition("reentrant channel access"))?;
258 inner.flush()?;
259 if let Some(error) = &inner.cleanup_error {
260 return Err(error.clone());
261 }
262 let deadline = match absolute_deadline {
263 Some(deadline) => deadline,
264 None => options
265 .timeout
266 .or(inner.config.default_timeout)
267 .map(|duration| {
268 Instant::now()
269 .checked_add(duration)
270 .ok_or_else(|| Status::invalid_argument("RPC deadline overflow"))
271 })
272 .transpose()?,
273 };
274 let timeout = deadline
275 .map(|end| {
276 end.checked_duration_since(Instant::now())
277 .filter(|duration| !duration.is_zero())
278 .ok_or_else(|| Status::deadline_exceeded("RPC deadline elapsed"))
279 })
280 .transpose()?;
281 let encoding = options
282 .compression
283 .unwrap_or(inner.config.default_compression);
284 let authority = inner.config.authority.clone();
285 let id = inner.session.start_request_with_content_type(
286 &authority,
287 path,
288 &options.metadata,
289 timeout,
290 encoding,
291 false,
292 content_type,
293 )?;
294 Ok(ChannelStreamDriver {
295 inner: Rc::clone(&self.inner),
296 id,
297 codec,
298 encoding,
299 deadline,
300 initial: None,
301 trailing: None,
302 complete: false,
303 closed: false,
304 buffer: Vec::new(),
305 pending_cancellations: Rc::clone(&inner.pending_cancellations),
306 })
307 }
308 pub fn unary<C, Req, Resp>(
314 &mut self,
315 path: &str,
316 req: &Req,
317 codec: &mut C,
318 options: CallOptions,
319 ) -> Result<(Resp, Metadata, Metadata), Status>
320 where
321 C: Codec<EncodeItem = Req, DecodeItem = Resp>,
322 {
323 let deadline = self.deadline(&options)?;
324 check_deadline(deadline)?;
325 let mut payload = Vec::new();
326 codec.encode(req, &mut payload).map_err(codec_status)?;
327 check_deadline(deadline)?;
328 let mut driver = self.start(path, BytesCodec, options, C::CONTENT_TYPE, Some(deadline))?;
329 driver.send(&payload).map_err(status_only)?;
330 driver.half_close().map_err(status_only)?;
331 let mut response = None;
332 loop {
333 match driver.try_recv_next() {
334 Ok(Some(bytes)) => {
335 if response.replace(bytes).is_some() {
336 driver.cancel();
337 return Err(Status::internal("multiple unary responses"));
338 }
339 }
340 Ok(None) => break,
341 Err(StreamError::WouldBlock) => driver.wait_ready().map_err(status_only)?,
342 Err(error) => return Err(status_only(error)),
343 }
344 }
345 let bytes = response.ok_or_else(|| Status::internal("missing unary response"))?;
346 let response = codec.decode(bytes.payload()).map_err(codec_status)?;
347 check_deadline(deadline)?;
348 Ok((
349 response,
350 driver.initial.take().unwrap_or_default(),
351 driver.trailing.take().unwrap_or_default(),
352 ))
353 }
354 pub fn call_unary<C, Req, Resp>(
358 &mut self,
359 path: &str,
360 req: &Req,
361 codec: &mut C,
362 ) -> Result<Resp, Status>
363 where
364 C: Codec<EncodeItem = Req, DecodeItem = Resp>,
365 {
366 self.unary(path, req, codec, CallOptions::default())
367 .map(|(response, _, _)| response)
368 }
369 pub fn server_streaming<C, Req, Resp>(
373 &mut self,
374 path: &str,
375 req: &Req,
376 codec: C,
377 options: CallOptions,
378 ) -> Result<ServerStreamingCall<Resp>, Status>
379 where
380 C: Codec<EncodeItem = Req, DecodeItem = Resp> + 'static,
381 Req: 'static,
382 Resp: 'static,
383 {
384 let mut driver = self.start(path, codec, options, C::CONTENT_TYPE, None)?;
385 driver.send(req).map_err(status_only)?;
386 driver.half_close().map_err(status_only)?;
387 Ok(ServerStreamingCall::new(driver))
388 }
389 pub fn client_streaming<C, Req, Resp>(
393 &mut self,
394 path: &str,
395 codec: C,
396 options: CallOptions,
397 ) -> Result<ClientStreamingCall<Req, Resp>, Status>
398 where
399 C: Codec<EncodeItem = Req, DecodeItem = Resp> + 'static,
400 Req: 'static,
401 Resp: 'static,
402 {
403 Ok(ClientStreamingCall::new(self.start(
404 path,
405 codec,
406 options,
407 C::CONTENT_TYPE,
408 None,
409 )?))
410 }
411 pub fn bidi_streaming<C, Req, Resp>(
415 &mut self,
416 path: &str,
417 codec: C,
418 options: CallOptions,
419 ) -> Result<BidiStreamingCall<Req, Resp>, Status>
420 where
421 C: Codec<EncodeItem = Req, DecodeItem = Resp> + 'static,
422 Req: 'static,
423 Resp: 'static,
424 {
425 Ok(BidiStreamingCall::new(self.start(
426 path,
427 codec,
428 options,
429 C::CONTENT_TYPE,
430 None,
431 )?))
432 }
433}
434impl SyncChannel<TcpTransport> {
435 pub fn connect(address: impl std::net::ToSocketAddrs) -> Result<Self, Status> {
439 let transport = TcpTransport::connect(address).map_err(io_status)?;
440 let authority = transport.peer_endpoint().map_err(io_status)?;
441 Self::with_config(
442 transport,
443 ChannelConfig {
444 authority,
445 ..ChannelConfig::default()
446 },
447 )
448 }
449 pub fn connect_tls(
453 address: impl std::net::ToSocketAddrs,
454 config: &grpc_core::tls::TlsClientConfig,
455 server_name: &str,
456 ) -> Result<SyncChannel<impl SyncTransport + 'static>, Status> {
457 let name = server_name.to_owned().try_into().map_err(codec_status)?;
458 let socket = TcpTransport::connect(address).map_err(io_status)?;
459 let mut tls =
460 grpc_core::tls::TlsTransport::client(socket, config, name).map_err(io_status)?;
461 tls.handshake().map_err(io_status)?;
462 SyncChannel::with_config(
463 tls,
464 ChannelConfig {
465 authority: server_name.into(),
466 scheme: "https".into(),
467 ..ChannelConfig::default()
468 },
469 )
470 }
471}
472#[cfg(unix)]
473impl SyncChannel<grpc_core::transport::UnixTransport> {
474 pub fn connect_unix(path: impl AsRef<std::path::Path>) -> Result<Self, Status> {
478 Self::new(grpc_core::transport::UnixTransport::connect(path).map_err(io_status)?)
479 }
480}
481fn status_only(error: StreamError) -> Status {
482 match error {
483 StreamError::Status(status) => status,
484 StreamError::WouldBlock => Status::unavailable("RPC operation would block"),
485 }
486}
487struct BytesCodec;
488impl Codec for BytesCodec {
489 type EncodeItem = Vec<u8>;
490 type DecodeItem = grpc_core::AlignedMessageBuf;
491 type Error = Status;
492 const CONTENT_TYPE: &'static str = "application/grpc";
493 fn encode(&mut self, item: &Vec<u8>, destination: &mut Vec<u8>) -> Result<(), Self::Error> {
494 destination.extend_from_slice(item);
495 Ok(())
496 }
497 fn decode(&mut self, payload: &[u8]) -> Result<Self::DecodeItem, Self::Error> {
498 grpc_core::AlignedMessageBuf::new(payload, false)
499 }
500}
501pub struct ChannelStreamDriver<T: SyncTransport, C: Codec> {
503 inner: Rc<RefCell<ChannelInner<T>>>,
504 id: i32,
505 codec: C,
506 encoding: CompressionAlgorithm,
507 deadline: Option<Instant>,
508 initial: Option<Metadata>,
509 trailing: Option<Metadata>,
510 complete: bool,
511 closed: bool,
512 buffer: Vec<u8>,
513 pending_cancellations: Rc<RefCell<Vec<i32>>>,
514}
515impl<T: SyncTransport, C: Codec> ChannelStreamDriver<T, C> {
516 fn check_deadline(&self) -> Result<(), StreamError> {
517 check_deadline(self.deadline).map_err(Into::into)
518 }
519 fn receive(&mut self) -> Result<Option<C::DecodeItem>, StreamError> {
520 let mut inner = self
521 .inner
522 .try_borrow_mut()
523 .map_err(|_| Status::failed_precondition("reentrant channel access"))?;
524 if self.initial.is_none() {
525 self.initial = inner.session.pop_initial_metadata(self.id)?;
526 }
527 if let Some(message) = inner.session.pop_message(self.id)? {
528 drop(inner);
529 let value = self.codec.decode(message.payload()).map_err(codec_status)?;
530 self.check_deadline()?;
531 return Ok(Some(value));
532 }
533 if let Some((status, trailers)) = inner.session.take_trailers(self.id)? {
534 self.trailing = Some(trailers);
535 if !self.closed {
536 inner.session.cancel_stream(self.id, 0)?;
537 self.closed = true;
538 }
539 inner.pending_reap.insert(self.id);
540 inner.flush()?;
541 self.complete = true;
542 return if status.code() == Code::Ok {
543 Ok(None)
544 } else {
545 Err(status.into())
546 };
547 }
548 Err(StreamError::WouldBlock)
549 }
550}
551impl<T: SyncTransport, C: Codec> StreamDriver for ChannelStreamDriver<T, C>
552where
553 C::EncodeItem: Sized,
554{
555 type Send = C::EncodeItem;
556 type Receive = C::DecodeItem;
557 fn send(&mut self, message: &Self::Send) -> Result<(), StreamError> {
558 self.check_deadline()?;
559 if self.closed || self.complete {
560 return Err(Status::failed_precondition("request sending half is closed").into());
561 }
562 self.buffer.clear();
563 self.codec
564 .encode(message, &mut self.buffer)
565 .map_err(codec_status)?;
566 self.check_deadline()?;
567 let mut inner = self
568 .inner
569 .try_borrow_mut()
570 .map_err(|_| Status::failed_precondition("reentrant channel access"))?;
571 inner
572 .session
573 .send_message(self.id, &self.buffer, self.encoding)?;
574 inner.flush()?;
575 Ok(())
576 }
577 fn half_close(&mut self) -> Result<(), StreamError> {
578 self.check_deadline()?;
579 if self.closed {
580 return Ok(());
581 }
582 let mut inner = self
583 .inner
584 .try_borrow_mut()
585 .map_err(|_| Status::failed_precondition("reentrant channel access"))?;
586 inner.session.half_close(self.id)?;
587 self.closed = true;
588 inner.flush()?;
589 Ok(())
590 }
591 fn try_recv_next(&mut self) -> Result<Option<Self::Receive>, StreamError> {
592 self.check_deadline()?;
593 if self.complete {
594 return Ok(None);
595 }
596 match self.receive() {
597 Err(StreamError::WouldBlock) => {}
598 result => return result,
599 }
600 self.inner
601 .try_borrow_mut()
602 .map_err(|_| Status::failed_precondition("reentrant channel access"))?
603 .pump(false, self.deadline)?;
604 self.receive()
605 }
606 fn wait_ready(&mut self) -> Result<(), StreamError> {
607 self.check_deadline()?;
608 self.inner
609 .try_borrow_mut()
610 .map_err(|_| Status::failed_precondition("reentrant channel access"))?
611 .pump(true, self.deadline)?;
612 Ok(())
613 }
614 fn finish(&mut self, _: &Status, _: &Metadata) -> Result<(), StreamError> {
615 Err(Status::failed_precondition("client cannot finish server response").into())
616 }
617 fn initial_metadata(&self) -> Option<&Metadata> {
618 self.initial.as_ref()
619 }
620 fn trailing_metadata(&self) -> Option<&Metadata> {
621 self.trailing.as_ref()
622 }
623 fn cancel(&mut self) {
624 if self.complete {
625 return;
626 }
627 self.pending_cancellations.borrow_mut().push(self.id);
628 if let Ok(mut inner) = self.inner.try_borrow_mut()
629 && let Err(error) = inner.flush()
630 {
631 inner.cleanup_error = Some(error);
632 }
633 self.complete = true;
634 }
635}
636
637impl<T: SyncTransport, C: Codec> Drop for ChannelStreamDriver<T, C> {
638 fn drop(&mut self) {
639 if self.complete {
640 return;
641 }
642 self.pending_cancellations.borrow_mut().push(self.id);
643 if let Ok(mut inner) = self.inner.try_borrow_mut()
644 && let Err(error) = inner.flush()
645 {
646 inner.cleanup_error = Some(error);
647 }
648 }
649}
650fn check_deadline(deadline: Option<Instant>) -> Result<(), Status> {
651 if deadline.is_some_and(|at| Instant::now() >= at) {
652 Err(Status::deadline_exceeded("RPC deadline elapsed"))
653 } else {
654 Ok(())
655 }
656}