1use std::convert::Infallible;
5use std::future::Future;
6use std::net::SocketAddr;
7use std::pin::Pin;
8use std::sync::Arc;
9use std::task::{Context, Poll};
10
11use axum::extract::connect_info::ConnectInfo;
12use axum::response::IntoResponse;
13use axum::routing::future::RouteFuture;
14use bytes::Bytes;
15use pin_project_lite::pin_project;
16use rustls::pki_types::CertificateDer;
17use tonic::transport::server::{Connected, TcpConnectInfo};
18use tower::{Layer, Service, ServiceExt};
19use tower_http::cors::{Cors, CorsLayer};
20
21use crate::guard::{BoxedService, Class, GrpcRejections, Guards};
22
23pub type TlsConnectInfo =
27 <tokio_rustls::server::TlsStream<tokio::net::TcpStream> as Connected>::ConnectInfo;
28
29use crate::upstream::{
30 grpc_protocol, is_grpc_web_preflight, BoxError, GrpcProtocol, PassThrough, Upstream,
31};
32
33#[derive(Clone)]
73pub struct ProxyService<U> {
74 upstream: U,
75 routes: axum::Router,
76 grpc_web: Option<GrpcWebCors<U>>,
79 boxed: BoxedGrpc,
82 guards: Arc<Guards>,
84 connection: Option<ConnectionInfo>,
87}
88
89impl<U> std::fmt::Debug for ProxyService<U> {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 f.debug_struct("ProxyService")
92 .field("boxed_grpc", &self.boxed.grpc.is_some())
93 .field("boxed_grpc_web", &self.boxed.web.is_some())
94 .field("connection", &self.connection)
95 .finish_non_exhaustive()
96 }
97}
98
99#[derive(Clone, Default)]
103struct BoxedGrpc {
104 grpc: Option<BoxedService>,
105 web: Option<BoxedService>,
106 web_text: Option<BoxedService>,
107}
108
109#[derive(Clone, Debug)]
112struct Translated<U> {
113 upstream: U,
114}
115
116impl<U: Upstream> Service<http::Request<tonic::body::Body>> for Translated<U> {
117 type Response = http::Response<axum::body::Body>;
118 type Error = Infallible;
119 type Future = PassThrough<U>;
120
121 #[inline]
122 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
123 Poll::Ready(Ok(()))
124 }
125
126 fn call(&mut self, request: http::Request<tonic::body::Body>) -> Self::Future {
127 let (mut parts, body) = request.into_parts();
128 parts.version = http::Version::HTTP_2;
131 let body = tonic::body::Body::new(Unsized { body });
135 PassThrough::new(
136 self.upstream.clone(),
137 http::Request::from_parts(parts, body),
138 GrpcProtocol::Grpc,
139 )
140 }
141}
142
143pin_project! {
144 struct Unsized {
146 #[pin]
147 body: tonic::body::Body,
148 }
149}
150
151impl http_body::Body for Unsized {
152 type Data = Bytes;
153 type Error = tonic::Status;
154
155 #[inline]
156 fn poll_frame(
157 self: Pin<&mut Self>,
158 cx: &mut Context<'_>,
159 ) -> Poll<Option<Result<http_body::Frame<Bytes>, tonic::Status>>> {
160 self.project().body.poll_frame(cx)
161 }
162
163 #[inline]
164 fn is_end_stream(&self) -> bool {
165 self.body.is_end_stream()
166 }
167}
168
169#[derive(Clone, Debug)]
171struct GrpcWebCors<U> {
172 web: Cors<Forward<U>>,
173 web_text: Cors<Forward<U>>,
174}
175
176#[derive(Clone, Debug)]
179struct Forward<U> {
180 upstream: U,
181 protocol: GrpcProtocol,
182}
183
184impl<U: Upstream> Service<http::Request<tonic::body::Body>> for Forward<U> {
185 type Response = http::Response<axum::body::Body>;
186 type Error = Infallible;
187 type Future = PassThrough<U>;
188
189 #[inline]
190 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
191 Poll::Ready(Ok(()))
193 }
194
195 fn call(&mut self, request: http::Request<tonic::body::Body>) -> Self::Future {
196 PassThrough::new(self.upstream.clone(), request, self.protocol)
197 }
198}
199
200impl<U: Upstream> Service<axum::extract::Request> for Forward<U> {
202 type Response = http::Response<axum::body::Body>;
203 type Error = Infallible;
204 type Future = PassThrough<U>;
205
206 #[inline]
207 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
208 Poll::Ready(Ok(()))
209 }
210
211 fn call(&mut self, request: axum::extract::Request) -> Self::Future {
212 let request = request.map(tonic::body::Body::new);
213 PassThrough::new(self.upstream.clone(), request, self.protocol)
214 }
215}
216
217#[derive(Clone, Debug)]
228pub struct ConnectionInfo {
229 tcp: TcpConnectInfo,
230 tls: Option<TlsConnectInfo>,
231}
232
233impl ConnectionInfo {
234 pub fn remote_addr(&self) -> Option<SocketAddr> {
236 self.tcp.remote_addr
237 }
238
239 pub fn peer_certs(&self) -> Option<Arc<Vec<CertificateDer<'static>>>> {
241 self.tls.as_ref().and_then(|tls| tls.peer_certs())
242 }
243
244 pub(crate) fn into_tonic_extensions(self, extensions: &mut http::Extensions) {
248 extensions.insert(self.tcp);
249 if let Some(tls) = self.tls {
250 extensions.insert(tls);
251 }
252 }
253}
254
255impl From<TcpConnectInfo> for ConnectionInfo {
256 fn from(tcp: TcpConnectInfo) -> Self {
257 Self { tcp, tls: None }
258 }
259}
260
261impl ConnectionInfo {
262 pub fn tls(tls: TlsConnectInfo) -> Self {
266 Self {
267 tcp: tls.get_ref().clone(),
268 tls: Some(tls),
269 }
270 }
271}
272
273impl<U: Upstream> ProxyService<U> {
274 pub(crate) fn new(
278 upstream: U,
279 routes: axum::Router,
280 grpc_web_cors: Option<CorsLayer>,
281 guards: Arc<Guards>,
282 translate_grpc_web: bool,
283 ) -> Self {
284 let forward = |protocol| Forward {
285 upstream: upstream.clone(),
286 protocol,
287 };
288 let guard_grpc = guards.cover(Class::Grpc);
289 let stack = |protocol: GrpcProtocol| {
290 let web = protocol != GrpcProtocol::Grpc;
291 let mut service = if web && translate_grpc_web {
292 let translator = tonic_web::GrpcWebLayer::new().layer(Translated {
293 upstream: upstream.clone(),
294 });
295 BoxedService::new(ServiceExt::<axum::extract::Request>::map_response(
296 translator,
297 |response| response.map(axum::body::Body::new),
298 ))
299 } else {
300 BoxedService::new(forward(protocol))
301 };
302 if guard_grpc {
303 service = BoxedService::new(GrpcRejections {
304 inner: guards.service(service, Class::Grpc),
305 protocol,
306 });
307 }
308 match &grpc_web_cors {
310 Some(cors) if web => BoxedService::new(cors.layer(service)),
311 _ => service,
312 }
313 };
314 let boxed_web = guard_grpc || translate_grpc_web;
315 let boxed = BoxedGrpc {
316 grpc: guard_grpc.then(|| stack(GrpcProtocol::Grpc)),
317 web: boxed_web.then(|| stack(GrpcProtocol::Web)),
318 web_text: boxed_web.then(|| stack(GrpcProtocol::WebText)),
319 };
320 let grpc_web = grpc_web_cors.map(|cors| GrpcWebCors {
322 web: cors.layer(forward(GrpcProtocol::Web)),
323 web_text: cors.layer(forward(GrpcProtocol::WebText)),
324 });
325 Self {
326 upstream,
327 routes,
328 grpc_web,
329 boxed,
330 guards,
331 connection: None,
332 }
333 }
334
335 #[must_use]
344 pub fn with_fallback<F>(mut self, fallback: F) -> Self
345 where
346 F: Service<axum::extract::Request, Error = Infallible> + Clone + Send + Sync + 'static,
347 F::Response: IntoResponse,
348 F::Future: Send + 'static,
349 {
350 self.routes = if self.guards.cover(Class::Fallback) {
351 let fallback = BoxedService::new(fallback.map_response(IntoResponse::into_response));
352 self.routes
353 .fallback_service(self.guards.service(fallback, Class::Fallback))
354 } else {
355 self.routes.fallback_service(fallback)
356 };
357 self
358 }
359
360 #[must_use]
392 pub fn for_connection(&self, connection: impl Into<ConnectionInfo>) -> Self {
393 Self {
394 upstream: self.upstream.clone(),
395 routes: self.routes.clone(),
396 grpc_web: self.grpc_web.clone(),
397 boxed: self.boxed.clone(),
398 guards: self.guards.clone(),
399 connection: Some(connection.into()),
400 }
401 }
402}
403
404fn connection_of<B>(
409 connection: Option<&ConnectionInfo>,
410 request: &http::Request<B>,
411) -> Option<ConnectionInfo> {
412 if let Some(connection) = connection {
413 return Some(connection.clone());
414 }
415 let ConnectInfo(remote) = request.extensions().get::<ConnectInfo<SocketAddr>>()?;
416 Some(ConnectionInfo::from(TcpConnectInfo {
417 local_addr: None,
418 remote_addr: Some(*remote),
419 }))
420}
421
422impl<U, B> Service<http::Request<B>> for ProxyService<U>
423where
424 U: Upstream,
425 B: http_body::Body<Data = Bytes> + Send + 'static,
426 B::Error: Into<BoxError>,
427{
428 type Response = http::Response<axum::body::Body>;
429 type Error = Infallible;
430 type Future = ResponseFuture<U>;
431
432 #[inline]
433 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
434 Poll::Ready(Ok(()))
436 }
437
438 fn call(&mut self, mut request: http::Request<B>) -> Self::Future {
439 let call = grpc_protocol(request.headers());
443 let protocol = call.or_else(|| {
444 is_grpc_web_preflight(request.method(), request.headers()).then_some(GrpcProtocol::Web)
445 });
446 let boxed = match call {
448 Some(GrpcProtocol::Grpc) => self.boxed.grpc.as_mut(),
449 Some(GrpcProtocol::Web) => self.boxed.web.as_mut(),
450 Some(GrpcProtocol::WebText) => self.boxed.web_text.as_mut(),
451 None => None,
452 };
453 let inner = if let Some(service) = boxed {
454 if let Some(connection) = connection_of(self.connection.as_ref(), &request) {
457 let extensions = request.extensions_mut();
458 if let Some(remote) = connection.remote_addr() {
459 extensions.insert(ConnectInfo(remote));
460 }
461 connection.into_tonic_extensions(extensions);
462 }
463 Inner::Boxed {
467 future: service.call(request.map(axum::body::Body::new)),
468 }
469 } else if let Some(protocol) = protocol {
470 if let Some(connection) = connection_of(self.connection.as_ref(), &request) {
473 connection.into_tonic_extensions(request.extensions_mut());
474 }
475 let request = request.map(tonic::body::Body::new);
476 let cors = match (&mut self.grpc_web, protocol) {
479 (Some(cors), GrpcProtocol::Web) => Some(&mut cors.web),
480 (Some(cors), GrpcProtocol::WebText) => Some(&mut cors.web_text),
481 _ => None,
482 };
483 match cors {
484 Some(cors) => Inner::GrpcWeb {
486 future: cors.call(request),
487 },
488 None => Inner::Grpc {
489 call: PassThrough::new(self.upstream.clone(), request, protocol),
490 },
491 }
492 } else {
493 if let Some(connection) = &self.connection {
496 let extensions = request.extensions_mut();
497 if let Some(remote) = connection.remote_addr() {
498 extensions.insert(ConnectInfo(remote));
499 }
500 extensions.insert(connection.clone());
501 } else if let Some(connection) = connection_of(None, &request) {
502 request.extensions_mut().insert(connection);
503 }
504 Inner::Routes {
505 future: self.routes.call(request),
506 }
507 };
508 ResponseFuture { inner }
509 }
510}
511
512pin_project! {
513 pub struct ResponseFuture<U: Upstream> {
515 #[pin]
516 inner: Inner<U>,
517 }
518}
519
520pin_project! {
521 #[project = InnerProj]
522 enum Inner<U: Upstream> {
523 Routes {
524 #[pin]
525 future: RouteFuture<Infallible>,
526 },
527 Grpc {
528 #[pin]
529 call: PassThrough<U>,
530 },
531 GrpcWeb {
532 #[pin]
533 future: tower_http::cors::ResponseFuture<PassThrough<U>>,
534 },
535 Boxed {
536 #[pin]
537 future: <BoxedService as Service<axum::extract::Request>>::Future,
538 },
539 }
540}
541
542impl<U: Upstream> Future for ResponseFuture<U> {
543 type Output = Result<http::Response<axum::body::Body>, Infallible>;
544
545 #[inline]
546 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
547 match self.project().inner.project() {
548 InnerProj::Routes { future } => future.poll(cx),
549 InnerProj::Grpc { call } => call.poll(cx),
550 InnerProj::GrpcWeb { future } => future.poll(cx),
551 InnerProj::Boxed { future } => future.poll(cx),
552 }
553 }
554}
555
556#[cfg(test)]
557mod tests;