1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use crate::{
config::{CompressionConfig, ServerConfig},
files::{path_context::PathContext, Files},
};
use actix_http::http::uri::InvalidUri;
use actix_rt::Runtime;
use actix_web::{
dev::ServerHandle,
middleware::{self, Condition, Logger},
App, HttpServer,
};
use collective::thread::{self, handle::ThreadHandle};
use snafu::{ResultExt, Snafu};
use std::{
convert::{TryFrom, TryInto},
path::PathBuf,
sync::{
mpsc::{self, RecvError, Sender},
Arc,
},
thread::Thread,
};
use tokio::sync::RwLock;
#[derive(Debug, Snafu)]
pub enum Error {
ChannelReceive {
source: RecvError,
},
Bind {
source: std::io::Error,
},
SystemRun {
source: std::io::Error,
},
RedirectUrlParseError {
source: InvalidUri,
},
CreatePathContext {
source: crate::files::path_context::Error,
},
}
pub type Result<T, E = Error> = std::result::Result<T, E>;
pub struct Server {
config: ServerConfig,
serve_dir: Arc<RwLock<Option<PathBuf>>>,
}
impl Server {
pub fn new(config: ServerConfig, serve_dir: Arc<RwLock<Option<PathBuf>>>) -> Self {
Server { config, serve_dir }
}
pub async fn spawn(
self,
notify_sender: Sender<Thread>,
) -> Result<(ServerHandle, ThreadHandle<Result<()>>)> {
log::debug!("Starting server thread");
let (tx, rx) = mpsc::channel();
let server_address = self.config.address;
let serve_dir = Arc::clone(&self.serve_dir);
let redirect_to_slash = self.config.redirect_to_slash;
let enable_auto_compression = matches!(
self.config.compression,
Some(CompressionConfig::Auto) | None
);
let root_path_context =
Arc::new(PathContext::try_from(&self.config).context(CreatePathContext)?);
let mut path_contexts: Vec<PathContext> = Vec::new();
if let Some(path_configs) = &self.config.path_configs {
for path_config in path_configs {
path_contexts.push(path_config.try_into().context(CreatePathContext)?);
}
}
let path_contexts = Arc::new(path_contexts);
let thread_handle = thread::handle::spawn(notify_sender, move || {
let rt = Runtime::new().unwrap();
let srv = HttpServer::new(move || {
let mut files_service = Files::new(
"/",
Arc::clone(&serve_dir),
Arc::clone(&root_path_context),
Arc::clone(&path_contexts),
);
if let Some(true) = redirect_to_slash {
files_service = files_service.redirect_to_slash_directory();
}
App::new()
.wrap(Logger::default())
.wrap(Condition::new(
enable_auto_compression,
middleware::Compress::default(),
))
.default_service(files_service)
})
.bind(server_address)
.context(Bind)?
.shutdown_timeout(60)
.run();
let _ = tx.send(srv.handle());
rt.block_on(async { srv.await }).context(SystemRun)?;
Ok(())
});
Ok((rx.recv().context(ChannelReceive)?, thread_handle))
}
}