summaryrefslogtreecommitdiffstats
path: root/lib/ws.c
diff options
context:
space:
mode:
authorCurl Upstream <curl-library@lists.haxx.se>2022-12-21 07:00:59 (GMT)
committerBrad King <brad.king@kitware.com>2023-01-27 20:58:30 (GMT)
commitdac458ddbf2b48168779821654a7e69cbd828c14 (patch)
tree8191fa2b92ceb3469c8ac5878b4ded3112f5f16c /lib/ws.c
parentec122fff08ab9a8e56fb90126ecedb99c759011b (diff)
downloadCMake-dac458ddbf2b48168779821654a7e69cbd828c14.zip
CMake-dac458ddbf2b48168779821654a7e69cbd828c14.tar.gz
CMake-dac458ddbf2b48168779821654a7e69cbd828c14.tar.bz2
curl 2022-12-21 (c12fb3dd)
Code extracted from: https://github.com/curl/curl.git at commit c12fb3ddaf48e709a7a4deaa55ec485e4df163ee (curl-7_87_0).
Diffstat (limited to 'lib/ws.c')
-rw-r--r--lib/ws.c369
1 files changed, 178 insertions, 191 deletions
diff --git a/lib/ws.c b/lib/ws.c
index a673446..c1b2622 100644
--- a/lib/ws.c
+++ b/lib/ws.c
@@ -172,10 +172,9 @@ CURLcode Curl_ws_accept(struct Curl_easy *data)
/* remove the spent bytes from the beginning of the buffer as that part has
now been delivered to the application */
-static void ws_decode_clear(struct Curl_easy *data)
+static void ws_decode_shift(struct Curl_easy *data, size_t spent)
{
struct websocket *wsp = &data->req.p.http->ws;
- size_t spent = wsp->usedbuf;
size_t len = Curl_dyn_len(&wsp->buf);
size_t keep = len - spent;
DEBUGASSERT(len >= spent);
@@ -198,42 +197,27 @@ static void ws_decode_clear(struct Curl_easy *data)
*/
static CURLcode ws_decode(struct Curl_easy *data,
- unsigned char *wpkt, size_t ilen,
- unsigned char **out, size_t *olen,
+ unsigned char *inbuf, size_t inlen,
+ size_t *headlen, size_t *olen,
curl_off_t *oleft,
- bool *more,
unsigned int *flags)
{
bool fin;
unsigned char opcode;
curl_off_t total;
size_t dataindex = 2;
- curl_off_t plen; /* size of data in the buffer */
curl_off_t payloadsize;
- struct websocket *wsp = &data->req.p.http->ws;
- unsigned char *p;
- CURLcode result;
- *olen = 0;
+ *olen = *headlen = 0;
- /* add the incoming bytes, if any */
- if(wpkt) {
- result = Curl_dyn_addn(&wsp->buf, wpkt, ilen);
- if(result)
- return result;
- }
-
- plen = Curl_dyn_len(&wsp->buf);
- if(plen < 2) {
+ if(inlen < 2) {
/* the smallest possible frame is two bytes */
- infof(data, "WS: plen == %u, EAGAIN", (int)plen);
+ infof(data, "WS: plen == %u, EAGAIN", (int)inlen);
return CURLE_AGAIN;
}
- p = Curl_dyn_uptr(&wsp->buf);
-
- fin = p[0] & WSBIT_FIN;
- opcode = p[0] & WSBIT_OPCODE_MASK;
+ fin = inbuf[0] & WSBIT_FIN;
+ opcode = inbuf[0] & WSBIT_OPCODE_MASK;
infof(data, "WS:%d received FIN bit %u", __LINE__, (int)fin);
*flags = 0;
switch(opcode) {
@@ -264,62 +248,56 @@ static CURLcode ws_decode(struct Curl_easy *data,
break;
}
- if(p[1] & WSBIT_MASK) {
+ if(inbuf[1] & WSBIT_MASK) {
/* A client MUST close a connection if it detects a masked frame. */
failf(data, "WS: masked input frame");
return CURLE_RECV_ERROR;
}
- payloadsize = p[1];
+ payloadsize = inbuf[1];
if(payloadsize == 126) {
- if(plen < 4) {
- infof(data, "WS:%d plen == %u, EAGAIN", __LINE__, (int)plen);
+ if(inlen < 4) {
+ infof(data, "WS:%d plen == %u, EAGAIN", __LINE__, (int)inlen);
return CURLE_AGAIN; /* not enough data available */
}
- payloadsize = (p[2] << 8) | p[3];
+ payloadsize = (inbuf[2] << 8) | inbuf[3];
dataindex += 2;
}
else if(payloadsize == 127) {
/* 64 bit payload size */
- if(plen < 10)
+ if(inlen < 10)
return CURLE_AGAIN;
- if(p[2] & 80) {
+ if(inbuf[2] & 80) {
failf(data, "WS: too large frame");
return CURLE_RECV_ERROR;
}
dataindex += 8;
- payloadsize = ((curl_off_t)p[2] << 56) |
- (curl_off_t)p[3] << 48 |
- (curl_off_t)p[4] << 40 |
- (curl_off_t)p[5] << 32 |
- (curl_off_t)p[6] << 24 |
- (curl_off_t)p[7] << 16 |
- (curl_off_t)p[8] << 8 |
- p[9];
+ payloadsize = ((curl_off_t)inbuf[2] << 56) |
+ (curl_off_t)inbuf[3] << 48 |
+ (curl_off_t)inbuf[4] << 40 |
+ (curl_off_t)inbuf[5] << 32 |
+ (curl_off_t)inbuf[6] << 24 |
+ (curl_off_t)inbuf[7] << 16 |
+ (curl_off_t)inbuf[8] << 8 |
+ inbuf[9];
}
+ /* point to the payload */
+ *headlen = dataindex;
total = dataindex + payloadsize;
- if(total > plen) {
- /* deliver a partial frame */
- *oleft = total - dataindex;
+ if(total > (curl_off_t)inlen) {
+ /* buffer contains partial frame */
+ *olen = inlen - dataindex; /* bytes to write out */
+ *oleft = total - inlen; /* bytes yet to come (for this frame) */
payloadsize = total - dataindex;
}
else {
- *oleft = 0;
- if(plen > total)
- /* there is another fragment after */
- *more = TRUE;
+ /* we have the complete frame (`total` bytes) in buffer */
+ *olen = payloadsize; /* bytes to write out */
+ *oleft = 0; /* bytes yet to come (for this frame) */
}
- /* point to the payload */
- *out = &p[dataindex];
-
- /* return the payload length */
- *olen = payloadsize;
-
- /* number of bytes "used" from the buffer */
- wsp->usedbuf = dataindex + payloadsize;
- infof(data, "WS: received %zu bytes payload (%zu left)",
- payloadsize, *oleft);
+ infof(data, "WS: received %zu bytes payload (%zu left, buflen was %zu)",
+ payloadsize, *oleft, inlen);
return CURLE_OK;
}
@@ -332,76 +310,83 @@ size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */,
{
struct HTTP *ws = (struct HTTP *)userp;
struct Curl_easy *data = ws->ws.data;
+ struct websocket *wsp = &data->req.p.http->ws;
void *writebody_ptr = data->set.out;
if(data->set.ws_raw_mode)
return data->set.fwrite_func(buffer, size, nitems, writebody_ptr);
else if(nitems) {
- unsigned char *frame = NULL;
- size_t flen = 0;
- size_t wrote = 0;
+ size_t wrote = 0, headlen;
CURLcode result;
- bool more; /* there's is more to parse in the buffer */
- curl_off_t oleft;
-
- decode:
- more = FALSE;
- oleft = ws->ws.frame.bytesleft;
- if(!oleft) {
- unsigned int recvflags;
- result = ws_decode(data, (unsigned char *)buffer, nitems,
- &frame, &flen, &oleft, &more, &recvflags);
- if(result == CURLE_AGAIN)
- /* insufficient amount of data, keep it for later */
- return nitems;
- else if(result) {
- infof(data, "WS: decode error %d", (int)result);
+
+ if(buffer) {
+ result = Curl_dyn_addn(&wsp->buf, buffer, nitems);
+ if(result) {
+ infof(data, "WS: error adding data to buffer %d", (int)result);
return nitems - 1;
}
- /* Store details about the frame to be reachable with curl_ws_meta()
- from within the write callback */
- ws->ws.frame.age = 0;
- ws->ws.frame.offset = 0;
- ws->ws.frame.flags = recvflags;
- ws->ws.frame.bytesleft = oleft;
+ buffer = NULL;
}
- else {
- if(nitems > (size_t)ws->ws.frame.bytesleft) {
- nitems = ws->ws.frame.bytesleft;
- more = TRUE;
+
+ while(Curl_dyn_len(&wsp->buf)) {
+ unsigned char *wsbuf = Curl_dyn_uptr(&wsp->buf);
+ size_t buflen = Curl_dyn_len(&wsp->buf);
+ size_t write_len = 0;
+ size_t consumed = 0;
+
+ if(!ws->ws.frame.bytesleft) {
+ unsigned int recvflags;
+ curl_off_t fb_left;
+
+ result = ws_decode(data, wsbuf, buflen,
+ &headlen, &write_len, &fb_left, &recvflags);
+ consumed += headlen;
+ wsbuf += headlen;
+ buflen -= headlen;
+ if(result == CURLE_AGAIN)
+ /* insufficient amount of data, keep it for later.
+ * we pretend to have written all since we have a copy */
+ return nitems;
+ else if(result) {
+ infof(data, "WS: decode error %d", (int)result);
+ return nitems - 1;
+ }
+ /* New frame. store details about the frame to be reachable with
+ curl_ws_meta() from within the write callback */
+ ws->ws.frame.age = 0;
+ ws->ws.frame.offset = 0;
+ ws->ws.frame.flags = recvflags;
+ ws->ws.frame.bytesleft = fb_left;
}
- else
- more = FALSE;
- ws->ws.frame.offset += nitems;
- ws->ws.frame.bytesleft -= nitems;
- frame = (unsigned char *)buffer;
- flen = nitems;
- }
- if((ws->ws.frame.flags & CURLWS_PING) && !oleft) {
- /* auto-respond to PINGs, only works for single-frame payloads atm */
- size_t bytes;
- infof(data, "WS: auto-respond to PING with a PONG");
- DEBUGASSERT(frame);
- /* send back the exact same content as a PONG */
- result = curl_ws_send(data, frame, flen, &bytes, 0, CURLWS_PONG);
- if(result)
- return result;
- }
- else {
- /* deliver the decoded frame to the user callback */
- Curl_set_in_callback(data, true);
- wrote = data->set.fwrite_func((char *)frame, 1, flen, writebody_ptr);
- Curl_set_in_callback(data, false);
- if(wrote != flen)
- return 0;
- }
- if(oleft)
- ws->ws.frame.offset += flen;
- /* the websocket frame has been delivered */
- ws_decode_clear(data);
- if(more) {
- /* there's more websocket data to deal with in the buffer */
- buffer = NULL; /* the buffer as been drained already */
- goto decode;
+ else {
+ /* continuing frame */
+ write_len = (size_t)ws->ws.frame.bytesleft;
+ if(write_len > buflen)
+ write_len = buflen;
+ ws->ws.frame.offset += write_len;
+ ws->ws.frame.bytesleft -= write_len;
+ }
+ if((ws->ws.frame.flags & CURLWS_PING) && !ws->ws.frame.bytesleft) {
+ /* auto-respond to PINGs, only works for single-frame payloads atm */
+ size_t bytes;
+ infof(data, "WS: auto-respond to PING with a PONG");
+ /* send back the exact same content as a PONG */
+ result = curl_ws_send(data, wsbuf, write_len,
+ &bytes, 0, CURLWS_PONG);
+ if(result)
+ return result;
+ }
+ else if(write_len || !wsp->frame.bytesleft) {
+ /* deliver the decoded frame to the user callback */
+ Curl_set_in_callback(data, true);
+ wrote = data->set.fwrite_func((char *)wsbuf, 1,
+ write_len, writebody_ptr);
+ Curl_set_in_callback(data, false);
+ if(wrote != write_len)
+ return 0;
+ }
+ /* get rid of the buffered data consumed */
+ consumed += write_len;
+ ws_decode_shift(data, consumed);
}
}
return nitems;
@@ -412,9 +397,9 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
size_t buflen, size_t *nread,
struct curl_ws_frame **metap)
{
- size_t bytes;
CURLcode result;
struct websocket *wsp = &data->req.p.http->ws;
+ bool done = FALSE; /* not filled passed buffer yet */
*nread = 0;
*metap = NULL;
@@ -423,84 +408,81 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
if(result)
return result;
- do {
- bool drain = FALSE; /* if there is pending buffered data to drain */
- char *inbuf = data->state.buffer;
- bytes = wsp->stillbuffer;
- if(!bytes) {
+ while(!done) {
+ size_t write_len;
+ unsigned int recvflags;
+
+ if(!wsp->stillblen) {
+ /* try to get more data */
+ size_t n;
result = curl_easy_recv(data, data->state.buffer,
- data->set.buffer_size, &bytes);
+ data->set.buffer_size, &n);
if(result)
return result;
+ if(!n)
+ /* connection closed */
+ return CURLE_GOT_NOTHING;
+ wsp->stillb = data->state.buffer;
+ wsp->stillblen = n;
+ }
+
+ infof(data, "WS: got %u websocket bytes to decode",
+ (int)wsp->stillblen);
+ if(!wsp->frame.bytesleft) {
+ size_t headlen;
+ curl_off_t oleft;
+ /* detect new frame */
+ result = ws_decode(data, (unsigned char *)wsp->stillb, wsp->stillblen,
+ &headlen, &write_len, &oleft, &recvflags);
+ if(result == CURLE_AGAIN)
+ /* a packet fragment only */
+ break;
+ else if(result)
+ return result;
+ wsp->stillb += headlen;
+ wsp->stillblen -= headlen;
+ wsp->frame.offset = 0;
+ wsp->frame.bytesleft = oleft;
+ wsp->frame.flags = recvflags;
}
else {
- /* the pending bytes can be found here */
- inbuf = wsp->stillb;
- drain = TRUE;
+ /* existing frame, remaining payload handling */
+ write_len = wsp->frame.bytesleft;
+ if(write_len > wsp->stillblen)
+ write_len = wsp->stillblen;
}
- if(bytes) {
- unsigned char *out;
- size_t olen;
- bool more;
- unsigned int recvflags;
- curl_off_t oleft = wsp->frame.bytesleft;
-
- infof(data, "WS: got %u websocket bytes to decode", (int)bytes);
- if(!oleft && !drain) {
- result = ws_decode(data, (unsigned char *)inbuf, bytes,
- &out, &olen, &oleft, &more, &recvflags);
- if(result == CURLE_AGAIN)
- /* a packet fragment only */
- break;
- else if(result)
- return result;
- wsp->frame.offset = 0;
- wsp->frame.bytesleft = oleft;
- wsp->frame.flags = recvflags;
- }
- else {
- olen = oleft;
- out = (unsigned char *)wsp->stillb;
- recvflags = wsp->frame.flags;
- if((curl_off_t)buflen < oleft)
- /* there is still data left after this */
- wsp->frame.bytesleft -= buflen;
- else
- wsp->frame.bytesleft = 0;
- }
- /* auto-respond to PINGs */
- if((recvflags & CURLWS_PING) && !oleft) {
- infof(data, "WS: auto-respond to PING with a PONG");
- /* send back the exact same content as a PONG */
- result = curl_ws_send(data, out, olen, &bytes, 0, CURLWS_PONG);
- if(result)
- return result;
- }
- else {
- if(olen < buflen) {
- /* copy the payload to the user buffer */
- memcpy(buffer, out, olen);
- *nread = olen;
- if(!oleft)
- /* websocket frame has been delivered */
- ws_decode_clear(data);
- }
- else {
- /* copy a partial payload */
- memcpy(buffer, out, buflen);
- *nread = buflen;
- /* remember what is left and where */
- wsp->stillbuffer = olen - buflen;
- wsp->stillb = (char *)buffer + buflen;
- }
- wsp->frame.offset += *nread;
- }
+ /* auto-respond to PINGs */
+ if((wsp->frame.flags & CURLWS_PING) && !wsp->frame.bytesleft) {
+ infof(data, "WS: auto-respond to PING with a PONG");
+ /* send back the exact same content as a PONG */
+ result = curl_ws_send(data, wsp->stillb, write_len,
+ &write_len, 0, CURLWS_PONG);
+ if(result)
+ return result;
}
- else
- *nread = bytes;
- break;
- } while(1);
+ else if(write_len || !wsp->frame.bytesleft) {
+ if(write_len > buflen)
+ write_len = buflen;
+ /* copy the payload to the user buffer */
+ memcpy(buffer, wsp->stillb, write_len);
+ *nread = write_len;
+ done = TRUE;
+ }
+ if(write_len) {
+ /* update buffer and frame info */
+ wsp->frame.offset += write_len;
+ DEBUGASSERT(wsp->frame.bytesleft >= (curl_off_t)write_len);
+ if(wsp->frame.bytesleft)
+ wsp->frame.bytesleft -= write_len;
+ DEBUGASSERT(write_len <= wsp->stillblen);
+ wsp->stillblen -= write_len;
+ if(wsp->stillblen)
+ wsp->stillb += write_len;
+ else
+ wsp->stillb = NULL;
+ }
+ }
*metap = &wsp->frame;
return CURLE_OK;
}
@@ -649,9 +631,14 @@ CURL_EXTERN CURLcode curl_ws_send(struct Curl_easy *data, const void *buffer,
return CURLE_OK;
/* raw mode sends exactly what was requested, and this is from within
the write callback */
- if(Curl_is_in_callback(data))
+ if(Curl_is_in_callback(data)) {
+ if(!data->conn) {
+ failf(data, "No associated connection");
+ return CURLE_SEND_ERROR;
+ }
result = Curl_write(data, data->conn->writesockfd, buffer, buflen,
&written);
+ }
else
result = Curl_senddata(data, buffer, buflen, &written);
@@ -732,7 +719,7 @@ CURL_EXTERN CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen,
(void)buflen;
(void)nread;
(void)metap;
- return CURLE_OK;
+ return CURLE_NOT_BUILT_IN;
}
CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer,
@@ -746,7 +733,7 @@ CURL_EXTERN CURLcode curl_ws_send(CURL *curl, const void *buffer,
(void)sent;
(void)framesize;
(void)sendflags;
- return CURLE_OK;
+ return CURLE_NOT_BUILT_IN;
}
CURL_EXTERN struct curl_ws_frame *curl_ws_meta(struct Curl_easy *data)