chitey_server/server/
http_server.rs1use std::net::SocketAddr;
2
3use bytes::Bytes;
4use http::{Request, Response, StatusCode, HeaderValue};
5use hyper::{Body, Server, service::{make_service_fn, service_fn}, server::conn::AddrStream};
6use urlpattern::UrlPatternMatchInput;
7
8use crate::web_server::{ChiteyError, Factories};
9
10use super::util::{throw_chitey_internal_server_error, cors_builder};
11
12
13#[derive(Clone)]
14pub struct HttpServerOpt {
15 pub listen: SocketAddr,
16 pub redirect: Option<String>,
17}
18
19pub async fn launch_http_server<F> (http_server_opt: HttpServerOpt, func: F, factories: Factories) -> Result<(), Box<dyn std::error::Error>>
20where
21 F: Fn()
22{
23 let HttpServerOpt{listen, redirect} = http_server_opt;
24
25 if let Some(redirect) = redirect {
27 let http_make_service = make_service_fn(move |_conn: &AddrStream| {
29 let location = redirect.clone().to_owned();
30 let service = service_fn(move |req| {
31 redirect_to_https(location.clone(), req)
32 });
33 async move { Ok::<_, http::Error>(service) }
34 });
35 let http_server = Server::bind(&listen).serve(http_make_service);
36 println!("Listening on http://{}", listen);
37 func();
38 let _ = http_server.await?;
39 } else {
40 let http_make_service = make_service_fn(move |_conn: &AddrStream| {
41 let factories = factories.clone();
42 let service = service_fn(move |req| {
43 not_redirect_to_https_wrap(req, factories.clone(), listen.to_string())
44 });
45 async move { Ok::<_, http::Error>(service) }
46 });
47 let http_server = Server::bind(&listen).serve(http_make_service);
48 println!("Listening on http://{}", listen);
49 func();
50 let _ = http_server.await?;
51 }
52 Ok(())
53}
54
55#[inline]
56async fn redirect_to_https(
57 location: String,
58 _req: Request<Body>,
59) -> Result<Response<Body>, http::Error> {
60 let mut builder = cors_builder();
61 builder = builder
62 .status(StatusCode::PERMANENT_REDIRECT)
63 .header("Location", location);
64 builder.body(Body::empty())
66}
67
68#[inline]
69async fn not_redirect_to_https_wrap(
70 req: Request<Body>,
71 factories: Factories,
72 listen: String,
73) -> Result<Response<Body>, ChiteyError> {
74 match not_redirect_to_https(req, factories.clone(), listen.to_string()).await {
75 Ok(v) => Ok(v),
76 Err(e) => {
77 tracing::error!("http: {}", e);
78 Err(e)
79 },
80 }
81}
82
83#[inline]
84async fn not_redirect_to_https(
85 req: Request<Body>,
86 factories: Factories,
87 listen: String,
88) -> Result<Response<Body>, ChiteyError> {
89 if req.uri().path().contains("..") {
90 let builder = Response::builder()
91 .header("Alt-Svc", "h3=\":443\"; ma=2592000")
92 .status(StatusCode::NOT_FOUND);
93 return match builder.body(Body::empty()) {
94 Ok(v) => Ok(v),
95 Err(e) => Err(ChiteyError::InternalServerError(e.to_string())),
96 }
97 }
98
99 let input = UrlPatternMatchInput::Url(throw_chitey_internal_server_error((format!("http://{}{}",listen , &req.uri().to_string())).parse())?);
100 {
101 let method = req.method().clone();
102 let req_contain_key = req.headers().contains_key("Another-Header");
103 for (res, factory) in factories.factories {
104 if res.guard == method {
106 if let Ok(Some(_)) = res.rdef.exec(input.clone()) {
107 let factory_loc = factory.lock().await;
108 if factory_loc.analyze_types(input.clone()) {
109 return match factory_loc.handler_func(input.clone(), (req, false, factories.contexts.clone())).await {
110 Ok(mut resp) => {
111 if req_contain_key {
112 resp.headers_mut().append("Another-Header", HeaderValue::from_static("Ack"));
113 }
114 resp.headers_mut().append("Alt-Svc", HeaderValue::from_static("h3=\":443\"; ma=2592000"));
115 Ok(resp)
116 },
117 Err(e) => Err(ChiteyError::InternalServerError(e.to_string())),
118 }
119 }
120 };
121 }
122 }
123 }
124
125 let builder = cors_builder()
126 .header("Alt-Svc", "h3=\":443\"; ma=2592000")
127 .status(StatusCode::NOT_FOUND);
128
129 match builder.body(Body::from(Bytes::copy_from_slice(b"page not found"))) {
130 Ok(v) => Ok(v),
132 Err(e) => Err(ChiteyError::InternalServerError(e.to_string())),
133 }
134}