This commit is contained in:
Mateusz Gruszczyński committed 2026-10-14 13:38:59 +02:00
1 parent df403c0aa7
commit 9e133628e5
17 files changed
+270 -34

No files matched your search

+8
View File
@@ -20,6 +20,7 @@ use axum::{
use pages::*;
use tower::{ServiceBuilder, service_fn};
use tower_http::{
compression::CompressionLayer,
services::ServeDir,
set_header::SetResponseHeaderLayer,
trace::{MakeSpan, TraceLayer},
@@ -48,6 +49,7 @@ pub fn router(
static_dir: &str,
upload_body_limit_bytes: usize,
asset_cache_max_age_seconds: u64,
http_compression: bool,
) -> Router {
let asset_version = state.asset_version.clone();
let asset_not_found = service_fn(move |_request| {
@@ -259,6 +261,12 @@ pub fn router(
.layer(TraceLayer::new_for_http().make_span_with(PathOnlyMakeSpan))
.layer(middleware::from_fn(require_csrf_token))
.layer(middleware::from_fn(apply_response_header_policy))
// Negotiate HTTP compression only when enabled; WebSocket frames are unaffected.
.layer(
CompressionLayer::new()
.br(http_compression)
.gzip(http_compression),
)
.with_state(state)
}
+2
View File
@@ -59,6 +59,7 @@ pub struct Config {
pub guest_upload_max_size_bytes: usize,
pub asset_version: String,
pub asset_cache_max_age_seconds: u64,
pub http_compression: bool,
pub file_cache_max_age_seconds: u64,
pub files_public_url: Option<String>,
pub smtp: Option<crate::state::SmtpConfig>,
@@ -174,6 +175,7 @@ impl Config {
guest_upload_max_size_mb,
)?,
asset_version: env!("CARGO_PKG_VERSION").to_owned(),
http_compression: values.bool("HTTP_COMPRESSION", false)?,
asset_cache_max_age_seconds: values
.nonnegative_u64("ASSET_CACHE_MAX_AGE_SECONDS", 600)?,
file_cache_max_age_seconds: values
+1
View File
@@ -22,6 +22,7 @@ const KNOWN_CONFIG_KEYS: &[&str] = &[
"GUEST_UPLOAD_ENABLED",
"GUEST_UPLOAD_MAX_SIZE_MB",
"ASSET_CACHE_MAX_AGE_SECONDS",
"HTTP_COMPRESSION",
"FILE_CACHE_MAX_AGE_SECONDS",
"REGISTRATION_ENABLED",
"ACCOUNT_CONFIRMATION_REQUIRED",
+2
View File
@@ -68,6 +68,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
guest_upload_enabled = config.guest_upload_enabled,
guest_upload_max_size_bytes = config.guest_upload_max_size_bytes,
asset_cache_max_age_seconds = config.asset_cache_max_age_seconds,
http_compression = config.http_compression,
file_cache_max_age_seconds = config.file_cache_max_age_seconds,
files_public_url = config.files_public_url.as_deref().unwrap_or("application origin"),
registration_enabled = config.registration_enabled,
@@ -173,6 +174,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
&config.static_dir,
config.upload_body_limit_bytes(),
config.asset_cache_max_age_seconds,
config.http_compression,
);
let address = SocketAddr::new(config.host, config.port);
let listener = TcpListener::bind(address).await?;
+18 -11
View File
@@ -863,6 +863,7 @@ async fn handle_socket(
access_refresh.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
access_refresh.tick().await;
let (mut sender, mut receiver) = socket.split();
let mut last_broadcast_access_check = Instant::now() - Duration::from_secs(2);
if send_split(
&mut sender,
&ServerMessage::Diagnostics {
@@ -1053,17 +1054,23 @@ async fn handle_socket(
}
continue;
}
let (read_allowed, _) = current_resource_access(
&state,
"workspace",
&workspace_slug,
&external_tokens,
session_token.as_deref(),
password_ok,
).await;
if !read_allowed {
let _=send_split(&mut sender,&ServerMessage::Error{message:"Access expired or revoked".into()}).await;
break;
// Broadcasts can arrive hundreds of times a second per connection.
// Keep access checks off the per-message hot path; the separate
// access_refresh ticker also enforces revocation while idle.
if last_broadcast_access_check.elapsed() >= Duration::from_secs(1) {
let (read_allowed, _) = current_resource_access(
&state,
"workspace",
&workspace_slug,
&external_tokens,
session_token.as_deref(),
password_ok,
).await;
if !read_allowed {
let _=send_split(&mut sender,&ServerMessage::Error{message:"Access expired or revoked".into()}).await;
break;
}
last_broadcast_access_check = Instant::now();
}
match update {
Ok(RoomEvent::Document(update))=>if send_split(&mut sender,&ServerMessage::Document{base_revision_id:update.base_revision_id,revision_id:update.revision_id,updated_at:update.updated_at,author:update.author,client_id:update.client_id,update_id:update.update_id,operation:update.operation,owner_replacements:update.owner_replacements}).await.is_err(){break;},
+18 -11
View File
@@ -356,6 +356,7 @@ async fn handle_pad_socket(
.await;
let _ = channel.send(RoomEvent::Presence(users));
let mut last_chat = Instant::now() - Duration::from_secs(1);
let mut last_broadcast_access_check = Instant::now() - Duration::from_secs(2);
let mut access_refresh = tokio::time::interval(Duration::from_secs(10));
access_refresh.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
access_refresh.tick().await;
@@ -552,17 +553,23 @@ async fn handle_pad_socket(
}
continue;
}
let (read_allowed, _) = current_resource_access(
&state,
"pad",
&slug,
&external_tokens,
session_token.as_deref(),
password_ok,
).await;
if !read_allowed {
let _=send_pad_split(&mut sender,&PadServerMessage::Error{message:"Access expired or revoked".into()}).await;
break;
// Broadcasts can arrive hundreds of times a second per connection.
// Keep access checks off the per-message hot path; the separate
// access_refresh ticker also enforces revocation while idle.
if last_broadcast_access_check.elapsed() >= Duration::from_secs(1) {
let (read_allowed, _) = current_resource_access(
&state,
"pad",
&slug,
&external_tokens,
session_token.as_deref(),
password_ok,
).await;
if !read_allowed {
let _=send_pad_split(&mut sender,&PadServerMessage::Error{message:"Access expired or revoked".into()}).await;
break;
}
last_broadcast_access_check = Instant::now();
}
match update {
Ok(RoomEvent::Document(u))=>if send_pad_split(&mut sender,&PadServerMessage::Document{base_revision_id:u.base_revision_id,revision_id:u.revision_id,updated_at:u.updated_at,author:u.author,client_id:u.client_id,update_id:u.update_id,operation:u.operation,owner_replacements:u.owner_replacements}).await.is_err(){break;},