ntex_grpc/client/
transport.rs1use std::{convert::TryFrom, str::FromStr};
2
3use ntex_bytes::{Buf, BufMut, BytePages};
4use ntex_error::Error;
5use ntex_h2::{self as h2};
6use ntex_http::{HeaderMap, Method, header};
7
8use super::{Client, ClientError, Transport, request::RequestContext, request::Response};
9use crate::{DecodeError, GrpcStatus, Message, consts, service::MethodDef, utils::Data};
10
11impl<T: MethodDef> Transport<T> for Client {
12 type Error = Error<ClientError>;
13
14 #[inline]
15 async fn request(
16 &self,
17 val: &T::Input,
18 ctx: &mut RequestContext,
19 ) -> Result<Response<T>, Self::Error> {
20 Transport::request(&self.0, val, ctx).await
21 }
22}
23
24impl<T: MethodDef> Transport<T> for h2::client::Client {
25 type Error = Error<ClientError>;
26
27 #[inline]
28 async fn request(
29 &self,
30 val: &T::Input,
31 ctx: &mut RequestContext,
32 ) -> Result<Response<T>, Self::Error> {
33 Transport::request(
34 &self.client().await.map_err(|e| e.map(ClientError::from))?,
35 val,
36 ctx,
37 )
38 .await
39 }
40}
41
42impl<T: MethodDef> Transport<T> for h2::client::SimpleClient {
43 type Error = Error<ClientError>;
44
45 #[allow(clippy::too_many_lines)]
46 async fn request(
47 &self,
48 val: &T::Input,
49 ctx: &mut RequestContext,
50 ) -> Result<Response<T>, Self::Error> {
51 let len = val.encoded_len();
52 let mut buf = BytePages::default();
53 buf.put_u8(0); buf.put_u32(len as u32); val.write(&mut buf);
56 let req_size = buf.len();
57
58 let mut hdrs = HeaderMap::new();
59 hdrs.append(header::CONTENT_TYPE, consts::HDRV_CT_GRPC);
60 hdrs.append(header::USER_AGENT, consts::HDRV_USER_AGENT);
61 hdrs.insert(header::TE, consts::HDRV_TRAILERS);
62 hdrs.insert(consts::GRPC_ENCODING, consts::IDENTITY);
63 hdrs.insert(consts::GRPC_ACCEPT_ENCODING, consts::IDENTITY);
64 for (key, val) in ctx.headers() {
65 hdrs.insert(key.clone(), val.clone());
66 }
67
68 let (snd_stream, rcv_stream) = self
70 .send(Method::POST, T::PATH, hdrs, false)
71 .await
72 .map_err(|e| e.map(ClientError::from))?;
73 if ctx.get_disconnect_on_drop() {
74 snd_stream.disconnect_on_drop();
75 }
76 snd_stream
77 .send_pages(buf, true)
78 .await
79 .map_err(|e| e.map(ClientError::from))?;
80
81 let mut status = None;
83 let mut hdrs = HeaderMap::default();
84 let mut trailers = HeaderMap::default();
85 let mut payload = Data::Empty;
86
87 async {
88 loop {
89 let Some(msg) = rcv_stream.recv().await else {
90 return Err(Error::from(ClientError::UnexpectedEof(status, hdrs)));
91 };
92
93 match msg.kind {
94 h2::MessageKind::Headers {
95 headers,
96 pseudo,
97 eof,
98 } => {
99 if eof {
100 match check_grpc_status(&headers) {
102 Some(Ok(GrpcStatus::DeadlineExceeded)) => {
103 return Err(Error::from(ClientError::DeadlineExceeded(hdrs)));
104 }
105 Some(Ok(status)) if status != GrpcStatus::Ok => {
106 return Err(Error::from(ClientError::GrpcStatus(
107 status, headers,
108 )));
109 }
110 Some(Err(())) => {
111 return Err(Error::from(ClientError::Decode(
112 DecodeError::new("Cannot parse grpc status"),
113 )));
114 }
115 Some(Ok(_)) | None => {}
116 }
117
118 return Err(Error::from(ClientError::UnexpectedEof(
119 pseudo.status,
120 headers,
121 )));
122 }
123 hdrs = headers;
124 status = pseudo.status;
125 continue;
126 }
127 h2::MessageKind::Data(data, _cap) => {
128 payload.push(data);
129 continue;
130 }
131 h2::MessageKind::Eof(data) => {
132 match data {
133 h2::StreamEof::Data(data) => {
134 payload.push(data);
135 }
136 h2::StreamEof::Trailers(hdrs) => {
137 match check_grpc_status(&hdrs) {
139 Some(Ok(GrpcStatus::Ok)) | None => Ok(()),
140 Some(Ok(GrpcStatus::DeadlineExceeded)) => {
141 return Err(Error::from(ClientError::DeadlineExceeded(
142 hdrs,
143 )));
144 }
145 Some(Ok(st)) => {
146 return Err(Error::from(ClientError::GrpcStatus(
147 st, hdrs,
148 )));
149 }
150 Some(Err(())) => Err(Error::from(ClientError::Decode(
151 DecodeError::new("Cannot parse grpc status"),
152 ))),
153 }?;
154 trailers = hdrs;
155 }
156 h2::StreamEof::Error(err) => {
157 return Err(err.map(ClientError::Stream));
158 }
159 }
160 }
161 h2::MessageKind::Disconnect(err) => {
162 return Err(err.map(ClientError::Operation));
163 }
164 }
165
166 let mut data = payload.get();
167 match status {
168 Some(st) => {
169 if !st.is_success() {
170 return Err(Error::from(ClientError::Response(Some(st), hdrs, data)));
171 }
172 }
173 None => return Err(Error::from(ClientError::Response(None, hdrs, data))),
174 }
175 let _compressed = data.get_u8();
176 let len = data.get_u32();
177 let Some(mut block) = data.split_to_checked(len as usize) else {
178 return Err(Error::from(ClientError::UnexpectedEof(None, hdrs)));
179 };
180
181 return match <T::Output as Message>::read(&mut block) {
182 Ok(output) => Ok(Response {
183 output,
184 trailers,
185 req_size,
186 headers: hdrs,
187 res_size: data.len(),
188 }),
189 Err(e) => Err(Error::from(ClientError::Decode(e))),
190 };
191 }
192 }
193 .await
194 .map_err(|e| e.set_service(self.service()))
195 }
196}
197
198fn check_grpc_status(hdrs: &HeaderMap) -> Option<Result<GrpcStatus, ()>> {
199 if let Some(val) = hdrs.get(consts::GRPC_STATUS) {
201 if let Ok(status) = val
202 .to_str()
203 .map_err(|_| ())
204 .and_then(|v| u8::from_str(v).map_err(|_| ()))
205 .and_then(GrpcStatus::try_from)
206 {
207 Some(Ok(status))
208 } else {
209 Some(Err(()))
210 }
211 } else {
212 None
213 }
214}