fix: handle empty writes (#84)

This commit is contained in:
MorganaFuture
2025-07-22 15:14:26 +03:00
committed by GitHub
parent 128645547d
commit 2697a3a1e1
2 changed files with 136 additions and 0 deletions

View File

@@ -154,6 +154,26 @@ proc send(
): Future[int] {.async.} =
let written = addr result
var datagram = trySend(connection, streamId, messagePtr, messageLen, written, isFin)
# Special handling for empty data - avoid infinite waiting
if messageLen == 0:
if isFin:
# For FIN-only packets, handle them properly without waiting
if datagram.data.len > 0:
connection.onSend(datagram)
else:
# Force a general send to flush any pending packets
connection.send()
connection.updateExpiryTimer()
return
else:
# For empty writes without FIN, treat as no-op
# Return 0 bytes written since there was nothing to write
result = 0
connection.updateExpiryTimer()
return
# Normal flow control for data packets
while datagram.data.len == 0:
connection.flowing.clear()
await connection.flowing.wait()

View File

@@ -242,3 +242,119 @@ suite "streams":
await clientStream.write(@[4'u8, 5'u8, 6'u8])
await simulation.cancelAndWait()
asyncTest "empty write + closeWrite pattern works":
let simulation = simulateNetwork(client, server)
let clientStream = await client.openStream()
await clientStream.write(@[])
await clientStream.closeWrite()
await simulation.cancelAndWait()
asyncTest "empty write + data + closeWrite (libp2p pattern) works":
let simulation = simulateNetwork(client, server)
var uploadData = @[1'u8, 2'u8, 3'u8, 4'u8, 5'u8]
let clientStream = await client.openStream()
await clientStream.write(@[])
await clientStream.write(uploadData)
await clientStream.closeWrite()
let serverStream = await server.incomingStream()
let received = await serverStream.read()
check received == uploadData
await simulation.cancelAndWait()
asyncTest "multiple empty writes before closeWrite works":
let simulation = simulateNetwork(client, server)
let clientStream = await client.openStream()
await clientStream.write(@[])
await clientStream.write(@[])
await clientStream.closeWrite()
await simulation.cancelAndWait()
asyncTest "closeWrite immediately after openStream works":
let simulation = simulateNetwork(client, server)
let clientStream = await client.openStream()
await clientStream.closeWrite()
await simulation.cancelAndWait()
asyncTest "perf-like upload/download pattern works":
let simulation = simulateNetwork(client, server)
var uploadData = @[6'u8, 7'u8, 8'u8, 9'u8, 10'u8]
let clientStream = await client.openStream()
await clientStream.write(@[])
await clientStream.write(uploadData)
await clientStream.closeWrite()
let serverStream = await server.incomingStream()
let received = await serverStream.read()
check received == uploadData
# Server sends response back
var downloadData = @[11'u8, 12'u8, 13'u8, 14'u8, 15'u8]
await serverStream.write(downloadData)
await serverStream.closeWrite()
let response = await clientStream.read()
check response == downloadData
await simulation.cancelAndWait()
asyncTest "large data transfers with empty write activation work":
let simulation = simulateNetwork(client, server)
var largeData = newSeq[uint8](1000)
for i in 0..<1000:
largeData[i] = uint8(i mod 256)
let clientStream = await client.openStream()
await clientStream.write(@[])
await clientStream.write(largeData)
await clientStream.closeWrite()
let serverStream = await server.incomingStream()
let received = await serverStream.read()
check received == largeData
await simulation.cancelAndWait()
asyncTest "multiple data chunks after empty write activation work":
let simulation = simulateNetwork(client, server)
var chunk1 = @[20'u8, 21'u8, 22'u8]
var chunk2 = @[23'u8, 24'u8, 25'u8]
var chunk3 = @[26'u8, 27'u8, 28'u8]
let clientStream = await client.openStream()
await clientStream.write(@[])
await clientStream.write(chunk1)
await clientStream.write(chunk2)
await clientStream.write(chunk3)
await clientStream.closeWrite()
let serverStream = await server.incomingStream()
var allReceived: seq[uint8]
# Read all chunks
let received1 = await serverStream.read()
allReceived.add(received1)
try:
let received2 = await serverStream.read()
allReceived.add(received2)
let received3 = await serverStream.read()
allReceived.add(received3)
except:
# May receive combined chunks due to TCP-like behavior
discard
var expectedData = chunk1 & chunk2 & chunk3
check allReceived == expectedData
await simulation.cancelAndWait()