time out a stalled frame body and drop the dead serde defaults

a peer that sends a length then stalls is cut after 30s; idle pooled
connections waiting for the next request are left alone. the serde
defaults were no-ops under a positional binary format.
This commit is contained in:
WiseDev 2026-08-28 23:47:34 +03:00
parent 5c35bef330
commit 0f563891c5
4 changed files with 9 additions and 13 deletions

View file

@ -3,25 +3,15 @@ use crate::game::AccountRef;
use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DeviceInfo {
#[serde(default)]
pub udid: Option<String>,
#[serde(default)]
pub open_udid: Option<String>,
#[serde(default)]
pub device: Option<String>,
#[serde(default)]
pub os_version: Option<String>,
#[serde(default)]
pub android: bool,
#[serde(default)]
pub preferred_language: String,
#[serde(default)]
pub client_major_version: i32,
#[serde(default)]
pub client_minor_version: i32,
#[serde(default)]
pub client_build: i32,
#[serde(default)]
pub resource_sha: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]

View file

@ -10,6 +10,8 @@ pub enum RpcError {
FrameTooLarge { actual: usize, limit: usize },
#[error("peer closed the connection")]
Closed,
#[error("peer stalled mid-frame")]
Timeout,
#[error("service rejected the call: {0}")]
Rejected(String),
#[error("service is unavailable: {0}")]

View file

@ -2,8 +2,10 @@ use crate::error::{RpcError, RpcResult, WireError};
use serde::{de::DeserializeOwned, Serialize};
use std::sync::Arc;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::time::{timeout, Duration};
use tokio::net::{TcpListener, TcpStream};
pub const MAX_FRAME_LEN: usize = 8 * 1024 * 1024;
pub const BODY_READ_TIMEOUT: Duration = Duration::from_secs(30);
#[async_trait::async_trait]
pub trait RpcService: Send + Sync + 'static {
type Request: DeserializeOwned + Send;
@ -56,8 +58,11 @@ pub async fn read_frame(stream: &mut TcpStream) -> RpcResult<Vec<u8>> {
});
}
let mut payload = vec![0u8; length];
stream.read_exact(&mut payload).await?;
Ok(payload)
match timeout(BODY_READ_TIMEOUT, stream.read_exact(&mut payload)).await {
Ok(Ok(_)) => Ok(payload),
Ok(Err(error)) => Err(error.into()),
Err(_) => Err(RpcError::Timeout),
}
}
pub async fn write_frame(stream: &mut TcpStream, payload: &[u8]) -> RpcResult<()> {
if payload.len() > MAX_FRAME_LEN {

View file

@ -2,7 +2,6 @@ use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct WireMessage {
pub message_type: u16,
#[serde(default)]
pub message_version: u16,
pub payload: Vec<u8>,
}