chitey_server/server/
https_server.rs

1use std::{sync::{self, Arc}, net::SocketAddr, convert::Infallible, pin::Pin, task::{Context, Poll}, io::{BufWriter, Write}, fs::{File, self}, collections::HashMap};
2
3use crate::web_server::{Factories, ChiteyError};
4
5use super::util::{TlsCertsKey, throw_chitey_internal_server_error, cors_builder};
6use bytes::{BytesMut, BufMut, Bytes};
7use futures_util::{ready, Future, TryStreamExt};
8use http::{Request, Response, StatusCode, HeaderValue};
9use hyper::{server::{conn::{AddrIncoming, AddrStream}, accept::Accept}, service::{make_service_fn, service_fn}, Server, Body};
10use mime::Mime;
11use rustls::ServerConfig;
12use tokio::io::{AsyncRead, ReadBuf, self, AsyncWrite};
13use urlpattern::UrlPatternMatchInput;
14
15// HTTP/2 TLS など  chromeなどのブラウザはこちらに最初にアクセスしてくる
16// https://github.com/quic-go/quic-go/issues/3890
17// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Alt-Svc#browser_compatibility
18// https://http3-explained.haxx.se/ja/h3/h3-altsvc
19
20#[derive(Clone)]
21pub struct HttpsServerOpt {
22    pub listen: SocketAddr,
23}
24
25pub async fn launch_https_server (tls_cert_key: TlsCertsKey, https_server_opt: HttpsServerOpt, factories: Factories) -> Result<(), ChiteyError> {
26    let TlsCertsKey{certs, key} = tls_cert_key;
27    let HttpsServerOpt{listen} = https_server_opt;
28
29    let tls_config = {
30        // Do not use client certificate authentication.
31        let mut cfg = rustls::ServerConfig::builder()
32            .with_safe_defaults()
33            .with_no_client_auth()
34            .with_single_cert(certs, key).unwrap();        // Configure ALPN to accept HTTP/2, HTTP/1.1 in that order.
35
36        // cfg.alpn_protocols = b"\x02h2\x08http/1.1";
37        cfg.alpn_protocols = vec![b"h2".to_vec(), b"http/1.1".to_vec()];
38        sync::Arc::new(cfg)
39    };
40
41    let incoming = AddrIncoming::bind(&listen)
42        .map_err(|e| ChiteyError::InternalServerError(format!("Incoming failed: {:?}", e))).expect("error");
43    let make_service = make_service_fn(move |_| {
44        let factories = factories.clone();
45        let service = service_fn(move |req| {
46            handle_https_service_wrap(req, factories.clone())
47        });
48
49        async move { Ok::<_, Infallible>(service) }
50    });
51    let https_server = Server::builder(HyperAcceptor::new(tls_config, incoming)).serve(make_service);
52
53    // Prepare a long-running future stream to accept and serve clients.
54
55    // handle incoming connections and requests
56
57    println!("Starting to serve on https://{}.", listen);
58    match https_server.await {
59        Ok(_) => Ok(()),
60        Err(e) => Err(ChiteyError::InternalServerError(e.to_string())),
61    }
62}
63
64pub struct HyperAcceptor {
65    config: Arc<ServerConfig>,
66    incoming: AddrIncoming,
67}
68
69impl HyperAcceptor {
70    #[inline]
71    pub fn new(config: Arc<ServerConfig>, incoming: AddrIncoming) -> HyperAcceptor {
72        HyperAcceptor { config, incoming }
73    }
74}
75
76enum State {
77    Handshaking(tokio_rustls::Accept<AddrStream>),
78    Streaming(tokio_rustls::server::TlsStream<AddrStream>),
79}
80
81// tokio_rustls::server::TlsStream doesn't expose constructor methods,
82// so we have to TlsAcceptor::accept and handshake to have access to it
83// TlsStream implements AsyncRead/AsyncWrite handshaking tokio_rustls::Accept first
84pub struct HyperStream {
85    state: State,
86}
87
88impl HyperStream {
89    #[inline]
90    fn new(stream: AddrStream, config: Arc<ServerConfig>) -> HyperStream {
91        let accept = tokio_rustls::TlsAcceptor::from(config).accept(stream);
92        HyperStream {
93            state: State::Handshaking(accept),
94        }
95    }
96}
97
98impl AsyncRead for HyperStream {
99    #[inline]
100    fn poll_read(
101        self: Pin<&mut Self>,
102        cx: &mut Context,
103        buf: &mut ReadBuf,
104    ) -> Poll<io::Result<()>> {
105        // info!("impl AsyncRead for HyperStream");
106        let pin = self.get_mut();
107        match pin.state {
108            State::Handshaking(ref mut accept) => match ready!(Pin::new(accept).poll(cx)) {
109                Ok(mut stream) => {
110                    let result = Pin::new(&mut stream).poll_read(cx, buf);
111                    pin.state = State::Streaming(stream);
112                    result
113                }
114                Err(err) => Poll::Ready(Err(err)),
115            },
116            State::Streaming(ref mut stream) => Pin::new(stream).poll_read(cx, buf),
117        }
118    }
119}
120
121impl AsyncWrite for HyperStream {
122    #[inline]
123    fn poll_write(
124        self: Pin<&mut Self>,
125        cx: &mut Context<'_>,
126        buf: &[u8],
127    ) -> Poll<io::Result<usize>> {
128        let pin = self.get_mut();
129        match pin.state {
130            State::Handshaking(ref mut accept) => match ready!(Pin::new(accept).poll(cx)) {
131                Ok(mut stream) => {
132                    let result = Pin::new(&mut stream).poll_write(cx, buf);
133                    pin.state = State::Streaming(stream);
134                    result
135                }
136                Err(err) => Poll::Ready(Err(err)),
137            },
138            State::Streaming(ref mut stream) => Pin::new(stream).poll_write(cx, buf),
139        }
140    }
141
142    #[inline]
143    fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
144        match self.state {
145            State::Handshaking(_) => Poll::Ready(Ok(())),
146            State::Streaming(ref mut stream) => Pin::new(stream).poll_flush(cx),
147        }
148    }
149
150    #[inline]
151    fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
152        match self.state {
153            State::Handshaking(_) => Poll::Ready(Ok(())),
154            State::Streaming(ref mut stream) => Pin::new(stream).poll_shutdown(cx),
155        }
156    }
157}
158
159impl Accept for HyperAcceptor {
160    type Conn = HyperStream;
161    type Error = io::Error;
162
163    #[inline]
164    fn poll_accept(
165        self: Pin<&mut Self>,
166        cx: &mut Context<'_>,
167    ) -> Poll<Option<Result<Self::Conn, Self::Error>>> {
168        let pin = self.get_mut();
169        match ready!(Pin::new(&mut pin.incoming).poll_accept(cx)) {
170            Some(Ok(sock)) => Poll::Ready(Some(Ok(HyperStream::new(sock, pin.config.clone())))),
171            Some(Err(e)) => Poll::Ready(Some(Err(e))),
172            None => Poll::Ready(None),
173        }
174    }
175}
176
177#[inline]
178async fn handle_https_service_wrap(req: Request<Body>, factories: Factories) -> Result<Response<Body>, ChiteyError> {
179    match handle_https_service(req, factories.clone()).await {
180        Ok(v) => Ok(v),
181        Err(e) => {
182            tracing::error!("https: {}", e);
183            Err(e)
184        },
185    }
186}
187
188#[inline]
189async fn handle_https_service(req: Request<Body>, factories: Factories) -> Result<Response<Body>, ChiteyError> {
190    if req.uri().path().contains("..") {
191        let builder = cors_builder()
192        .header("Alt-Svc", "h3=\":443\"; ma=2592000")
193        .status(StatusCode::NOT_FOUND);
194        return match builder.body(Body::empty()) {
195            Ok(v) => Ok(v),
196            Err(e) => Err(ChiteyError::InternalServerError(e.to_string())),
197        }
198    }
199
200    let url = throw_chitey_internal_server_error(req.uri().to_string().parse())?;
201    let input = UrlPatternMatchInput::Url(url);
202    {
203        let method = req.method().clone();
204        let req_contain_key = req.headers().contains_key("Another-Header");
205        for (res, factory) in factories.factories {
206            // GET && POST
207            if res.guard == method {
208                if let Ok(Some(_)) = res.rdef.exec(input.clone()) {
209                    let factory_loc = factory.lock().await;
210                    if factory_loc.analyze_types(input.clone()) {
211                        return match factory_loc.handler_func(input.clone(), (req, false, factories.contexts.clone())).await {
212                            Ok(mut resp) => {
213                                if req_contain_key {
214                                    resp.headers_mut().append("Another-Header", HeaderValue::from_static("Ack"));
215                                }
216                                resp.headers_mut().append("Alt-Svc", HeaderValue::from_static("h3=\":443\"; ma=2592000"));
217                                Ok(resp)
218                            },
219                            Err(e) => Err(ChiteyError::InternalServerError(e.to_string())),
220                        }
221                    }
222                };
223            }
224        }
225    }
226
227    let builder = cors_builder()
228        .header("Alt-Svc", "h3=\":443\"; ma=2592000")
229        .status(StatusCode::NOT_FOUND);
230
231    match builder.body(Body::from(Bytes::copy_from_slice(b"page not found"))) {
232        // match builder.body(Body::empty()) {
233        Ok(v) => Ok(v),
234        Err(e) => Err(ChiteyError::InternalServerError(e.to_string())),
235    }
236}
237
238//uploadIDを表示させる関数
239pub async fn process_upload(id:String, builder:http::response::Builder, req: Request<Body>) -> Result<Response<Body>, http::Error>{
240    // println!("uploadID: {}",id);
241    let content_type_option = req.headers().get("content-type");
242    if content_type_option.is_none() {
243        return builder.body(Body::from(""));
244    }
245    let content_type = content_type_option.unwrap();
246    let mime_type_result: Result<mime::Mime, _> = match content_type.to_str() {
247        Ok(s) => s
248            .parse()
249            .map_err(|err| std::io::Error::new(std::io::ErrorKind::Other, err)),
250        Err(err) => Err(std::io::Error::new(std::io::ErrorKind::Other, err)),
251    };
252    if mime_type_result.is_err() {
253        return builder.body(Body::from(""));
254    }
255    let mime_type = mime_type_result.unwrap();
256    if mime_type.essence_str() != "multipart/form-data" {
257        return builder.body(Body::from(""));
258    }
259    let a = parse_mpart(req, mime_type).await;
260    dbg!(&a);
261    builder.status(StatusCode::OK).body(Body::from(format!("uploadID: {}",id)))
262}
263
264// multipartをパースしてhashmapにして返す関数
265// ファイルがアップロードされたときはuploadフォルダに保存しhashmapにはそのファイル名を入れる
266// アップロードされたファイル名は元のファイル名は使わずに年月日時分秒のファイル名としている
267async fn parse_mpart(req: Request<Body>, mime_type: Mime) -> HashMap<String, String>{
268    let mut a = HashMap::new();
269    let boundary = mime_type.get_param("boundary").map(|v| v.to_string()).ok_or_else(|| std::io::Error::new(std::io::ErrorKind::Other, "boundary not found")).unwrap();
270    let (_parts, body) = req.into_parts();
271    let mut multipart_stream = mpart_async::server::MultipartStream::new(boundary, body);
272    while let Ok(Some(mut field)) = multipart_stream.try_next().await {
273        let name = field.name().unwrap().to_string();
274        if let Ok(_filename) = field.filename() {
275        const UPLOAD_DIRNAME: &str = "upload";
276        if fs::create_dir_all(UPLOAD_DIRNAME).is_err(){
277            // println!("** ディレクトリの作成失敗 **");
278            continue;
279        }
280        let filename = chrono::Utc::now().format("%Y%m%d%H%M%S").to_string();
281        let filename = format!("{UPLOAD_DIRNAME}/{filename}.dat");
282        let mut writer = BufWriter::new(File::create(&filename).unwrap());
283        // let mut bufferlen: i64 = 0;
284        while let Ok(Some(bytes)) = field.try_next().await {
285            // bufferlen += bytes.len() as i64;
286            writer.write_all(&bytes).unwrap();
287        }
288        // println!("{bufferlen}");
289        a.insert(name, filename);
290        }else{
291        let mut buffer = BytesMut::new();
292        while let Ok(Some(bytes)) = field.try_next().await {
293            buffer.put(bytes);
294        }
295        let value = String::from_utf8(buffer.to_vec()).unwrap();
296        a.insert(name, value);
297        }
298
299    }
300    a
301}