curl_ws_start_frame(3)
π¦ curl_ws_start_frame(3) π¦
okay bestie, this is a page from the Library Functions Manual and it's literally all about curl_ws_start_frame β the glow-up your WebSocket code has been waiting for π
β¨ NAME β¨
curl_ws_start_frame β start a new WebSocket frame π₯
β¨ SYNOPSIS β¨
#include <curl/curl.h>
CURLcode curl_ws_start_frame(CURL *curl,
unsigned int flags,
curl_off_t frame_len);
β¨ DESCRIPTION β¨
So what does this icon actually do? It adds the WebSocket frame header for the given flags and length to the transfer's send buffer for WebSocket encoded data π¦. It's intended for use inside a CURLOPT_READFUNCTION(3) callback. iykyk.
Here's the tea π΅ β when you're using a CURLOPT_READFUNCTION(3) in a WebSocket transfer, any data that function hands back gets sent as a CURLWS_BINARY frame, with the length being however much data was read. That's the default behaviour, no cap.
But what if you want larger frames, or frames of a different type? π That's the whole moment β call curl_ws_start_frame() from within the read function, and then return the data belonging to the frame. Main character energy.
π¨ Heads up: the function fails if a previous frame has not been completely read yet. It also fails in CURLWS_RAW_MODE. Not negotiable, bestie.
Okay so here's a lil detail that trips people up π β the read function in libcurl usually treats a return value of 0 as the end-of-file indication and stops any further reads. Which would mean you literally cannot send WebSocket frames of length 0. Tragic.
But plot twist π β if the read function calls curl_ws_start_frame(), then a return value of 0 is not treated as an end of file, and libcurl calls the read function again. Slay.
β¨ FLAGS β¨
Supports all flags documented in curl_ws_meta(3). All of them. Every single one. π
β¨ PROTOCOLS β¨
This functionality affects ws only β no other protocol gets an invite to this party π«Ά
β¨ EXAMPLE β¨
here's the whole thing, copy-paste ready, you're welcome π
#include <string.h> /* for strlen */
struct read_ctx {
CURL *easy;
const char *message;
size_t msg_len;
size_t nsent;
};
static size_t readcb(char *buf, size_t nitems, size_t buflen, void *p)
{
struct read_ctx *ctx = p;
size_t len = nitems * buflen;
size_t left = ctx->msg_len - ctx->nsent;
if(!ctx->nsent) {
CURLcode result;
/* Want to send TEXT frame. */
result = curl_ws_start_frame(ctx->easy, CURLWS_TEXT,
(curl_off_t)ctx->msg_len);
if(result != CURLE_OK) {
fprintf(stderr, "error starting frame: %d\n", (int)result);
return CURL_READFUNC_ABORT;
}
}
if(left) {
if(left < len)
len = left;
memcpy(buf, ctx->message + ctx->nsent, len);
ctx->nsent += len;
return len;
}
return 0;
}
int main(void)
{
CURL *easy;
struct read_ctx rctx;
CURLcode result;
easy = curl_easy_init();
if(!easy)
return 1;
curl_easy_setopt(easy, CURLOPT_URL, "wss://example.com");
curl_easy_setopt(easy, CURLOPT_READFUNCTION, readcb);
/* tell curl that we want to send the payload */
memset(&rctx, 0, sizeof(rctx));
rctx.easy = easy;
rctx.message = "Hello, friend!";
rctx.msg_len = strlen(rctx.message);
curl_easy_setopt(easy, CURLOPT_READDATA, &rctx);
curl_easy_setopt(easy, CURLOPT_UPLOAD, 1L);
/* Perform the request, result gets the return code */
result = curl_easy_perform(easy);
/* Check for errors */
if(result != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n",
curl_easy_strerror(result));
/* always cleanup */
curl_easy_cleanup(easy);
return 0;
}
β¨ AVAILABILITY β¨
Added in curl 8.16.0 π (yes, she's new-new, treat her right)
β¨ RETURN VALUE β¨
This function returns a CURLcode indicating success or error. Standard behaviour, no surprises.
CURLE_OK (0) means everything was OK π β non-zero means an error occurred, see libcurl-errors(3). And if CURLOPT_ERRORBUFFER(3) was set with curl_easy_setopt(3), there can be an error message stored in the error buffer when non-zero is returned. Little gift for your debugging era π
Now the interesting bit π β instead of blocking, the function returns CURLE_AGAIN. The correct behavior is then to wait for the socket to signal readability before calling this function again. Don't just spin, bestie, wait for the signal fr fr.
Any other non-zero return value indicates an error. See the libcurl-errors(3) man page for the full list with descriptions. All of them. π
β¨ SEE ALSO β¨
go read them, they're all besties with this one π
and that's the whole page, bestie π this came to you from libcurl, dated 2026-10-25 β take care of her, she's iconic ππ
π¦ where this page comes from
- alpine 3.23: curl-doc 8.23.0-r0 Β· curl
- alpine 3.24: curl-doc 8.23.0-r0 Β· curl
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms