aws_smithy_http_server/routing/
route.rs1use crate::body::BoxBody;
36use http::{Request, Response};
37use std::{
38 convert::Infallible,
39 fmt,
40 future::Future,
41 pin::Pin,
42 task::{Context, Poll},
43};
44use tower::{
45 util::{BoxCloneService, BoxCloneSyncService, Oneshot},
46 Service, ServiceExt,
47};
48
49pub struct Route<B = hyper::body::Incoming> {
53 service: BoxCloneService<Request<B>, Response<BoxBody>, Infallible>,
54}
55
56impl<B> Route<B> {
57 pub fn new<T>(svc: T) -> Self
59 where
60 T: Service<Request<B>, Response = Response<BoxBody>, Error = Infallible> + Clone + Send + 'static,
61 T::Future: Send + 'static,
62 {
63 Self {
64 service: BoxCloneService::new(svc),
65 }
66 }
67}
68
69impl<ReqBody> Clone for Route<ReqBody> {
70 fn clone(&self) -> Self {
71 Self {
72 service: self.service.clone(),
73 }
74 }
75}
76
77impl<ReqBody> fmt::Debug for Route<ReqBody> {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 f.debug_struct("Route").finish()
80 }
81}
82
83impl<B> Service<Request<B>> for Route<B> {
84 type Response = Response<BoxBody>;
85 type Error = Infallible;
86 type Future = RouteFuture<B>;
87
88 #[inline]
89 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
90 Poll::Ready(Ok(()))
91 }
92
93 #[inline]
94 fn call(&mut self, req: Request<B>) -> Self::Future {
95 RouteFuture::new(self.service.clone().oneshot(req))
96 }
97}
98
99pin_project_lite::pin_project! {
100 pub struct RouteFuture<B> {
102 #[pin]
103 future: Oneshot<BoxCloneService<Request<B>, Response<BoxBody>, Infallible>, Request<B>>,
104 }
105}
106
107impl<B> RouteFuture<B> {
108 pub(crate) fn new(future: Oneshot<BoxCloneService<Request<B>, Response<BoxBody>, Infallible>, Request<B>>) -> Self {
109 RouteFuture { future }
110 }
111}
112
113impl<B> Future for RouteFuture<B> {
114 type Output = Result<Response<BoxBody>, Infallible>;
115
116 #[inline]
117 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
118 self.project().future.poll(cx)
119 }
120}
121
122pub struct SyncRoute<B = hyper::body::Incoming> {
130 service: BoxCloneSyncService<Request<B>, Response<BoxBody>, Infallible>,
131}
132
133impl<B> SyncRoute<B> {
134 pub fn new<T>(svc: T) -> Self
140 where
141 B: 'static,
142 T: Service<Request<B>, Response = Response<BoxBody>, Error = Infallible> + Clone + Send + Sync + 'static,
143 T::Future: Send + 'static,
144 {
145 match try_downcast::<Self, T>(svc) {
146 Ok(route) => route,
147 Err(svc) => Self {
148 service: BoxCloneSyncService::new(svc),
149 },
150 }
151 }
152
153 pub(crate) fn call_owned(self, req: Request<B>) -> SyncRouteFuture<B> {
155 SyncRouteFuture {
156 future: self.service.oneshot(req),
157 }
158 }
159}
160
161fn try_downcast<T: 'static, K: 'static>(value: K) -> Result<T, K> {
163 let mut slot = Some(value);
164 if let Some(slot) = (&mut slot as &mut dyn std::any::Any).downcast_mut::<Option<T>>() {
165 return Ok(slot.take().expect("slot is filled"));
166 }
167 Err(slot.expect("slot is filled"))
168}
169
170impl<ReqBody> Clone for SyncRoute<ReqBody> {
171 fn clone(&self) -> Self {
172 Self {
173 service: self.service.clone(),
174 }
175 }
176}
177
178impl<ReqBody> fmt::Debug for SyncRoute<ReqBody> {
179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180 f.debug_struct("SyncRoute").finish()
181 }
182}
183
184impl<B> Service<Request<B>> for SyncRoute<B> {
185 type Response = Response<BoxBody>;
186 type Error = Infallible;
187 type Future = SyncRouteFuture<B>;
188
189 #[inline]
190 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
191 Poll::Ready(Ok(()))
192 }
193
194 #[inline]
195 fn call(&mut self, req: Request<B>) -> Self::Future {
196 self.clone().call_owned(req)
197 }
198}
199
200pin_project_lite::pin_project! {
201 pub struct SyncRouteFuture<B> {
203 #[pin]
204 future: Oneshot<BoxCloneSyncService<Request<B>, Response<BoxBody>, Infallible>, Request<B>>,
205 }
206}
207
208impl<B> Future for SyncRouteFuture<B> {
209 type Output = Result<Response<BoxBody>, Infallible>;
210
211 #[inline]
212 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
213 self.project().future.poll(cx)
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use super::*;
220
221 #[test]
222 fn traits() {
223 use crate::test_helpers::*;
224
225 assert_send::<Route<()>>();
226 assert_send::<SyncRoute<()>>();
227 assert_sync::<SyncRoute<()>>();
228 }
229
230 #[test]
232 fn route_accepts_a_non_sync_service() {
233 let hits = std::cell::Cell::new(0u32); let _route: Route<()> = Route::new(tower::service_fn(move |_: Request<()>| {
235 hits.set(hits.get() + 1);
236 async { Ok::<_, Infallible>(Response::new(crate::body::empty())) }
237 }));
238 }
239
240 #[test]
241 fn try_downcast_moves_out_only_the_matching_type() {
242 assert_eq!(
243 try_downcast::<String, _>(String::from("route")),
244 Ok(String::from("route"))
245 );
246 assert_eq!(try_downcast::<String, _>(7u8), Err(7u8));
247 }
248
249 #[tokio::test]
250 async fn wrapping_a_sync_route_keeps_serving_it() {
251 let route: SyncRoute<()> = SyncRoute::new(tower::service_fn(|_: Request<()>| async {
252 Ok::<_, Infallible>(Response::builder().status(204).body(crate::body::empty()).unwrap())
253 }));
254 let rewrapped = SyncRoute::new(tower::Layer::layer(&tower::layer::util::Identity::new(), route));
255 let response = rewrapped.oneshot(Request::new(())).await.unwrap();
256 assert_eq!(response.status(), 204);
257 }
258}