mirror of
https://github.com/telemt/telemt.git
synced 2026-10-10 19:35:58 +03:00
Hardswap Invariants in tests + Quota fixes
This commit is contained in:
@@ -20,6 +20,13 @@ impl WebSession {
|
||||
completion: StreamCompletion,
|
||||
retain_reservation_on_reject: bool,
|
||||
) -> bool {
|
||||
if self.close_if_cancelled() {
|
||||
completion
|
||||
.retain_rejected
|
||||
.store(retain_reservation_on_reject, Ordering::Release);
|
||||
drop(completion);
|
||||
return false;
|
||||
}
|
||||
let stream = completion.stream;
|
||||
let peer_port = completion.peer_port;
|
||||
let Some(manager) = self.manager.upgrade() else {
|
||||
@@ -60,6 +67,7 @@ impl WebSession {
|
||||
);
|
||||
let logical_stream = WebLogicalStream::new(Arc::clone(&session), stream);
|
||||
tokio::select! {
|
||||
biased;
|
||||
_ = cancel.cancelled() => {}
|
||||
_ = run_stream(
|
||||
Arc::clone(&session),
|
||||
|
||||
+37
-12
@@ -26,9 +26,14 @@ impl WebSession {
|
||||
if !self.carrier().is_multiplexed() {
|
||||
return Err(ManagerError::Protocol);
|
||||
}
|
||||
if self.close_if_cancelled() {
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
let (epoch, healthy) = {
|
||||
let mut state = self.state.lock();
|
||||
if state.closed {
|
||||
if state.closed || self.cancel.is_cancelled() {
|
||||
drop(state);
|
||||
self.close_if_cancelled();
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
if let Some(unacked) = &state.unacked {
|
||||
@@ -92,6 +97,11 @@ impl WebSession {
|
||||
notified.as_mut().enable();
|
||||
{
|
||||
let mut state = self.state.lock();
|
||||
if self.cancel.is_cancelled() {
|
||||
drop(state);
|
||||
self.close_if_cancelled();
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
if state.down_epoch != epoch {
|
||||
return Ok(PollResult {
|
||||
body: Bytes::new(),
|
||||
@@ -129,18 +139,33 @@ impl WebSession {
|
||||
notified.await;
|
||||
}
|
||||
};
|
||||
match tokio::time::timeout(deadline, poll).await {
|
||||
Ok(result) => result,
|
||||
Err(_) => {
|
||||
let mut state = self.state.lock();
|
||||
if state.down_epoch == epoch {
|
||||
state.activity.touch_progress(Instant::now());
|
||||
tokio::select! {
|
||||
biased;
|
||||
_ = self.cancel.cancelled() => {
|
||||
self.close_if_cancelled();
|
||||
Err(ManagerError::Closed)
|
||||
}
|
||||
result = tokio::time::timeout(deadline, poll) => match result {
|
||||
Ok(result) => result,
|
||||
Err(_) => {
|
||||
if self.close_if_cancelled() {
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
let mut state = self.state.lock();
|
||||
if self.cancel.is_cancelled() {
|
||||
drop(state);
|
||||
self.close_if_cancelled();
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
if state.down_epoch == epoch {
|
||||
state.activity.touch_progress(Instant::now());
|
||||
}
|
||||
Ok(PollResult {
|
||||
body: Bytes::new(),
|
||||
next_cursor: cursor,
|
||||
lane_closed: false,
|
||||
})
|
||||
}
|
||||
Ok(PollResult {
|
||||
body: Bytes::new(),
|
||||
next_cursor: cursor,
|
||||
lane_closed: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ impl WebSession {
|
||||
if self.carrier() != WebCarrier::HttpsLanes || lane_id > frame::MAX_STREAM_ID {
|
||||
return Err(ManagerError::Protocol);
|
||||
}
|
||||
if self.close_if_cancelled() {
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
let frames = match frame::parse_all(body, &self.limits) {
|
||||
Ok(frames) => frames,
|
||||
Err(_) => {
|
||||
@@ -175,6 +178,10 @@ impl WebSession {
|
||||
if matches!(result, Err(ManagerError::Backpressure)) {
|
||||
return result;
|
||||
}
|
||||
if matches!(result, Err(ManagerError::Closed)) && self.close_if_cancelled() {
|
||||
drop(opened);
|
||||
return result;
|
||||
}
|
||||
if result.is_err() {
|
||||
self.close(SessionCloseReason::Protocol);
|
||||
drop(opened);
|
||||
|
||||
+79
-41
@@ -42,9 +42,14 @@ impl WebSession {
|
||||
if !self.carrier().uses_lanes() || lane_id > frame::MAX_STREAM_ID {
|
||||
return Err(ManagerError::Protocol);
|
||||
}
|
||||
if self.close_if_cancelled() {
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
let lane_ready = if let Some(expected_instance) = expected_instance {
|
||||
let state = self.state.lock();
|
||||
if state.closed {
|
||||
if state.closed || self.cancel.is_cancelled() {
|
||||
drop(state);
|
||||
self.close_if_cancelled();
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
state
|
||||
@@ -63,7 +68,9 @@ impl WebSession {
|
||||
}
|
||||
let (instance, epoch, notify, healthy) = {
|
||||
let mut state = self.state.lock();
|
||||
if state.closed {
|
||||
if state.closed || self.cancel.is_cancelled() {
|
||||
drop(state);
|
||||
self.close_if_cancelled();
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
let (acknowledged, replay) = {
|
||||
@@ -175,7 +182,9 @@ impl WebSession {
|
||||
notified.as_mut().enable();
|
||||
{
|
||||
let mut state = self.state.lock();
|
||||
if state.closed {
|
||||
if state.closed || self.cancel.is_cancelled() {
|
||||
drop(state);
|
||||
self.close_if_cancelled();
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
let carrier_health_eligible = lane_id != 0
|
||||
@@ -245,55 +254,72 @@ impl WebSession {
|
||||
notified.await;
|
||||
}
|
||||
};
|
||||
match tokio::time::timeout(deadline, poll).await {
|
||||
Ok(result) => result,
|
||||
Err(_) => {
|
||||
let mut state = self.state.lock();
|
||||
if state.closed {
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
if !state.carrier_lanes.contains_key(&lane_id) {
|
||||
return Ok(PollResult {
|
||||
body: Bytes::new(),
|
||||
next_cursor: cursor,
|
||||
lane_closed: true,
|
||||
});
|
||||
}
|
||||
if lane_id != 0
|
||||
&& !state.streams.contains_key(&lane_id)
|
||||
&& state.closed_streams.contains(&lane_id)
|
||||
{
|
||||
return Ok(PollResult {
|
||||
body: Bytes::new(),
|
||||
next_cursor: cursor,
|
||||
lane_closed: true,
|
||||
});
|
||||
}
|
||||
if let Some(lane) = state.carrier_lanes.get(&lane_id) {
|
||||
if lane.instance != instance {
|
||||
tokio::select! {
|
||||
biased;
|
||||
_ = self.cancel.cancelled() => {
|
||||
self.close_if_cancelled();
|
||||
Err(ManagerError::Closed)
|
||||
}
|
||||
result = tokio::time::timeout(deadline, poll) => match result {
|
||||
Ok(result) => result,
|
||||
Err(_) => {
|
||||
if self.close_if_cancelled() {
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
let mut state = self.state.lock();
|
||||
if state.closed || self.cancel.is_cancelled() {
|
||||
drop(state);
|
||||
self.close_if_cancelled();
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
if !state.carrier_lanes.contains_key(&lane_id) {
|
||||
return Ok(PollResult {
|
||||
body: Bytes::new(),
|
||||
next_cursor: cursor,
|
||||
lane_closed: true,
|
||||
});
|
||||
}
|
||||
if lane.down_epoch == epoch {
|
||||
state.activity.touch_progress(Instant::now());
|
||||
if lane_id != 0
|
||||
&& !state.streams.contains_key(&lane_id)
|
||||
&& state.closed_streams.contains(&lane_id)
|
||||
{
|
||||
return Ok(PollResult {
|
||||
body: Bytes::new(),
|
||||
next_cursor: cursor,
|
||||
lane_closed: true,
|
||||
});
|
||||
}
|
||||
if let Some(lane) = state.carrier_lanes.get(&lane_id) {
|
||||
if lane.instance != instance {
|
||||
return Ok(PollResult {
|
||||
body: Bytes::new(),
|
||||
next_cursor: cursor,
|
||||
lane_closed: true,
|
||||
});
|
||||
}
|
||||
if lane.down_epoch == epoch {
|
||||
state.activity.touch_progress(Instant::now());
|
||||
}
|
||||
}
|
||||
Ok(PollResult {
|
||||
body: Bytes::new(),
|
||||
next_cursor: cursor,
|
||||
lane_closed: false,
|
||||
})
|
||||
}
|
||||
Ok(PollResult {
|
||||
body: Bytes::new(),
|
||||
next_cursor: cursor,
|
||||
lane_closed: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn wait_for_lane_open(&self, lane_id: u32, cursor: u64) -> Result<bool, ManagerError> {
|
||||
if self.close_if_cancelled() {
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
let wait = {
|
||||
let mut state = self.state.lock();
|
||||
if state.closed {
|
||||
if state.closed || self.cancel.is_cancelled() {
|
||||
drop(state);
|
||||
self.close_if_cancelled();
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
if state.carrier_lanes.contains_key(&lane_id) {
|
||||
@@ -334,7 +360,9 @@ impl WebSession {
|
||||
notified.as_mut().enable();
|
||||
{
|
||||
let state = self.state.lock();
|
||||
if state.closed {
|
||||
if state.closed || self.cancel.is_cancelled() {
|
||||
drop(state);
|
||||
self.close_if_cancelled();
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
if state.carrier_lanes.contains_key(&lane_id)
|
||||
@@ -346,14 +374,24 @@ impl WebSession {
|
||||
}
|
||||
notified.await;
|
||||
}
|
||||
})
|
||||
.await;
|
||||
});
|
||||
let opened = tokio::select! {
|
||||
biased;
|
||||
_ = self.cancel.cancelled() => {
|
||||
drop(wait);
|
||||
self.close_if_cancelled();
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
opened = opened => opened,
|
||||
};
|
||||
drop(wait);
|
||||
match opened {
|
||||
Ok(result) => result,
|
||||
Err(_) => {
|
||||
let state = self.state.lock();
|
||||
if state.closed {
|
||||
if state.closed || self.cancel.is_cancelled() {
|
||||
drop(state);
|
||||
self.close_if_cancelled();
|
||||
Err(ManagerError::Closed)
|
||||
} else {
|
||||
Ok(state.carrier_lanes.contains_key(&lane_id)
|
||||
|
||||
@@ -121,6 +121,15 @@ impl CarrierSupersedeCompletion<'_> {
|
||||
}
|
||||
|
||||
impl WebSession {
|
||||
/// Closes a bearer as soon as its process-owned user registration is revoked.
|
||||
pub(crate) fn close_if_cancelled(&self) -> bool {
|
||||
if !self.cancel.is_cancelled() {
|
||||
return false;
|
||||
}
|
||||
self.close(SessionCloseReason::UserDisabled);
|
||||
true
|
||||
}
|
||||
|
||||
/// Closes carrier state while relay tasks retain their admission until exit.
|
||||
pub(crate) fn close(&self, reason: SessionCloseReason) -> SessionCloseOutcome {
|
||||
let mut state = self.state.lock();
|
||||
@@ -221,19 +230,8 @@ impl WebSession {
|
||||
|
||||
/// Atomically closes a session only when reconnect grace is still due.
|
||||
pub(crate) fn close_if_due(&self, now: Instant) -> bool {
|
||||
if self.cancel.is_cancelled() {
|
||||
let released = {
|
||||
let mut state = self.state.lock();
|
||||
if state.closed || state.close_requested.is_some() {
|
||||
None
|
||||
} else {
|
||||
Some(self.release_on_close_locked(&mut state, SessionCloseReason::UserDisabled))
|
||||
}
|
||||
};
|
||||
if let Some(released) = released {
|
||||
self.finish_close(released);
|
||||
return true;
|
||||
}
|
||||
if self.close_if_cancelled() {
|
||||
return true;
|
||||
}
|
||||
let healthy = {
|
||||
let mut state = self.state.lock();
|
||||
|
||||
@@ -34,6 +34,9 @@ impl WebSession {
|
||||
&self,
|
||||
state: &SessionState,
|
||||
) -> Result<(), crate::web::manager::ManagerError> {
|
||||
if self.cancel.is_cancelled() {
|
||||
return Err(crate::web::manager::ManagerError::Closed);
|
||||
}
|
||||
if state.negotiation_phase == SessionNegotiationPhase::Uncommitted
|
||||
&& self
|
||||
.carrier_deadline_at
|
||||
|
||||
@@ -69,6 +69,9 @@ impl WebSession {
|
||||
if !self.carrier().is_multiplexed() {
|
||||
return Err(ManagerError::Protocol);
|
||||
}
|
||||
if self.close_if_cancelled() {
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
if self
|
||||
.up_active
|
||||
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
|
||||
@@ -157,6 +160,10 @@ impl WebSession {
|
||||
if matches!(result, Err(ManagerError::Backpressure)) {
|
||||
return result;
|
||||
}
|
||||
if matches!(result, Err(ManagerError::Closed)) && self.close_if_cancelled() {
|
||||
drop(opened);
|
||||
return result;
|
||||
}
|
||||
if result.is_err() {
|
||||
self.close(SessionCloseReason::Protocol);
|
||||
drop(opened);
|
||||
|
||||
@@ -39,8 +39,12 @@ pub(crate) struct WebSocketProbeReservation {
|
||||
impl WebSocketProbeReservation {
|
||||
/// Binds the admitted process connection to the future commit acknowledgement.
|
||||
pub(crate) fn bind(&mut self, owner: u64) -> Result<(), ManagerError> {
|
||||
if self.session.close_if_cancelled() {
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
let mut state = self.session.state.lock();
|
||||
if state.closed
|
||||
|| self.session.cancel.is_cancelled()
|
||||
|| !state.websocket_probe_claimed
|
||||
|| state.websocket_commit_ack_owner.is_some()
|
||||
{
|
||||
@@ -85,8 +89,12 @@ impl WebSocketLaneReservation {
|
||||
if self.phase != WebSocketLaneReservationPhase::Reserved {
|
||||
return Err(ManagerError::Concurrent);
|
||||
}
|
||||
if self.session.close_if_cancelled() {
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
let mut state = self.session.state.lock();
|
||||
if state.closed
|
||||
|| self.session.cancel.is_cancelled()
|
||||
|| state
|
||||
.carrier_lanes
|
||||
.get(&self.claim.lane.lane_id)
|
||||
@@ -113,8 +121,12 @@ impl WebSocketLaneReservation {
|
||||
{
|
||||
return Err(ManagerError::Protocol);
|
||||
}
|
||||
if self.session.close_if_cancelled() {
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
let mut state = self.session.state.lock();
|
||||
if state
|
||||
if self.session.cancel.is_cancelled()
|
||||
|| state
|
||||
.carrier_lanes
|
||||
.get(&self.claim.lane.lane_id)
|
||||
.is_none_or(|lane| lane.instance != self.claim.lane.instance)
|
||||
@@ -174,8 +186,11 @@ impl WebSession {
|
||||
self: &Arc<Self>,
|
||||
acknowledge_commit: bool,
|
||||
) -> Result<Option<WebSocketProbeReservation>, ManagerError> {
|
||||
if self.close_if_cancelled() {
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
let mut state = self.state.lock();
|
||||
if state.closed {
|
||||
if state.closed || self.cancel.is_cancelled() {
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
self.ensure_carrier_active_locked(&state)?;
|
||||
@@ -216,8 +231,11 @@ impl WebSession {
|
||||
{
|
||||
return Err(ManagerError::Protocol);
|
||||
}
|
||||
if self.close_if_cancelled() {
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
let mut state = self.state.lock();
|
||||
if state.closed {
|
||||
if state.closed || self.cancel.is_cancelled() {
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
if state.active_peer_ports.len() >= self.profile.max_streams_per_session
|
||||
@@ -306,6 +324,9 @@ impl WebSession {
|
||||
{
|
||||
return Err(ManagerError::Protocol);
|
||||
}
|
||||
if self.close_if_cancelled() {
|
||||
return Err(ManagerError::Closed);
|
||||
}
|
||||
let lane_id = reservation.lane_id();
|
||||
let frames = frame::parse_all(body, &self.limits).map_err(|_| ManagerError::Protocol)?;
|
||||
if frames
|
||||
|
||||
Reference in New Issue
Block a user