1use crate::endpoint::Endpoint;
2use crate::filter::{Filter, Next};
3use crate::router::{RouteTarget, Router};
4use crate::state::State;
5use crate::static_files::StaticFiles;
6use crate::test_client::TestClient;
7use crate::ws::{WebSocketReceiver, WebSocketSender};
8use crate::{Request, Responder, Response, Result};
9use async_trait::async_trait;
10use hyper::server::conn::{AddrIncoming, AddrStream};
11use hyper::server::Builder;
12use hyper::service::{make_service_fn, service_fn};
13use hyper::{Body, Method};
14use std::convert::Infallible;
15use std::future::Future;
16use std::net::SocketAddr;
17use std::path::PathBuf;
18use std::sync::Arc;
19use tokio::net::ToSocketAddrs;
20use tracing::info;
21
22pub struct App<S: State> {
27 state: S,
28 routes: Router<S>,
29 filters: Vec<Box<dyn Filter<S> + Send + Sync + 'static>>,
30}
31
32pub struct Route<'a, 'p, S: State> {
34 path: &'p str,
35 app: &'a mut App<S>,
36}
37
38impl<'a, 'p, S: State> Route<'a, 'p, S> {
39 pub fn method(self, method: Method, ep: impl Endpoint<S> + Send + Sync + 'static) -> Self {
41 self.app.routes.add(method, self.path, ep);
42 self
43 }
44
45 pub fn all(self, ep: impl Endpoint<S> + Send + Sync + 'static) -> Self {
48 self.app.routes.add_all(self.path, ep);
49 self
50 }
51
52 pub fn get(self, ep: impl Endpoint<S> + Send + Sync + 'static) -> Self {
54 self.method(Method::GET, ep)
55 }
56
57 pub fn post(self, ep: impl Endpoint<S> + Send + Sync + 'static) -> Self {
59 self.method(Method::POST, ep)
60 }
61
62 pub fn put(self, ep: impl Endpoint<S> + Send + Sync + 'static) -> Self {
64 self.method(Method::PUT, ep)
65 }
66
67 pub fn delete(self, ep: impl Endpoint<S> + Send + Sync + 'static) -> Self {
69 self.method(Method::DELETE, ep)
70 }
71
72 pub fn static_files(self, root: impl Into<PathBuf>) -> Self {
78 let prefix = self.path.to_owned(); self.method(Method::GET, StaticFiles::new(root, prefix))
80 }
81
82 pub fn mount<S2>(&mut self, app: App<S2>)
89 where
90 S2: State,
91 S2::Context: From<S::Context>,
92 {
93 let path = self.path.to_owned() + "/*-highnoon-path-rest-";
94 let mounted = MountedApp { app: Arc::new(app) };
95 self.app.at(&path).all(mounted);
96 }
97
98 pub fn ws<H, F>(self, handler: H)
100 where
101 H: Send + Sync + 'static + Fn(Request<S>, WebSocketSender, WebSocketReceiver) -> F,
102 F: Future<Output = Result<()>> + Send + 'static,
103 {
104 self.method(Method::GET, crate::ws::endpoint(handler));
105 }
106}
107
108impl<S: State> App<S> {
109 pub fn new(state: S) -> Self {
113 Self {
114 state,
115 routes: Router::new(),
116 filters: vec![],
117 }
118 }
119
120 pub fn test(self) -> TestClient<S> {
124 TestClient::new(self)
125 }
126
127 pub fn state(&self) -> &S {
129 &self.state
130 }
131
132 pub fn with<F>(&mut self, filter: F)
135 where
136 F: Filter<S> + Send + Sync + 'static,
137 {
138 self.filters.push(Box::new(filter));
139 }
140
141 pub fn at<'a, 'p>(&'a mut self, path: &'p str) -> Route<'a, 'p, S> {
144 Route { path, app: self }
145 }
146
147 pub async fn listen(self, host: impl ToSocketAddrs) -> anyhow::Result<()> {
150 let mut addrs = tokio::net::lookup_host(host).await?;
151 let addr = addrs
152 .next()
153 .ok_or_else(|| anyhow::Error::msg("host lookup returned no hosts"))?;
154
155 let builder = hyper::Server::try_bind(&addr)?;
156 self.internal_serve(builder).await
157 }
158
159 pub async fn listen_on(self, tcp: std::net::TcpListener) -> anyhow::Result<()> {
162 let builder = hyper::Server::from_tcp(tcp)?;
163 self.internal_serve(builder).await
164 }
165
166 async fn internal_serve(self, builder: Builder<AddrIncoming>) -> anyhow::Result<()> {
167 let app = Arc::new(self);
168
169 let make_svc = make_service_fn(|addr_stream: &AddrStream| {
170 let app = app.clone();
171 let addr = addr_stream.remote_addr();
172
173 async move {
174 Ok::<_, Infallible>(service_fn(move |req: hyper::Request<Body>| {
175 let app = app.clone();
176 async move {
177 App::serve_one_req(app, req, addr)
178 .await
179 .map_err(|err| err.into_std())
180 }
181 }))
182 }
183 });
184
185 let server = builder.serve(make_svc);
186 info!("server listening on {}", server.local_addr());
187 server.await?;
188 Ok(())
189 }
190
191 pub(crate) async fn serve_one_req(
192 app: Arc<App<S>>,
193 req: hyper::Request<Body>,
194 addr: SocketAddr,
195 ) -> Result<hyper::Response<Body>> {
196 let RouteTarget { ep, params } = app.routes.lookup(req.method(), req.uri().path());
197
198 let ctx = app.state.new_context();
199 let req = Request::new(app.clone(), req, params, addr, ctx);
200
201 let next = Next {
202 ep,
203 rest: &*app.filters,
204 };
205
206 next.next(req)
207 .await
208 .or_else(|err| err.into_response())
209 .map(|resp| resp.into_inner())
210 }
211}
212
213struct MountedApp<S: State> {
214 app: Arc<App<S>>,
215}
216
217#[async_trait]
218impl<S: State, S2: State> Endpoint<S> for MountedApp<S2>
219where
220 S2::Context: From<S::Context>,
221{
222 async fn call(&self, req: Request<S>) -> Result<Response> {
223 let (inner, params, remote_addr, context) = req.into_parts();
225 let path_rest = params
227 .find("-highnoon-path-rest-")
228 .expect("-highnoon-path-rest- is missing!");
229 let RouteTarget {
231 ep,
232 params: params2,
233 } = self.app.routes.lookup(inner.method(), path_rest);
234
235 let mut req2 = Request::new(self.app.clone(), inner, params, remote_addr, context.into());
237
238 req2.merge_params(params2);
240
241 let next = Next {
243 ep,
244 rest: &*self.app.filters,
245 };
246
247 next.next(req2).await
248 }
249}