mirror of
https://github.com/telemt/telemt.git
synced 2026-09-05 18:16:06 +03:00
211 lines
7.9 KiB
Rust
211 lines
7.9 KiB
Rust
use std::net::IpAddr;
|
|
use std::sync::Arc;
|
|
|
|
use hyper::header::{self, HeaderName, HeaderValue};
|
|
use hyper::{Method, Request, StatusCode};
|
|
|
|
use super::body::{CollectBodyError, CollectedBody, RequestBody, collect_body};
|
|
use super::decoy::serve_decoy;
|
|
use super::request::{binary_content_type, carrier_ip_learning_eligible, carrier_request};
|
|
use super::response::{
|
|
carrier_empty, carrier_headers, full_response, insert_header, service_unavailable,
|
|
};
|
|
use super::{HttpResponse, request_trace};
|
|
use crate::config::WebRuntimeVhost;
|
|
use crate::web::frame::{self, FrameType};
|
|
use crate::web::manager::{ManagerError, TokenHash, WebProcessRuntime};
|
|
use crate::web::trace::{TraceDirection, TraceLifecycleEvent, TraceRoute};
|
|
|
|
const CREATE_BODY_LIMIT: usize = 64;
|
|
|
|
/// Handles session creation, replacement replay, and authenticated closure.
|
|
pub(super) async fn handle_session(
|
|
request: Request<RequestBody>,
|
|
runtime: Arc<WebProcessRuntime>,
|
|
vhost: Arc<WebRuntimeVhost>,
|
|
token_hash: TokenHash,
|
|
client_ip: IpAddr,
|
|
) -> HttpResponse {
|
|
if request.headers().contains_key("x-lane-id") {
|
|
return serve_decoy(request, vhost, true, &runtime).await;
|
|
}
|
|
if request.method() == Method::DELETE {
|
|
if request.headers().contains_key(header::CONTENT_TYPE) {
|
|
return serve_decoy(request, vhost, true, &runtime).await;
|
|
}
|
|
if let Some(trace) = request_trace(&request)
|
|
&& let Ok(session) = runtime.get_session(token_hash, &vhost.host)
|
|
{
|
|
trace.set_route(TraceRoute::Session);
|
|
trace.bind_identity(session.trace_identity());
|
|
}
|
|
let CollectedBody {
|
|
request,
|
|
body,
|
|
_body_budget,
|
|
} = match collect_body(request, &runtime, 1, true).await {
|
|
Ok(result) => result,
|
|
Err(CollectBodyError::Limit) => return service_unavailable(),
|
|
Err(CollectBodyError::Invalid(request)) => {
|
|
return serve_decoy(request, vhost, true, &runtime).await;
|
|
}
|
|
};
|
|
if !body.is_empty() || runtime.close_token(token_hash, &vhost.host).is_err() {
|
|
return serve_decoy(request, vhost, true, &runtime).await;
|
|
}
|
|
return carrier_empty(StatusCode::NO_CONTENT);
|
|
}
|
|
if request.method() != Method::POST || !binary_content_type(&request) {
|
|
return serve_decoy(request, vhost, true, &runtime).await;
|
|
}
|
|
let Some(carrier_request) = carrier_request(&request, &vhost.host) else {
|
|
return serve_decoy(request, vhost, true, &runtime).await;
|
|
};
|
|
let ip_learning_eligible = carrier_ip_learning_eligible(&request, client_ip);
|
|
let Some((trace_session_id, profile)) =
|
|
runtime.bootstrap_trace_identity(token_hash, &vhost.host)
|
|
else {
|
|
return serve_decoy(request, vhost, true, &runtime).await;
|
|
};
|
|
if let Some(trace) = request_trace(&request) {
|
|
trace.set_route(TraceRoute::Session);
|
|
trace.bind_profile(&profile, trace_session_id);
|
|
}
|
|
let CollectedBody {
|
|
request,
|
|
body,
|
|
_body_budget,
|
|
} = match collect_body(request, &runtime, CREATE_BODY_LIMIT, false).await {
|
|
Ok(result) => result,
|
|
Err(CollectBodyError::Limit) => return service_unavailable(),
|
|
Err(CollectBodyError::Invalid(request)) => {
|
|
return serve_decoy(request, vhost, true, &runtime).await;
|
|
}
|
|
};
|
|
if let Some(trace) = request_trace(&request) {
|
|
trace.record_frames(
|
|
TraceDirection::Request,
|
|
&body,
|
|
&runtime.active_generation().config().web.limits,
|
|
);
|
|
}
|
|
match runtime.create_session(
|
|
token_hash,
|
|
&vhost.host,
|
|
client_ip,
|
|
&body,
|
|
carrier_request,
|
|
ip_learning_eligible,
|
|
) {
|
|
Ok(result) => {
|
|
let welcome = frame::encode(FrameType::Welcome, 0, &[]);
|
|
if let Some(trace) = request_trace(&request) {
|
|
trace.register_redaction(result.token.as_bytes());
|
|
trace.record_frames(
|
|
TraceDirection::Response,
|
|
&welcome,
|
|
&runtime.active_generation().config().web.limits,
|
|
);
|
|
}
|
|
let mut response = full_response(StatusCode::OK, welcome);
|
|
carrier_headers(&mut response);
|
|
insert_header(
|
|
&mut response,
|
|
HeaderName::from_static("x-session-token"),
|
|
&result.token,
|
|
);
|
|
response.headers_mut().insert(
|
|
HeaderName::from_static("x-carrier-mode"),
|
|
HeaderValue::from_static(result.carrier.as_str()),
|
|
);
|
|
response.headers_mut().insert(
|
|
HeaderName::from_static("x-down-cursor"),
|
|
HeaderValue::from_static("0"),
|
|
);
|
|
if let Some(attempt) = result.attempt {
|
|
insert_header(
|
|
&mut response,
|
|
HeaderName::from_static("x-carrier-attempt"),
|
|
&attempt.to_string(),
|
|
);
|
|
}
|
|
if let Some(candidate_count) = result.candidate_count {
|
|
insert_header(
|
|
&mut response,
|
|
HeaderName::from_static("x-carrier-candidate-count"),
|
|
&candidate_count.to_string(),
|
|
);
|
|
insert_header(
|
|
&mut response,
|
|
HeaderName::from_static("x-carrier-deadline"),
|
|
&result.deadline_secs.unwrap_or_default().to_string(),
|
|
);
|
|
if let Some(state) = result.carrier_state {
|
|
insert_header(
|
|
&mut response,
|
|
HeaderName::from_static("x-carrier-state"),
|
|
state,
|
|
);
|
|
}
|
|
}
|
|
response
|
|
}
|
|
Err(ManagerError::Committed) => {
|
|
let mut response = carrier_empty(StatusCode::CONFLICT);
|
|
if let Some(echo) =
|
|
runtime.carrier_echo(token_hash, &vhost.host, client_ip, carrier_request)
|
|
{
|
|
response.headers_mut().insert(
|
|
HeaderName::from_static("x-carrier-mode"),
|
|
HeaderValue::from_static(echo.carrier.as_str()),
|
|
);
|
|
insert_header(
|
|
&mut response,
|
|
HeaderName::from_static("x-carrier-attempt"),
|
|
&echo.attempt.to_string(),
|
|
);
|
|
insert_header(
|
|
&mut response,
|
|
HeaderName::from_static("x-carrier-candidate-count"),
|
|
&echo.candidate_count.to_string(),
|
|
);
|
|
insert_header(
|
|
&mut response,
|
|
HeaderName::from_static("x-carrier-deadline"),
|
|
&echo.deadline_secs.to_string(),
|
|
);
|
|
insert_header(
|
|
&mut response,
|
|
HeaderName::from_static("x-carrier-state"),
|
|
echo.state,
|
|
);
|
|
}
|
|
response
|
|
}
|
|
Err(
|
|
error @ (ManagerError::Limit | ManagerError::Backpressure | ManagerError::Concurrent),
|
|
) => {
|
|
runtime.trace().record_profile_lifecycle(
|
|
client_ip,
|
|
Some(trace_session_id),
|
|
&profile,
|
|
TraceLifecycleEvent::SessionRejected,
|
|
None,
|
|
Some(error.as_str()),
|
|
);
|
|
service_unavailable()
|
|
}
|
|
Err(error) => {
|
|
runtime.trace().record_profile_lifecycle(
|
|
client_ip,
|
|
Some(trace_session_id),
|
|
&profile,
|
|
TraceLifecycleEvent::SessionRejected,
|
|
None,
|
|
Some(error.as_str()),
|
|
);
|
|
serve_decoy(request, vhost, true, &runtime).await
|
|
}
|
|
}
|
|
}
|