aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
authorOgulcan Celik <ogulcancelik@gmail.com>2026-05-14 18:10:48 +0300
committerOgulcan Celik <ogulcancelik@gmail.com>2026-05-14 18:11:22 +0300
commitc269eeea138c002b46844b4d25c8af0c34e72447 (patch)
tree30e482d3205cd408dfdbe533ac1377b1a8c103e9 /src/server
parentfff55440305437b2e221156d9e6a5f59e7853213 (diff)
fix: allow larger kitty graphics frames
Diffstat (limited to 'src/server')
-rw-r--r--src/server/headless.rs30
-rw-r--r--src/server/protocol.rs5
2 files changed, 29 insertions, 6 deletions
diff --git a/src/server/headless.rs b/src/server/headless.rs
index 3ff3e587..11c486cb 100644
--- a/src/server/headless.rs
+++ b/src/server/headless.rs
@@ -37,7 +37,9 @@ use crate::detect::AgentState;
use crate::events::AppEvent;
use crate::layout::PaneId;
use crate::server::client_transport::{self, ClientWriter, ServerEvent};
-use crate::server::protocol::{self, FrameData, RenderEncoding, ServerMessage, MAX_FRAME_SIZE};
+use crate::server::protocol::{
+ self, FrameData, RenderEncoding, ServerMessage, MAX_FRAME_SIZE, MAX_GRAPHICS_FRAME_SIZE,
+};
use crate::server::render_stream::ClientRenderState;
// ---------------------------------------------------------------------------
@@ -1008,13 +1010,21 @@ impl HeadlessServer {
/// Encodes a server message into a length-prefixed frame.
fn frame_server_message(msg: &ServerMessage) -> Result<Vec<u8>, protocol::FramingError> {
+ Self::frame_server_message_with_max(msg, MAX_FRAME_SIZE)
+ }
+
+ /// Encodes a server message using an explicit payload cap.
+ fn frame_server_message_with_max(
+ msg: &ServerMessage,
+ max_frame_size: usize,
+ ) -> Result<Vec<u8>, protocol::FramingError> {
let mut framed = Vec::new();
protocol::write_message(&mut framed, msg)?;
let payload_len = framed.len().saturating_sub(4);
- if payload_len > MAX_FRAME_SIZE {
+ if payload_len > max_frame_size {
return Err(protocol::FramingError::Oversized {
claimed: payload_len,
- max: MAX_FRAME_SIZE,
+ max: max_frame_size,
});
}
Ok(framed)
@@ -1542,11 +1552,11 @@ impl HeadlessServer {
};
let mut commit_graphics_cache = true;
- if frame.graphics.len() > MAX_FRAME_SIZE {
+ if frame.graphics.len() > MAX_GRAPHICS_FRAME_SIZE {
warn!(
client_id,
graphics_bytes = frame.graphics.len(),
- max = MAX_FRAME_SIZE,
+ max = MAX_GRAPHICS_FRAME_SIZE,
"dropping oversized graphics payload for client frame"
);
frame.graphics.clear();
@@ -1559,7 +1569,15 @@ impl HeadlessServer {
};
let mut frame_to_commit = frame.clone();
- let serialized = match Self::frame_server_message(prepared.message()) {
+ let max_frame_size = if frame.graphics.is_empty() {
+ MAX_FRAME_SIZE
+ } else {
+ MAX_GRAPHICS_FRAME_SIZE
+ };
+ let serialized = match Self::frame_server_message_with_max(
+ prepared.message(),
+ max_frame_size,
+ ) {
Ok(framed) => framed,
Err(protocol::FramingError::Oversized { claimed, max })
if !frame.graphics.is_empty() =>
diff --git a/src/server/protocol.rs b/src/server/protocol.rs
index e0a3c4a0..1a220767 100644
--- a/src/server/protocol.rs
+++ b/src/server/protocol.rs
@@ -19,6 +19,11 @@ pub const PROTOCOL_VERSION: u32 = 5;
/// rejected to prevent denial-of-service via oversized length prefixes.
pub const MAX_FRAME_SIZE: usize = 2 * 1024 * 1024;
+/// Maximum allowed server-to-client frame payload when Kitty graphics are enabled.
+/// Normal traffic keeps `MAX_FRAME_SIZE`; this larger cap is only for explicit
+/// image payloads that are naturally much larger after base64 encoding.
+pub const MAX_GRAPHICS_FRAME_SIZE: usize = 32 * 1024 * 1024;
+
/// Length of the u32 little-endian length prefix in bytes.
const LENGTH_PREFIX_BYTES: usize = 4;