init here

This commit is contained in:
2025-11-26 21:32:41 +03:00
commit 33c97acade
91 changed files with 9155 additions and 0 deletions

188
wrapper/src/main.rs Normal file
View File

@@ -0,0 +1,188 @@
use core::net::SocketAddr;
use core::time::Duration;
use std::path::PathBuf;
use std::process::Stdio;
use std::{env, error};
use axum::body::Body;
use axum::http::{HeaderValue, Request, header};
use axum::middleware::Next;
use axum::response::Response;
use axum::{Router, middleware};
use hyper_util::rt::{TokioExecutor, TokioIo};
use hyper_util::server::conn::auto;
use hyper_util::service::TowerToHyperService;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};
use tokio::process::Command;
use tokio::sync::OnceCell;
use tokio::sync::mpsc::{self, Receiver};
use tokio::task::JoinSet;
use tower_http::services::ServeDir;
const MAX_CONCURRENT: usize = 100;
const MAX_BUFFERED: usize = 500;
const DEFAULT_BIN_PATH: &str = "../build/target.exe";
const DEFAULT_STATIC_PATH: &str = "../frontend";
const TIMEOUT_SECS: u64 = 10;
static BIN_PATH: OnceCell<PathBuf> = OnceCell::const_new();
static STATIC_PATH: OnceCell<PathBuf> = OnceCell::const_new();
#[derive(Debug)]
#[allow(dead_code)]
struct NetworkConnection(pub TcpStream, pub SocketAddr);
#[tokio::main]
async fn main() -> Result<(), Box<dyn error::Error>> {
if let Ok(path) = env::var("BIN_PATH")
&& path != ""
{
BIN_PATH.set(PathBuf::from(path)).unwrap();
} else {
BIN_PATH.set(PathBuf::from(DEFAULT_BIN_PATH)).unwrap();
}
if let Ok(path) = env::var("STATIC_PATH")
&& path != ""
{
STATIC_PATH.set(PathBuf::from(path)).unwrap();
} else {
STATIC_PATH.set(PathBuf::from(DEFAULT_STATIC_PATH)).unwrap();
}
Command::new(BIN_PATH.get().unwrap())
.arg("-роанапур=истина")
.spawn()?
.wait()
.await?;
let listener = TcpListener::bind("0.0.0.0:1337").await?;
let (tx, rx) = mpsc::channel::<NetworkConnection>(MAX_BUFFERED);
tokio::spawn(process_connections(rx));
loop {
let connection = listener.accept().await?;
if tx
.try_send(NetworkConnection(connection.0, connection.1))
.is_err()
{
println!(
"Connection queue full, dropping connection from {}",
connection.1
);
}
}
}
async fn process_connections(mut rx: Receiver<NetworkConnection>) {
let mut join_set = JoinSet::new();
loop {
let available = MAX_CONCURRENT - join_set.len();
tokio::select! {
connection = rx.recv(), if available > 0 => {
if let Some(connection) = connection {
join_set.spawn(handle_connection_or_timeout(connection));
}
},
_ = join_set.join_next(), if join_set.len() > 0 => {}
}
}
}
async fn handle_connection_or_timeout(
connection: NetworkConnection,
) -> Result<(), Box<dyn error::Error + Send + Sync>> {
tokio::select! {
result = handle_connection(connection) => {
result
},
_ = tokio::time::sleep(Duration::from_secs(TIMEOUT_SECS)) => {
Err("Connection timed out".into())
}
}
}
async fn handle_connection(
mut connection: NetworkConnection,
) -> Result<(), Box<dyn error::Error + Send + Sync>> {
let is_frontend = check_is_frontend_request(&mut connection).await?;
if is_frontend {
return serve_frontend_file(connection).await;
}
let mut child = Command::new(BIN_PATH.get().unwrap())
.arg("-подшефный=истина")
.stdin(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
let mut stderr = child.stderr.take().unwrap();
let mut stdin = child.stdin.take().unwrap();
loop {
let mut tcp_buf = [0u8; 4096];
let mut stderr_buf = [0u8; 4096];
tokio::select! {
result = connection.0.read(&mut tcp_buf) => match result {
Ok(how_many) => stdin.write_all(&tcp_buf[0..how_many]).await?,
Err(e) => break Err(e.into()),
},
result = stderr.read(&mut stderr_buf) => match result {
Ok(0) => break Ok(()),
Ok(how_many) => connection.0.write_all(&stderr_buf[0..how_many]).await?,
Err(e) => break Err(e.into()),
},
}
}
}
async fn check_is_frontend_request(
connection: &mut NetworkConnection,
) -> Result<bool, Box<dyn error::Error + Send + Sync>> {
let mut peek_buf = [0u8; 8];
let bytes_read = connection.0.peek(&mut peek_buf).await?;
if bytes_read == 0 {
return Err("Connection closed before sending data".into());
}
let request_str = String::from_utf8_lossy(&peek_buf);
return Ok(bytes_read == peek_buf.len()
&& request_str.starts_with("GET")
&& !request_str.ends_with("api"));
}
async fn serve_frontend_file(
connection: NetworkConnection,
) -> Result<(), Box<dyn error::Error + Send + Sync>> {
let stream = connection.0;
let app = Router::new()
.fallback_service(
ServeDir::new(STATIC_PATH.get().unwrap()).append_index_html_on_directories(true),
)
.layer(middleware::from_fn(add_connection_close));
let tower_svc = app.into_service();
let hyper_svc = TowerToHyperService::new(tower_svc);
auto::Builder::new(TokioExecutor::new())
.serve_connection(TokioIo::new(stream), hyper_svc)
.await?;
Ok(())
}
async fn add_connection_close(req: Request<Body>, next: Next) -> Response {
let mut res = next.run(req).await;
res.headers_mut()
.insert(header::CONNECTION, HeaderValue::from_static("close"));
res
}