tiny_web/sys/
worker.rs

1use core::{fmt, str};
2use std::{cmp::min, collections::HashMap, io::Error, sync::Arc};
3
4#[cfg(any(feature = "http", feature = "https"))]
5use std::net::IpAddr;
6
7use chrono::{TimeDelta, Utc};
8use serde_json::Value;
9use tokio::{
10    io::{AsyncReadExt, AsyncWriteExt},
11    net::TcpStream,
12    sync::{
13        mpsc::{self, Sender},
14        Mutex,
15    },
16    task::JoinHandle,
17};
18
19#[cfg(not(feature = "https"))]
20use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
21
22#[cfg(debug_assertions)]
23use tokio::sync::RwLock;
24#[cfg(feature = "https")]
25use tokio_rustls::TlsAcceptor;
26
27use super::{
28    action::{ActMap, Action, ActionData, Answer},
29    cache::CacheSys,
30    dbs::adapter::DB,
31    file::TempFile,
32    html::Html,
33    init::Addr,
34    lang::Lang,
35    log::Log,
36    mail::Mail,
37    request::{HttpVersion, RawData, WebFile},
38    route::Route,
39};
40
41#[cfg(feature = "fastcgi")]
42use super::workers::fastcgi;
43#[cfg(any(feature = "http", feature = "https"))]
44use super::workers::http;
45#[cfg(feature = "scgi")]
46use super::workers::scgi;
47#[cfg(feature = "uwsgi")]
48use super::workers::uwsgi;
49
50/// Buffer for read data from TcpStream
51pub const BUFFER_SIZE: usize = 8192;
52
53/// One year in seconds
54const ONE_YEAR: i64 = 31622400;
55
56/// Connection processing workflow
57#[derive(Debug)]
58pub(crate) struct Worker;
59
60/// General data
61pub(crate) struct WorkerData {
62    /// Engine - binary tree of controller functions.
63    pub engine: Arc<ActMap>,
64    /// I18n system.
65    #[cfg(not(debug_assertions))]
66    pub lang: Arc<Lang>,
67    /// I18n system.
68    #[cfg(debug_assertions)]
69    pub lang: Arc<RwLock<Lang>>,
70    /// Template maker.
71    #[cfg(not(debug_assertions))]
72    pub html: Arc<Html>,
73    /// Template maker.
74    #[cfg(debug_assertions)]
75    pub html: Arc<RwLock<Html>>,
76    /// Cache system.
77    pub cache: Arc<Mutex<CacheSys>>,
78    /// Database connections pool.
79    pub db: Arc<DB>,
80    /// Session key.
81    pub session_key: Arc<String>,
82    /// Salt for a crypto functions.
83    pub salt: Arc<String>,
84    /// Mail provider.
85    pub mail: Arc<Mutex<Mail>>,
86    /// Default controller for request "/" or default class or default action
87    pub action_index: Arc<Route>,
88    /// Default controller for 404 Not Found
89    pub action_not_found: Arc<Route>,
90    /// Default controller for error_route
91    pub action_err: Arc<Route>,
92    /// Stop signal
93    pub(crate) stop: Option<(Arc<Addr>, i64)>,
94    /// The full path to the folder where the server was started.
95    pub(crate) root: Arc<String>,
96    /// Remote IP
97    #[cfg(any(feature = "http", feature = "https"))]
98    pub(crate) ip: IpAddr,
99    /// A wrapper around a rustls::ServerConfig, providing an async accept method
100    #[cfg(feature = "https")]
101    pub(crate) acceptor: Arc<TlsAcceptor>,
102}
103
104/// A network stream errors
105pub(crate) enum StreamError {
106    /// Stream are closed
107    Closed,
108    /// Error reading from stream
109    Error(Error),
110    /// Buffer is small
111    Buffer,
112    /// Read timeout
113    Timeout,
114}
115
116/// Half of the network to read
117pub(crate) struct StreamRead {
118    /// A Tokio network stream
119    #[cfg(not(feature = "https"))]
120    tcp: OwnedReadHalf,
121    /// A Tokio network stream
122    #[cfg(feature = "https")]
123    tcp: tokio::io::ReadHalf<tokio_rustls::server::TlsStream<TcpStream>>,
124    /// Reading buffer
125    buf: [u8; BUFFER_SIZE],
126    /// The number of bytes in the buffer
127    len: usize,
128    // Reading shift
129    shift: usize,
130}
131
132impl StreamRead {
133    /// Read from stream to buffer
134    ///
135    /// # Params
136    ///
137    /// * `timeout: u64` - How long to wait for a reading? (in milliseconds)
138    ///
139    /// If timeout = 0, it will wait until data appears or an error occurs.
140    /// After `StreamError::Timeout`, saving data in the buffer and correct operation of the protocol are not guaranteed, so it is advisable to close the stream.
141    pub async fn read(&mut self, timeout: u64) -> Result<(), StreamError> {
142        if self.shift == 0 && self.len == BUFFER_SIZE {
143            return Err(StreamError::Buffer);
144        } else if self.shift > 0 && self.len <= BUFFER_SIZE {
145            self.buf.copy_within(self.shift.., 0);
146            self.len -= self.shift;
147            self.shift = 0;
148        }
149
150        if timeout > 0 {
151            match tokio::time::timeout(std::time::Duration::from_millis(timeout), async {
152                match self.tcp.read(unsafe { self.buf.get_unchecked_mut(self.len..) }).await {
153                    Ok(len) => {
154                        if len == 0 {
155                            Err(StreamError::Closed)
156                        } else {
157                            Ok(len)
158                        }
159                    }
160                    Err(e) => Err(StreamError::Error(e)),
161                }
162            })
163            .await
164            {
165                Ok(res) => match res {
166                    Ok(len) => {
167                        self.len += len;
168                        Ok(())
169                    }
170                    Err(e) => Err(e),
171                },
172                Err(_) => Err(StreamError::Timeout),
173            }
174        } else {
175            match self.tcp.read(unsafe { self.buf.get_unchecked_mut(self.len..) }).await {
176                Ok(len) => {
177                    if len == 0 {
178                        Err(StreamError::Closed)
179                    } else {
180                        self.len += len;
181                        Ok(())
182                    }
183                }
184                Err(e) => Err(StreamError::Error(e)),
185            }
186        }
187    }
188
189    /// Gets the bytes in the local buffer without moving the buffer pointers
190    pub fn get(&self, size: usize) -> &[u8] {
191        let size = min(self.shift + size, self.len);
192        unsafe { self.buf.get_unchecked(self.shift..size) }
193    }
194
195    /// Adds shift in the data
196    pub fn shift(&mut self, shift: usize) {
197        self.shift += shift;
198        if self.shift > self.len {
199            self.shift = self.len;
200        }
201    }
202
203    /// Gets length of available data
204    pub fn available(&self) -> usize {
205        self.len - self.shift
206    }
207}
208
209/// Half of the network to write
210pub(crate) struct StreamWrite {
211    /// Sender
212    pub tx: Arc<Sender<MessageWrite>>,
213}
214
215#[derive(Debug)]
216pub(crate) enum MessageWrite {
217    Message(Vec<u8>, bool),
218    End,
219}
220
221impl StreamWrite {
222    pub async fn new(
223        #[cfg(not(feature = "https"))] mut tcp: OwnedWriteHalf,
224        #[cfg(feature = "https")] mut tcp: tokio::io::WriteHalf<tokio_rustls::server::TlsStream<TcpStream>>,
225    ) -> (Arc<StreamWrite>, JoinHandle<()>) {
226        let (tx, mut rx) = mpsc::channel(32);
227        let stream = Arc::new(StreamWrite { tx: Arc::new(tx) });
228
229        let handle = tokio::spawn(async move {
230            while let Some(message) = rx.recv().await {
231                match message {
232                    MessageWrite::Message(message, end) => {
233                        #[cfg(feature = "fastcgi")]
234                        let data = fastcgi::Net::write(message, end);
235                        #[cfg(feature = "uwsgi")]
236                        let data = uwsgi::Net::write(message, end);
237                        #[cfg(feature = "scgi")]
238                        let data = scgi::Net::write(message, end);
239                        #[cfg(any(feature = "http", feature = "https"))]
240                        let data = http::Net::write(message, end);
241
242                        if let Err(e) = tcp.write_all(&data).await {
243                            Log::warning(101, Some(e.to_string()));
244                        }
245                    }
246                    MessageWrite::End => break,
247                }
248            }
249        });
250        (stream, handle)
251    }
252
253    /// Write unswer to the stream
254    pub async fn write(&self, data: Vec<u8>) {
255        if let Err(e) = self.tx.send(MessageWrite::Message(data, true)).await {
256            Log::warning(100, Some(e.to_string()));
257        }
258    }
259
260    async fn end(handle: JoinHandle<()>, tx: Arc<Sender<MessageWrite>>) {
261        if let Err(e) = tx.send(MessageWrite::End).await {
262            Log::warning(100, Some(e.to_string()));
263        }
264        if let Err(e) = handle.await {
265            Log::warning(102, Some(e.to_string()));
266        }
267    }
268}
269
270impl Worker {
271    /// Run main worker
272    ///
273    /// # Params
274    ///
275    /// * `stream: TcpStream` - Tokio tcp stream.
276    /// * `data: WorkerData` - General data for the web engine.
277    pub async fn run(stream: TcpStream, data: WorkerData) {
278        #[cfg(not(feature = "https"))]
279        let (read, write) = stream.into_split();
280        #[cfg(feature = "https")]
281        let (read, write) = {
282            let tls_stream = match data.acceptor.accept(stream).await {
283                Ok(stream) => stream,
284                Err(e) => {
285                    Log::warning(2007, Some(e.to_string()));
286                    return;
287                }
288            };
289            let (tls_read, tls_write) = tokio::io::split(tls_stream);
290            (tls_read, tls_write)
291        };
292
293        let mut stream_read = StreamRead {
294            tcp: read,
295            buf: [0; BUFFER_SIZE],
296            len: 0,
297            shift: 0,
298        };
299        let (stream_write, handle) = StreamWrite::new(write).await;
300        let tx = Arc::clone(&stream_write.tx);
301        if let Err(e) = stream_read.read(300).await {
302            match e {
303                StreamError::Closed => {}
304                StreamError::Error(e) => {
305                    Log::warning(2000, Some(e.to_string()));
306                }
307                StreamError::Buffer => {
308                    Log::warning(2006, None);
309                }
310                StreamError::Timeout => {
311                    Log::warning(2001, None);
312                }
313            }
314            return;
315        }
316        #[cfg(feature = "fastcgi")]
317        fastcgi::Net::run(stream_read, stream_write, data).await;
318        #[cfg(feature = "uwsgi")]
319        uwsgi::Net::run(stream_read, stream_write, data).await;
320        #[cfg(feature = "scgi")]
321        scgi::Net::run(stream_read, stream_write, data).await;
322        #[cfg(any(feature = "http", feature = "https"))]
323        http::Net::run(stream_read, stream_write, data).await;
324
325        StreamWrite::end(handle, tx).await;
326    }
327
328    /// Return a text description of the return code
329    pub fn http_code_get(code: u16) -> &'static str {
330        match code {
331            100 => "Continue",
332            101 => "Switching Protocols",
333            102 => "Processing",
334            103 => "Early Hints",
335            200 => "OK",
336            201 => "Created",
337            202 => "Accepted",
338            203 => "Non-Authoritative Information",
339            204 => "No Content",
340            205 => "Reset Content",
341            206 => "Partial Content",
342            207 => "Multi-Status",
343            208 => "Already Reported",
344            226 => "IM Used",
345            300 => "Multiple Choices",
346            301 => "Moved Permanently",
347            302 => "Found",
348            303 => "See Other",
349            304 => "Not Modified",
350            305 => "Use Proxy",
351            306 => "(Unused)",
352            307 => "Temporary Redirect",
353            308 => "Permanent Redirect",
354            400 => "Bad Request",
355            401 => "Unauthorized",
356            402 => "Payment Required",
357            403 => "Forbidden",
358            404 => "Not Found",
359            405 => "Method Not Allowed",
360            406 => "Not Acceptable",
361            407 => "Proxy Authentication Required",
362            408 => "Request Timeout",
363            409 => "Conflict",
364            410 => "Gone",
365            411 => "Length Required",
366            412 => "Precondition Failed",
367            413 => "Content Too Large",
368            414 => "URI Too Long",
369            415 => "Unsupported Media Type",
370            416 => "Range Not Satisfiable",
371            417 => "Expectation Failed",
372            418 => "(Unused)",
373            421 => "Misdirected Request",
374            422 => "Unprocessable Content",
375            423 => "Locked",
376            424 => "Failed Dependency",
377            425 => "Too Early",
378            426 => "Upgrade Required",
379            428 => "Precondition Required",
380            429 => "Too Many Requests",
381            431 => "Request Header Fields Too Large",
382            451 => "Unavailable For Legal Reasons",
383            500 => "Internal Server Error",
384            501 => "Not Implemented",
385            502 => "Bad Gateway",
386            503 => "Service Unavailable",
387            504 => "Gateway Timeout",
388            505 => "HTTP Version Not Supported",
389            506 => "Variant Also Negotiates",
390            507 => "Insufficient Storage",
391            508 => "Loop Detected",
392            510 => "Not Extended (OBSOLETED)",
393            511 => "Network Authentication Required",
394            _ => "Unassigned",
395        }
396    }
397
398    /// Run web engine
399    ///
400    /// # Return
401    ///
402    /// Vector with a binary data
403    pub(crate) async fn call_action(data: ActionData) -> Vec<u8> {
404        #[cfg(debug_assertions)]
405        Log::info(228, Some(format!("{} {:?} {}{}", data.request.ip, data.request.method, data.request.site, data.request.url)));
406
407        // Check and reload langs and templates
408        #[cfg(debug_assertions)]
409        Worker::reload(Arc::clone(&data.lang), Arc::clone(&data.html)).await;
410
411        let status = match data.request.version {
412            HttpVersion::None => "Status:",
413            HttpVersion::HTTP1_0 => "HTTP/1.0",
414            HttpVersion::HTTP1_1 => "HTTP/1.1",
415            HttpVersion::HTTP2 => "HTTP/2",
416        };
417
418        let mut action = match Action::new(data).await {
419            Ok(action) => action,
420            Err((redirect, files)) => {
421                // Stopping a call in a parallel thread
422                tokio::spawn(async move {
423                    let mut vec = Vec::with_capacity(32);
424                    for files in files.into_values() {
425                        for f in files {
426                            vec.push(f.tmp)
427                        }
428                    }
429                    Action::clean_file(vec).await;
430                });
431
432                // Write status
433                let mut answer: Vec<u8> = Vec::with_capacity(512);
434                if redirect.permanently {
435                    answer.extend_from_slice(
436                        format!("{status} 301 {}\r\nLocation: {}\r\n\r\n", Worker::http_code_get(301), redirect.url).as_bytes(),
437                    );
438                } else {
439                    answer.extend_from_slice(
440                        format!("{status} 302 {}\r\nLocation: {}\r\n\r\n", Worker::http_code_get(302), redirect.url).as_bytes(),
441                    );
442                }
443                return answer;
444            }
445        };
446
447        let result = match Action::run(&mut action).await {
448            Answer::Raw(answer) => answer,
449            Answer::String(answer) => answer.into_bytes(),
450            Answer::None => Vec::new(),
451        };
452
453        let answer = if !action.header_send {
454            // + Status + Cookie + Keep-alive + Content-Type + Content-Length + headers
455            // max length
456            let capacity = result.len() + 4096;
457            let mut answer = Worker::get_header(capacity, &action, Some(result.len()));
458            answer.extend_from_slice(&result);
459            answer
460        } else {
461            result
462        };
463
464        // Stopping a call in a parallel thread
465        tokio::spawn(async move {
466            Action::end(action).await;
467        });
468        answer
469    }
470
471    /// Reload lang and template
472    #[cfg(debug_assertions)]
473    async fn reload(lang: Arc<RwLock<Lang>>, html: Arc<RwLock<Html>>) {
474        let changed = lang.read().await.check_time().await;
475        if changed {
476            let mut lang = lang.write().await;
477            let files = Lang::get_files(Arc::clone(&lang.root)).await;
478            lang.load(files).await;
479        }
480        let changed = html.read().await.check_time().await;
481        if changed {
482            let mut html = html.write().await;
483            html.load().await;
484        }
485    }
486
487    fn get_header(capacity: usize, action: &Action, content_length: Option<usize>) -> Vec<u8> {
488        let status = match action.request.version {
489            HttpVersion::None => "Status:",
490            HttpVersion::HTTP1_0 => "HTTP/1.0",
491            HttpVersion::HTTP1_1 => "HTTP/1.1",
492            HttpVersion::HTTP2 => "HTTP/2",
493        };
494
495        // Write status
496        let mut answer: Vec<u8> = Vec::with_capacity(capacity);
497        if let Some(redirect) = action.response.redirect.as_ref() {
498            if redirect.permanently {
499                answer
500                    .extend_from_slice(format!("{status} 301 {}\r\nLocation: {}\r\n", Worker::http_code_get(301), redirect.url).as_bytes());
501            } else {
502                answer
503                    .extend_from_slice(format!("{status} 302 {}\r\nLocation: {}\r\n", Worker::http_code_get(302), redirect.url).as_bytes());
504            }
505        } else if let Some(code) = action.response.http_code {
506            answer.extend_from_slice(format!("{status} {} {}\r\n", code, Worker::http_code_get(code)).as_bytes());
507        } else {
508            answer.extend_from_slice(format!("{status} 200 {}\r\n", Worker::http_code_get(200)).as_bytes());
509        }
510
511        // Write Cookie
512        let sec = TimeDelta::new(ONE_YEAR, 0).unwrap_or_else(TimeDelta::zero);
513        let time = Utc::now() + sec;
514        let date = time.format("%a, %d-%b-%Y %H:%M:%S GMT").to_string();
515        let secure = if action.request.scheme == "https" { "Secure; " } else { "" };
516
517        answer.extend_from_slice(
518            format!(
519                "Set-Cookie: {}={}; Expires={}; Max-Age={}; path=/; domain={}; {}SameSite=none\r\n",
520                action.session.session_key, &action.session.key, date, ONE_YEAR, action.request.host, secure
521            )
522            .as_bytes(),
523        );
524        // Write Content-Type
525        match &action.response.content_type {
526            Some(content_type) => answer.extend_from_slice(format!("Content-Type: {}\r\n", content_type).as_bytes()),
527            None => answer.extend_from_slice(b"Content-Type: text/html; charset=utf-8\r\n"),
528        }
529        answer.extend_from_slice(b"Connection: Keep-Alive\r\n");
530        // Write headers
531        for (name, val) in &action.response.headers {
532            answer.extend_from_slice(format!("{}: {}\r\n", name, val).as_bytes());
533        }
534        // Write Content-Length
535        if let Some(len) = content_length {
536            answer.extend_from_slice(format!("Content-Length: {}\r\n", len).as_bytes());
537        }
538        answer.extend_from_slice(b"\r\n");
539
540        answer
541    }
542
543    pub async fn write(action: &Action, src: Vec<u8>) {
544        let src = if !action.header_send {
545            let mut vec = Worker::get_header(src.len() + 4096, action, None);
546            vec.extend_from_slice(&src);
547            vec
548        } else {
549            src
550        };
551        if let Err(e) = action.tx.send(MessageWrite::Message(src, false)).await {
552            Log::warning(100, Some(e.to_string()));
553        }
554    }
555
556    /// Read post and file datas.
557    ///
558    /// # Params
559    ///
560    /// * `data: Vec<u8>` - Data.
561    /// * `content_type: Option<String>` - CONTENT_TYPE parameter
562    ///
563    /// # Return
564    ///
565    /// * `HashMap<String, String>` - Post data.
566    /// * `HashMap<String, Vec<WebFile>>` - File data.
567    pub async fn read_input(
568        data: Vec<u8>,
569        content_type: Option<String>,
570    ) -> (HashMap<String, String>, HashMap<String, Vec<WebFile>>, RawData) {
571        let mut post = HashMap::new();
572        let mut file = HashMap::new();
573        let mut raw = RawData::None;
574
575        // Different types of CONTENT_TYPE need to be processed differently
576        if let Some(c) = content_type {
577            // Simple post
578            if c == "application/x-www-form-urlencoded" {
579                if !data.is_empty() {
580                    if let Ok(s) = std::str::from_utf8(&data) {
581                        let val: Vec<&str> = s.split('&').collect();
582                        post.reserve(val.len());
583                        for v in val {
584                            let val: Vec<&str> = v.splitn(2, '=').collect();
585                            match val.len() {
586                                1 => post.insert(v.to_owned(), String::new()),
587                                _ => post.insert(val[0].to_owned(), val[1].to_owned()),
588                            };
589                        }
590                    }
591                }
592            } else if c == "application/json;charset=UTF-8" {
593                raw = match serde_json::from_slice::<Value>(&data) {
594                    Ok(v) => RawData::Json(v),
595                    Err(_) => match String::from_utf8(data.clone()) {
596                        Ok(s) => RawData::String(s),
597                        Err(e) => RawData::Raw(e.into_bytes()),
598                    },
599                };
600            } else if c.len() > 30 {
601                // Multi post with files
602                if let "multipart/form-data; boundary=" = &c[..30] {
603                    let boundary = format!("--{}", &c[30..]);
604                    let stop: [u8; 4] = [13, 10, 13, 10];
605                    if !data.is_empty() {
606                        let mut seek: usize = 0;
607                        let mut start: usize;
608                        let b_len = boundary.len();
609                        let len = data.len() - 4;
610                        let mut found: Option<(usize, &str)> = None;
611                        while seek < len {
612                            // Find a boundary
613                            if boundary.as_bytes() == &data[seek..seek + b_len] {
614                                if seek + b_len == len {
615                                    if let Some((l, h)) = found {
616                                        let d = &data[l..seek - 2];
617                                        Worker::get_post_file(h, d, &mut post, &mut file).await;
618                                    };
619                                    break;
620                                }
621                                seek += b_len + 2;
622                                start = seek;
623                                while seek < len {
624                                    if stop == data[seek..seek + 4] {
625                                        if let Ok(s) = std::str::from_utf8(&data[start..seek]) {
626                                            if let Some((l, h)) = found {
627                                                let d = &data[l..start - b_len - 4];
628                                                Worker::get_post_file(h, d, &mut post, &mut file).await;
629                                            };
630                                            found = Some((seek + 4, s));
631                                        }
632                                        seek += 4;
633                                        break;
634                                    } else {
635                                        seek += 1;
636                                    }
637                                }
638                            } else {
639                                seek += 1;
640                            }
641                        }
642                    }
643                } else {
644                    raw = match String::from_utf8(data.clone()) {
645                        Ok(s) => RawData::String(s),
646                        Err(e) => RawData::Raw(e.into_bytes()),
647                    }
648                }
649            } else {
650                raw = match String::from_utf8(data.clone()) {
651                    Ok(s) => RawData::String(s),
652                    Err(e) => RawData::Raw(e.into_bytes()),
653                }
654            }
655        } else if !data.is_empty() {
656            raw = match String::from_utf8(data.clone()) {
657                Ok(s) => RawData::String(s),
658                Err(e) => RawData::Raw(e.into_bytes()),
659            }
660        }
661        (post, file, raw)
662    }
663
664    /// Gets post and file records from multipart/form-data
665    async fn get_post_file(header: &str, data: &[u8], post: &mut HashMap<String, String>, file: &mut HashMap<String, Vec<WebFile>>) {
666        let h: Vec<&str> = header.splitn(3, "; ").collect();
667        let len = h.len();
668
669        // Post data found
670        if len == 2 {
671            if let Ok(v) = std::str::from_utf8(data) {
672                let k = &h[1][6..h[1].len() - 1];
673                post.insert(k.to_owned(), v.to_owned());
674            }
675        } else if len == 3 {
676            // File data found
677            let k = h[1][6..h[1].len() - 1].to_owned();
678            let n: Vec<&str> = h[2].splitn(2, "\r\n").collect();
679            let n = &n[0][10..n[0].len() - 1];
680
681            let path = TempFile::new_name();
682            if TempFile::write(&path, data).await.is_ok() {
683                if file.get(&k).is_none() {
684                    file.insert(k.to_owned(), Vec::with_capacity(16));
685                } else if let Some(d) = file.get_mut(&k) {
686                    d.push(WebFile {
687                        size: data.len(),
688                        name: n.to_owned(),
689                        tmp: path,
690                    })
691                };
692            }
693        }
694    }
695
696    #[cfg(feature = "https")]
697    pub fn load_cert(root: Arc<String>) -> Result<Arc<TlsAcceptor>, Error> {
698        use std::{
699            fs::File,
700            io::{BufReader, ErrorKind},
701        };
702
703        use rustls::{
704            pki_types::{CertificateDer, PrivateKeyDer},
705            ServerConfig,
706        };
707        use rustls_pemfile::{certs, read_all, Item};
708
709        let mut cert_file = BufReader::new(File::open(format!("{root}/ssl/certificate.crt"))?);
710        let mut key_file = BufReader::new(File::open(format!("{root}/ssl/privateKey.key"))?);
711
712        let certs = certs(&mut cert_file).collect::<Result<Vec<CertificateDer<'static>>, Error>>()?;
713
714        let pem_files = match read_all(&mut key_file).next() {
715            Some(file) => file?,
716            None => return Err(Error::new(ErrorKind::Other, format!("Private key not found in file {root}/ssl/privateKey.key",))),
717        };
718        let key = match pem_files {
719            Item::Pkcs1Key(key) => PrivateKeyDer::Pkcs1(key),
720            Item::Pkcs8Key(key) => PrivateKeyDer::Pkcs8(key),
721            Item::Sec1Key(key) => PrivateKeyDer::Sec1(key),
722            e => return Err(Error::new(ErrorKind::Other, format!("Private key not support {:?} in file {root}/ssl/privateKey.key", e))),
723        };
724
725        let tls_config = match ServerConfig::builder().with_no_client_auth().with_single_cert(certs, key) {
726            Ok(config) => Arc::new(config),
727            Err(e) => return Err(Error::new(ErrorKind::Other, e)),
728        };
729
730        Ok(Arc::new(TlsAcceptor::from(tls_config)))
731    }
732}
733
734impl fmt::Debug for WorkerData {
735    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
736        let WorkerData {
737            engine,
738            lang,
739            html,
740            cache,
741            db,
742            session_key,
743            salt,
744            mail,
745            action_index,
746            action_not_found,
747            action_err,
748            stop,
749            root,
750            #[cfg(any(feature = "http", feature = "https"))]
751            ip,
752            #[cfg(feature = "https")]
753                acceptor: _,
754        } = self;
755        #[cfg(feature = "https")]
756        let res = f
757            .debug_struct("WorkerData")
758            .field("engine", &engine)
759            .field("lang", &lang)
760            .field("html", &html)
761            .field("cache", &cache)
762            .field("db", &db)
763            .field("session_key", &session_key)
764            .field("salt", &salt)
765            .field("mail", &mail)
766            .field("action_index", &action_index)
767            .field("action_not_found", &action_not_found)
768            .field("action_err", &action_err)
769            .field("stop", &stop)
770            .field("root", &root)
771            .field("ip", &ip)
772            .field("acceptor", &"Not implemented")
773            .finish();
774        #[cfg(feature = "http")]
775        let res = f
776            .debug_struct("WorkerData")
777            .field("engine", &engine)
778            .field("lang", &lang)
779            .field("html", &html)
780            .field("cache", &cache)
781            .field("db", &db)
782            .field("session_key", &session_key)
783            .field("salt", &salt)
784            .field("mail", &mail)
785            .field("action_index", &action_index)
786            .field("action_not_found", &action_not_found)
787            .field("action_err", &action_err)
788            .field("stop", &stop)
789            .field("root", &root)
790            .field("ip", &ip)
791            .field("acceptor", &"Not implemented")
792            .finish();
793        #[cfg(not(any(feature = "http", feature = "https")))]
794        let res = f
795            .debug_struct("WorkerData")
796            .field("engine", &engine)
797            .field("lang", &lang)
798            .field("html", &html)
799            .field("cache", &cache)
800            .field("db", &db)
801            .field("session_key", &session_key)
802            .field("salt", &salt)
803            .field("mail", &mail)
804            .field("action_index", &action_index)
805            .field("action_not_found", &action_not_found)
806            .field("action_err", &action_err)
807            .field("stop", &stop)
808            .field("root", &root)
809            .finish();
810        res
811    }
812}