/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 */ |