staged m3u to xtream fix (#877)

* staged m3u to xtream fix

* staged m3u to xtream fix

* staged m3u to xtream fix

* fixed race some conditions

* fixed race some conditions

* fixed race some conditions

* fixed race some conditions

* fixed race some conditions

---------

Co-authored-by: euzu <euzu@proton.me>
This commit is contained in:
euzu
2026-09-24 00:51:12 +02:00
committed by GitHub
co-authored by euzu
parent e6ceddec9a
commit fc51a3acd4
12 changed files with 1973 additions and 483 deletions
+85 -17
View File
@@ -6052,7 +6052,7 @@ async fn grace_context_is_populated_when_grace_strategy_is_actually_granted() {
async fn evaluate_remaining_strategies_evicts_after_used_grace() {
// Strategies: [GraceHoldStream, EvictUserOldest]
// Grace was used at index 0, so only EvictUserOldest (index 1) is evaluated.
// Eviction frees the slot -> Allowed.
// The new request stays retryable while the evicted stream still owns its provider slot.
let strategies = vec![AdmissionStrategy::GraceHoldStream, AdmissionStrategy::EvictUserOldest];
let grace_context = GraceResolutionContext { strategy_index: 0, strategies: strategies.into(), kind: None };
@@ -6073,6 +6073,9 @@ async fn evaluate_remaining_strategies_evicts_after_used_grace() {
recent_eviction_reentry_ttl: std::time::Duration::from_millis(1500),
admission_strategies: Some(vec![AdmissionStrategy::GraceHoldStream, AdmissionStrategy::EvictUserOldest]),
});
let provider_config = create_test_provider_app_config();
app_state.app_config.sources.store(provider_config.sources.load_full());
app_state.active_provider.update_config(&app_state.app_config);
let addr1: SocketAddr = "127.0.0.1:55701".parse().unwrap_or_else(|_| unreachable!());
let addr2: SocketAddr = "10.0.0.5:55702".parse().unwrap_or_else(|_| unreachable!());
@@ -6121,23 +6124,71 @@ async fn evaluate_remaining_strategies_evicts_after_used_grace() {
.await
.expect("stream should be created");
let result = evaluate_remaining_strategies_after_grace(
&app_state.admission_ctx(),
AdmissionRequest {
username: "remaining-evict",
max_connections: 1,
soft_connections: 0,
client_ip: &fingerprint2.client_ip,
request_addr: &fingerprint2.addr,
use_session_admission: true,
session_token: Some("tok-new"),
activate_unbound_session: true,
eviction_reentry_guard: EvictionReentryGuard::Session("tok-new"),
},
&grace_context,
Some(crate::api::model::ConnectionKind::Normal),
let provider_handle = app_state
.active_provider
.acquire_connection_with_grace_for_session(
&"provider_1".intern(),
&addr1,
false,
0,
crate::api::model::ConnectionKind::Normal,
Some("tok-counted"),
)
.expect("old stream should occupy the only provider slot");
assert!(app_state.active_provider.register_body_owner(provider_handle.allocation_id));
let close_rx = app_state.connection_manager.register_close_socket(addr1);
let manager = Arc::clone(&app_state.connection_manager);
let provider = Arc::clone(&app_state.active_provider);
let release_body = Arc::new(tokio::sync::Notify::new());
let release_body_after_timeout = Arc::clone(&release_body);
let close_task = tokio::spawn(async move {
assert_eq!(close_rx.await.expect("kick close signal"), shared::model::DisconnectReason::ClientKicked);
tokio::time::sleep(std::time::Duration::from_millis(50)).await;
manager.release_provider_deferred(&addr1).await;
assert_eq!(provider.get_provider_connections_count(), 1, "body owner still holds provider capacity");
release_body_after_timeout.notified().await;
provider.release_handle(&provider_handle);
provider_handle.completion_token.as_ref().expect("body completion token").cancel();
manager.unregister_close_socket(&addr1);
});
let request = || AdmissionRequest {
username: "remaining-evict",
max_connections: 1,
soft_connections: 0,
client_ip: &fingerprint2.client_ip,
request_addr: &fingerprint2.addr,
use_session_admission: true,
session_token: Some("tok-new"),
activate_unbound_session: true,
eviction_reentry_guard: EvictionReentryGuard::Session("tok-new"),
};
let result = tokio::time::timeout(
std::time::Duration::from_secs(5),
evaluate_remaining_strategies_after_grace(
&app_state.admission_ctx(),
request(),
&grace_context,
Some(crate::api::model::ConnectionKind::Normal),
),
)
.await;
.await
.expect("admission must remain retryable while the provider slot is held");
assert_eq!(result.admission.permission(), UserConnectionPermission::Exhausted);
assert_eq!(app_state.active_provider.get_provider_connections_count(), 1);
let retry = tokio::time::timeout(
std::time::Duration::from_secs(5),
resolve_admission_with_strategies(&app_state.admission_ctx(), request()),
)
.await
.expect("retry must remain bounded while the provider slot is held");
assert_eq!(retry.admission.permission(), UserConnectionPermission::Exhausted);
release_body.notify_one();
close_task.await.expect("old transport cleanup");
let result = resolve_admission_with_strategies(&app_state.admission_ctx(), request()).await;
assert_eq!(
result.admission.permission(),
@@ -6145,6 +6196,23 @@ async fn evaluate_remaining_strategies_evicts_after_used_grace() {
"EvictUserOldest should free the slot"
);
assert!(result.grace_context.is_none(), "no grace context on eviction success");
assert_eq!(
app_state.active_provider.get_provider_connections_count(),
0,
"admission must not return while the evicted stream still owns the provider slot"
);
let replacement = app_state.active_provider.acquire_connection_with_grace_for_session(
&"provider_1".intern(),
&addr2,
false,
0,
crate::api::model::ConnectionKind::Normal,
Some("tok-new"),
);
assert!(replacement.is_some(), "newly admitted stream must acquire the freed provider slot");
if let Some(replacement) = replacement {
app_state.active_provider.release_handle(&replacement);
}
}
#[tokio::test]