fix: ⚡ browser freezing because of blocking channel
This commit is contained in:
parent
8391ef31ba
commit
1e27b9280e
9 changed files with 172 additions and 386 deletions
|
@ -4,13 +4,8 @@ version = "0.1.0"
|
|||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
axum = { version = "0.8.3", features = ["json"] }
|
||||
axum-macros = "0.5.0"
|
||||
tokio = "1.42.1"
|
||||
tower-http = { version = "0.6.2", features = ["cors", "trace"] }
|
||||
tracing-subscriber = "0.3.19"
|
||||
tracing = "0.1.41"
|
||||
webrtc = "0.12.0"
|
||||
tokio = { version = "1.42.1", default-features = false, features = ["macros", "rt-multi-thread"] }
|
||||
fastwebsockets = "0.10.0"
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
|
|
|
@ -1,75 +1,2 @@
|
|||
use std::sync::LazyLock;
|
||||
|
||||
use utils::naive_toml_parser;
|
||||
|
||||
mod middleware;
|
||||
pub mod signal;
|
||||
pub mod utils;
|
||||
|
||||
const SERVER_CONFIG_FILE_LOCATION: &str = "./configs/server_config.toml";
|
||||
const DATABASE_CONFIG_FILE_LOCATION: &str = "./configs/database_config.toml";
|
||||
|
||||
pub static SERVER_CONFIG: LazyLock<ServerConfig> = LazyLock::new(ServerConfig::default);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DatabaseConfig {
|
||||
pub address: String,
|
||||
pub username: String,
|
||||
pub password: String,
|
||||
pub database: String,
|
||||
pub backend: String,
|
||||
pub connection_pool_size: u32,
|
||||
}
|
||||
impl Default for DatabaseConfig {
|
||||
fn default() -> Self {
|
||||
let (header, mut database_configs) = naive_toml_parser(DATABASE_CONFIG_FILE_LOCATION);
|
||||
|
||||
if header == "[database_config]" {
|
||||
Self {
|
||||
address: database_configs.pop_front().unwrap().parse().unwrap(),
|
||||
username: database_configs.pop_front().unwrap().parse().unwrap(),
|
||||
password: database_configs.pop_front().unwrap().parse().unwrap(),
|
||||
database: database_configs.pop_front().unwrap().parse().unwrap(),
|
||||
backend: database_configs.pop_front().unwrap().parse().unwrap(),
|
||||
connection_pool_size: database_configs.pop_front().unwrap().parse().unwrap(),
|
||||
}
|
||||
} else {
|
||||
panic!("Database Config File Must Include [database_config] at the First Line")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ServerConfig {
|
||||
pub address: String,
|
||||
pub otp_time_limit: usize,
|
||||
pub login_token_expiration_time_limit: usize,
|
||||
pub login_token_refresh_time_limit: usize,
|
||||
pub concurrency_limit: usize,
|
||||
}
|
||||
|
||||
impl Default for ServerConfig {
|
||||
fn default() -> Self {
|
||||
let (header, mut server_configs) = naive_toml_parser(SERVER_CONFIG_FILE_LOCATION);
|
||||
let value_or_max = |value: String| value.parse().map_or(usize::MAX, |value| value);
|
||||
let value_or_semaphore_max = |value: String| {
|
||||
value
|
||||
.parse()
|
||||
.map_or(tokio::sync::Semaphore::MAX_PERMITS, |value| value)
|
||||
};
|
||||
|
||||
if header == "[server_config]" {
|
||||
Self {
|
||||
address: server_configs.pop_front().unwrap().parse().unwrap(),
|
||||
otp_time_limit: value_or_max(server_configs.pop_front().unwrap()),
|
||||
login_token_expiration_time_limit: value_or_max(
|
||||
server_configs.pop_front().unwrap(),
|
||||
),
|
||||
login_token_refresh_time_limit: value_or_max(server_configs.pop_front().unwrap()),
|
||||
concurrency_limit: value_or_semaphore_max(server_configs.pop_front().unwrap()),
|
||||
}
|
||||
} else {
|
||||
panic!("Server Config File Must Include [server_config] at the First Line")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
use rust_communication_server::signal::start_signalling;
|
||||
use tracing::Level;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
println!("Hello, world!");
|
||||
tracing_subscriber::fmt()
|
||||
.with_max_level(Level::TRACE)
|
||||
.init();
|
||||
start_signalling().await;
|
||||
}
|
||||
|
|
|
@ -1,82 +0,0 @@
|
|||
use std::{str::FromStr, sync::Arc};
|
||||
|
||||
use axum::{
|
||||
extract::Request,
|
||||
http::{self, HeaderMap, StatusCode},
|
||||
middleware::Next,
|
||||
response::IntoResponse,
|
||||
};
|
||||
use protocol::{SignalType, User};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct UserAndExpectedSignal {
|
||||
pub user: User,
|
||||
pub expected_signal: SignalType,
|
||||
}
|
||||
|
||||
async fn extract_user_from_authorization_header(headers: &HeaderMap) -> Option<User> {
|
||||
if let Some(authorization_header) = headers.get(http::header::AUTHORIZATION) {
|
||||
dbg!(authorization_header);
|
||||
if let Ok(authorization_header) = authorization_header.to_str() {
|
||||
if let Some((bearer, authorization_token)) = authorization_header.split_once(' ') {
|
||||
println!(
|
||||
"Info: Extraction | Authorization Header | {} || {}",
|
||||
bearer, authorization_token
|
||||
);
|
||||
if bearer.to_lowercase() == "bearer" {
|
||||
let user = User {
|
||||
username: authorization_token.to_string(),
|
||||
};
|
||||
return Some(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub async fn verify_then_get_user(mut request: Request, next: Next) -> impl IntoResponse {
|
||||
let headers = request.headers();
|
||||
println!("Info: Verify | Headers| {:#?}", headers);
|
||||
if let Some(user) = extract_user_from_authorization_header(headers).await {
|
||||
let user = Arc::new(user);
|
||||
request.extensions_mut().insert(user);
|
||||
return next.run(request).await;
|
||||
}
|
||||
|
||||
StatusCode::FORBIDDEN.into_response()
|
||||
}
|
||||
|
||||
pub async fn verify_then_get_user_and_expected_signal(
|
||||
mut request: Request,
|
||||
next: Next,
|
||||
) -> impl IntoResponse {
|
||||
let headers = request.headers();
|
||||
if let Some(user) = extract_user_from_authorization_header(headers).await {
|
||||
if let Ok(expected_signal) = headers.get("EXPECTED_SIGNAL").unwrap().to_str() {
|
||||
match SignalType::from_str(expected_signal) {
|
||||
Ok(expected_signal) => {
|
||||
let user_and_expected_signal = UserAndExpectedSignal {
|
||||
user,
|
||||
expected_signal,
|
||||
};
|
||||
let user_and_expected_signal = Arc::new(user_and_expected_signal);
|
||||
request.extensions_mut().insert(user_and_expected_signal);
|
||||
next.run(request).await
|
||||
}
|
||||
Err(err_val) => {
|
||||
eprintln!(
|
||||
"Error: Verify and Get Expected Signal | Signal Type Conversion | {}",
|
||||
err_val
|
||||
);
|
||||
StatusCode::BAD_REQUEST.into_response()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
StatusCode::BAD_REQUEST.into_response()
|
||||
}
|
||||
} else {
|
||||
StatusCode::FORBIDDEN.into_response()
|
||||
}
|
||||
}
|
|
@ -1,82 +1,25 @@
|
|||
use std::sync::{Arc, LazyLock, RwLock};
|
||||
|
||||
use axum::{
|
||||
Extension, Json, Router,
|
||||
http::StatusCode,
|
||||
response::IntoResponse,
|
||||
routing::{get, post},
|
||||
};
|
||||
use axum_macros::debug_handler;
|
||||
use protocol::{Signal, User, UserAndSignal};
|
||||
use fastwebsockets::{FragmentCollector, OpCode, Role, WebSocket};
|
||||
use tokio::net::TcpListener;
|
||||
use tower_http::{cors::CorsLayer, trace::TraceLayer};
|
||||
|
||||
use crate::middleware::{
|
||||
UserAndExpectedSignal, verify_then_get_user, verify_then_get_user_and_expected_signal,
|
||||
};
|
||||
|
||||
static USERS_AND_SIGNALS: LazyLock<RwLock<Vec<UserAndSignal>>> =
|
||||
LazyLock::new(|| RwLock::new(vec![]));
|
||||
const SERVER_ADDRESS: &str = "192.168.1.3:4546";
|
||||
|
||||
pub async fn start_signalling() {
|
||||
let route = route()
|
||||
.layer(CorsLayer::permissive())
|
||||
.layer(TraceLayer::new_for_http());
|
||||
let listener = TcpListener::bind("192.168.1.3:4546").await.unwrap();
|
||||
println!("http://192.168.1.3:4546");
|
||||
axum::serve(listener, route).await.unwrap();
|
||||
}
|
||||
let tcp_listener = TcpListener::bind(SERVER_ADDRESS).await.unwrap();
|
||||
while let Ok((tcp_stream, client_address)) = tcp_listener.accept().await {
|
||||
let mut websocket = WebSocket::after_handshake(tcp_stream, Role::Server);
|
||||
websocket.set_writev(false);
|
||||
websocket.set_auto_close(true);
|
||||
websocket.set_auto_pong(true);
|
||||
let mut websocket = FragmentCollector::new(websocket);
|
||||
|
||||
fn route() -> Router {
|
||||
Router::new()
|
||||
.route("/alive", get(alive))
|
||||
.route(
|
||||
"/",
|
||||
get(read_signal).route_layer(axum::middleware::from_fn(
|
||||
verify_then_get_user_and_expected_signal,
|
||||
)),
|
||||
)
|
||||
.route(
|
||||
"/",
|
||||
post(create_signal).route_layer(axum::middleware::from_fn(verify_then_get_user)),
|
||||
)
|
||||
}
|
||||
|
||||
async fn alive() -> impl IntoResponse {
|
||||
StatusCode::OK
|
||||
}
|
||||
|
||||
#[debug_handler]
|
||||
async fn create_signal(
|
||||
Extension(user): Extension<Arc<User>>,
|
||||
Json(signal): Json<Signal>,
|
||||
) -> impl IntoResponse {
|
||||
let user = (*user).clone();
|
||||
let user_and_signal = UserAndSignal::new(user, signal).await;
|
||||
USERS_AND_SIGNALS.write().unwrap().push(user_and_signal);
|
||||
StatusCode::OK
|
||||
}
|
||||
|
||||
#[debug_handler]
|
||||
async fn read_signal(
|
||||
Extension(user_and_expected_signal): Extension<Arc<UserAndExpectedSignal>>,
|
||||
) -> impl IntoResponse {
|
||||
let mut target_index = None;
|
||||
let mut json_body = serde_json::json!("");
|
||||
for (index, user_and_signal) in USERS_AND_SIGNALS.read().unwrap().iter().enumerate() {
|
||||
if user_and_signal.signal.get_signal_type() == user_and_expected_signal.expected_signal
|
||||
&& user_and_signal.user != user_and_expected_signal.user
|
||||
{
|
||||
json_body = serde_json::json!(user_and_signal);
|
||||
target_index = Some(index);
|
||||
while let Ok(received_frame) = websocket.read_frame().await {
|
||||
if let OpCode::Text = received_frame.opcode {
|
||||
let received_payload = received_frame.payload;
|
||||
println!(
|
||||
"Client: {:#?} | Sent:\n{:#?}",
|
||||
client_address, received_payload
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match target_index {
|
||||
Some(target_index) => {
|
||||
USERS_AND_SIGNALS.write().unwrap().remove(target_index);
|
||||
(StatusCode::OK, Json(json_body)).into_response()
|
||||
}
|
||||
None => StatusCode::BAD_REQUEST.into_response(),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue