Coverage Report

Created: 2026-10-22 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/curl/lib/vquic/cf-capsule.c
Line
Count
Source
1
/***************************************************************************
2
 *                                  _   _ ____  _
3
 *  Project                     ___| | | |  _ \| |
4
 *                             / __| | | | |_) | |
5
 *                            | (__| |_| |  _ <| |___
6
 *                             \___|\___/|_| \_\_____|
7
 *
8
 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9
 *
10
 * This software is licensed as described in the file COPYING, which
11
 * you should have received as part of this distribution. The terms
12
 * are also available at https://curl.se/docs/copyright.html.
13
 *
14
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15
 * copies of the Software, and permit persons to whom the Software is
16
 * furnished to do so, under the terms of the COPYING file.
17
 *
18
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19
 * KIND, either express or implied.
20
 *
21
 * SPDX-License-Identifier: curl
22
 *
23
 ***************************************************************************/
24
#include "curl_setup.h"
25
26
#if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
27
28
#include "urldata.h"
29
#include "cfilters.h"
30
#include "curl_trc.h"
31
#include "bufq.h"
32
#include "select.h"
33
#include "vquic/capsule.h"
34
#include "vquic/cf-capsule.h"
35
36
/* send/recv buffer: 4 chunks of 16KB = 64KB, enough for large datagrams */
37
1.31k
#define CAPSULE_RECV_CHUNKS    4
38
1.31k
#define CAPSULE_SEND_CHUNKS    4
39
2.63k
#define CAPSULE_CHUNK_SIZE     (16 * 1024)
40
41
struct cf_capsule_ctx {
42
  struct bufq recvbuf;
43
  struct bufq sendbuf;
44
};
45
46
static void cf_capsule_destroy(struct Curl_cfilter *cf,
47
                               struct Curl_easy *data)
48
1.31k
{
49
1.31k
  struct cf_capsule_ctx *ctx = cf->ctx;
50
1.31k
  (void)data;
51
1.31k
  if(ctx) {
52
1.31k
    Curl_bufq_free(&ctx->recvbuf);
53
1.31k
    Curl_bufq_free(&ctx->sendbuf);
54
1.31k
    curlx_safefree(ctx);
55
1.31k
  }
56
1.31k
}
57
58
static CURLcode cf_capsule_connect(struct Curl_cfilter *cf,
59
                                   struct Curl_easy *data,
60
                                   bool *done)
61
1.31k
{
62
1.31k
  if(cf->connected) {
63
0
    *done = TRUE;
64
0
    return CURLE_OK;
65
0
  }
66
1.31k
  if(cf->next) {
67
1.31k
    CURLcode result = cf->next->cft->do_connect(cf->next, data, done);
68
1.31k
    if(!result && *done)
69
1.31k
      cf->connected = TRUE;
70
1.31k
    return result;
71
1.31k
  }
72
0
  *done = FALSE;
73
0
  return CURLE_OK;
74
1.31k
}
75
76
static CURLcode cf_capsule_flush(struct Curl_cfilter *cf,
77
                                 struct Curl_easy *data)
78
7.91k
{
79
7.91k
  struct cf_capsule_ctx *ctx = cf->ctx;
80
7.91k
  CURLcode result = CURLE_OK;
81
7.91k
  size_t nwritten;
82
83
7.91k
  if(Curl_bufq_is_empty(&ctx->sendbuf))
84
0
    return CURLE_OK;
85
86
7.91k
  result = Curl_cf_send_bufq(cf->next, data, &ctx->sendbuf, NULL, 0,
87
7.91k
                             &nwritten);
88
7.91k
  if(result) {
89
5.36k
    if(result == CURLE_AGAIN) {
90
5.36k
      CURL_TRC_CF(data, cf, "flush send buffer(%zu) -> EAGAIN",
91
5.36k
                  Curl_bufq_len(&ctx->sendbuf));
92
5.36k
    }
93
5.36k
    return result;
94
5.36k
  }
95
2.55k
  return Curl_bufq_is_empty(&ctx->sendbuf) ? CURLE_OK : CURLE_AGAIN;
96
7.91k
}
97
98
static CURLcode cf_capsule_send(struct Curl_cfilter *cf,
99
                                struct Curl_easy *data,
100
                                const uint8_t *buf, size_t len,
101
                                bool eos, size_t *pnwritten)
102
2.56k
{
103
2.56k
  struct cf_capsule_ctx *ctx = cf->ctx;
104
2.56k
  CURLcode result;
105
106
2.56k
  (void)eos;
107
2.56k
  *pnwritten = 0;
108
109
2.56k
  if(Curl_bufq_is_full(&ctx->sendbuf)) {
110
0
    result = cf_capsule_flush(cf, data);
111
0
    if(result)
112
0
      return result;
113
0
  }
114
115
  /* encapsulate new payload into a capsule */
116
2.56k
  result = Curl_capsule_encap_udp_datagram(&ctx->sendbuf, buf, len);
117
2.56k
  if(result)
118
1
    return result;
119
120
2.56k
  result = cf_capsule_flush(cf, data);
121
2.56k
  if(result == CURLE_AGAIN) {
122
    /* Could not send it (or all), report success nevertheless as we
123
     * have the payload buffered now and will flush it later. */
124
42
    result = CURLE_OK;
125
42
  }
126
127
2.56k
  if(!result)
128
2.56k
    *pnwritten = len;
129
2.56k
  return result;
130
2.56k
}
131
132
static CURLcode cf_capsule_recv(struct Curl_cfilter *cf,
133
                                struct Curl_easy *data,
134
                                char *buf, size_t len,
135
                                size_t *pnread)
136
98.4k
{
137
98.4k
  struct cf_capsule_ctx *ctx = cf->ctx;
138
98.4k
  CURLcode result;
139
98.4k
  size_t nread;
140
141
98.4k
  *pnread = 0;
142
143
  /* fill our receive buffer from the filter below */
144
102k
  while(!Curl_bufq_is_full(&ctx->recvbuf)) {
145
102k
    result = Curl_cf_recv_bufq(cf->next, data, &ctx->recvbuf, 0, &nread);
146
102k
    if(result == CURLE_AGAIN)
147
4.89k
      break;
148
97.1k
    if(result)
149
1
      return result;
150
97.1k
    if(!nread)
151
93.5k
      break;
152
97.1k
  }
153
154
  /* try to extract a complete capsule datagram */
155
98.4k
  *pnread = Curl_capsule_process_udp_raw(cf, data, &ctx->recvbuf,
156
98.4k
                                         (unsigned char *)buf, len,
157
98.4k
                                         &result);
158
98.4k
  return result;
159
98.4k
}
160
161
static bool cf_capsule_data_pending(struct Curl_cfilter *cf,
162
                                    const struct Curl_easy *data)
163
0
{
164
0
  struct cf_capsule_ctx *ctx = cf->ctx;
165
166
0
  if(ctx && !Curl_bufq_is_empty(&ctx->recvbuf))
167
0
    return TRUE;
168
0
  return cf->next ? cf->next->cft->has_data_pending(cf->next, data) : FALSE;
169
0
}
170
171
static CURLcode cf_capsule_cntrl(struct Curl_cfilter *cf,
172
                                 struct Curl_easy *data,
173
                                 int event, int arg1, void *arg2)
174
7.21k
{
175
7.21k
  CURLcode result = CURLE_OK;
176
177
7.21k
  (void)arg1;
178
7.21k
  (void)arg2;
179
7.21k
  switch(event) {
180
5.35k
  case CF_CTRL_FLUSH:
181
5.35k
    result = cf_capsule_flush(cf, data);
182
5.35k
    break;
183
1.86k
  default:
184
1.86k
    break;
185
7.21k
  }
186
7.21k
  return result;
187
7.21k
}
188
189
static CURLcode cf_capsule_query(struct Curl_cfilter *cf,
190
                                 struct Curl_easy *data,
191
                                 int query, int *pres1, void *pres2)
192
119k
{
193
119k
  struct cf_capsule_ctx *ctx = cf->ctx;
194
195
119k
  (void)pres2;
196
119k
  switch(query) {
197
81.2k
  case CF_QUERY_NEED_FLUSH: {
198
81.2k
    if(!Curl_bufq_is_empty(&ctx->sendbuf)) {
199
5.35k
      *pres1 = TRUE;
200
5.35k
      return CURLE_OK;
201
5.35k
    }
202
75.8k
    break;
203
81.2k
  }
204
75.8k
  default:
205
38.3k
    break;
206
119k
  }
207
114k
  return cf->next ?
208
114k
    cf->next->cft->query(cf->next, data, query, pres1, pres2) :
209
114k
    CURLE_UNKNOWN_OPTION;
210
119k
}
211
212
static CURLcode cf_capsule_adjust_pollset(struct Curl_cfilter *cf,
213
                                          struct Curl_easy *data,
214
                                          struct easy_pollset *ps)
215
35.8k
{
216
35.8k
  struct cf_capsule_ctx *ctx = cf->ctx;
217
218
35.8k
  if(!Curl_bufq_is_empty(&ctx->sendbuf)) {
219
28
    curl_socket_t sock = Curl_conn_cf_get_socket(cf, data);
220
28
    if(sock != CURL_SOCKET_BAD)
221
28
      return Curl_pollset_add_out(data, ps, sock);
222
28
  }
223
35.8k
  return CURLE_OK;
224
35.8k
}
225
226
static CURLcode cf_capsule_shutdown(struct Curl_cfilter *cf,
227
                                    struct Curl_easy *data, bool *done)
228
0
{
229
0
  CURLcode result = CURLE_OK;
230
231
0
  if(!cf->connected || cf->shutdown) {
232
0
    *done = TRUE;
233
0
  }
234
0
  else {
235
0
    result = cf_capsule_flush(cf, data);
236
0
    *done = !result;
237
0
    if(result == CURLE_AGAIN)
238
0
      result = CURLE_OK;
239
0
  }
240
0
  return result;
241
0
}
242
243
struct Curl_cftype Curl_cft_capsule = {
244
  "CAPSULE",
245
  0,
246
  0,
247
  cf_capsule_destroy,
248
  cf_capsule_connect,
249
  cf_capsule_shutdown,
250
  cf_capsule_adjust_pollset,
251
  cf_capsule_data_pending,
252
  cf_capsule_send,
253
  cf_capsule_recv,
254
  cf_capsule_cntrl,
255
  Curl_cf_def_conn_is_alive,
256
  Curl_cf_def_conn_keep_alive,
257
  cf_capsule_query,
258
};
259
260
static CURLcode cf_capsule_create(struct Curl_cfilter **pcf,
261
                                  struct Curl_easy *data,
262
                                  struct connectdata *conn)
263
1.31k
{
264
1.31k
  struct Curl_cfilter *cf = NULL;
265
1.31k
  struct cf_capsule_ctx *ctx;
266
1.31k
  CURLcode result;
267
268
1.31k
  (void)data;
269
1.31k
  (void)conn;
270
1.31k
  *pcf = NULL;
271
1.31k
  ctx = curlx_calloc(1, sizeof(*ctx));
272
1.31k
  if(!ctx) {
273
1
    result = CURLE_OUT_OF_MEMORY;
274
1
    goto out;
275
1
  }
276
277
1.31k
  Curl_bufq_init2(&ctx->recvbuf, CAPSULE_CHUNK_SIZE, CAPSULE_RECV_CHUNKS,
278
1.31k
                  BUFQ_OPT_SOFT_LIMIT);
279
1.31k
  Curl_bufq_init2(&ctx->sendbuf, CAPSULE_CHUNK_SIZE, CAPSULE_SEND_CHUNKS,
280
1.31k
                  BUFQ_OPT_SOFT_LIMIT);
281
282
1.31k
  result = Curl_cf_create(&cf, &Curl_cft_capsule, ctx);
283
284
1.31k
out:
285
1.31k
  *pcf = (!result) ? cf : NULL;
286
1.31k
  if(result && ctx) {
287
2
    Curl_bufq_free(&ctx->recvbuf);
288
2
    Curl_bufq_free(&ctx->sendbuf);
289
2
    curlx_free(ctx);
290
2
  }
291
1.31k
  return result;
292
1.31k
}
293
294
CURLcode Curl_cf_capsule_insert_after(struct Curl_cfilter *cf_at,
295
                                      struct Curl_easy *data)
296
1.31k
{
297
1.31k
  struct Curl_cfilter *cf;
298
1.31k
  CURLcode result;
299
300
1.31k
  result = cf_capsule_create(&cf, data, cf_at->conn);
301
1.31k
  if(!result)
302
1.31k
    Curl_conn_cf_insert_after(cf_at, cf);
303
1.31k
  return result;
304
1.31k
}
305
306
#endif /* !CURL_DISABLE_PROXY && !CURL_DISABLE_HTTP */