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