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}; use serde::{Deserialize, Serialize};
#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DeviceInfo { pub struct DeviceInfo {
#[serde(default)]
pub udid: Option<String>, pub udid: Option<String>,
#[serde(default)]
pub open_udid: Option<String>, pub open_udid: Option<String>,
#[serde(default)]
pub device: Option<String>, pub device: Option<String>,
#[serde(default)]
pub os_version: Option<String>, pub os_version: Option<String>,
#[serde(default)]
pub android: bool, pub android: bool,
#[serde(default)]
pub preferred_language: String, pub preferred_language: String,
#[serde(default)]
pub client_major_version: i32, pub client_major_version: i32,
#[serde(default)]
pub client_minor_version: i32, pub client_minor_version: i32,
#[serde(default)]
pub client_build: i32, pub client_build: i32,
#[serde(default)]
pub resource_sha: Option<String>, pub resource_sha: Option<String>,
} }
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]

View file

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

View file

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

View file

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