-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.rs
More file actions
199 lines (165 loc) · 5.63 KB
/
main.rs
File metadata and controls
199 lines (165 loc) · 5.63 KB
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
use std::{net::SocketAddr, sync::Arc};
use ::ring::rand::SystemRandom;
use axum::{extract::State, routing::get, Router};
use log::{info, warn};
use serde::Deserialize;
use simplelog::{ColorChoice, LevelFilter, TermLogger, TerminalMode};
use sqlite::Connection;
use tokio::sync::Mutex;
#[cfg(feature = "app-tls")]
use {axum_server::tls_rustls::RustlsConfig, rustls::crypto::ring};
mod auth;
mod cookie;
mod rankinfo;
mod statics;
mod util;
const DEFAULT_HTTP_PORT: u16 = 8080;
const CONFIG_PATH: &str = "config.toml";
#[cfg(feature = "app-tls")]
const DEFAULT_HTTPS_PORT: u16 = 443;
#[derive(Deserialize, Clone)]
struct CoreConfig {
db_path: String,
port: Option<u16>,
}
#[allow(dead_code)]
#[derive(Deserialize, Clone)]
struct TlsConfig {
cert_path: String,
key_path: String,
port: Option<u16>,
}
#[derive(Deserialize, Clone)]
struct Config {
core: CoreConfig,
app_tls: Option<TlsConfig>,
rankinfo: Option<rankinfo::RankInfoConfig>,
auth: Option<auth::AuthConfig>,
cookie: Option<cookie::CookieConfig>,
}
impl Config {
fn load() -> Self {
let config = std::fs::read_to_string(CONFIG_PATH).expect("Failed to open config file");
toml::from_str(&config).expect("Failed to parse config file")
}
}
#[derive(Clone)]
struct AppState {
db: Arc<Mutex<Connection>>,
rng: Arc<SystemRandom>,
config: Config,
}
impl AppState {
fn new(config: &Config) -> Self {
info!(
"SQLite version {}",
util::version_to_string(sqlite::version())
);
let conn = util::connect_to_db(&config.core.db_path);
Self {
db: Arc::new(Mutex::new(conn)),
rng: Arc::new(SystemRandom::new()),
config: config.clone(),
}
}
fn is_using_app_tls(self: &Self) -> bool {
#[cfg(not(feature = "app-tls"))]
{
return false;
}
#[allow(unreachable_code)]
self.config.app_tls.is_some()
}
}
#[tokio::main]
async fn main() {
// load config
let config = Config::load();
// init logging
TermLogger::init(
LevelFilter::Info,
simplelog::Config::default(),
TerminalMode::Mixed,
ColorChoice::Auto,
)
.expect("Failed to init logger");
info!("OFAPI v{}", env!("CARGO_PKG_VERSION"));
// init app state
let state = AppState::new(&config);
// register endpoints for both HTTP and HTTPS
let mut routes = Router::new().route("/", get(get_info));
routes = statics::register(routes);
if let Some(ref rankinfo_config) = config.rankinfo {
routes = rankinfo::register(routes, rankinfo_config);
}
// register secure endpoints
let mut routes_secure = Router::new();
if let Some(ref auth_config) = config.auth {
routes_secure = auth::register(routes_secure, auth_config, &state.rng);
}
if let Some(ref cookie_config) = config.cookie {
routes_secure = cookie::register(routes_secure, cookie_config);
}
#[cfg(not(feature = "app-tls"))]
{
warn!("OFAPI was not compiled with the `app-tls` feature. Encryption should be done at the proxy level!");
let routes = Router::new().merge(routes).merge(routes_secure);
let http = init_http(routes, &config, state.clone());
let _ = tokio::join!(http);
}
#[cfg(feature = "app-tls")]
{
info!("Using application-level TLS termination.");
// init secure endpoints on a separate port
// N.B. these listen concurrently, but NOT in parallel (see tokio::join!)
if state.is_using_app_tls() {
let routes_secure = Router::new().merge(routes.clone()).merge(routes_secure);
let http = init_http(routes, &config, state.clone());
let https = init_https(routes_secure, &config, state);
let _ = tokio::join!(http, https);
} else {
warn!("Application-level TLS termination disabled. Encryption should be done at the proxy level!");
let routes = Router::new().merge(routes).merge(routes_secure);
let http = init_http(routes, &config, state.clone());
let _ = tokio::join!(http);
}
}
}
const BIND_IP: [u8; 4] = [127, 0, 0, 1];
async fn init_http(routes: Router<Arc<AppState>>, config: &Config, state: AppState) {
let addr = SocketAddr::from((BIND_IP, config.core.port.unwrap_or(DEFAULT_HTTP_PORT)));
let app = routes.with_state(Arc::new(state));
info!("HTTP listening on {}", addr);
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
axum::serve(listener, app).await.unwrap();
}
#[cfg(feature = "app-tls")]
async fn init_https(routes: Router<Arc<AppState>>, config: &Config, state: AppState) {
let app = routes.with_state(Arc::new(state));
let Some(ref tls_config) = config.app_tls else {
warn!("Missing or malformed TLS config. HTTPS disabled");
return;
};
let addr = SocketAddr::from((BIND_IP, tls_config.port.unwrap_or(DEFAULT_HTTPS_PORT)));
info!("HTTPS listening on {}", addr);
ring::default_provider().install_default().unwrap();
let rustls_cfg =
match RustlsConfig::from_pem_file(&tls_config.cert_path, &tls_config.key_path).await {
Err(e) => {
warn!("Failed to activate TLS ({}); HTTPS disabled", e);
return;
}
Ok(cfg) => cfg,
};
axum_server::bind_rustls(addr, rustls_cfg)
.serve(app.into_make_service())
.await
.unwrap();
}
async fn get_info(State(state): State<Arc<AppState>>) -> String {
format!(
"OFAPI v{}\ntls: {:?}\n",
env!("CARGO_PKG_VERSION"),
state.is_using_app_tls()
)
}