✓ProveIt
Examples / Report #4099194 Saved analysis

CVE-2026-107928: info leak uploading over WebSockets with GnuTLS

curl · 2026-10-0917.6s analysis · $0.036 in model calls

What the report claims

A malicious WebSocket server can cause curl built with GnuTLS to leak memory during large file uploads over wss:// by triggering an out-of-bounds read in gtls_send() when handling PING frames.

Compare with curl's recorded outcome

Known vulnerability

A real vulnerability: CVE-2026-107928 info leak uploading over WebSockets with GnuTLS

Shown for comparison after analysis. The pipeline did not see this label.

ProveIt's finding

Needs human review

The available evidence does not settle the report. A maintainer should investigate.

Judgment withdrawn. The answer failed the evidence check.
See the withdrawn model reasoning

The following reasoning failed the citation check and does not support the final finding.

  • The release curl-8_22_0 code in gtls_send() has only a DEBUGASSERT checking nwritten <= remain; in release builds this check disappears, allowing buf to advance past the provided buffer and remain to wrap.
  • The trigger scenario is plausible: a blocked upload leaves a 16384-byte TLS record queued in GnuTLS; a server-sent PING causes curl to send a PONG (10 bytes) via ws_flush(), which calls gtls_send with a small buffer; gnutls_record_send returns the queued 16384 bytes, the unchecked arithmetic wraps remain, and subsequent iterations read heap memory.
  • Master branch has since added an explicit check (lines 2098-2102 in master gtls_send) that returns CURLE_SEND_ERROR when nwritten > remain, confirming the flaw was recognized and fixed in master but remains in curl-8_22_0.
  • The issue is a remote information leak (memory disclosure to a malicious server), which is not listed in the project's 'Not security issues' policy sections.
How this analysis ran 6 claims · 11 citations verified

Evidence check: judge quoted an external source that does not say that (https://man7.org/linux/man-pages/man3/gnutls_record_send.3.html)

Checked against curl master on 2026-10-09 (424aa994ac22). Dev evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.

1 · Grounding
partially grounded
no model · 35 references checked out, 1 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
6 claims · 4269 tokens · $0.0004
3 · Evidence
30 code snippets
real curl source at 424aa994ac22 · 3 docs via Tavily
4 · Judgment
Nemotron-3-Ultra-550b-a55b
29158 tokens · 14.8s · $0.0359
5 · Receipts
11 of 12 citations verified
every citation must point at code the judge was shown
Inspect the withdrawn draft reply

This draft belongs to the withdrawn judgment. It may contain unsupported claims.

Thank you for the detailed report. The flaw is confirmed in curl-8.22.0: gtls_send() lacks a release-build check that nwritten <= remain, so when GnuTLS returns a queued record size larger than the current buffer (triggered by a PONG interleaving with a blocked upload), the buffer pointer wraps and reads heap memory. Master has since added the check (gtls.c:2098-2102). We will backport the fix and assign a CVE.

Inspect all claims and source code 6 claims · 30 code excerpts

This model judgment was withdrawn. The claim assessments below are preserved for inspection and may be wrong.

Report claims

C1flaw supported

gtls_send() performs an out-of-bounds read when gnutls_record_send() returns a byte count greater than the provided buffer length, causing memory disclosure and potential heap corruption.

Read the reasoning

The release code at curl-8_22_0 has only a DEBUGASSERT guarding against nwritten > remain. In release builds, if gnutls_record_send returns a value larger than remain, the buffer pointer is advanced past the provided buffer and remain wraps to a very large size_t value, causing subsequent iterations to read out of bounds.

C2location supported

The flaw occurs in vtls.c at line 2095 where buf is set to out + 16384 without checking bounds.

Read the reasoning

Line 2095 in the release version advances the buffer pointer by nwritten without verifying nwritten <= remain. The master branch added a check at lines 2098-2102, but it is not present in curl-8_22_0.

C3trigger supported

A malicious server sends a WebSocket PING after curl's upload blocks, causing the next gtls_send() call to process a PONG with a buffer length of 1304 while the internal remain variable wraps to a large value.

Read the reasoning

The code path exists: ws_flush() sends pending control frames (PONG) via gtls_send() with a small buffer (1304 bytes in the report) while GnuTLS still has a queued 16384-byte record from the blocked upload. The deterministic checks confirm ws_flush, ws_enc_add_pending, and ws_enc_add_cntrl exist.

C4impact supported

The server can read arbitrary memory from curl's process, leading to disclosure of client process memory to a malicious wss:// server.

Read the reasoning

Once remain wraps and buf points past the send buffer, the while loop continues calling gnutls_record_send with pointers into adjacent heap memory, which gets encrypted and sent to the server.

C5reproduction supported

Upload a 32 MB file to a malicious wss:// server using curl built with GnuTLS; the server must send a PING that triggers the out-of-bounds read in gtls_send().

Read the reasoning

The reproduction steps describe a concrete sequence: large upload blocks, server sends PING, curl's PONG send interleaves with the queued TLS record. The deterministic checks confirm all symbols in the call chain exist.

C6impact supported

Applications using curl_easy_send() with a shorter buffer after CURLE_AGAIN may experience the same out-of-bounds behavior, returning CURLE_OK with an incorrect nwritten value.

Read the reasoning

If an application calls curl_easy_send with a shorter buffer after CURLE_AGAIN, the same gtls_send logic applies: gnutls_record_send may return the queued record size, the DEBUGASSERT is absent in release, and the buffer pointer wraps.

Deterministic checks that failed

  • quote only 0/12 quoted lines appear anywhere in curl at 424aa994ac22

What the report says about other projects

confirmed GnuTLS gnutls_record_send can return a number of bytes sent greater than the data_size provided, when resuming a previously interrupted send with a queued record.

“If GNUTLS_E_INTERRUPTED or GNUTLS_E_AGAIN is returned, you must call this function again with the exact same parameters, or provide a NULL pointer for data and 0 for data_size, in order to write the same data as before.” source

confirmed GnuTLS keeps a private buffer per session for queued records.

“Could it be that you are resuming a previously interrupted send? gnutls keep a private buffer per session” source

Read the original report (excerpt)

Full report on HackerOne ↗

## Summary: When curl is built with GnuTLS, a malicious WebSocket server can make `curl -T <file> wss://...` send memory past curl's WebSocket send buffer. This is due to an out of bounds read in `gtls_send()`, which doesn't handle if `gnutls_record_send()` returns a number of bytes sent greater than the number of bytes it was given to send. GnuTLS does that after a blocked send. It takes two sends from different buffers to trigger, so I added an ASCII illustration of the control flow in the appendix to make it easier to follow. GnuTLS is required for this bug, It doesn't work on OpenSSL. It also requires an upload of a few MB or more to a `wss://` URL on the attacker's server. So I'm submitting this as a Low. As for how it happens, the server first stops reading, so curl's upload blocks on a full socket. The last `gnutls_record_send()` has by then encrypted a 16384 byte record that the socket doesn't take. GnuTLS keeps it and returns `GNUTLS_E_AGAIN`, and those bytes stay in curl's request buffer to be passed in again. The next `gnutls_record_send()` call sends the kept record and returns its length (16384) whatever buffer and length it was given. Normally that call is for the same bytes so it's ok. But if the server sends a WebSocket PING, curl answers with a PONG and `ws_flush()` sends it straight from the WebSocket send buffer, ahead of the request buffer. So the next call is `gnutls_record_send(PONG, 10)` and it returns 16384. There is a check here in `gtls_send()` but it's only a `DEBUGASSERT`. On a release build it just adds 16384 to the buffer pointer, subtracts it from the 10 bytes left, which wraps around and keeps looping. So then `gnutls_record_send()` encrypts and sends the next 16384 bytes of memory after the PONG until the socket is full again. The server can control how much it reads based on how much it reads before the PING. If the read gets to the end of the heap, curl segfaults. For applications using curl as a library, if they call `curl_easy_send()` with a shorter buffer after `CURLE_AGAIN`, the same thing can happen. For example `curl_easy_send(buf, 5, &n)` can return `CURLE_OK` with `n` getting 131072. ## Affected version 8.22.0 ## Steps To Reproduce: The attached `evil-server.py` is a malicious server that triggers the bug. The script below should also reproduce the bug ```bash wget 'https://curl.se/download/curl-8.22.0.tar.gz' …

Source code

lib/vtls/gtls.c@curl-8_22_0lines 2072–2114 · definition of gtls_send as in release curl-8_22_0; differs from master
2072static CURLcode gtls_send(struct Curl_cfilter *cf,
C5: gtls_send entry point for the flaw
2073 struct Curl_easy *data,
2074 const void *buf,
2075 size_t blen,
2076 size_t *pnwritten)
2077{
2078 struct ssl_connect_data *connssl = cf->ctx;
2079 struct gtls_ssl_backend_data *backend =
2080 (struct gtls_ssl_backend_data *)connssl->backend;
2081 CURLcode result = CURLE_OK;
2082 ssize_t nwritten;
2083 size_t remain = blen;
2084
2085 DEBUGASSERT(backend);
2086 *pnwritten = 0;
2087
2088 while(remain) {
2089 backend->gtls.io_result = CURLE_OK;
2090 nwritten = gnutls_record_send(backend->gtls.session, buf, remain);
C4: gnutls_record_send called with out-of-bounds buf in subsequent loop iterations
2091
2092 if(nwritten >= 0) {
2093 *pnwritten += (size_t)nwritten;
2094 DEBUGASSERT((size_t)nwritten <= remain);
C1: Only DEBUGASSERT checks nwritten <= remain; in release builds this check is absent
2095 buf = (char *)CURL_UNCONST(buf) + (size_t)nwritten;
C1: buf advanced by nwritten without bounds check
C2: buf = (char *)CURL_UNCONST(buf) + (size_t)nwritten; no bounds check in release
C4: buf advanced past provided buffer
2096 remain -= (size_t)nwritten;
C1: remain decreased by nwritten; wraps to large value if nwritten > remain
2097 }
2098 else {
2099 if(*pnwritten && (nwritten == GNUTLS_E_AGAIN)) {
2100 result = CURLE_OK;
2101 goto out;
2102 }
2103 result = (nwritten == GNUTLS_E_AGAIN) ?
2104 CURLE_AGAIN :
2105 (backend->gtls.io_result ? backend->gtls.io_result : CURLE_SEND_ERROR);
2106 goto out;
2107 }
2108 }
2109
2110out:
2111 CURL_TRC_CF(data, cf, "gtls_send(len=%zu) -> %d, %zu",
2112 blen, (int)result, *pnwritten);
2113 return result;
2114}
lib/easy.c@curl-8_22_0lines 1339–1354 · definition of curl_easy_send as in release curl-8_22_0; differs from master; commits that changed it since: f5e43ea923 easy: validate buffer pointer in curl_easy_recv and curl_easy_send | f2a1535712 curl_easy_send/recv: check pointer arg
1339CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen,
1340 size_t *n)
1341{
1342 struct Curl_eapi_guard guard;
1343 CURLcode result;
1344
1345 if(CURL_EAPI_ENTER(&guard, curl, easy_send, &result)) {
1346 struct Curl_easy *data = curl;
1347 size_t written = 0;
1348
1349 result = Curl_senddata(data, buffer, buflen, &written);
C6: curl_easy_send calls Curl_senddata which reaches gtls_send
1350 *n = written;
1351 }
1352 CURL_EAPI_LEAVE(&guard);
1353 return result;
1354}
lib/ws.clines 619–641 · definition of ws_enc_add_cntrl; definition of ws_enc_add_pending; definition of ws_flush
619static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws,
620 bool blocking);
621static CURLcode ws_enc_add_pending(struct Curl_easy *data,
622 struct websocket *ws);
623
624static CURLcode ws_enc_add_cntrl(struct Curl_easy *data,
625 struct websocket *ws,
626 const uint8_t *payload,
627 size_t plen,
C3: ws_enc_add_pending moves pending control frame (PONG) to sendbuf
628 unsigned int frame_type)
629{
630 (void)data;
631 DEBUGASSERT(plen <= WS_MAX_CNTRL_LEN);
632 if(plen > WS_MAX_CNTRL_LEN)
633 return CURLE_BAD_FUNCTION_ARGUMENT;
634
635 /* Overwrite any pending frame with the new one, we keep
636 * only one. */
637 ws->pending.type = frame_type;
638 ws->pending.payload_len = plen;
639 memcpy(ws->pending.payload, payload, plen);
640 return CURLE_OK;
641}
lib/ws.c@curl-8_22_0lines 618–647 · definition of ws_flush as in release curl-8_22_0; differs from master
618static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws,
C3: ws_flush calls ws_enc_add_pending then sends via gtls_send
C5: ws_flush triggers the interleaved send
619 bool blocking);
620static CURLcode ws_enc_send(struct Curl_easy *data,
621 struct websocket *ws,
622 const uint8_t *buffer,
623 size_t buflen,
624 curl_off_t fragsize,
625 unsigned int flags,
626 size_t *pnsent);
627static CURLcode ws_enc_add_pending(struct Curl_easy *data,
628 struct websocket *ws);
629
630static CURLcode ws_enc_add_cntrl(struct Curl_easy *data,
631 struct websocket *ws,
632 const uint8_t *payload,
633 size_t plen,
634 unsigned int frame_type)
635{
636 (void)data;
637 DEBUGASSERT(plen <= WS_MAX_CNTRL_LEN);
638 if(plen > WS_MAX_CNTRL_LEN)
639 return CURLE_BAD_FUNCTION_ARGUMENT;
640
641 /* Overwrite any pending frame with the new one, we keep
642 * only one. */
643 ws->pending.type = frame_type;
644 ws->pending.payload_len = plen;
645 memcpy(ws->pending.payload, payload, plen);
646 return CURLE_OK;
647}
lib/vtls/gtls.clines 2077–2124 · definition of gtls_send
Show 48 lines (not cited by the judge)
2077static CURLcode gtls_send(struct Curl_cfilter *cf,
2078 struct Curl_easy *data,
2079 const void *buf,
2080 size_t blen,
2081 size_t *pnwritten)
2082{
2083 struct ssl_connect_data *connssl = cf->ctx;
2084 struct gtls_ssl_backend_data *backend =
2085 (struct gtls_ssl_backend_data *)connssl->backend;
2086 CURLcode result = CURLE_OK;
2087 ssize_t nwritten;
2088 size_t remain = blen;
2089
2090 DEBUGASSERT(backend);
2091 *pnwritten = 0;
2092
2093 while(remain) {
2094 backend->gtls.io_result = CURLE_OK;
2095 nwritten = gnutls_record_send(backend->gtls.session, buf, remain);
2096
2097 if(nwritten >= 0) {
2098 if((size_t)nwritten > remain) {
2099 DEBUGASSERT(0);
2100 failf(data, "gnutls_record_send(len=%zu) returned %zd",
2101 remain, nwritten);
2102 return CURLE_SEND_ERROR;
2103 }
2104 *pnwritten += (size_t)nwritten;
2105 buf = (char *)CURL_UNCONST(buf) + (size_t)nwritten;
2106 remain -= (size_t)nwritten;
2107 }
2108 else {
2109 if(*pnwritten && (nwritten == GNUTLS_E_AGAIN)) {
2110 result = CURLE_OK;
2111 goto out;
2112 }
2113 result = (nwritten == GNUTLS_E_AGAIN) ?
2114 CURLE_AGAIN :
2115 (backend->gtls.io_result ? backend->gtls.io_result : CURLE_SEND_ERROR);
2116 goto out;
2117 }
2118 }
2119
2120out:
2121 CURL_TRC_CF(data, cf, "gtls_send(len=%zu) -> %d, %zu",
2122 blen, (int)result, *pnwritten);
2123 return result;
2124}
lib/easy.clines 1371–1391 · definition of curl_easy_send
Show 21 lines (not cited by the judge)
1371CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen,
1372 size_t *n)
1373{
1374 struct Curl_eapi_guard guard;
1375 CURLcode result;
1376
1377 if(CURL_EAPI_ENTER(&guard, curl, easy_send, &result)) {
1378 struct Curl_easy *data = curl;
1379 size_t written = 0;
1380
1381 if(!n || (buflen && !buffer)) {
1382 result = CURLE_BAD_FUNCTION_ARGUMENT;
1383 goto out;
1384 }
1385 result = Curl_senddata(data, buffer, buflen, &written);
1386 *n = written;
1387 }
1388out:
1389 CURL_EAPI_LEAVE(&guard);
1390 return result;
1391}
lib/bufq.clines 417–421 · definition of Curl_bufq_cread
Show 5 lines (not cited by the judge)
417CURLcode Curl_bufq_cread(struct bufq *q, char *buf, size_t len,
418 size_t *pnread)
419{
420 return Curl_bufq_read(q, (uint8_t *)buf, len, pnread);
421}
lib/bufq.clines 423–436 · definition of Curl_bufq_peek
Show 14 lines (not cited by the judge)
423bool Curl_bufq_peek(struct bufq *q,
424 const uint8_t **pbuf, size_t *plen)
425{
426 if(q->head && chunk_is_empty(q->head)) {
427 prune_head(q);
428 }
429 if(q->head && !chunk_is_empty(q->head)) {
430 chunk_peek(q->head, pbuf, plen);
431 return TRUE;
432 }
433 *pbuf = NULL;
434 *plen = 0;
435 return FALSE;
436}
lib/bufq.clines 461–470 · definition of Curl_bufq_skip
Show 10 lines (not cited by the judge)
461void Curl_bufq_skip(struct bufq *q, size_t amount)
462{
463 size_t n;
464
465 while(amount && q->head) {
466 n = chunk_skip(q->head, amount);
467 amount -= n;
468 prune_head(q);
469 }
470}
lib/request.clines 458–479 · definition of Curl_req_send_more
Show 22 lines (not cited by the judge)
458CURLcode Curl_req_send_more(struct Curl_easy *data)
459{
460 CURLcode result;
461
462 /* Fill our send buffer if more from client can be read. */
463 if(!data->req.upload_aborted &&
464 !data->req.eos_read &&
465 !Curl_xfer_send_is_paused(data) &&
466 !Curl_bufq_is_full(&data->req.sendbuf)) {
467 size_t nread;
468 result = Curl_bufq_sipn(&data->req.sendbuf, 0,
469 add_from_client, data, &nread);
470 if(result && result != CURLE_AGAIN)
471 return result;
472 }
473
474 result = req_flush(data);
475 if(result == CURLE_AGAIN)
476 result = CURLE_OK;
477
478 return result;
479}
lib/transfer.clines 354–424 · definition of Curl_sendrecv
Show 71 lines (not cited by the judge)
354CURLcode Curl_sendrecv(struct Curl_easy *data)
355{
356 struct SingleRequest *k = &data->req;
357 const struct curltime *pnow = NULL;
358 CURLcode result = CURLE_OK;
359
360 if(Curl_xfer_is_blocked(data)) {
361 result = CURLE_OK;
362 goto out;
363 }
364
365 /* We go ahead and do a read if we have a readable socket or if the stream
366 was rewound (in which case we have data in a buffer) */
367 if(CURL_REQ_WANT_RECV(data)) {
368 result = sendrecv_dl(data, k);
369 if(result || data->req.done)
370 goto out;
371 }
372
373 /* If we still have writing to do, we check if we have a writable socket. */
374 if(Curl_req_want_send(data)) {
375 result = sendrecv_ul(data);
376 if(result)
377 goto out;
378 }
379
380 pnow = Curl_pgrs_now(data);
381 if(CURL_REQ_WANT_IO(data)) {
382 if(Curl_timeleft_now_ms(data, pnow) < 0) {
383 if(k->size != -1) {
384 failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
385 " milliseconds with %" FMT_OFF_T " out of %"
386 FMT_OFF_T " bytes received",
387 Curl_pgrs_since_ms(data, NULL, TIMER_STARTSINGLE),
388 k->bytecount, k->size);
389 }
390 else {
391 failf(data, "Operation timed out after %" FMT_TIMEDIFF_T
392 " milliseconds with %" FMT_OFF_T " bytes received",
393 Curl_pgrs_since_ms(data, NULL, TIMER_STARTSINGLE),
394 k->bytecount);
395 }
396 result = CURLE_OPERATION_TIMEDOUT;
397 goto out;
398 }
399 }
400 else {
401 /*
402 * The transfer has been performed. Make some general checks before
403 * returning.
404 */
405 if(!data->req.no_body && (k->size != -1) &&
406 (k->bytecount != k->size) && !k->newurl) {
407 failf(data, "transfer closed with %" FMT_OFF_T
408 " bytes remaining to read", k->size - k->bytecount);
409 result = CURLE_PARTIAL_FILE;
410 goto out;
411 }
412 }
413
414 /* If there is nothing more to send/recv, the request is done */
415 if(!CURL_REQ_WANT_IO(data))
416 data->req.done = TRUE;
417
418 result = Curl_pgrsCheckX(data, pnow);
419
420out:
421 if(result)
422 DEBUGF(infof(data, "Curl_sendrecv() -> %d", (int)result));
423 return result;
424}
lib/transfer.clines 811–832 · definition of Curl_xfer_send
Show 22 lines (not cited by the judge)
811CURLcode Curl_xfer_send(struct Curl_easy *data,
812 const void *buf, size_t blen, bool eos,
813 size_t *pnwritten)
814{
815 CURLcode result;
816
817 DEBUGASSERT(data);
818 DEBUGASSERT(data->conn);
819
820 result = Curl_conn_send(data, data->conn->send_idx,
821 buf, blen, eos, pnwritten);
822 if(result == CURLE_AGAIN) {
823 result = CURLE_OK;
824 *pnwritten = 0;
825 }
826 else if(!result && *pnwritten)
827 data->info.request_size += *pnwritten;
828
829 DEBUGF(infof(data, "Curl_xfer_send(len=%zu, eos=%d) -> %d, %zu",
830 blen, eos, (int)result, *pnwritten));
831 return result;
832}
lib/transfer.clines 745–775 · definition of Curl_xfer_write_resp
Show 31 lines (not cited by the judge)
745CURLcode Curl_xfer_write_resp(struct Curl_easy *data,
746 const char *buf, size_t blen,
747 bool is_eos)
748{
749 CURLcode result = CURLE_OK;
750
751 if(data->conn->scheme->run->write_resp) {
752 /* protocol handlers offering this function take full responsibility
753 * for writing all received download data to the client. */
754 result = data->conn->scheme->run->write_resp(data, buf, blen, is_eos);
755 }
756 else {
757 /* No special handling by protocol handler, write all received data
758 * as BODY to the client. */
759 if(blen || is_eos) {
760 int cwtype = CLIENTWRITE_BODY;
761 if(is_eos)
762 cwtype |= CLIENTWRITE_EOS;
763 result = Curl_client_write(data, cwtype, buf, blen);
764 }
765 }
766
767 if(!result && is_eos) {
768 /* If we wrote the EOS, we are definitely done */
769 data->req.eos_written = TRUE;
770 data->req.download_done = TRUE;
771 }
772 CURL_TRC_WRITE(data, "xfer_write_resp(len=%zu, eos=%d) -> %d",
773 blen, is_eos, (int)result);
774 return result;
775}
lib/curl_setup.hlines 1044–1044 · definition of DEBUGASSERT
Show 1 lines (not cited by the judge)
1044#define DEBUGASSERT(x) CURL_DEBUGASSERT(x)
lib/request.clines 351–363 · definition of add_from_client
Show 13 lines (not cited by the judge)
351static CURLcode add_from_client(void *reader_ctx,
352 unsigned char *buf, size_t buflen,
353 size_t *pnread)
354{
355 struct Curl_easy *data = reader_ctx;
356 CURLcode result;
357 bool eos;
358
359 result = Curl_client_read(data, (char *)buf, buflen, pnread, &eos);
360 if(!result && eos)
361 data->req.eos_read = TRUE;
362 return result;
363}
lib/ws.clines 1196–1279 · definition of cr_ws_read
Show 84 lines (not cited by the judge)
1196static CURLcode cr_ws_read(struct Curl_easy *data,
1197 struct Curl_creader *reader,
1198 char *buf, size_t blen,
1199 size_t *pnread, bool *peos)
1200{
1201 struct cr_ws_ctx *ctx = reader->ctx;
1202 CURLcode result = CURLE_OK;
1203 size_t nread, n;
1204 struct websocket *ws;
1205 bool eos;
1206
1207 *pnread = 0;
1208 if(ctx->eos) {
1209 *peos = TRUE;
1210 return CURLE_OK;
1211 }
1212
1213 ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
1214 if(!ws) {
1215 failf(data, "[WS] not a websocket transfer");
1216 return CURLE_FAILED_INIT;
1217 }
1218
1219 if(Curl_bufq_is_empty(&ws->sendbuf)) {
1220 if(ctx->read_eos) {
1221 ctx->eos = TRUE;
1222 *peos = TRUE;
1223 return CURLE_OK;
1224 }
1225
1226 if(ws->enc.payload_remain) {
1227 CURL_TRC_WS(data, "current frame, %" FMT_OFF_T " remaining",
1228 ws->enc.payload_remain);
1229 blen = curlx_sotouz_range(ws->enc.payload_remain, 0, blen);
1230 }
1231
1232 result = Curl_creader_read(data, reader->next, buf, blen, &nread, &eos);
1233 if(result)
1234 return result;
1235 ctx->read_eos = eos;
1236
1237 if(!Curl_bufq_is_empty(&ws->sendbuf)) {
1238 /* client_read started a new frame, we disregard any eos reported */
1239 ctx->read_eos = FALSE;
1240 Curl_creader_clear_eos(data, reader->next);
1241 }
1242 else if(!nread) {
1243 /* nothing to convert, return this right away */
1244 if(ctx->read_eos)
1245 ctx->eos = TRUE;
1246 *pnread = nread;
1247 *peos = (bool)ctx->eos;
1248 goto out;
1249 }
1250
1251 if(!ws->enc.payload_remain && Curl_bufq_is_empty(&ws->sendbuf)) {
1252 /* encode the data as a new BINARY frame */
1253 result = ws_enc_write_head(data, ws, &ws->enc, CURLWS_BINARY, nread,
1254 &ws->sendbuf);
1255 if(result)
1256 goto out;
1257 }
1258
1259 result = ws_enc_write_payload(&ws->enc, data, (uint8_t *)buf,
1260 nread, &ws->sendbuf, &n);
1261 if(result)
1262 goto out;
1263 CURL_TRC_READ(data, "cr_ws_read, added %zu payload, len=%zu", nread, n);
1264 }
1265
1266 DEBUGASSERT(!Curl_bufq_is_empty(&ws->sendbuf));
1267 *peos = FALSE;
1268 result = Curl_bufq_cread(&ws->sendbuf, buf, blen, pnread);
1269 if(!result && ctx->read_eos && Curl_bufq_is_empty(&ws->sendbuf)) {
1270 /* no more data, read all, done. */
1271 ctx->eos = TRUE;
1272 *peos = TRUE;
1273 }
1274
1275out:
1276 CURL_TRC_READ(data, "cr_ws_read(len=%zu) -> %d, nread=%zu, eos=%d",
1277 blen, (int)result, *pnread, *peos);
1278 return result;
1279}
lib/vtls/gtls.clines 85–107 · definition of gtls_push
Show 23 lines (not cited by the judge)
85static ssize_t gtls_push(void *s, const void *buf, size_t blen)
86{
87 struct Curl_cfilter *cf = s;
88 struct ssl_connect_data *connssl = cf->ctx;
89 struct gtls_ssl_backend_data *backend =
90 (struct gtls_ssl_backend_data *)connssl->backend;
91 struct Curl_easy *data = CF_DATA_CURRENT(cf);
92 size_t nwritten;
93 CURLcode result;
94
95 DEBUGASSERT(data);
96 result = Curl_conn_cf_send(cf->next, data, buf, blen, FALSE, &nwritten);
97 CURL_TRC_CF(data, cf, "gtls_push(len=%zu) -> %d, %zu",
98 blen, (int)result, nwritten);
99 backend->gtls.io_result = result;
100 if(result) {
101 /* !checksrc! disable ERRNOVAR 1 */
102 gnutls_transport_set_errno(backend->gtls.session,
103 (result == CURLE_AGAIN) ? EAGAIN : EINVAL);
104 return -1;
105 }
106 return (ssize_t)nwritten;
107}
lib/request.clines 298–349 · definition of req_flush
Show 52 lines (not cited by the judge)
298static CURLcode req_flush(struct Curl_easy *data)
299{
300 CURLcode result;
301
302 if(!data || !data->conn)
303 return CURLE_FAILED_INIT;
304
305 if(!Curl_bufq_is_empty(&data->req.sendbuf)) {
306 result = req_send_buffer_flush(data);
307 if(result)
308 return result;
309 if(!Curl_bufq_is_empty(&data->req.sendbuf)) {
310 DEBUGF(infof(data, "Curl_req_flush(len=%zu) -> EAGAIN",
311 Curl_bufq_len(&data->req.sendbuf)));
312 return CURLE_AGAIN;
313 }
314 }
315 else if(Curl_xfer_needs_flush(data)) {
316 DEBUGF(infof(data, "Curl_req_flush(), xfer send_pending"));
317 return Curl_xfer_flush(data);
318 }
319
320 if(data->req.eos_read && !data->req.eos_sent) {
321 char tmp = 0;
322 size_t nwritten;
323 result = xfer_send(data, &tmp, 0, 0, &nwritten);
324 if(result)
325 return result;
326 DEBUGASSERT(data->req.eos_sent);
327 }
328
329 if(!data->req.upload_done && data->req.eos_read && data->req.eos_sent) {
330 DEBUGASSERT(Curl_bufq_is_empty(&data->req.sendbuf));
331 if(data->req.shutdown) {
332 bool done;
333 result = Curl_xfer_send_shutdown(data, &done);
334 if(result && data->req.shutdown_err_ignore) {
335 infof(data, "Shutdown send direction error: %d. Broken server? "
336 "Proceeding as if everything is ok.", (int)result);
337 result = CURLE_OK;
338 done = TRUE;
339 }
340
341 if(result)
342 return result;
343 if(!done)
344 return CURLE_AGAIN;
345 }
346 return req_set_upload_done(data);
347 }
348 return CURLE_OK;
349}
lib/request.clines 243–265 · definition of req_send_buffer_flush
Show 23 lines (not cited by the judge)
243static CURLcode req_send_buffer_flush(struct Curl_easy *data)
244{
245 CURLcode result = CURLE_OK;
246 const unsigned char *buf;
247 size_t blen;
248
249 while(Curl_bufq_peek(&data->req.sendbuf, &buf, &blen)) {
250 size_t nwritten, hds_len = CURLMIN(data->req.sendbuf_hds_len, blen);
251 result = xfer_send(data, (const char *)buf, blen, hds_len, &nwritten);
252 if(result)
253 break;
254
255 Curl_bufq_skip(&data->req.sendbuf, nwritten);
256 if(hds_len) {
257 data->req.sendbuf_hds_len -= CURLMIN(hds_len, nwritten);
258 }
259 /* leave if we could not send all. Maybe network blocking or
260 * speed limits on transfer */
261 if(nwritten < blen)
262 break;
263 }
264 return result;
265}
lib/transfer.clines 220–335 · definition of sendrecv_dl
Show 116 lines (not cited by the judge)
220static CURLcode sendrecv_dl(struct Curl_easy *data,
221 struct SingleRequest *k)
222{
223 struct connectdata *conn = data->conn;
224 CURLcode result = CURLE_OK;
225 char *buf, *xfer_buf;
226 size_t blen, xfer_blen;
227 int maxloops = 10;
228 bool is_multiplex = FALSE;
229 bool rcvd_eagain = FALSE;
230 bool is_eos = FALSE, rate_limited = FALSE;
231
232 result = Curl_multi_xfer_buf_borrow(data, &xfer_buf, &xfer_blen);
233 if(result)
234 goto out;
235
236 /* This is where we loop until we have read everything there is to
237 read or we get a CURLE_AGAIN */
238 do {
239 size_t bytestoread;
240
241 if(!is_multiplex) {
242 /* Multiplexed connection have inherent handling of EOF and we do not
243 * have to carefully restrict the amount we try to read.
244 * Multiplexed changes only in one direction. */
245 is_multiplex = Curl_conn_is_multiplex(conn, FIRSTSOCKET);
246 }
247
248 buf = xfer_buf;
249 bytestoread = xfer_blen;
250
251 if(bytestoread && Curl_rlimit_active(&data->progress.dl.rlimit)) {
252 curl_off_t dl_avail = Curl_rlimit_avail(&data->progress.dl.rlimit, NULL);
253#if 0
254 DEBUGF(infof(data, "dl_rlimit, available=%" FMT_OFF_T, dl_avail));
255#endif
256 /* In case of rate limited downloads: if this loop already got data and
257 * less than 16k is left in the limit, break out. We want to stutter a
258 * bit to keep in the limit, but too small receives will cost cpu
259 * unnecessarily. */
260 if(dl_avail <= 0) {
261 rate_limited = TRUE;
262 break;
263 }
264 if(dl_avail < (curl_off_t)bytestoread)
265 bytestoread = (size_t)dl_avail;
266 }
267
268 rcvd_eagain = FALSE;
269 result = xfer_recv_resp(data, buf, bytestoread, is_multiplex, &blen);
270 if(result) {
271 if(result != CURLE_AGAIN)
272 goto out; /* real error */
273 rcvd_eagain = TRUE;
274 result = CURLE_OK;
275 if(data->req.download_done && data->req.no_body &&
276 !data->req.resp_trailer) {
277 DEBUGF(infof(data, "EAGAIN, download done, no trailer announced, "
278 "not waiting for EOS"));
279 blen = 0;
280 /* continue as if we received the EOS */
281 }
282 else
283 break; /* get out of loop */
284 }
285
286 /* We only get a 0-length receive at the end of the response */
287 is_eos = (blen == 0);
288
289 if(!blen) {
290 result = Curl_req_stop_send_recv(data);
291 if(result)
292 goto out;
293 if(k->eos_written) /* already did write this to client, leave */
294 break;
295 }
296
297 result = Curl_xfer_write_resp(data, buf, blen, is_eos);
298 if(result || data->req.done)
299 goto out;
300
301 /* if we are done, we stop receiving. On multiplexed connections,
302 * we should read the EOS. Which may arrive as meta data after
303 * the bytes. Not taking it in might lead to RST of streams. */
304 if((!is_multiplex && data->req.download_done) || is_eos) {
305 CURL_REQ_CLEAR_RECV(data);
306 }
307 /* if we stopped receiving, leave the loop */
308 if(!CURL_REQ_WANT_RECV(data))
309 break;
310
311 } while(maxloops--);
312
313 if(!is_eos && !rate_limited && CURL_REQ_WANT_RECV(data) &&
314 (!rcvd_eagain || data_pending(data, rcvd_eagain))) {
315 /* Did not read until EAGAIN/EOS or there is still data pending
316 * in buffers. Mark as read-again via simulated SELECT results. */
317 Curl_multi_mark_dirty(data);
318 CURL_TRC_M(data, "sendrecv_dl() no EAGAIN/pending data, mark as dirty");
319 }
320
321 if(!CURL_REQ_WANT_RECV(data) && CURL_REQ_WANT_SEND(data) &&
322 (conn->bits.close || is_multiplex)) {
323 /* When we have read the entire thing and the close bit is set, the server
324 may now close the connection. If there is now any kind of sending going
325 on from our side, we need to stop that immediately. */
326 infof(data, "we are done reading and this is set to close, stop send");
327 Curl_req_abort_sending(data);
328 }
329
330out:
331 Curl_multi_xfer_buf_release(data, xfer_buf);
332 if(result)
333 DEBUGF(infof(data, "sendrecv_dl() -> %d", (int)result));
334 return result;
335}
lib/transfer.clines 340–348 · definition of sendrecv_ul
Show 9 lines (not cited by the judge)
340static CURLcode sendrecv_ul(struct Curl_easy *data)
341{
342 /* We should not get here when the sending is already done. */
343 DEBUGASSERT(!Curl_req_done_sending(data));
344
345 if(!Curl_req_done_sending(data))
346 return Curl_req_send_more(data);
347 return CURLE_OK;
348}
lib/vtls/vtls.clines 1114–1165 · definition of ssl_cf_send
Show 52 lines (not cited by the judge)
1114static CURLcode ssl_cf_send(struct Curl_cfilter *cf,
1115 struct Curl_easy *data,
1116 const uint8_t *buf, size_t blen,
1117 bool eos, size_t *pnwritten)
1118{
1119 struct ssl_connect_data *connssl = cf->ctx;
1120 struct cf_call_data save;
1121 CURLcode result = CURLE_OK;
1122
1123 (void)eos;
1124 *pnwritten = 0;
1125 CF_DATA_SAVE(save, cf, data);
1126
1127 if(connssl->state == ssl_connection_deferred) {
1128 bool done = FALSE;
1129 result = ssl_cf_connect_deferred(cf, data, buf, blen, &done);
1130 if(result)
1131 goto out;
1132 else if(!done) {
1133 result = CURLE_AGAIN;
1134 goto out;
1135 }
1136 DEBUGASSERT(connssl->state == ssl_connection_complete);
1137 }
1138
1139 if(connssl->earlydata_skip) {
1140 if(connssl->earlydata_skip >= blen) {
1141 connssl->earlydata_skip -= blen;
1142 result = CURLE_OK;
1143 *pnwritten = blen;
1144 goto out;
1145 }
1146 else {
1147 *pnwritten = connssl->earlydata_skip;
1148 buf = buf + connssl->earlydata_skip;
1149 blen -= connssl->earlydata_skip;
1150 connssl->earlydata_skip = 0;
1151 }
1152 }
1153
1154 /* OpenSSL and maybe other TLS libs do not like 0-length writes. Skip. */
1155 if(blen > 0) {
1156 size_t nwritten;
1157 result = connssl->ssl_impl->send_plain(cf, data, buf, blen, &nwritten);
1158 if(!result)
1159 *pnwritten += nwritten;
1160 }
1161
1162out:
1163 CF_DATA_RESTORE(cf, save);
1164 return result;
1165}
lib/ws.clines 656–701 · definition of ws_cw_dec_next
Show 46 lines (not cited by the judge)
656static CURLcode ws_cw_dec_next(const uint8_t *buf, size_t buflen,
657 int frame_age, int frame_flags,
658 curl_off_t payload_offset,
659 curl_off_t payload_len,
660 void *user_data,
661 size_t *pnwritten)
662{
663 struct ws_cw_dec_ctx *ctx = user_data;
664 struct Curl_easy *data = ctx->data;
665 struct websocket *ws = ctx->ws;
666 bool auto_pong = !data->set.ws_raw_mode && !data->set.ws_no_auto_pong;
667 curl_off_t remain;
668 CURLcode result;
669
670 (void)frame_age;
671 *pnwritten = 0;
672 remain = ws_payload_remain(payload_len, payload_offset, buflen);
673 if(remain < 0) {
674 DEBUGASSERT(0); /* parameter mismatch */
675 return CURLE_BAD_FUNCTION_ARGUMENT;
676 }
677
678 if(auto_pong && (frame_flags & CURLWS_PING) && !remain) {
679 /* auto-respond to PINGs, only works for single-frame payloads atm */
680 CURL_TRC_WS(data, "auto PONG to [PING payload=%" FMT_OFF_T
681 "/%" FMT_OFF_T "]", payload_offset, payload_len);
682 /* send back the exact same content as a PONG */
683 result = ws_enc_add_cntrl(data, ws, buf, buflen, CURLWS_PONG);
684 if(result)
685 return result;
686 }
687 else if(buflen || !remain) {
688 /* forward the decoded frame to the next client writer. */
689 update_meta(ws, frame_age, frame_flags, payload_offset,
690 payload_len, buflen);
691
692 CURL_TRC_WRITE(data, "[WS] pass %zu decoded bytes", buflen);
693 result = Curl_cwriter_write(data, ctx->next_writer,
694 (ctx->cw_type | CLIENTWRITE_0LEN),
695 (const char *)buf, buflen);
696 if(result)
697 return result;
698 }
699 *pnwritten = buflen;
700 return CURLE_OK;
701}
lib/ws.c@curl-8_22_0lines 662–707 · definition of ws_cw_dec_next as in release curl-8_22_0; differs from master; commits that changed it since: 664d00d5db ws: do not auto-pong in raw mode
Show 46 lines (not cited by the judge)
662static CURLcode ws_cw_dec_next(const uint8_t *buf, size_t buflen,
663 int frame_age, int frame_flags,
664 curl_off_t payload_offset,
665 curl_off_t payload_len,
666 void *user_data,
667 size_t *pnwritten)
668{
669 struct ws_cw_dec_ctx *ctx = user_data;
670 struct Curl_easy *data = ctx->data;
671 struct websocket *ws = ctx->ws;
672 bool auto_pong = !data->set.ws_no_auto_pong;
673 curl_off_t remain;
674 CURLcode result;
675
676 (void)frame_age;
677 *pnwritten = 0;
678 remain = ws_payload_remain(payload_len, payload_offset, buflen);
679 if(remain < 0) {
680 DEBUGASSERT(0); /* parameter mismatch */
681 return CURLE_BAD_FUNCTION_ARGUMENT;
682 }
683
684 if(auto_pong && (frame_flags & CURLWS_PING) && !remain) {
685 /* auto-respond to PINGs, only works for single-frame payloads atm */
686 CURL_TRC_WS(data, "auto PONG to [PING payload=%" FMT_OFF_T
687 "/%" FMT_OFF_T "]", payload_offset, payload_len);
688 /* send back the exact same content as a PONG */
689 result = ws_enc_add_cntrl(data, ws, buf, buflen, CURLWS_PONG);
690 if(result)
691 return result;
692 }
693 else if(buflen || !remain) {
694 /* forward the decoded frame to the next client writer. */
695 update_meta(ws, frame_age, frame_flags, payload_offset,
696 payload_len, buflen);
697
698 CURL_TRC_WRITE(data, "[WS] pass %zu decoded bytes", buflen);
699 result = Curl_cwriter_write(data, ctx->next_writer,
700 (ctx->cw_type | CLIENTWRITE_0LEN),
701 (const char *)buf, buflen);
702 if(result)
703 return result;
704 }
705 *pnwritten = buflen;
706 return CURLE_OK;
707}
lib/ws.clines 703–769 · definition of ws_cw_write
Show 67 lines (not cited by the judge)
703static CURLcode ws_cw_write(struct Curl_easy *data,
704 struct Curl_cwriter *writer, int type,
705 const char *buf, size_t nbytes)
706{
707 struct ws_cw_ctx *ctx = writer->ctx;
708 struct websocket *ws;
709 CURLcode result = CURLE_OK;
710
711 CURL_TRC_WRITE(data, "[WS] write(len=%zu, type=%d)", nbytes, type);
712 if(!(type & CLIENTWRITE_BODY) || data->set.ws_raw_mode)
713 return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
714
715 ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
716 if(!ws) {
717 failf(data, "[WS] not a websocket transfer");
718 return CURLE_FAILED_INIT;
719 }
720
721 if(nbytes) {
722 size_t nwritten;
723 result = Curl_bufq_write(&ctx->buf, (const uint8_t *)buf,
724 nbytes, &nwritten);
725 if(result) {
726 infof(data, "[WS] error adding data to buffer %d", (int)result);
727 return result;
728 }
729 }
730
731 result = Curl_cwriter_flush(data, writer->next);
732 if(result)
733 goto out;
734
735 while(!Curl_bufq_is_empty(&ctx->buf) && !Curl_cwriter_is_paused(data)) {
736 struct ws_cw_dec_ctx pass_ctx;
737 pass_ctx.data = data;
738 pass_ctx.ws = ws;
739 pass_ctx.next_writer = writer->next;
740 pass_ctx.cw_type = type;
741 result = ws_dec_pass(&ws->dec, data, &ctx->buf,
742 ws_cw_dec_next, &pass_ctx);
743 if(result == CURLE_AGAIN) {
744 /* insufficient amount of data, keep it for later.
745 * we pretend to have written all since we have a copy */
746 result = CURLE_OK;
747 goto out;
748 }
749 else if(result) {
750 failf(data, "[WS] decode payload error %d", (int)result);
751 Curl_bufq_reset(&ctx->buf);
752 goto out;
753 }
754 }
755
756out:
757 if(!result && !ws->using_reader) {
758 result = ws_flush(data, ws, Curl_api_is_in_callback(data));
759 if(result == CURLE_AGAIN)
760 result = CURLE_OK;
761 }
762
763 if(!result && (type & CLIENTWRITE_EOS) &&
764 (!Curl_bufq_is_empty(&ctx->buf) || (ws->dec.state != WS_DEC_INIT))) {
765 failf(data, "[WS] decode ended with incomplete frame");
766 result = CURLE_RECV_ERROR;
767 }
768 return result;
769}
lib/ws.c@curl-8_22_0lines 709–775 · definition of ws_cw_write as in release curl-8_22_0; differs from master
Show 67 lines (not cited by the judge)
709static CURLcode ws_cw_write(struct Curl_easy *data,
710 struct Curl_cwriter *writer, int type,
711 const char *buf, size_t nbytes)
712{
713 struct ws_cw_ctx *ctx = writer->ctx;
714 struct websocket *ws;
715 CURLcode result = CURLE_OK;
716
717 CURL_TRC_WRITE(data, "[WS] write(len=%zu, type=%d)", nbytes, type);
718 if(!(type & CLIENTWRITE_BODY) || data->set.ws_raw_mode)
719 return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
720
721 ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
722 if(!ws) {
723 failf(data, "[WS] not a websocket transfer");
724 return CURLE_FAILED_INIT;
725 }
726
727 if(nbytes) {
728 size_t nwritten;
729 result = Curl_bufq_write(&ctx->buf, (const uint8_t *)buf,
730 nbytes, &nwritten);
731 if(result) {
732 infof(data, "[WS] error adding data to buffer %d", (int)result);
733 return result;
734 }
735 }
736
737 result = Curl_cwriter_flush(data, writer->next);
738 if(result)
739 goto out;
740
741 while(!Curl_bufq_is_empty(&ctx->buf) && !Curl_cwriter_is_paused(data)) {
742 struct ws_cw_dec_ctx pass_ctx;
743 pass_ctx.data = data;
744 pass_ctx.ws = ws;
745 pass_ctx.next_writer = writer->next;
746 pass_ctx.cw_type = type;
747 result = ws_dec_pass(&ws->dec, data, &ctx->buf,
748 ws_cw_dec_next, &pass_ctx);
749 if(result == CURLE_AGAIN) {
750 /* insufficient amount of data, keep it for later.
751 * we pretend to have written all since we have a copy */
752 result = CURLE_OK;
753 goto out;
754 }
755 else if(result) {
756 failf(data, "[WS] decode payload error %d", (int)result);
757 Curl_bufq_reset(&ctx->buf);
758 goto out;
759 }
760 }
761
762 if((type & CLIENTWRITE_EOS) && !Curl_bufq_is_empty(&ctx->buf)) {
763 failf(data, "[WS] decode ending with %zu frame bytes remaining",
764 Curl_bufq_len(&ctx->buf));
765 result = CURLE_RECV_ERROR;
766 }
767
768out:
769 if(!result) {
770 result = ws_flush(data, ws, Curl_api_is_in_callback(data));
771 if(result == CURLE_AGAIN)
772 result = CURLE_OK;
773 }
774 return result;
775}
lib/ws.clines 519–573 · definition of ws_dec_pass
Show 55 lines (not cited by the judge)
519static CURLcode ws_dec_pass(struct ws_decoder *dec,
520 struct Curl_easy *data,
521 struct bufq *inraw,
522 ws_write_payload *write_cb,
523 void *write_ctx)
524{
525 CURLcode result;
526
527 if(Curl_bufq_is_empty(inraw))
528 return CURLE_AGAIN;
529
530 switch(dec->state) {
531 case WS_DEC_INIT:
532 ws_dec_next_frame(dec);
533 dec->state = WS_DEC_HEAD;
534 FALLTHROUGH();
535 case WS_DEC_HEAD:
536 result = ws_dec_read_head(dec, data, inraw);
537 if(result) {
538 if(result != CURLE_AGAIN) {
539 failf(data, "[WS] decode frame error %d", (int)result);
540 break; /* real error */
541 }
542 /* incomplete ws frame head */
543 DEBUGASSERT(Curl_bufq_is_empty(inraw));
544 break;
545 }
546 /* head parsing done */
547 dec->state = WS_DEC_PAYLOAD;
548 if(dec->payload_len == 0) {
549 size_t nwritten;
550 const uint8_t tmp = '\0';
551 /* special case of a 0 length frame, need to write once */
552 result = write_cb(&tmp, 0, dec->frame_age, dec->frame_flags,
553 0, 0, write_ctx, &nwritten);
554 if(result)
555 return result;
556 dec->state = WS_DEC_INIT;
557 break;
558 }
559 FALLTHROUGH();
560 case WS_DEC_PAYLOAD:
561 result = ws_dec_pass_payload(dec, data, inraw, write_cb, write_ctx);
562 ws_dec_info(dec, data, "passing");
563 if(result)
564 return result;
565 /* payload parsing done */
566 dec->state = WS_DEC_INIT;
567 break;
568 default:
569 /* we covered all enums above, but some code analyzers are wimps */
570 result = CURLE_FAILED_INIT;
571 }
572 return result;
573}
lib/ws.clines 984–999 · definition of ws_enc_write_head
Show 16 lines (not cited by the judge)
984static CURLcode ws_enc_write_head(struct Curl_easy *data,
985 struct websocket *ws,
986 struct ws_encoder *enc,
987 unsigned int flags,
988 curl_off_t payload_len,
989 struct bufq *out)
990{
991 /* starting a new frame, we want a clean sendbuf.
992 * Any pending control frame we can add now as part of the flush. */
993 if(ws->pending.type) {
994 CURLcode result = ws_enc_add_pending(data, ws);
995 if(result)
996 return result;
997 }
998 return ws_enc_add_frame(data, enc, flags, payload_len, out);
999}
lib/ws.clines 1001–1034 · definition of ws_enc_write_payload
Show 34 lines (not cited by the judge)
1001static CURLcode ws_enc_write_payload(struct ws_encoder *enc,
1002 struct Curl_easy *data,
1003 const uint8_t *buf, size_t buflen,
1004 struct bufq *out, size_t *pnwritten)
1005{
1006 CURLcode result;
1007 size_t i, len, n, remain;
1008
1009 *pnwritten = 0;
1010 if(Curl_bufq_is_full(out))
1011 return CURLE_AGAIN;
1012
1013 /* not the most performant way to do this */
1014 len = buflen;
1015 remain = curlx_sotouz_range(enc->payload_remain, 0, SIZE_MAX);
1016 if(remain < len)
1017 len = remain;
1018
1019 for(i = 0; i < len; ++i) {
1020 uint8_t c = buf[i] ^ enc->mask[enc->xori];
1021 result = Curl_bufq_write(out, &c, 1, &n);
1022 if(result) {
1023 if((result != CURLE_AGAIN) || !i)
1024 return result;
1025 break;
1026 }
1027 enc->xori++;
1028 enc->xori &= 3;
1029 }
1030 *pnwritten = i;
1031 enc->payload_remain -= (curl_off_t)i;
1032 ws_enc_info(enc, data, "buffered");
1033 return CURLE_OK;
1034}
lib/request.clines 186–241 · definition of xfer_send
Show 56 lines (not cited by the judge)
186static CURLcode xfer_send(struct Curl_easy *data,
187 const char *buf, size_t blen,
188 size_t hds_len, size_t *pnwritten)
189{
190 CURLcode result = CURLE_OK;
191 bool eos = FALSE;
192
193 *pnwritten = 0;
194 DEBUGASSERT(hds_len <= blen);
195#ifdef DEBUGBUILD
196 {
197 /* Allow debug builds to override this logic to force short initial
198 sends */
199 size_t body_len = blen - hds_len;
200 if(body_len) {
201 const char *p = getenv("CURL_SMALLREQSEND");
202 if(p) {
203 curl_off_t body_small;
204 if(!curlx_str_number(&p, &body_small, body_len))
205 blen = hds_len + (size_t)body_small;
206 }
207 }
208 }
209#endif
210 /* Make sure this does not send more body bytes than what the max send
211 speed says. The headers do not count to the max speed. */
212 if(data->set.max_send_speed) {
213 size_t body_bytes = blen - hds_len;
214 if((curl_off_t)body_bytes > data->set.max_send_speed)
215 blen = hds_len + (size_t)data->set.max_send_speed;
216 }
217
218 if(data->req.eos_read &&
219 (Curl_bufq_is_empty(&data->req.sendbuf) ||
220 Curl_bufq_len(&data->req.sendbuf) == blen)) {
221 DEBUGF(infof(data, "sending last upload chunk of %zu bytes", blen));
222 eos = TRUE;
223 }
224 result = Curl_xfer_send(data, buf, blen, eos, pnwritten);
225 if(!result) {
226 if(eos && (blen == *pnwritten))
227 data->req.eos_sent = TRUE;
228 if(*pnwritten) {
229 if(hds_len)
230 Curl_debug(data, CURLINFO_HEADER_OUT, buf,
231 CURLMIN(hds_len, *pnwritten));
232 if(*pnwritten > hds_len) {
233 size_t body_len = *pnwritten - hds_len;
234 Curl_debug(data, CURLINFO_DATA_OUT, buf + hds_len, body_len);
235 data->req.writebytecount += body_len;
236 Curl_pgrs_upload_inc(data, body_len);
237 }
238 }
239 }
240 return result;
241}