Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 30 additions & 3 deletions src/ios_webkit_debug_proxy.c
Original file line number Diff line number Diff line change
Expand Up @@ -1415,6 +1415,10 @@ rpc_status iwdp_on_applicationSentListing(rpc_t rpc,
return RPC_SUCCESS;
}

// Maximum WebSocket frame payload before splitting into continuation frames.
// 1 MB chunks reduce peak memory vs. buffering the entire payload at once.
#define WS_MAX_FRAME_PAYLOAD (1 << 20)

rpc_status iwdp_on_applicationSentData(rpc_t rpc,
const char *app_id, const char *dest_id,
const char *data, const size_t length) {
Expand All @@ -1424,9 +1428,32 @@ rpc_status iwdp_on_applicationSentData(rpc_t rpc,
return RPC_SUCCESS; // error but don't kill the inspector!
}
ws_t ws = iws->ws;
return ws->send_frame(ws,
true, OPCODE_TEXT, false,
data, length);
// Small messages: send as a single frame (fast path).
if (length <= WS_MAX_FRAME_PAYLOAD) {
return ws->send_frame(ws,
true, OPCODE_TEXT, false,
data, length);
}
// Large messages (e.g. heap snapshots): split into continuation frames
// to reduce peak memory. Each chunk is sent as its own WebSocket frame.
// Always pass OPCODE_TEXT — ws_send_frame tracks continuation state
// internally and converts non-first frames to OPCODE_CONTINUATION.
size_t offset = 0;
while (offset < length) {
size_t chunk = length - offset;
if (chunk > WS_MAX_FRAME_PAYLOAD) {
chunk = WS_MAX_FRAME_PAYLOAD;
}
bool is_last = (offset + chunk >= length);
ws_status ret = ws->send_frame(ws,
is_last, OPCODE_TEXT, false,
data + offset, chunk);
if (ret) {
return ret;
}
offset += chunk;
}
return RPC_SUCCESS;
}

rpc_status iwdp_on_applicationUpdated(rpc_t rpc,
Expand Down
5 changes: 3 additions & 2 deletions src/webinspector.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@
// TODO figure out exact value
#define MAX_RPC_LEN 8096 - 500

// some arbitrarly limit, to catch bad packets
#define MAX_BODY_LENGTH 1<<26
// some arbitrarily limit, to catch bad packets
// 256 MB — large enough for heap snapshots (50-200MB+) from heavy pages
#define MAX_BODY_LENGTH (1 << 28)

struct wi_private {
bool is_sim;
Expand Down
9 changes: 7 additions & 2 deletions src/websocket.c
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,10 @@ ws_status ws_send_frame(ws_t self,

size_t i;
bool is_utf8 = (opcode2 == OPCODE_TEXT ? true : false);
if (is_utf8) {
// Skip UTF-8 validation for large payloads (>1 MB) — the data is JSON from
// WebKit Inspector and is always valid UTF-8. Validating 50-200 MB payloads
// is expensive and unnecessary.
if (is_utf8 && payload_length <= (1 << 20)) {
unsigned int utf8_state = UTF8_VALID;
const char *payload_head = payload_data;
for (i = 0; i < payload_length; i++) {
Expand Down Expand Up @@ -336,7 +339,9 @@ ws_status ws_send_frame(ws_t self,
out_tail += payload_length;
}

if (!is_fin && !my->continued_opcode) {
if (is_fin) {
my->continued_opcode = 0;
} else if (!my->continued_opcode) {
my->continued_opcode = opcode;
}

Expand Down