websockets: buffer ugprade data at connection level
No binary (build failed)
—
(log yok)
| @@ -165,6 +165,7 @@ LIB_CFILES = \ | |
| cf-haproxy.c \ | |
| cf-https-connect.c \ | |
| cf-ip-happy.c \ | |
| + | cf-recvbuf.c \ |
| cf-setup.c \ | |
| cf-socket.c \ | |
| cfilters.c \ | |
| @@ -298,6 +299,7 @@ LIB_HFILES = \ | |
| cf-haproxy.h \ | |
| cf-https-connect.h \ | |
| cf-ip-happy.h \ | |
| + | cf-recvbuf.h \ |
| cf-setup.h \ | |
| cf-socket.h \ | |
| cfilters.h \ | |
| @@ -0,0 +1,157 @@ | |
| + | /*************************************************************************** |
| + | * _ _ ____ _ |
| + | * Project ___| | | | _ \| | |
| + | * / __| | | | |_) | | |
| + | * | (__| |_| | _ <| |___ |
| + | * \___|\___/|_| \_\_____| |
| + | * |
| + | * Copyright (C) Daniel Stenberg, <[email protected]>, et al. |
| + | * |
| + | * This software is licensed as described in the file COPYING, which |
| + | * you should have received as part of this distribution. The terms |
| + | * are also available at https://curl.se/docs/copyright.html. |
| + | * |
| + | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
| + | * copies of the Software, and permit persons to whom the Software is |
| + | * furnished to do so, under the terms of the COPYING file. |
| + | * |
| + | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| + | * KIND, either express or implied. |
| + | * |
| + | * SPDX-License-Identifier: curl |
| + | * |
| + | ***************************************************************************/ |
| + | #include "curl_setup.h" |
| + | |
| + | #ifndef CURL_DISABLE_WEBSOCKETS |
| + | /* only used for this protocol, so far */ |
| + | |
| + | #include "urldata.h" |
| + | #include "bufq.h" |
| + | #include "cfilters.h" |
| + | #include "cf-recvbuf.h" |
| + | #include "curl_trc.h" |
| + | |
| + | #define CURL_CF_RECVBUF_CHUNK (16 * 1024) |
| + | |
| + | struct cf_recvbuf_ctx { |
| + | struct bufq recvbuf; |
| + | }; |
| + | |
| + | static void cf_recvbuf_destroy(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data) |
| + | { |
| + | struct cf_recvbuf_ctx *ctx = cf->ctx; |
| + | (void)data; |
| + | if(ctx) { |
| + | Curl_bufq_free(&ctx->recvbuf); |
| + | curlx_free(ctx); |
| + | } |
| + | } |
| + | |
| + | static CURLcode cf_recvbuf_recv(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, |
| + | char *buf, size_t len, |
| + | size_t *pnread) |
| + | { |
| + | struct cf_recvbuf_ctx *ctx = cf->ctx; |
| + | |
| + | if(!Curl_bufq_is_empty(&ctx->recvbuf)) { |
| + | return Curl_bufq_cread(&ctx->recvbuf, buf, len, pnread); |
| + | } |
| + | |
| + | if(cf->next) |
| + | return cf->next->cft->do_recv(cf->next, data, buf, len, pnread); |
| + | *pnread = 0; |
| + | return CURLE_RECV_ERROR; |
| + | } |
| + | |
| + | static bool cf_recvbuf_data_pending(struct Curl_cfilter *cf, |
| + | const struct Curl_easy *data) |
| + | { |
| + | struct cf_recvbuf_ctx *ctx = cf->ctx; |
| + | |
| + | if(!Curl_bufq_is_empty(&ctx->recvbuf)) |
| + | return TRUE; |
| + | |
| + | return cf->next ? |
| + | cf->next->cft->has_data_pending(cf->next, data) : FALSE; |
| + | } |
| + | |
| + | struct Curl_cftype Curl_cft_recvbuf = { |
| + | "RECVBUF", |
| + | 0, |
| + | CURL_LOG_LVL_NONE, |
| + | cf_recvbuf_destroy, |
| + | Curl_cf_def_connect, |
| + | Curl_cf_def_shutdown, |
| + | Curl_cf_def_adjust_pollset, |
| + | cf_recvbuf_data_pending, |
| + | Curl_cf_def_send, |
| + | cf_recvbuf_recv, |
| + | Curl_cf_def_cntrl, |
| + | Curl_cf_def_conn_is_alive, |
| + | Curl_cf_def_conn_keep_alive, |
| + | Curl_cf_def_query, |
| + | }; |
| + | |
| + | static CURLcode cf_recvbuf_create(struct Curl_cfilter **pcf, |
| + | struct Curl_easy *data, |
| + | const uint8_t *buf, size_t blen) |
| + | { |
| + | struct Curl_cfilter *cf = NULL; |
| + | struct cf_recvbuf_ctx *ctx; |
| + | CURLcode result = CURLE_OK; |
| + | size_t nwritten = 0; |
| + | |
| + | (void)data; |
| + | ctx = curlx_calloc(1, sizeof(*ctx)); |
| + | if(!ctx) { |
| + | result = CURLE_OUT_OF_MEMORY; |
| + | goto out; |
| + | } |
| + | Curl_bufq_init2(&ctx->recvbuf, CURL_CF_RECVBUF_CHUNK, |
| + | (blen / CURL_CF_RECVBUF_CHUNK) + 1, |
| + | (BUFQ_OPT_SOFT_LIMIT | BUFQ_OPT_NO_SPARES)); |
| + | result = Curl_bufq_write(&ctx->recvbuf, buf, blen, &nwritten); |
| + | if(result) |
| + | goto out; |
| + | if(nwritten != blen) { |
| + | result = CURLE_FAILED_INIT; |
| + | goto out; |
| + | } |
| + | |
| + | result = Curl_cf_create(&cf, &Curl_cft_recvbuf, ctx); |
| + | if(result) |
| + | goto out; |
| + | ctx = NULL; |
| + | |
| + | out: |
| + | *pcf = result ? NULL : cf; |
| + | if(ctx) { |
| + | Curl_bufq_free(&ctx->recvbuf); |
| + | curlx_free(ctx); |
| + | } |
| + | return result; |
| + | } |
| + | |
| + | CURLcode Curl_cf_recvbuf_add(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex, |
| + | const uint8_t *buf, size_t blen) |
| + | { |
| + | struct Curl_cfilter *cf; |
| + | CURLcode result = CURLE_OK; |
| + | |
| + | DEBUGASSERT(data); |
| + | result = cf_recvbuf_create(&cf, data, buf, blen); |
| + | if(result) |
| + | goto out; |
| + | |
| + | cf->connected = Curl_conn_is_connected(conn, sockindex); |
| + | Curl_conn_cf_add(data, conn, sockindex, cf); |
| + | out: |
| + | return result; |
| + | } |
| + | |
| + | #endif /* !CURL_DISABLE_WEBSOCKETS */ |
| @@ -0,0 +1,40 @@ | |
| + | #ifndef HEADER_CURL_CF_RECVBUF_H |
| + | #define HEADER_CURL_CF_RECVBUF_H |
| + | /*************************************************************************** |
| + | * _ _ ____ _ |
| + | * Project ___| | | | _ \| | |
| + | * / __| | | | |_) | | |
| + | * | (__| |_| | _ <| |___ |
| + | * \___|\___/|_| \_\_____| |
| + | * |
| + | * Copyright (C) Daniel Stenberg, <[email protected]>, et al. |
| + | * |
| + | * This software is licensed as described in the file COPYING, which |
| + | * you should have received as part of this distribution. The terms |
| + | * are also available at https://curl.se/docs/copyright.html. |
| + | * |
| + | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
| + | * copies of the Software, and permit persons to whom the Software is |
| + | * furnished to do so, under the terms of the COPYING file. |
| + | * |
| + | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| + | * KIND, either express or implied. |
| + | * |
| + | * SPDX-License-Identifier: curl |
| + | * |
| + | ***************************************************************************/ |
| + | #include "curl_setup.h" |
| + | |
| + | #ifndef CURL_DISABLE_WEBSOCKETS |
| + | /* only used for this protocol, so far */ |
| + | |
| + | CURLcode Curl_cf_recvbuf_add(struct Curl_easy *data, |
| + | struct connectdata *conn, |
| + | int sockindex, |
| + | const uint8_t *buf, size_t blen); |
| + | |
| + | extern struct Curl_cftype Curl_cft_recvbuf; |
| + | |
| + | #endif /* !CURL_DISABLE_WEBSOCKETS */ |
| + | |
| + | #endif /* HEADER_CURL_CF_RECVBUF_H */ |
| @@ -33,6 +33,27 @@ | |
| #include "select.h" | |
| #include "curlx/strparse.h" | |
| + | CURLcode Curl_cf_def_connect(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, bool *done) |
| + | { |
| + | CURLcode result; |
| + | |
| + | if(cf->connected) { |
| + | *done = TRUE; |
| + | return CURLE_OK; |
| + | } |
| + | |
| + | if(cf->next) { |
| + | result = cf->next->cft->do_connect(cf->next, data, done); |
| + | if(result || !*done) |
| + | return result; |
| + | } |
| + | |
| + | cf->connected = TRUE; |
| + | *done = TRUE; |
| + | return CURLE_OK; |
| + | } |
| + | |
| CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf, | |
| struct Curl_easy *data, bool *done) | |
| { | |
| @@ -265,6 +265,8 @@ CURLcode Curl_cf_def_query(struct Curl_cfilter *cf, | |
| int query, int *pres1, void *pres2); | |
| CURLcode Curl_cf_def_shutdown(struct Curl_cfilter *cf, | |
| struct Curl_easy *data, bool *done); | |
| + | CURLcode Curl_cf_def_connect(struct Curl_cfilter *cf, |
| + | struct Curl_easy *data, bool *done); |
| /** | |
| * Create a new filter instance, unattached to the filter chain. | |
| @@ -29,6 +29,7 @@ | |
| #include "multiif.h" | |
| #include "cf-dns.h" | |
| + | #include "cf-recvbuf.h" |
| #include "cf-socket.h" | |
| #include "cf-setup.h" | |
| #include "http2.h" | |
| @@ -563,6 +564,9 @@ static struct trc_cft_def trc_cfts[] = { | |
| { &Curl_cft_unix, TRC_CT_NETWORK }, | |
| { &Curl_cft_tcp_accept, TRC_CT_NETWORK }, | |
| { &Curl_cft_ip_happy, TRC_CT_NETWORK }, | |
| + | #ifndef CURL_DISABLE_WEBSOCKETS |
| + | { &Curl_cft_recvbuf, TRC_CT_PROTOCOL }, |
| + | #endif |
| { &Curl_cft_setup, TRC_CT_PROTOCOL }, | |
| #if !defined(CURL_DISABLE_HTTP) && defined(USE_NGHTTP2) | |
| { &Curl_cft_nghttp2, TRC_CT_PROTOCOL }, | |
| @@ -32,6 +32,7 @@ | |
| #include "curlx/dynbuf.h" | |
| #include "rand.h" | |
| #include "curlx/base64.h" | |
| + | #include "cf-recvbuf.h" |
| #include "connect.h" | |
| #include "sendf.h" | |
| #include "curl_trc.h" | |
| @@ -1392,15 +1393,17 @@ CURLcode Curl_ws_accept(struct Curl_easy *data, | |
| k->header = FALSE; /* we will not get more response headers */ | |
| if(data->set.connect_only) { | |
| - | size_t nwritten; |
| /* In CONNECT_ONLY setup, the payloads from `mem` need to be received | |
| - | * when using `curl_ws_recv` later on after this transfer is already |
| - | * marked as DONE. */ |
| - | result = Curl_bufq_write(&ws->recvbuf, (const uint8_t *)mem, |
| - | nread, &nwritten); |
| - | if(result) |
| - | goto out; |
| - | DEBUGASSERT(nread == nwritten); |
| + | * when using `curl_ws_recv/curl_easy_recv` later on, after this transfer |
| + | * is already marked as DONE. |
| + | * Since `curl_easy_recv()` is also supposed to work, we need |
| + | * to buffer the data at connection level. See #22107 */ |
| + | if(nread) { |
| + | result = Curl_cf_recvbuf_add(data, data->conn, FIRSTSOCKET, |
| + | (const uint8_t *)mem, nread); |
| + | if(result) |
| + | goto out; |
| + | } |
| CURL_REQ_CLEAR_RECV(data); /* read no more content */ | |
| } | |
| else { /* !connect_only */ | |