I did this
Follow up to #22227. I'm fairly confident this is an actual bug now.
I expected the following
A receiver using the CURL_WRITEFUNC callback API reads chunks of incoming WebSocket frames using curl_ws_meta, which provides the information required to reconstruct the frames. We'd expect to receive exactly one such chunk per write callback invocation, so curl_ws_meta()->len should be equal to nmemb (number of bytes in the write callback buffer).
My reproducer sends three messages to be received: Hello 1, Hello 2, Hello 3, so I expect three callbacks. My receiver pauses before and after receiving Hello 1. During the fourth callback (after pausing) I observed nmemb == 14 and the buffer contains Hello 2Hello 3. For this callback, curl_ws_meta()->len returns 7, the length of the second message Hello 2.
There seems to be no way to handle this situation correctly, since it's impossible to get the metadata for the bytes [7; 13] with curl_ws_meta. If the write callback returns just 7, it results in CURLE_WRITE_ERROR since curl seems to expect the entire buffer to be consumed. Perhaps curl appends more bytes to the receive buffer internally, after unpausing in some lower level API that isn't aware of WebSocket frame boundaries?
client.c
server.py
Reproducer logs:
hhuebner@hhuebnerMacBookPro ws-meta-repro % python3 server.py &
[15] 90424
hhuebner@hhuebnerMacBookPro ws-meta-repro % listening on ws://127.0.0.1:8765
./client
client from 127.0.0.1:58981
handshake complete
sent frame 1: b'Hello 1'
callback #1 bytes=7 data='Hello 1'
meta age=0 flags=0x1 offset=0 bytesleft=0 len=7
returning CURL_WRITEFUNC_PAUSE
sent frame 2: b'Hello 2'
sent frame 3: b'Hello 3'
unpause #1 with curl_easy_pause(..., CURLPAUSE_CONT)
callback #2 bytes=7 data='Hello 1'
meta age=0 flags=0x1 offset=0 bytesleft=0 len=7
accepted callback #2
returning 7
callback #3 bytes=7 data='Hello 2'
meta age=0 flags=0x1 offset=0 bytesleft=0 len=7
returning CURL_WRITEFUNC_PAUSE
unpause #2 with curl_easy_pause(..., CURLPAUSE_CONT)
callback #4 bytes=14 data='Hello 2Hello 3'
meta age=0 flags=0x1 offset=0 bytesleft=0 len=7
accepted callback #4
returning 14
server done
done result=0 (No error)
hhuebner@hhuebnerMacBookPro ws-meta-repro %
[15] done python3 server.py
curl/libcurl version
8.21.0
operating system
macOS 26.3, Darwin 25.3.0 on arm64
I did this
Follow up to #22227. I'm fairly confident this is an actual bug now.
I expected the following
A receiver using the
CURL_WRITEFUNCcallback API reads chunks of incoming WebSocket frames usingcurl_ws_meta, which provides the information required to reconstruct the frames. We'd expect to receive exactly one such chunk per write callback invocation, socurl_ws_meta()->lenshould be equal tonmemb(number of bytes in the write callback buffer).My reproducer sends three messages to be received:
Hello 1,Hello 2,Hello 3, so I expect three callbacks. My receiver pauses before and after receivingHello 1. During the fourth callback (after pausing) I observednmemb == 14and the buffer containsHello 2Hello 3. For this callback,curl_ws_meta()->lenreturns7, the length of the second messageHello 2.There seems to be no way to handle this situation correctly, since it's impossible to get the metadata for the bytes
[7; 13]withcurl_ws_meta. If the write callback returns just7, it results inCURLE_WRITE_ERRORsince curl seems to expect the entire buffer to be consumed. Perhaps curl appends more bytes to the receive buffer internally, after unpausing in some lower level API that isn't aware of WebSocket frame boundaries?client.c
server.py
Reproducer logs:
curl/libcurl version
8.21.0
operating system
macOS 26.3, Darwin 25.3.0 on arm64