mirror of
https://github.com/telemt/telemt.git
synced 2026-09-05 18:16:06 +03:00
c75cf5cc9d
Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
118 lines
4.0 KiB
Rust
118 lines
4.0 KiB
Rust
use std::sync::Arc;
|
|
use std::time::Duration;
|
|
|
|
use hyper::header::{self, HeaderName, HeaderValue};
|
|
use hyper::{Request, StatusCode};
|
|
|
|
use super::body::{CollectBodyError, CollectedBody, RequestBody, collect_body};
|
|
use super::decoy::serve_decoy;
|
|
use super::request::canonical_u64_header;
|
|
use super::{
|
|
HttpResponse, carrier_empty, carrier_headers, carrier_lane, full_response, insert_header,
|
|
request_trace, service_unavailable,
|
|
};
|
|
use crate::config::WebRuntimeVhost;
|
|
use crate::web::manager::{ManagerError, TokenHash, WebProcessRuntime};
|
|
use crate::web::trace::{TraceDirection, TraceRoute};
|
|
|
|
/// Handles one authenticated long-poll downlink exchange.
|
|
pub(super) async fn handle_down(
|
|
request: Request<RequestBody>,
|
|
runtime: Arc<WebProcessRuntime>,
|
|
vhost: Arc<WebRuntimeVhost>,
|
|
token_hash: TokenHash,
|
|
) -> HttpResponse {
|
|
if request.method() != hyper::Method::POST
|
|
|| request.headers().contains_key(header::CONTENT_TYPE)
|
|
{
|
|
return serve_decoy(request, vhost, true, &runtime).await;
|
|
}
|
|
let Some(cursor) = canonical_u64_header(&request, "x-down-cursor") else {
|
|
return serve_decoy(request, vhost, true, &runtime).await;
|
|
};
|
|
let Ok(session) = runtime.get_session(token_hash, &vhost.host) else {
|
|
return serve_decoy(request, vhost, true, &runtime).await;
|
|
};
|
|
if session.carrier().uses_websocket() {
|
|
return serve_decoy(request, vhost, true, &runtime).await;
|
|
}
|
|
if let Some(trace) = request_trace(&request) {
|
|
trace.set_route(TraceRoute::Downlink);
|
|
trace.bind_identity(session.trace_identity());
|
|
}
|
|
let Some(lane_id) = carrier_lane(&request, session.carrier()) else {
|
|
return serve_decoy(request, vhost, true, &runtime).await;
|
|
};
|
|
let CollectedBody {
|
|
request,
|
|
body,
|
|
_body_budget,
|
|
} = match collect_body(
|
|
request,
|
|
&runtime,
|
|
Duration::from_secs(session.timeouts().body_secs),
|
|
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() {
|
|
return serve_decoy(request, vhost, true, &runtime).await;
|
|
}
|
|
let Some(_down_poll) = runtime.try_lane_poll(false) else {
|
|
return service_unavailable();
|
|
};
|
|
let _control_lane_poll = if lane_id == Some(0) {
|
|
let Some(permit) = runtime.try_lane_poll(true) else {
|
|
return service_unavailable();
|
|
};
|
|
Some(permit)
|
|
} else {
|
|
None
|
|
};
|
|
let result = match lane_id {
|
|
Some(lane_id) => session.poll_down_lane(lane_id, cursor).await,
|
|
None => session.poll_down(cursor).await,
|
|
};
|
|
match result {
|
|
Ok(result) if result.body.is_empty() => {
|
|
let mut response = carrier_empty(StatusCode::NO_CONTENT);
|
|
insert_header(
|
|
&mut response,
|
|
HeaderName::from_static("x-down-cursor"),
|
|
&result.next_cursor.to_string(),
|
|
);
|
|
if result.lane_closed {
|
|
response.headers_mut().insert(
|
|
HeaderName::from_static("x-lane-closed"),
|
|
HeaderValue::from_static("1"),
|
|
);
|
|
}
|
|
response
|
|
}
|
|
Ok(result) => {
|
|
if let Some(trace) = request_trace(&request) {
|
|
trace.record_frames(TraceDirection::Response, &result.body, session.limits());
|
|
}
|
|
let mut response = full_response(StatusCode::OK, result.body);
|
|
carrier_headers(&mut response);
|
|
insert_header(
|
|
&mut response,
|
|
HeaderName::from_static("x-down-cursor"),
|
|
&result.next_cursor.to_string(),
|
|
);
|
|
response
|
|
}
|
|
Err(ManagerError::Concurrent | ManagerError::Backpressure | ManagerError::Limit) => {
|
|
service_unavailable()
|
|
}
|
|
Err(_) => serve_decoy(request, vhost, true, &runtime).await,
|
|
}
|
|
}
|