1use crate::handler::HandlerResponse;
4use crate::regexset_map;
5use crate::regexset_map::RegexSetMap;
6use crate::service_protocol::{self, RuntimeError, ToErrorResponse};
7use derivative::Derivative;
8use tracing_futures::Instrument;
9
10use anyhow::Context;
11use hyper::Body;
12use hyper::Request;
13use hyper::Response;
14
15use std::convert::Infallible;
16use std::net::SocketAddr;
17use std::sync::Arc;
18
19use rand::Rng;
20
21pub async fn listen_and_run_forever(
26 services: RegexSetMap<Request<Body>, Service>,
27 addr: &SocketAddr,
28) -> anyhow::Result<()> {
29 let services = Arc::new(services);
31 let server = hyper::Server::bind(addr).serve(hyper::service::make_service_fn(
32 move |_sock: &hyper::server::conn::AddrStream| {
33 let services = Arc::clone(&services);
34 async move {
35 Ok::<_, Infallible>(hyper::service::service_fn(
36 move |req: hyper::Request<hyper::Body>| {
37 let services = Arc::clone(&services);
38 async move {
39 let resp = handle_request(services, req).await;
40 Ok::<Response<hyper::Body>, Infallible>(resp)
41 }
42 },
43 ))
44 }
45 },
46 ));
47
48 server.await.context("server error")?;
49 Ok(())
50}
51
52const REQUEST_ID_HEADER_NAME: &'static str = "Request-ID";
53
54pub async fn handle_request(
57 services: Arc<RegexSetMap<Request<Body>, Service>>,
58 req: Request<Body>,
59) -> Response<Body> {
60 let request_id: String = rand::thread_rng()
61 .sample_iter(&rand::distributions::Alphanumeric)
62 .take(30)
63 .collect();
64 let span = tracing::error_span!("handle_request", request_id = ?request_id);
65 handle_request_impl(services, req, request_id)
66 .instrument(span)
67 .await
68}
69
70pub async fn handle_request_impl(
71 services: Arc<RegexSetMap<Request<Body>, Service>>,
72 req: Request<Body>,
73 request_id: String,
74) -> Response<Body> {
75 let path = req.uri().path().to_string(); let mut response = match services.get(&path, &req) {
78 regexset_map::GetResult::None => RuntimeError::NoServiceMounted
79 .to_error_response()
80 .to_hyper_response(),
81 regexset_map::GetResult::Ambiguous => RuntimeError::ServiceMountsAmbiguous
82 .to_error_response()
83 .to_hyper_response(),
84 regexset_map::GetResult::One(service) => {
85 tracing::debug!(service_regex = (service.0).0.as_str(), "service matched");
86 let tuple = &service.0;
87 let service_regex_captures = tuple.0.captures(&path).unwrap();
88 let service = service_regex_captures["root"].to_string();
89 let suffix = &service_regex_captures["suffix"];
90 match tuple.1.get(&suffix, &req) {
91 regexset_map::GetResult::None => RuntimeError::NoRouteMountedInService { service }
92 .to_error_response()
93 .to_hyper_response(),
94 regexset_map::GetResult::Ambiguous => {
95 RuntimeError::RouteMountsAmbiguous { service }
96 .to_error_response()
97 .to_hyper_response()
98 }
99 regexset_map::GetResult::One(route) => {
100 tracing::debug!(route_regex = route.regex.as_str(), "route matched");
101 let captures = route.regex.captures(suffix).unwrap();
102 let dispatcher = &route.dispatcher;
103
104 let dispatcher_result = {
105 let dispatcher_span = tracing::error_span!("invoke_dispatcher");
106 dispatcher(req, captures).instrument(dispatcher_span).await
107 };
108 match dispatcher_result {
109 Ok(r) => {
110 tracing::debug!("handler returned Ok");
111 r
112 }
113 Err(e) => {
114 tracing::error!(err = ?e, "handler returned error");
115 e.to_hyper_response()
116 }
117 }
118 }
119 }
120 }
121 };
122
123 response.headers_mut().insert(
124 REQUEST_ID_HEADER_NAME,
125 hyper::header::HeaderValue::from_str(&request_id)
126 .expect("request ID is expected to be valid header value"),
127 );
128
129 response.headers_mut().insert(
130 hyper::header::CONTENT_TYPE,
131 hyper::header::HeaderValue::from_static("application/json"),
132 );
133
134 tracing::debug!(http_status = ?response.status(), "finished request");
135
136 response
137}
138
139#[derive(Debug)]
143pub struct Service(pub (regex::Regex, RegexSetMap<Request<Body>, Route>));
144
145type BoxSyncFuture<Output> =
147 std::pin::Pin<Box<dyn Send + Sync + std::future::Future<Output = Output>>>;
148
149type DispatcherClosure = dyn Fn(
152 Request<Body>,
153 regex::Captures,
154 ) -> BoxSyncFuture<Result<Response<Body>, service_protocol::ErrorResponse>>
155 + Send
156 + Sync;
157
158#[derive(Derivative)]
163#[derivative(Debug)]
164pub struct Route {
165 pub method: hyper::Method,
166 pub regex: regex::Regex,
167 #[derivative(Debug = "ignore")]
168 pub dispatcher: Box<DispatcherClosure>,
169}
170
171impl<'a> regexset_map::Entry<Request<Body>> for Route {
172 fn regex(&self) -> ®ex::Regex {
173 &self.regex
174 }
175 fn matches_input(&self, req: &Request<Body>) -> bool {
176 self.method == req.method()
177 }
178}
179
180impl<'a> regexset_map::Entry<Request<Body>> for Service {
181 fn regex(&self) -> ®ex::Regex {
182 let pair = &self.0;
183 &pair.0
184 }
185 fn matches_input(&self, _req: &Request<Body>) -> bool {
186 true
187 }
188}
189
190pub fn handler_response_to_hyper_response<T>(handler_response: HandlerResponse<T>) -> Response<Body>
193where
194 T: serde::Serialize,
195{
196 match handler_response {
197 Ok(x) => serde_json::to_string(&x)
198 .map(|s| Response::new(Body::from(s)))
199 .unwrap_or_else(|e| {
200 tracing::error!(error = ?e, "cannot serialize handler response");
201 RuntimeError::SerializeHandlerResponse(e.to_string())
202 .to_error_response()
203 .to_hyper_response()
204 }),
205 Err(e) => {
206 tracing::error!(error = ?e, "handler returned error");
207 service_protocol::ServiceError::from(e)
208 .to_error_response()
209 .to_hyper_response()
210 }
211 }
212}