ws: fix autoping handling

Reported-by: Alexey Savchuk
Fixes #10289
Closes #10294
This commit is contained in:
Daniel Stenberg 2023-01-13 13:34:18 +01:00
parent 521da2dbd3
commit abae4e31a2
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2

View File

@ -183,12 +183,12 @@ static void ws_decode_shift(struct Curl_easy *data, size_t spent)
/* ws_decode() decodes a binary frame into structured WebSocket data, /* ws_decode() decodes a binary frame into structured WebSocket data,
wpkt - the incoming raw data. If NULL, work on the already buffered data. data - the transfer
ilen - the size of the provided data, perhaps too little, perhaps too much inbuf - incoming raw data. If NULL, work on the already buffered data.
out - stored pointed to extracted data inlen - size of the provided data, perhaps too little, perhaps too much
headlen - stored length of the frame header
olen - stored length of the extracted data olen - stored length of the extracted data
oleft - number of unread bytes pending to that belongs to this frame oleft - number of unread bytes pending to that belongs to this frame
more - if there is more data in there
flags - stored bitmask about the frame flags - stored bitmask about the frame
Returns CURLE_AGAIN if there is only a partial frame in the buffer. Then it Returns CURLE_AGAIN if there is only a partial frame in the buffer. Then it
@ -339,9 +339,6 @@ size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */,
result = ws_decode(data, wsbuf, buflen, result = ws_decode(data, wsbuf, buflen,
&headlen, &write_len, &fb_left, &recvflags); &headlen, &write_len, &fb_left, &recvflags);
consumed += headlen;
wsbuf += headlen;
buflen -= headlen;
if(result == CURLE_AGAIN) if(result == CURLE_AGAIN)
/* insufficient amount of data, keep it for later. /* insufficient amount of data, keep it for later.
* we pretend to have written all since we have a copy */ * we pretend to have written all since we have a copy */
@ -350,6 +347,10 @@ size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */,
infof(data, "WS: decode error %d", (int)result); infof(data, "WS: decode error %d", (int)result);
return nitems - 1; return nitems - 1;
} }
consumed += headlen;
wsbuf += headlen;
buflen -= headlen;
/* New frame. store details about the frame to be reachable with /* New frame. store details about the frame to be reachable with
curl_ws_meta() from within the write callback */ curl_ws_meta() from within the write callback */
ws->ws.frame.age = 0; ws->ws.frame.age = 0;
@ -392,7 +393,6 @@ size_t Curl_ws_writecb(char *buffer, size_t size /* 1 */,
return nitems; return nitems;
} }
CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer, CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
size_t buflen, size_t *nread, size_t buflen, size_t *nread,
struct curl_ws_frame **metap) struct curl_ws_frame **metap)
@ -409,7 +409,7 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
return result; return result;
while(!done) { while(!done) {
size_t write_len; size_t datalen;
unsigned int recvflags; unsigned int recvflags;
if(!wsp->stillblen) { if(!wsp->stillblen) {
@ -433,7 +433,7 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
curl_off_t oleft; curl_off_t oleft;
/* detect new frame */ /* detect new frame */
result = ws_decode(data, (unsigned char *)wsp->stillb, wsp->stillblen, result = ws_decode(data, (unsigned char *)wsp->stillb, wsp->stillblen,
&headlen, &write_len, &oleft, &recvflags); &headlen, &datalen, &oleft, &recvflags);
if(result == CURLE_AGAIN) if(result == CURLE_AGAIN)
/* a packet fragment only */ /* a packet fragment only */
break; break;
@ -447,38 +447,45 @@ CURL_EXTERN CURLcode curl_ws_recv(struct Curl_easy *data, void *buffer,
} }
else { else {
/* existing frame, remaining payload handling */ /* existing frame, remaining payload handling */
write_len = wsp->frame.bytesleft; datalen = wsp->frame.bytesleft;
if(write_len > wsp->stillblen) if(datalen > wsp->stillblen)
write_len = wsp->stillblen; datalen = wsp->stillblen;
} }
/* auto-respond to PINGs */ /* auto-respond to PINGs */
if((wsp->frame.flags & CURLWS_PING) && !wsp->frame.bytesleft) { if((wsp->frame.flags & CURLWS_PING) && !wsp->frame.bytesleft) {
infof(data, "WS: auto-respond to PING with a PONG"); size_t nsent = 0;
infof(data, "WS: auto-respond to PING with a PONG, %zu bytes payload",
datalen);
/* send back the exact same content as a PONG */ /* send back the exact same content as a PONG */
result = curl_ws_send(data, wsp->stillb, write_len, result = curl_ws_send(data, wsp->stillb, datalen, &nsent, 0,
&write_len, 0, CURLWS_PONG); CURLWS_PONG);
if(result) if(result)
return result; return result;
infof(data, "WS: bytesleft %zu datalen %zu",
wsp->frame.bytesleft, datalen);
/* we handled the data part of the PING, advance over that */
wsp->stillb += nsent;
wsp->stillblen -= nsent;
} }
else if(write_len || !wsp->frame.bytesleft) { else if(datalen) {
if(write_len > buflen) /* there is payload for the user */
write_len = buflen; if(datalen > buflen)
datalen = buflen;
/* copy the payload to the user buffer */ /* copy the payload to the user buffer */
memcpy(buffer, wsp->stillb, write_len); memcpy(buffer, wsp->stillb, datalen);
*nread = write_len; *nread = datalen;
done = TRUE; done = TRUE;
}
if(write_len) {
/* update buffer and frame info */ /* update buffer and frame info */
wsp->frame.offset += write_len; wsp->frame.offset += datalen;
DEBUGASSERT(wsp->frame.bytesleft >= (curl_off_t)write_len); DEBUGASSERT(wsp->frame.bytesleft >= (curl_off_t)datalen);
if(wsp->frame.bytesleft) if(wsp->frame.bytesleft)
wsp->frame.bytesleft -= write_len; wsp->frame.bytesleft -= datalen;
DEBUGASSERT(write_len <= wsp->stillblen); DEBUGASSERT(datalen <= wsp->stillblen);
wsp->stillblen -= write_len; wsp->stillblen -= datalen;
if(wsp->stillblen) if(wsp->stillblen)
wsp->stillb += write_len; wsp->stillb += datalen;
else else
wsp->stillb = NULL; wsp->stillb = NULL;
} }
@ -686,8 +693,14 @@ CURL_EXTERN CURLcode curl_ws_send(struct Curl_easy *data, const void *buffer,
infof(data, "WS: wanted to send %zu bytes, sent %zu bytes", infof(data, "WS: wanted to send %zu bytes, sent %zu bytes",
headlen + buflen, written); headlen + buflen, written);
*sent = written;
if(!result) {
/* the *sent number only counts "payload", excluding the header */
if((size_t)written > headlen)
*sent = written - headlen;
else
*sent = 0;
}
return result; return result;
} }