NetworkTransport drops a JSON message when a TCP receive contains a heartbeat followed by the message

Aperta
#322 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 2 giorni

@bbdu3 ci sta già lavorando.

Dal 6/11/2026.

  • #323 di @bbdu3 — aperta

Valutazione

Difficoltà
3/5
Tempo stimato
1-2 giorni
Idoneità per principianti
25/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Ferma
Stack tecnologico
swift
Ambito
networking

Direzione di ricerca

Read NetworkTransport.receiveLoop() and the heartbeat framing logic to see how received bytes are buffered and consumed. Then inspect the transport tests for a way to deliver multiple frames in one chunk; done means a heartbeat followed by a JSON-RPC response and a later response are both delivered in order. A linked open pull request is already addressing this issue.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

bug

NetworkTransport.receiveLoop() skips an entire received data chunk when Heartbeat.isHeartbeat(newData) returns true. This check only examines the first four bytes, so a chunk containing a heartbeat followed by a JSON-RPC message is treated entirely as a heartbeat.

How to trigger

The sender writes a 12-byte heartbeat, followed by a newline-delimited JSON-RPC message. TCP can deliver these two writes together in one receive callback:

[heartbeat][{"jsonrpc":"2.0","id":1,"result":{}}\n]

The receiver recognizes the heartbeat prefix and executes continue before reaching buffer.append(newData). The JSON-RPC message following the heartbeat is therefore discarded.

I validated this using a Python model of the relevant receive-loop logic, with a second response as a marker:

Separate chunks: heartbeat, response 1, response 2
Delivered response IDs: [1, 2]

Combined chunk: heartbeat + response 1, followed by response 2
Delivered response IDs: [2]

I also tested two separate writes over Linux TCP loopback. In 20 trials, the heartbeat and first response arrived together in one recv() call, with all bytes intact.

These checks establish that the input arrangement can occur over TCP and that the modeled receive logic drops the trailing message. I have not run the Swift SDK or Network.framework on macOS.

Impact

A JSON-RPC message following a heartbeat in the same receive chunk is not delivered through the transport’s receive stream. If it is a response, its pending request can remain unresolved until cancellation, disconnection, or an application-level timeout.

This report is limited to TCP and the case of a complete heartbeat followed by a complete JSON-RPC message in one receive callback.

my fix thought

The receiver should parse frames from buffered bytes instead of treating each receive callback as a complete application message. After identifying a complete heartbeat, it should consume only those 12 bytes and continue processing any remaining bytes.

A regression test could queue heartbeat + response 1 as one chunk, then response 2 as another, and assert that both responses are delivered in order. The same assertion should pass when the heartbeat and responses arrive separately.

If my repair strategy is right, I will open a PR to fix it.

Lingua principale
Swift
Stelle
1.6k
Fork
270
Merge medio
2g 7h
PR unite (30g)
15

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di modelcontextprotocol/swift-sdk

Tutte le issue di modelcontextprotocol/swift-sdk

Issue simili

Altre issue su Swift

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.