chitey_server/server/
http_server.rs

1use 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    // 80ポートにhttpアクセスが来た時にリダイレクトしたりするため
26    if let Some(redirect) = redirect {
27        // println!("redirect to {}", redirect);
28        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  // info!("location {}", location);
65  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            // GET && POST
105            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        // match builder.body(Body::empty()) {
131        Ok(v) => Ok(v),
132        Err(e) => Err(ChiteyError::InternalServerError(e.to_string())),
133    }
134}