rewrite
This commit is contained in:
parent
2f1a435dda
commit
b3e51654f6
19
Cargo.toml
19
Cargo.toml
@ -1,12 +1,17 @@
|
||||
[package]
|
||||
name = "http_rrs"
|
||||
name = "flowgate"
|
||||
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"
|
||||
openssl = { version = "0.10.66", optional = true }
|
||||
rustls = { version = "0.23.13", optional = true }
|
||||
serde_yml = "0.0.12"
|
||||
log = "0.4.22"
|
||||
pretty_env_logger = "0.5.0"
|
||||
threadpool = "1.8.1"
|
||||
|
||||
[features]
|
||||
default = ["use-openssl"]
|
||||
use-openssl = ["dep:openssl"]
|
||||
use-rustls = ["dep:rustls"]
|
20
README.md
20
README.md
@ -1,5 +1,13 @@
|
||||
# HttpRRS
|
||||
HTTP request redirection system
|
||||
# Flowgate
|
||||
HTTP requests redirection system
|
||||
|
||||
Features:
|
||||
- Request redirection
|
||||
- SSL/TLS support
|
||||
- Rustls support (not yet)
|
||||
- Keep-alive streams (not yet)
|
||||
|
||||
## Config
|
||||
|
||||
Default `conf.yml`:
|
||||
```yml
|
||||
@ -19,8 +27,6 @@ sites:
|
||||
host: localhost:8080
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
This works as a proxy that redirects based on the Host header
|
||||
|
||||

|
||||
Rust features:
|
||||
- use-openssl
|
||||
- use-rustls ([rustls](https://github.com/rustls/rustls) - openssl alternative)
|
7
src/flowgate.rs
Normal file
7
src/flowgate.rs
Normal file
@ -0,0 +1,7 @@
|
||||
pub mod config;
|
||||
pub mod server;
|
||||
pub mod ssl_cert;
|
||||
|
||||
pub use config::*;
|
||||
pub use server::*;
|
||||
pub use ssl_cert::*;
|
81
src/flowgate/config.rs
Normal file
81
src/flowgate/config.rs
Normal file
@ -0,0 +1,81 @@
|
||||
use std::{fs, net::TcpStream, sync::Arc};
|
||||
|
||||
use serde_yml::Value;
|
||||
|
||||
use super::SslCert;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SiteConfig {
|
||||
pub domain: String,
|
||||
pub host: String,
|
||||
pub ssl: Option<SslCert>,
|
||||
}
|
||||
|
||||
impl SiteConfig {
|
||||
pub fn connect(self) -> Option<TcpStream> {
|
||||
TcpStream::connect(self.host).ok()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Config {
|
||||
pub sites: Arc<Vec<SiteConfig>>,
|
||||
pub http_host: String,
|
||||
pub https_host: String,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn parse(filename: &str) -> Option<Config> {
|
||||
let Ok(file_content) = fs::read_to_string(filename) else {
|
||||
return None;
|
||||
};
|
||||
let Ok(docs) = serde_yml::from_str::<Value>(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<SiteConfig> = Vec::new();
|
||||
|
||||
let sites_yaml = doc["sites"].as_sequence()?;
|
||||
|
||||
for s in sites_yaml {
|
||||
let mut cert: Option<SslCert> = None;
|
||||
let s = s.as_mapping()?;
|
||||
|
||||
if s.contains_key("ssl_cert") && !s.get("ssl_cert")?.is_null() {
|
||||
cert = Some(
|
||||
SslCert::new(
|
||||
s.get("ssl_cert")?.as_str()?,
|
||||
s.get("ssl_key")?.as_str()?,
|
||||
)?,
|
||||
);
|
||||
}
|
||||
|
||||
let site = SiteConfig {
|
||||
domain: s.get("domain")?.as_str()?.to_string(),
|
||||
host: s.get("host")?.as_str()?.to_string(),
|
||||
ssl: cert,
|
||||
};
|
||||
|
||||
sites.push(site);
|
||||
}
|
||||
|
||||
Some(Config {
|
||||
sites: Arc::new(sites),
|
||||
http_host,
|
||||
https_host,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_site(&self, domain: &str) -> Option<&SiteConfig> {
|
||||
for i in self.sites.as_ref() {
|
||||
if i.domain == domain {
|
||||
return Some(i);
|
||||
}
|
||||
}
|
||||
return None;
|
||||
}
|
||||
}
|
197
src/flowgate/server.rs
Normal file
197
src/flowgate/server.rs
Normal file
@ -0,0 +1,197 @@
|
||||
use std::{io::{Read, Write}, net::{Shutdown, SocketAddr, TcpListener}, sync::Arc, thread, time::Duration};
|
||||
|
||||
use log::info;
|
||||
use openssl::ssl::{NameType, SniError, SslAcceptor, SslAlert, SslMethod, SslRef};
|
||||
use threadpool::ThreadPool;
|
||||
|
||||
use crate::Config;
|
||||
|
||||
pub struct FlowgateServer {
|
||||
config: Arc<Config>,
|
||||
}
|
||||
|
||||
impl FlowgateServer {
|
||||
pub fn new(config: Config) -> Self {
|
||||
FlowgateServer { config: Arc::new(config) }
|
||||
}
|
||||
|
||||
pub fn start(self) {
|
||||
thread::spawn({
|
||||
let config = Arc::clone(&self.config);
|
||||
|
||||
move || {
|
||||
Self::run_http(config)
|
||||
}
|
||||
});
|
||||
|
||||
thread::spawn({
|
||||
let config = Arc::clone(&self.config);
|
||||
|
||||
move || {
|
||||
Self::run_https(config)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub fn run_http(
|
||||
config: Arc<Config>
|
||||
) -> Option<()> {
|
||||
let listener = TcpListener::bind(&config.http_host).ok()?;
|
||||
|
||||
let pool = ThreadPool::new(10);
|
||||
|
||||
info!("HTTP server runned on {}", &config.http_host);
|
||||
|
||||
for stream in listener.incoming() {
|
||||
pool.execute({
|
||||
let config = config.clone();
|
||||
|
||||
move || {
|
||||
let Ok(mut stream) = stream else { return };
|
||||
|
||||
let Ok(_) = stream.set_write_timeout(Some(Duration::from_secs(10))) else { return };
|
||||
let Ok(_) = stream.set_read_timeout(Some(Duration::from_secs(10))) else { return };
|
||||
|
||||
let Ok(addr) = stream.peer_addr() else { return };
|
||||
|
||||
Self::accept_stream(
|
||||
config,
|
||||
&mut stream,
|
||||
addr,
|
||||
true
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub fn run_https(
|
||||
config: Arc<Config>
|
||||
) -> Option<()> {
|
||||
let listener = TcpListener::bind(&config.https_host).ok()?;
|
||||
|
||||
let mut cert = SslAcceptor::mozilla_intermediate(SslMethod::tls()).ok()?;
|
||||
|
||||
cert.set_servername_callback(Box::new({
|
||||
let config = config.clone();
|
||||
|
||||
move |ssl: &mut SslRef, _: &mut SslAlert| -> Result<(), SniError> {
|
||||
let servname = ssl.servername(NameType::HOST_NAME).ok_or(SniError::NOACK)?;
|
||||
let cert = config.get_site(servname).ok_or(SniError::NOACK)?;
|
||||
ssl.set_ssl_context(&cert.ssl.as_ref().ok_or(SniError::NOACK)?.get_context()).ok().ok_or(SniError::NOACK)
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
let cert = cert.build();
|
||||
|
||||
let pool = ThreadPool::new(10);
|
||||
|
||||
info!("HTTPS server runned on {}", &config.https_host);
|
||||
|
||||
for stream in listener.incoming() {
|
||||
pool.execute({
|
||||
let config = config.clone();
|
||||
let cert = cert.clone();
|
||||
|
||||
move || {
|
||||
let Ok(stream) = stream else { return };
|
||||
|
||||
let Ok(_) = stream.set_write_timeout(Some(Duration::from_secs(10))) else { return };
|
||||
let Ok(_) = stream.set_read_timeout(Some(Duration::from_secs(10))) else { return };
|
||||
|
||||
let Ok(addr) = stream.peer_addr() else { return };
|
||||
|
||||
let Ok(mut stream) = cert.accept(stream) else { return };
|
||||
|
||||
Self::accept_stream(
|
||||
config,
|
||||
&mut stream,
|
||||
addr,
|
||||
true
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
pub fn accept_stream(
|
||||
config: Arc<Config>,
|
||||
stream: &mut (impl Read + Write),
|
||||
addr: SocketAddr,
|
||||
https: bool
|
||||
) -> Option<()> {
|
||||
let mut reqst_data: Vec<u8> = vec![0; 4096];
|
||||
|
||||
stream.read(&mut reqst_data).ok()?;
|
||||
|
||||
let reqst = String::from_utf8(reqst_data).ok()?;
|
||||
let reqst = reqst.trim_matches(char::from(0));
|
||||
|
||||
let (head, body) = reqst.split_once("\r\n\r\n")?;
|
||||
|
||||
let mut head_lines = head.split("\r\n");
|
||||
|
||||
let status = head_lines.next()?;
|
||||
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) = l.split_once(": ")?;
|
||||
let key = key.to_lowercase().replace("-", "_");
|
||||
|
||||
if key == "host" {
|
||||
host = &value;
|
||||
}
|
||||
if key == "content_length" {
|
||||
content_length = value.parse().ok()?;
|
||||
}
|
||||
}
|
||||
|
||||
let site = config.get_site(host);
|
||||
|
||||
if site.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let site = site?.clone();
|
||||
let mut site_stream = site.connect()?;
|
||||
|
||||
site_stream.write((addr.to_string() + "\n" + reqst).as_bytes()).ok()?;
|
||||
|
||||
let body_len = body.len();
|
||||
if body_len < content_length {
|
||||
let mut body_data: Vec<u8> = vec![0; content_length - body_len];
|
||||
stream.read_exact(&mut body_data).ok()?;
|
||||
site_stream.write_all(&body_data).ok()?;
|
||||
}
|
||||
|
||||
loop {
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
site_stream.read_to_end(&mut buf).ok()?;
|
||||
if buf.is_empty() {
|
||||
break;
|
||||
}
|
||||
stream.write_all(&buf).ok()?;
|
||||
}
|
||||
|
||||
let method = status[0];
|
||||
let page = status[1];
|
||||
|
||||
site_stream.shutdown(Shutdown::Both).ok()?;
|
||||
|
||||
if https {
|
||||
info!("{} > {} https://{}{}", addr.to_string(), method, host, page);
|
||||
} else {
|
||||
info!("{} > {} http://{}{}", addr.to_string(), method, host, page);
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
}
|
43
src/flowgate/ssl_cert.rs
Normal file
43
src/flowgate/ssl_cert.rs
Normal file
@ -0,0 +1,43 @@
|
||||
use openssl::ssl::{SslContext, SslFiletype, SslMethod};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SslCert {
|
||||
context: Option<SslContext>,
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
||||
impl SslCert {
|
||||
pub fn new(cert_file: &str, key_file: &str) -> Option<SslCert> {
|
||||
Some(SslCert {
|
||||
context: match generate_ctx(cert_file, key_file) {
|
||||
Some(i) => Some(i),
|
||||
None => {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_context(&self) -> SslContext {
|
||||
self.context.as_ref().unwrap().clone()
|
||||
}
|
||||
}
|
@ -1,590 +0,0 @@
|
||||
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
71
src/lib.rs
@ -1,71 +0,0 @@
|
||||
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
116
src/main.rs
@ -1,116 +1,12 @@
|
||||
pub mod http_server;
|
||||
extern crate yaml_rust;
|
||||
mod flowgate;
|
||||
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
use flowgate::{Config, FlowgateServer};
|
||||
|
||||
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();
|
||||
pretty_env_logger::init();
|
||||
|
||||
let config = AppConfig::parse("conf.yml").unwrap();
|
||||
let sites_arc = Arc::new(config.sites);
|
||||
let config = Config::parse("conf.yml").unwrap();
|
||||
let server = FlowgateServer::new(config);
|
||||
|
||||
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();
|
||||
server.start();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user