Files
telemt/src/web/session/lane_uplink.rs
T
2026-08-27 00:06:37 +03:00

197 lines
7.1 KiB
Rust

use std::sync::Arc;
use std::time::Instant;
use sha2::{Digest, Sha256};
use subtle::ConstantTimeEq;
use super::uplink::{AppliedProgress, inbound_reservation, validate_batch};
use super::{PendingClass, WebSession, insert_carrier_lane};
use crate::config::WebCarrier;
use crate::web::frame::{self, Frame, FrameType};
use crate::web::manager::{ManagerError, TokenHash};
impl WebSession {
/// Applies one exactly-once uplink batch to an independent HTTPS lane.
pub(crate) fn process_up_lane(
self: &Arc<Self>,
lane_id: u32,
sequence: u64,
body: &[u8],
) -> Result<u64, ManagerError> {
if self.carrier() != WebCarrier::HttpsLanes || lane_id > frame::MAX_STREAM_ID {
return Err(ManagerError::Protocol);
}
let frames = match frame::parse_all(body, &self.limits) {
Ok(frames) => frames,
Err(_) => {
self.close();
return Err(ManagerError::Protocol);
}
};
if frames
.iter()
.copied()
.any(|value| value.stream_id != lane_id || frame::validate_client_shape(value).is_err())
{
self.close();
return Err(ManagerError::Protocol);
}
let digest: TokenHash = Sha256::digest(body).into();
let mut opened = Vec::new();
let mut committed = false;
let mut healthy = false;
let result = {
let mut state = self.state.lock();
if state.closed {
return Err(ManagerError::Closed);
}
self.ensure_carrier_active_locked(&state)?;
state.last_activity = Instant::now();
let new_lane = !state.carrier_lanes.contains_key(&lane_id);
if new_lane {
if lane_id != 0
&& frames
.first()
.is_some_and(|value| value.frame_type != FrameType::Open)
&& only_late_frames(&frames)
{
return if self.automatic_carrier
&& state.negotiation_phase != super::SessionNegotiationPhase::Committed
{
Err(ManagerError::Backpressure)
} else {
Ok(sequence)
};
}
if lane_id == 0
|| frames
.first()
.is_none_or(|value| value.frame_type != FrameType::Open)
{
drop(state);
self.close();
return Err(ManagerError::Protocol);
}
let lane_limit = self
.profile
.max_streams_per_session
.saturating_add(self.limits.max_tombstones_per_session)
.saturating_add(1);
if state.carrier_lanes.len() >= lane_limit {
return Err(ManagerError::Limit);
}
}
let (last_sequence, last_digest, up_active) = state
.carrier_lanes
.get(&lane_id)
.map_or((0, [0; 32], false), |lane| {
(lane.last_up_sequence, lane.last_up_digest, lane.up_active)
});
if sequence == last_sequence && sequence != 0 {
return if bool::from(last_digest.ct_eq(&digest)) {
Ok(sequence)
} else {
drop(state);
self.close();
Err(ManagerError::Protocol)
};
}
if sequence == 0 || sequence != last_sequence.saturating_add(1) {
drop(state);
self.close();
return Err(ManagerError::Protocol);
}
if up_active {
return Err(ManagerError::Concurrent);
}
if !validate_batch(&state, &frames) {
drop(state);
self.close();
return Err(ManagerError::Protocol);
}
let (reserve_bytes, reserve_items) = inbound_reservation(&state, &frames);
if !self.reserve_locked(
&mut state,
reserve_bytes,
reserve_items,
PendingClass::Uplink,
) {
if let Some(lane) = state.carrier_lanes.get_mut(&lane_id) {
lane.up_active = false;
}
return Err(ManagerError::Backpressure);
}
if new_lane && insert_carrier_lane(&mut state, lane_id).is_none() {
self.release_locked(&mut state, reserve_bytes, reserve_items, false);
drop(state);
self.close();
return Err(ManagerError::Protocol);
}
let Some(lane) = state.carrier_lanes.get_mut(&lane_id) else {
self.release_locked(&mut state, reserve_bytes, reserve_items, false);
return Err(ManagerError::Closed);
};
lane.up_active = true;
let mut unused_bytes = reserve_bytes;
let mut unused_items = reserve_items;
let mut progress = AppliedProgress::default();
let applied = self.apply_batch_locked(
&mut state,
&frames,
&mut opened,
&mut None,
&mut unused_bytes,
&mut unused_items,
&mut progress,
);
self.release_locked(&mut state, unused_bytes, unused_items, false);
if let Some(lane) = state.carrier_lanes.get_mut(&lane_id) {
lane.up_active = false;
if applied {
lane.last_up_sequence = sequence;
lane.last_up_digest = digest;
}
}
if applied {
(committed, healthy) = self.record_uplink_progress_locked(&mut state, progress);
}
applied.then_some(sequence).ok_or(ManagerError::Closed)
};
if matches!(result, Err(ManagerError::Backpressure)) {
return result;
}
if result.is_err() {
self.close();
drop(opened);
return result;
}
if self.automatic_carrier && !self.is_carrier_committed() {
self.lane_open_notify.notify_waiters();
return Err(ManagerError::Backpressure);
}
if committed {
self.finish_carrier_commit();
}
if healthy {
self.finish_carrier_health();
}
self.lane_open_notify.notify_waiters();
for completion in opened {
self.spawn_stream(completion, false);
}
if let Some(manager) = self.manager.upgrade() {
manager.record_up(body.len());
}
result
}
}
fn only_late_frames(frames: &[Frame<'_>]) -> bool {
frames.iter().all(|value| {
matches!(
value.frame_type,
FrameType::Data | FrameType::Window | FrameType::Close
)
})
}