/rust/registry/src/index.crates.io-1949cf8c6b5b557f/axum-0.8.9/src/routing/route.rs
Line | Count | Source |
1 | | use crate::{ |
2 | | body::{Body, HttpBody}, |
3 | | response::Response, |
4 | | util::MapIntoResponse, |
5 | | }; |
6 | | use axum_core::{extract::Request, response::IntoResponse}; |
7 | | use bytes::Bytes; |
8 | | use http::{ |
9 | | header::{self, CONTENT_LENGTH}, |
10 | | HeaderMap, HeaderValue, Method, |
11 | | }; |
12 | | use pin_project_lite::pin_project; |
13 | | use std::{ |
14 | | convert::Infallible, |
15 | | fmt, |
16 | | future::Future, |
17 | | pin::Pin, |
18 | | task::{ready, Context, Poll}, |
19 | | }; |
20 | | use tower::{ |
21 | | util::{BoxCloneSyncService, MapErrLayer, Oneshot}, |
22 | | ServiceExt, |
23 | | }; |
24 | | use tower_layer::Layer; |
25 | | use tower_service::Service; |
26 | | |
27 | | /// How routes are stored inside a [`Router`](super::Router). |
28 | | /// |
29 | | /// You normally shouldn't need to care about this type. It's used in |
30 | | /// [`Router::layer`](super::Router::layer). |
31 | | pub struct Route<E = Infallible>(BoxCloneSyncService<Request, Response, E>); |
32 | | |
33 | | impl<E> Route<E> { |
34 | 0 | pub(crate) fn new<T>(svc: T) -> Self |
35 | 0 | where |
36 | 0 | T: Service<Request, Error = E> + Clone + Send + Sync + 'static, |
37 | 0 | T::Response: IntoResponse + 'static, |
38 | 0 | T::Future: Send + 'static, |
39 | | { |
40 | 0 | Self(BoxCloneSyncService::new(MapIntoResponse::new(svc))) |
41 | 0 | } Unexecuted instantiation: <axum::routing::route::Route>::new::<axum::handler::service::HandlerService<tonic::service::router::unimplemented, ((),), ()>> Unexecuted instantiation: <axum::routing::route::Route>::new::<tower::util::service_fn::ServiceFn<<axum::routing::method_routing::MethodRouter>::new::{closure#0}>>Unexecuted instantiation: <axum::routing::route::Route>::new::<axum::routing::not_found::NotFound> Unexecuted instantiation: <axum::routing::route::Route<_>>::new::<_> |
42 | | |
43 | | /// Variant of [`Route::call`] that takes ownership of the route to avoid cloning. |
44 | 0 | pub(crate) fn call_owned(self, req: Request<Body>) -> RouteFuture<E> { |
45 | 0 | let req = req.map(Body::new); |
46 | 0 | self.oneshot_inner_owned(req).not_top_level() |
47 | 0 | } |
48 | | |
49 | 0 | pub(crate) fn oneshot_inner(&mut self, req: Request) -> RouteFuture<E> { |
50 | 0 | let method = req.method().clone(); |
51 | 0 | RouteFuture::new(method, self.0.clone().oneshot(req)) |
52 | 0 | } Unexecuted instantiation: <axum::routing::route::Route>::oneshot_inner Unexecuted instantiation: <axum::routing::route::Route<_>>::oneshot_inner |
53 | | |
54 | | /// Variant of [`Route::oneshot_inner`] that takes ownership of the route to avoid cloning. |
55 | 0 | pub(crate) fn oneshot_inner_owned(self, req: Request) -> RouteFuture<E> { |
56 | 0 | let method = req.method().clone(); |
57 | 0 | RouteFuture::new(method, self.0.oneshot(req)) |
58 | 0 | } |
59 | | |
60 | 0 | pub(crate) fn layer<L, NewError>(self, layer: L) -> Route<NewError> |
61 | 0 | where |
62 | 0 | L: Layer<Route<E>> + Clone + Send + 'static, |
63 | 0 | L::Service: Service<Request> + Clone + Send + Sync + 'static, |
64 | 0 | <L::Service as Service<Request>>::Response: IntoResponse + 'static, |
65 | 0 | <L::Service as Service<Request>>::Error: Into<NewError> + 'static, |
66 | 0 | <L::Service as Service<Request>>::Future: Send + 'static, |
67 | 0 | NewError: 'static, |
68 | | { |
69 | 0 | let layer = (MapErrLayer::new(Into::into), layer); |
70 | | |
71 | 0 | Route::new(layer.layer(self)) |
72 | 0 | } |
73 | | } |
74 | | |
75 | | impl<E> Clone for Route<E> { |
76 | | #[track_caller] |
77 | 0 | fn clone(&self) -> Self { |
78 | 0 | Self(self.0.clone()) |
79 | 0 | } |
80 | | } |
81 | | |
82 | | impl<E> fmt::Debug for Route<E> { |
83 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
84 | 0 | f.debug_struct("Route").finish() |
85 | 0 | } |
86 | | } |
87 | | |
88 | | impl<B, E> Service<Request<B>> for Route<E> |
89 | | where |
90 | | B: HttpBody<Data = bytes::Bytes> + Send + 'static, |
91 | | B::Error: Into<axum_core::BoxError>, |
92 | | { |
93 | | type Response = Response; |
94 | | type Error = E; |
95 | | type Future = RouteFuture<E>; |
96 | | |
97 | | #[inline] |
98 | 0 | fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { |
99 | 0 | Poll::Ready(Ok(())) |
100 | 0 | } |
101 | | |
102 | | #[inline] |
103 | 0 | fn call(&mut self, req: Request<B>) -> Self::Future { |
104 | 0 | self.oneshot_inner(req.map(Body::new)).not_top_level() |
105 | 0 | } Unexecuted instantiation: <axum::routing::route::Route as tower_service::Service<http::request::Request<axum_core::body::Body>>>::call Unexecuted instantiation: <axum::routing::route::Route<_> as tower_service::Service<http::request::Request<_>>>::call |
106 | | } |
107 | | |
108 | | pin_project! { |
109 | | /// Response future for [`Route`]. |
110 | | pub struct RouteFuture<E> { |
111 | | #[pin] |
112 | | inner: Oneshot<BoxCloneSyncService<Request, Response, E>, Request>, |
113 | | method: Method, |
114 | | allow_header: Option<Bytes>, |
115 | | top_level: bool, |
116 | | } |
117 | | } |
118 | | |
119 | | impl<E> RouteFuture<E> { |
120 | 0 | fn new( |
121 | 0 | method: Method, |
122 | 0 | inner: Oneshot<BoxCloneSyncService<Request, Response, E>, Request>, |
123 | 0 | ) -> Self { |
124 | 0 | Self { |
125 | 0 | inner, |
126 | 0 | method, |
127 | 0 | allow_header: None, |
128 | 0 | top_level: true, |
129 | 0 | } |
130 | 0 | } Unexecuted instantiation: <axum::routing::route::RouteFuture<core::convert::Infallible>>::new Unexecuted instantiation: <axum::routing::route::RouteFuture<_>>::new |
131 | | |
132 | 0 | pub(crate) fn allow_header(mut self, allow_header: Bytes) -> Self { |
133 | 0 | self.allow_header = Some(allow_header); |
134 | 0 | self |
135 | 0 | } |
136 | | |
137 | 0 | pub(crate) fn not_top_level(mut self) -> Self { |
138 | 0 | self.top_level = false; |
139 | 0 | self |
140 | 0 | } Unexecuted instantiation: <axum::routing::route::RouteFuture<core::convert::Infallible>>::not_top_level Unexecuted instantiation: <axum::routing::route::RouteFuture<_>>::not_top_level |
141 | | } |
142 | | |
143 | | impl<E> Future for RouteFuture<E> { |
144 | | type Output = Result<Response, E>; |
145 | | |
146 | | #[inline] |
147 | 0 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
148 | 0 | let this = self.project(); |
149 | 0 | let mut res = ready!(this.inner.poll(cx))?; |
150 | | |
151 | 0 | if *this.method == Method::CONNECT && res.status().is_success() { |
152 | | // From https://httpwg.org/specs/rfc9110.html#CONNECT: |
153 | | // > A server MUST NOT send any Transfer-Encoding or |
154 | | // > Content-Length header fields in a 2xx (Successful) |
155 | | // > response to CONNECT. |
156 | 0 | if res.headers().contains_key(&CONTENT_LENGTH) |
157 | 0 | || res.headers().contains_key(&header::TRANSFER_ENCODING) |
158 | 0 | || res.size_hint().lower() != 0 |
159 | | { |
160 | | error!("response to CONNECT with nonempty body"); |
161 | 0 | res = res.map(|_| Body::empty()); |
162 | 0 | } |
163 | 0 | } else if *this.top_level { |
164 | 0 | set_allow_header(res.headers_mut(), this.allow_header); |
165 | | |
166 | | // make sure to set content-length before removing the body |
167 | 0 | set_content_length(res.size_hint(), res.headers_mut()); |
168 | | |
169 | 0 | if *this.method == Method::HEAD { |
170 | 0 | *res.body_mut() = Body::empty(); |
171 | 0 | } |
172 | 0 | } |
173 | | |
174 | 0 | Poll::Ready(Ok(res)) |
175 | 0 | } |
176 | | } |
177 | | |
178 | 0 | fn set_allow_header(headers: &mut HeaderMap, allow_header: &mut Option<Bytes>) { |
179 | 0 | match allow_header.take() { |
180 | 0 | Some(allow_header) if !headers.contains_key(header::ALLOW) => { |
181 | 0 | headers.insert( |
182 | 0 | header::ALLOW, |
183 | 0 | HeaderValue::from_maybe_shared(allow_header).expect("invalid `Allow` header"), |
184 | 0 | ); |
185 | 0 | } |
186 | 0 | _ => {} |
187 | | } |
188 | 0 | } |
189 | | |
190 | 0 | fn set_content_length(size_hint: http_body::SizeHint, headers: &mut HeaderMap) { |
191 | 0 | if headers.contains_key(CONTENT_LENGTH) { |
192 | 0 | return; |
193 | 0 | } |
194 | | |
195 | 0 | if let Some(size) = size_hint.exact() { |
196 | 0 | let header_value = if size == 0 { |
197 | | #[allow(clippy::declare_interior_mutable_const)] |
198 | | const ZERO: HeaderValue = HeaderValue::from_static("0"); |
199 | | |
200 | 0 | ZERO |
201 | | } else { |
202 | 0 | let mut buffer = itoa::Buffer::new(); |
203 | 0 | HeaderValue::from_str(buffer.format(size)).unwrap() |
204 | | }; |
205 | | |
206 | 0 | headers.insert(CONTENT_LENGTH, header_value); |
207 | 0 | } |
208 | 0 | } |
209 | | |
210 | | pin_project! { |
211 | | /// A [`RouteFuture`] that always yields a [`Response`]. |
212 | | pub struct InfallibleRouteFuture { |
213 | | #[pin] |
214 | | future: RouteFuture<Infallible>, |
215 | | } |
216 | | } |
217 | | |
218 | | impl InfallibleRouteFuture { |
219 | 0 | pub(crate) fn new(future: RouteFuture<Infallible>) -> Self { |
220 | 0 | Self { future } |
221 | 0 | } |
222 | | } |
223 | | |
224 | | impl Future for InfallibleRouteFuture { |
225 | | type Output = Response; |
226 | | |
227 | 0 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
228 | 0 | match ready!(self.project().future.poll(cx)) { |
229 | 0 | Ok(response) => Poll::Ready(response), |
230 | | Err(err) => match err {}, |
231 | | } |
232 | 0 | } |
233 | | } |
234 | | |
235 | | #[cfg(test)] |
236 | | mod tests { |
237 | | use super::*; |
238 | | |
239 | | #[test] |
240 | | fn traits() { |
241 | | use crate::test_helpers::*; |
242 | | assert_send::<Route<()>>(); |
243 | | } |
244 | | } |