1#![allow(async_fn_in_trait)]
2
3use ntex_bytes::Bytes;
4use ntex_error::{ErrorDiagnostic, ResultType};
5use ntex_h2::{OperationError, StreamError, client};
6use ntex_http::{HeaderMap, StatusCode, error::Error as HttpError};
7
8mod request;
9mod transport;
10
11pub use self::request::{Request, RequestContext, Response};
12
13use crate::{encoding::DecodeError, service::MethodDef, status::GrpcStatus};
14
15pub trait Transport<T: MethodDef> {
16 type Error;
18
19 async fn request(
20 &self,
21 args: &T::Input,
22 ctx: &mut RequestContext,
23 ) -> Result<Response<T>, Self::Error>;
24}
25
26pub trait ClientInformation<T> {
28 fn create(transport: T) -> Self;
30
31 fn transport(&self) -> &T;
33
34 fn transport_mut(&mut self) -> &mut T;
36
37 fn into_inner(self) -> T;
39}
40
41#[derive(Clone)]
42pub struct Client(client::Client);
43
44impl Client {
45 #[inline]
46 pub fn new(client: client::Client) -> Self {
48 Self(client)
49 }
50
51 #[inline]
52 pub fn get_ref(&self) -> &client::Client {
54 &self.0
55 }
56}
57
58#[derive(thiserror::Error, Debug)]
59pub enum ClientError {
60 #[error("")]
61 Client(
62 #[from]
63 #[source]
64 client::ClientError,
65 ),
66 #[error("Http error {0:?}")]
67 Http(
68 #[from]
69 #[source]
70 HttpError,
71 ),
72 #[error("Decode")]
73 Decode(
74 #[from]
75 #[source]
76 DecodeError,
77 ),
78 #[error("HTTP2 Operation")]
79 Operation(
80 #[from]
81 #[source]
82 OperationError,
83 ),
84 #[error("HTTP2 Stream")]
85 Stream(
86 #[from]
87 #[source]
88 StreamError,
89 ),
90 #[error("Http response {0:?}, headers: {1:?}, body: {2:?}")]
91 Response(Option<StatusCode>, HeaderMap, Bytes),
92 #[error("Got eof without payload")]
93 UnexpectedEof(Option<StatusCode>, HeaderMap),
94 #[error("Deadline exceeded")]
95 DeadlineExceeded(HeaderMap),
96 #[error("Grpc status")]
97 GrpcStatus(GrpcStatus, HeaderMap),
98}
99
100impl Clone for ClientError {
101 fn clone(&self) -> Self {
102 match self {
103 Self::Client(err) => Self::Client(err.clone()),
104 Self::Http(err) => Self::Http(*err),
105 Self::Decode(err) => Self::Decode(err.clone()),
106 Self::Operation(err) => Self::Operation(err.clone()),
107 Self::Stream(err) => Self::Stream(*err),
108 Self::Response(st, hdrs, payload) => {
109 Self::Response(*st, hdrs.clone(), payload.clone())
110 }
111 Self::UnexpectedEof(st, hdrs) => Self::UnexpectedEof(*st, hdrs.clone()),
112 Self::DeadlineExceeded(hdrs) => Self::DeadlineExceeded(hdrs.clone()),
113 Self::GrpcStatus(st, hdrs) => Self::GrpcStatus(*st, hdrs.clone()),
114 }
115 }
116}
117
118impl ErrorDiagnostic for ClientError {
119 fn typ(&self) -> ResultType {
120 if matches!(self, ClientError::Http(_)) {
121 ResultType::ClientError
122 } else {
123 ResultType::ServiceError
124 }
125 }
126
127 fn signature(&self) -> &'static str {
128 match self {
129 ClientError::Client(err) => err.signature(),
130 ClientError::Http(_) => "grpc-Http",
131 ClientError::Decode(_) => "grpc-Decode",
132 ClientError::Operation(err) => err.signature(),
133 ClientError::Stream(err) => err.signature(),
134 ClientError::Response(_, _, _) => "grpc-Response",
135 ClientError::UnexpectedEof(_, _) => "grpc-UnexpectedEof",
136 ClientError::DeadlineExceeded(_) => "grpc-BackendCallTimedout",
137 ClientError::GrpcStatus(status, _) => status.signature(),
138 }
139 }
140}