init commit
This commit is contained in:
commit
2f1a435dda
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "http_rrs"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
openssl = { version = "0.10.64", features = ["vendored"] }
|
||||
yaml-rust = "0.4.5"
|
||||
log = "0.4.21"
|
||||
log4rs = "1.3.0"
|
26
README.md
Normal file
26
README.md
Normal file
@ -0,0 +1,26 @@
|
||||
# HttpRRS
|
||||
HTTP request redirection system
|
||||
|
||||
Default `conf.yml`:
|
||||
```yml
|
||||
http_host: localhost:80
|
||||
https_host: localhost:443
|
||||
|
||||
sites:
|
||||
# - domain: example.com # Domain with SSL
|
||||
# host: localhost:8080
|
||||
# ssl_cert: "/path/to/public/certificate.txt"
|
||||
# ssl_key: "/path/to/private/key.txt"
|
||||
|
||||
# - domain: sub.example.com # Domain with no SSL
|
||||
# host: localhost:8081
|
||||
|
||||
- domain: localhost
|
||||
host: localhost:8080
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
This works as a proxy that redirects based on the Host header
|
||||
|
||||

|
14
conf.yml
Normal file
14
conf.yml
Normal file
@ -0,0 +1,14 @@
|
||||
http_host: localhost:80
|
||||
https_host: localhost:443
|
||||
|
||||
sites:
|
||||
# - domain: example.com # Domain with SSL
|
||||
# host: localhost:8080
|
||||
# ssl_cert: "/path/to/public/certificate.txt"
|
||||
# ssl_key: "/path/to/private/key.txt"
|
||||
|
||||
# - domain: sub.example.com # Domain with no SSL
|
||||
# host: localhost:8081
|
||||
|
||||
- domain: localhost
|
||||
host: localhost:8080
|
BIN
explaination.png
Normal file
BIN
explaination.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
590
src/http_server.rs
Normal file
590
src/http_server.rs
Normal file
@ -0,0 +1,590 @@
|
||||
use http_rrs::ThreadPool;
|
||||
use log::{debug, info, warn};
|
||||
use openssl::ssl::{
|
||||
NameType, SniError, SslAcceptor, SslAlert, SslContext, SslFiletype, SslMethod, SslRef,
|
||||
SslStream,
|
||||
};
|
||||
use std::time::Duration;
|
||||
use std::{
|
||||
io::{Read, Write},
|
||||
net::{IpAddr, Shutdown, TcpListener, TcpStream},
|
||||
sync::Arc,
|
||||
thread,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SslCert {
|
||||
pub cert_file: String,
|
||||
pub key_file: String,
|
||||
pub ctx_index: u8,
|
||||
pub ctx: Option<SslContext>,
|
||||
}
|
||||
|
||||
impl SslCert {
|
||||
pub fn generate_ctx(cert_file: &str, key_file: &str) -> Option<SslContext> {
|
||||
let mut ctx = match SslContext::builder(SslMethod::tls()) {
|
||||
Ok(i) => i,
|
||||
Err(_) => return None,
|
||||
};
|
||||
match ctx.set_private_key_file(&key_file, SslFiletype::PEM) {
|
||||
Ok(i) => i,
|
||||
Err(_) => return None,
|
||||
};
|
||||
match ctx.set_certificate_file(&cert_file, SslFiletype::PEM) {
|
||||
Ok(i) => i,
|
||||
Err(_) => return None,
|
||||
};
|
||||
match ctx.check_private_key() {
|
||||
Ok(i) => i,
|
||||
Err(_) => return None,
|
||||
};
|
||||
Some(ctx.build())
|
||||
}
|
||||
|
||||
pub fn new(cert_file: &str, key_file: &str) -> Option<SslCert> {
|
||||
let ctx = match Self::generate_ctx(cert_file, key_file) {
|
||||
Some(i) => Some(i),
|
||||
None => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
Some(SslCert {
|
||||
ctx: ctx,
|
||||
cert_file: cert_file.to_string(),
|
||||
key_file: key_file.to_string(),
|
||||
ctx_index: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_ctx(&mut self) -> Option<&SslContext> {
|
||||
// self.ctx_index += 1;
|
||||
// if self.ctx_index > 5 {
|
||||
// self.ctx_index = 0;
|
||||
// self.ctx = Self::generate_ctx(&self.cert_file, &self.key_file);
|
||||
// }
|
||||
self.ctx.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Site {
|
||||
pub domain: String,
|
||||
pub host: String,
|
||||
pub ssl: Option<SslCert>,
|
||||
}
|
||||
|
||||
impl Site {
|
||||
fn connect(self) -> Result<TcpStream, String> {
|
||||
match TcpStream::connect(self.host) {
|
||||
Ok(i) => Ok(i),
|
||||
Err(_) => Err("server not canacting".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SiteServer {
|
||||
host: String,
|
||||
sites: Arc<Vec<Site>>,
|
||||
}
|
||||
|
||||
fn split_once<'a>(in_string: &'a str, separator: &str) -> Result<(&'a str, &'a str), String> {
|
||||
let mut splitter = in_string.splitn(2, &separator);
|
||||
let first = match splitter.next() {
|
||||
Some(i) => i,
|
||||
None => return Err("first split nat foined".to_string()),
|
||||
};
|
||||
let second = match splitter.next() {
|
||||
Some(i) => i,
|
||||
None => return Err("escond split nat foined".to_string()),
|
||||
};
|
||||
Ok((first, second))
|
||||
}
|
||||
|
||||
fn get_site(sites: &Vec<Site>, domain: &str) -> Option<Site> {
|
||||
for i in sites.iter() {
|
||||
if i.domain == domain {
|
||||
return Some(i.clone());
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
impl SiteServer {
|
||||
pub fn new<'a>(host: String, sites: Arc<Vec<Site>>) -> Self {
|
||||
SiteServer { host, sites }
|
||||
}
|
||||
|
||||
fn get_site(self, domain: &str) -> Option<Site> {
|
||||
return get_site(&self.sites, domain);
|
||||
}
|
||||
}
|
||||
|
||||
impl SiteServer {
|
||||
pub fn start_http(self) {
|
||||
thread::spawn(move || {
|
||||
self.run_http();
|
||||
});
|
||||
}
|
||||
|
||||
pub fn run_http(self) {
|
||||
let listener: TcpListener = match TcpListener::bind(&self.host) {
|
||||
Ok(i) => i,
|
||||
Err(_) => {
|
||||
info!("Http server listener bind error");
|
||||
return;
|
||||
}
|
||||
};
|
||||
let pool = ThreadPool::new(10);
|
||||
|
||||
info!("HTTP server runned on {}", &self.host);
|
||||
|
||||
for stream in listener.incoming() {
|
||||
let local_self = self.clone();
|
||||
|
||||
pool.execute(move || {
|
||||
let mut stream = match stream {
|
||||
Ok(i) => i,
|
||||
Err(e) => {
|
||||
warn!("{}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
match stream.set_write_timeout(Some(Duration::from_secs(10))) {
|
||||
Ok(i) => i,
|
||||
Err(_) => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
match stream.set_read_timeout(Some(Duration::from_secs(10))) {
|
||||
Ok(i) => i,
|
||||
Err(_) => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let addr = stream.peer_addr();
|
||||
|
||||
match local_self.accept_http(
|
||||
&mut stream,
|
||||
match addr {
|
||||
Ok(v) => v,
|
||||
Err(_) => {
|
||||
return;
|
||||
}
|
||||
},
|
||||
) {
|
||||
Ok(v) => {
|
||||
let (addr, method, host, page) = v;
|
||||
info!("{} > {} http://{}{}", addr, method, host, page);
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_ssl(self) {
|
||||
let listener: TcpListener = match TcpListener::bind(&self.host) {
|
||||
Ok(i) => i,
|
||||
Err(_) => {
|
||||
info!("Ssl server listener bind error");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut cert = match SslAcceptor::mozilla_intermediate(SslMethod::tls()) {
|
||||
Ok(v) => v,
|
||||
Err(_) => {
|
||||
info!("Ssl acceptor create error");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let sites = self.clone().sites.clone();
|
||||
|
||||
cert.set_servername_callback(Box::new(
|
||||
move |_ssl: &mut SslRef, _alert: &mut SslAlert| -> Result<(), SniError> {
|
||||
debug!("hangs");
|
||||
let servname = match _ssl.servername(NameType::HOST_NAME) {
|
||||
Some(i) => i,
|
||||
None => return Err(SniError::NOACK),
|
||||
};
|
||||
let cert = match get_site(&sites, servname) {
|
||||
Some(i) => i,
|
||||
None => return Err(SniError::NOACK),
|
||||
};
|
||||
match _ssl.set_ssl_context(
|
||||
match match cert.ssl {
|
||||
Some(i) => i,
|
||||
None => return Err(SniError::NOACK),
|
||||
}
|
||||
.get_ctx()
|
||||
{
|
||||
Some(k) => k,
|
||||
None => return Err(SniError::NOACK),
|
||||
},
|
||||
) {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(SniError::NOACK),
|
||||
};
|
||||
return Ok(());
|
||||
},
|
||||
));
|
||||
|
||||
let cert = cert.build();
|
||||
|
||||
let pool = ThreadPool::new(10);
|
||||
|
||||
info!("HTTPS server runned on {}", &self.host);
|
||||
|
||||
for stream in listener.incoming() {
|
||||
let local_self = self.clone();
|
||||
let local_cert = cert.clone();
|
||||
|
||||
pool.execute(move || {
|
||||
let stream = match stream {
|
||||
Ok(i) => {
|
||||
debug!("norm esy");
|
||||
i
|
||||
}
|
||||
Err(_) => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
match stream.set_write_timeout(Some(Duration::from_secs(10))) {
|
||||
Ok(i) => i,
|
||||
Err(_) => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
match stream.set_read_timeout(Some(Duration::from_secs(10))) {
|
||||
Ok(i) => i,
|
||||
Err(_) => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let addr = stream.peer_addr();
|
||||
|
||||
let mut stream = match local_cert.accept(stream) {
|
||||
Ok(st) => {
|
||||
debug!("ssl esy");
|
||||
st
|
||||
}
|
||||
Err(_) => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
match local_self.accept_ssl(
|
||||
&mut stream,
|
||||
match addr {
|
||||
Ok(v) => v,
|
||||
Err(_) => {
|
||||
return;
|
||||
}
|
||||
},
|
||||
) {
|
||||
Ok(v) => {
|
||||
let (addr, method, host, page) = v;
|
||||
info!("{} > {} https://{}{}", addr, method, host, page);
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn accept_http(
|
||||
self,
|
||||
stream: &mut TcpStream,
|
||||
peer_addr: std::net::SocketAddr,
|
||||
) -> Result<(String, String, String, String), ()> {
|
||||
let octets = match peer_addr.ip() {
|
||||
IpAddr::V4(ip) => ip.octets(),
|
||||
_ => [127, 0, 0, 1],
|
||||
};
|
||||
|
||||
let dot: String = String::from(".");
|
||||
let ip_str = String::from(
|
||||
octets[0].to_string()
|
||||
+ &dot
|
||||
+ &octets[1].to_string()
|
||||
+ &dot
|
||||
+ &octets[2].to_string()
|
||||
+ &dot
|
||||
+ &octets[3].to_string(),
|
||||
);
|
||||
|
||||
println!("{}", &ip_str);
|
||||
|
||||
let addition: String = ip_str.clone() + ":" + peer_addr.port().to_string().as_str() + "\n";
|
||||
|
||||
let mut reqst_data: Vec<u8> = vec![0; 4096];
|
||||
|
||||
match stream.read(&mut reqst_data) {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(()),
|
||||
};
|
||||
|
||||
let reqst = match String::from_utf8(reqst_data) {
|
||||
Ok(v) => v,
|
||||
Err(_) => {
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
let reqst = reqst.trim_matches(char::from(0));
|
||||
|
||||
let (head, body) = match split_once(&reqst, "\r\n\r\n") {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(()),
|
||||
};
|
||||
|
||||
let mut head_lines = head.split("\r\n");
|
||||
|
||||
let status = match head_lines.next() {
|
||||
Some(i) => i,
|
||||
None => return Err(()),
|
||||
};
|
||||
let status: Vec<&str> = status.split(" ").collect();
|
||||
|
||||
let mut host: &str = "honk";
|
||||
let mut content_length: usize = 0;
|
||||
|
||||
for l in head_lines {
|
||||
let (key, value) = match split_once(&l, ": ") {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(()),
|
||||
};
|
||||
let key = key.to_lowercase().replace("-", "_");
|
||||
|
||||
if key == "host" {
|
||||
host = &value;
|
||||
}
|
||||
if key == "content_length" {
|
||||
content_length = match value.parse() {
|
||||
Ok(i) => i,
|
||||
Err(_) => {
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let site = self.get_site(host);
|
||||
|
||||
if site.is_none() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let site = match site {
|
||||
Some(i) => i,
|
||||
None => return Err(()),
|
||||
};
|
||||
let mut site_stream = match site.connect() {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(()),
|
||||
};
|
||||
|
||||
match site_stream.write((addition + reqst).as_bytes()) {
|
||||
Ok(i) => i,
|
||||
Err(_) => {
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
|
||||
let body_len = body.len();
|
||||
if body_len < content_length {
|
||||
let mut body_data: Vec<u8> = vec![0; content_length - body_len];
|
||||
match stream.read_exact(&mut body_data) {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(()),
|
||||
};
|
||||
match site_stream.write_all(&body_data) {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(()),
|
||||
};
|
||||
}
|
||||
|
||||
loop {
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
match site_stream.read_to_end(&mut buf) {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(()),
|
||||
};
|
||||
if buf.is_empty() {
|
||||
break;
|
||||
}
|
||||
match stream.write_all(&buf) {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(()),
|
||||
};
|
||||
}
|
||||
|
||||
let method = status[0];
|
||||
let page = status[1];
|
||||
|
||||
match site_stream.shutdown(Shutdown::Both) {
|
||||
Ok(i) => i,
|
||||
Err(_) => {
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
|
||||
Ok((
|
||||
ip_str.clone(),
|
||||
method.to_string().clone(),
|
||||
host.to_string().clone(),
|
||||
page.to_string().clone(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn accept_ssl(
|
||||
self,
|
||||
stream: &mut SslStream<TcpStream>,
|
||||
peer_addr: std::net::SocketAddr,
|
||||
) -> Result<(String, String, String, String), ()> {
|
||||
let octets = match peer_addr.ip() {
|
||||
IpAddr::V4(ip) => ip.octets(),
|
||||
_ => [127, 0, 0, 1],
|
||||
};
|
||||
|
||||
let dot: String = String::from(".");
|
||||
let ip_str = String::from(
|
||||
octets[0].to_string()
|
||||
+ &dot
|
||||
+ &octets[1].to_string()
|
||||
+ &dot
|
||||
+ &octets[2].to_string()
|
||||
+ &dot
|
||||
+ &octets[3].to_string(),
|
||||
);
|
||||
|
||||
let addition: String = ip_str.clone() + ":" + peer_addr.port().to_string().as_str() + "\n";
|
||||
|
||||
let mut reqst_data: Vec<u8> = vec![0; 4096];
|
||||
|
||||
match stream.read(&mut reqst_data) {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(()),
|
||||
};
|
||||
|
||||
let reqst = match String::from_utf8(reqst_data) {
|
||||
Ok(v) => v,
|
||||
Err(_) => {
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
let reqst = reqst.trim_matches(char::from(0));
|
||||
|
||||
let (head, body) = match split_once(&reqst, "\r\n\r\n") {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(()),
|
||||
};
|
||||
|
||||
let mut head_lines = head.split("\r\n");
|
||||
|
||||
let status = match head_lines.next() {
|
||||
Some(i) => i,
|
||||
None => return Err(()),
|
||||
};
|
||||
let status: Vec<&str> = status.split(" ").collect();
|
||||
|
||||
let mut host: &str = "honk";
|
||||
let mut content_length: usize = 0;
|
||||
|
||||
for l in head_lines {
|
||||
let (key, value) = match split_once(&l, ": ") {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(()),
|
||||
};
|
||||
let key = key.to_lowercase().replace("-", "_");
|
||||
|
||||
if key == "host" {
|
||||
host = &value;
|
||||
}
|
||||
if key == "content_length" {
|
||||
content_length = match value.parse() {
|
||||
Ok(i) => i,
|
||||
Err(_) => {
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let site = self.get_site(host);
|
||||
|
||||
if site.is_none() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let site = match site {
|
||||
Some(i) => i,
|
||||
None => return Err(()),
|
||||
};
|
||||
let mut site_stream = match site.connect() {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(()),
|
||||
};
|
||||
|
||||
match site_stream.write((addition + reqst).as_bytes()) {
|
||||
Ok(i) => i,
|
||||
Err(_) => {
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
|
||||
let body_len = body.len();
|
||||
if body_len < content_length {
|
||||
let mut body_data: Vec<u8> = vec![0; content_length - body_len];
|
||||
match stream.read_exact(&mut body_data) {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(()),
|
||||
};
|
||||
match site_stream.write_all(&body_data) {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(()),
|
||||
};
|
||||
}
|
||||
|
||||
loop {
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
match site_stream.read_to_end(&mut buf) {
|
||||
Ok(i) => i,
|
||||
Err(_) => return Err(()),
|
||||
};
|
||||
if buf.is_empty() {
|
||||
break;
|
||||
}
|
||||
match stream.write_all(&buf) {
|
||||
Ok(i) => i,
|
||||
Err(e) => {
|
||||
info!("{}", e);
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let method = status[0];
|
||||
let page = status[1];
|
||||
|
||||
match site_stream.shutdown(Shutdown::Both) {
|
||||
Ok(i) => i,
|
||||
Err(_) => {
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
|
||||
Ok((
|
||||
ip_str.clone(),
|
||||
method.to_string().clone(),
|
||||
host.to_string().clone(),
|
||||
page.to_string().clone(),
|
||||
))
|
||||
}
|
||||
}
|
71
src/lib.rs
Normal file
71
src/lib.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use std::{
|
||||
sync::{mpsc, Arc, Mutex},
|
||||
thread,
|
||||
};
|
||||
|
||||
type Job = Box<dyn FnOnce() + Send + 'static>;
|
||||
|
||||
pub struct ThreadPool {
|
||||
workers: Vec<Worker>,
|
||||
sender: mpsc::Sender<Job>,
|
||||
}
|
||||
|
||||
pub enum PoolCreationError {
|
||||
InvalidSize,
|
||||
}
|
||||
|
||||
impl ThreadPool {
|
||||
pub fn new(size: usize) -> ThreadPool {
|
||||
assert!(size > 0);
|
||||
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
let receiver = Arc::new(Mutex::new(receiver));
|
||||
|
||||
let mut workers = Vec::with_capacity(size);
|
||||
|
||||
for _ in 0..size {
|
||||
workers.push(Worker::new(Arc::clone(&receiver)));
|
||||
}
|
||||
|
||||
ThreadPool { workers, sender }
|
||||
}
|
||||
|
||||
pub fn build(size: usize) -> Result<ThreadPool, PoolCreationError> {
|
||||
if size <= 0 {
|
||||
Err(PoolCreationError::InvalidSize)
|
||||
} else {
|
||||
Ok(Self::new(size))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn join(self) {
|
||||
for ele in self.workers.into_iter() {
|
||||
ele.thread.join().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn execute<F>(&self, f: F)
|
||||
where
|
||||
F: FnOnce() + Send + 'static,
|
||||
{
|
||||
let job = Box::new(f);
|
||||
|
||||
self.sender.send(job).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
struct Worker {
|
||||
thread: thread::JoinHandle<()>,
|
||||
}
|
||||
|
||||
impl Worker {
|
||||
fn new(receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
|
||||
let thread = thread::spawn(move || {
|
||||
while let Ok(job) = receiver.lock().unwrap().recv() {
|
||||
job();
|
||||
}
|
||||
});
|
||||
|
||||
Worker { thread }
|
||||
}
|
||||
}
|
116
src/main.rs
Normal file
116
src/main.rs
Normal file
@ -0,0 +1,116 @@
|
||||
pub mod http_server;
|
||||
extern crate yaml_rust;
|
||||
|
||||
use http_server::*;
|
||||
use std::sync::Arc;
|
||||
use std::{fs, thread};
|
||||
use yaml_rust::YamlLoader;
|
||||
|
||||
use log::LevelFilter;
|
||||
use log4rs::append::console::ConsoleAppender;
|
||||
use log4rs::append::file::FileAppender;
|
||||
use log4rs::config::{Appender, Config, Root};
|
||||
use log4rs::encode::pattern::PatternEncoder;
|
||||
|
||||
struct AppConfig {
|
||||
sites: Vec<Site>,
|
||||
http_host: String,
|
||||
https_host: String,
|
||||
}
|
||||
|
||||
impl AppConfig {
|
||||
fn parse(filename: &str) -> Option<AppConfig> {
|
||||
let Ok(file_content) = fs::read_to_string(filename) else {
|
||||
return None;
|
||||
};
|
||||
let Ok(docs) = YamlLoader::load_from_str(file_content.as_str()) else {
|
||||
return None;
|
||||
};
|
||||
let doc = docs.get(0)?;
|
||||
|
||||
let http_host = doc["http_host"].as_str()?.to_string();
|
||||
let https_host = doc["https_host"].as_str()?.to_string();
|
||||
|
||||
let mut sites: Vec<Site> = Vec::new();
|
||||
|
||||
let sites_yaml = doc["sites"].as_vec()?;
|
||||
|
||||
for s in sites_yaml {
|
||||
let mut cert: Option<SslCert> = None;
|
||||
|
||||
if !s["ssl_cert"].is_badvalue() && !s["ssl_cert"].is_null() {
|
||||
cert = Some(
|
||||
SslCert::new(
|
||||
s["ssl_cert"].as_str().unwrap(),
|
||||
s["ssl_key"].as_str().unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
let site = Site {
|
||||
domain: s["domain"].as_str().unwrap().to_string(),
|
||||
host: s["host"].as_str().unwrap().to_string(),
|
||||
ssl: cert,
|
||||
};
|
||||
|
||||
sites.push(site);
|
||||
}
|
||||
|
||||
Some(AppConfig {
|
||||
sites,
|
||||
http_host,
|
||||
https_host,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
log4rs::init_config(
|
||||
Config::builder()
|
||||
.appender(
|
||||
Appender::builder().build(
|
||||
"logfile",
|
||||
Box::new(
|
||||
FileAppender::builder()
|
||||
.encoder(Box::new(PatternEncoder::new(
|
||||
"{d(%Y-%m-%d %H:%M:%S)} | {l} - {m}\n",
|
||||
)))
|
||||
.build("latest.log")
|
||||
.unwrap(),
|
||||
),
|
||||
),
|
||||
)
|
||||
.appender(
|
||||
Appender::builder().build(
|
||||
"stdout",
|
||||
Box::new(
|
||||
ConsoleAppender::builder()
|
||||
.encoder(Box::new(PatternEncoder::new(
|
||||
"{d(%Y-%m-%d %H:%M:%S)} | {l} - {m}\n",
|
||||
)))
|
||||
.build(),
|
||||
),
|
||||
),
|
||||
)
|
||||
.build(
|
||||
Root::builder()
|
||||
.appender("logfile")
|
||||
.appender("stdout")
|
||||
.build(LevelFilter::Debug),
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let config = AppConfig::parse("conf.yml").unwrap();
|
||||
let sites_arc = Arc::new(config.sites);
|
||||
|
||||
let sites = sites_arc.clone();
|
||||
thread::spawn(move || {
|
||||
SiteServer::new(config.http_host.to_string(), sites).run_http();
|
||||
});
|
||||
|
||||
let sites = sites_arc.clone();
|
||||
SiteServer::new(config.https_host.to_string(), sites).run_ssl();
|
||||
}
|
Loading…
Reference in New Issue
Block a user