1 | /***************************************************************************
|
---|
2 | * _ _ ____ _
|
---|
3 | * Project ___| | | | _ \| |
|
---|
4 | * / __| | | | |_) | |
|
---|
5 | * | (__| |_| | _ <| |___
|
---|
6 | * \___|\___/|_| \_\_____|
|
---|
7 | *
|
---|
8 | * Copyright (C) 1998 - 2022, Daniel Stenberg, <[email protected]>, 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 |
|
---|
25 | #include "curl_setup.h"
|
---|
26 |
|
---|
27 | #ifdef USE_MSH3
|
---|
28 |
|
---|
29 | #include "urldata.h"
|
---|
30 | #include "timeval.h"
|
---|
31 | #include "multiif.h"
|
---|
32 | #include "sendf.h"
|
---|
33 | #include "connect.h"
|
---|
34 | #include "h2h3.h"
|
---|
35 | #include "msh3.h"
|
---|
36 |
|
---|
37 | /* The last 3 #include files should be in this order */
|
---|
38 | #include "curl_printf.h"
|
---|
39 | #include "curl_memory.h"
|
---|
40 | #include "memdebug.h"
|
---|
41 |
|
---|
42 | /* #define DEBUG_HTTP3 1 */
|
---|
43 | #ifdef DEBUG_HTTP3
|
---|
44 | #define H3BUGF(x) x
|
---|
45 | #else
|
---|
46 | #define H3BUGF(x) do { } while(0)
|
---|
47 | #endif
|
---|
48 |
|
---|
49 | #define MSH3_REQ_INIT_BUF_LEN 8192
|
---|
50 |
|
---|
51 | static CURLcode msh3_do_it(struct Curl_easy *data, bool *done);
|
---|
52 | static int msh3_getsock(struct Curl_easy *data,
|
---|
53 | struct connectdata *conn, curl_socket_t *socks);
|
---|
54 | static CURLcode msh3_disconnect(struct Curl_easy *data,
|
---|
55 | struct connectdata *conn,
|
---|
56 | bool dead_connection);
|
---|
57 | static unsigned int msh3_conncheck(struct Curl_easy *data,
|
---|
58 | struct connectdata *conn,
|
---|
59 | unsigned int checks_to_perform);
|
---|
60 | static Curl_recv msh3_stream_recv;
|
---|
61 | static Curl_send msh3_stream_send;
|
---|
62 | static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
|
---|
63 | void *IfContext,
|
---|
64 | const MSH3_HEADER *Header);
|
---|
65 | static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
|
---|
66 | void *IfContext, uint32_t Length,
|
---|
67 | const uint8_t *Data);
|
---|
68 | static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
|
---|
69 | bool Aborted, uint64_t AbortError);
|
---|
70 | static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext);
|
---|
71 |
|
---|
72 | static const struct Curl_handler msh3_curl_handler_http3 = {
|
---|
73 | "HTTPS", /* scheme */
|
---|
74 | ZERO_NULL, /* setup_connection */
|
---|
75 | msh3_do_it, /* do_it */
|
---|
76 | Curl_http_done, /* done */
|
---|
77 | ZERO_NULL, /* do_more */
|
---|
78 | ZERO_NULL, /* connect_it */
|
---|
79 | ZERO_NULL, /* connecting */
|
---|
80 | ZERO_NULL, /* doing */
|
---|
81 | msh3_getsock, /* proto_getsock */
|
---|
82 | msh3_getsock, /* doing_getsock */
|
---|
83 | ZERO_NULL, /* domore_getsock */
|
---|
84 | msh3_getsock, /* perform_getsock */
|
---|
85 | msh3_disconnect, /* disconnect */
|
---|
86 | ZERO_NULL, /* readwrite */
|
---|
87 | msh3_conncheck, /* connection_check */
|
---|
88 | ZERO_NULL, /* attach connection */
|
---|
89 | PORT_HTTP, /* defport */
|
---|
90 | CURLPROTO_HTTPS, /* protocol */
|
---|
91 | CURLPROTO_HTTP, /* family */
|
---|
92 | PROTOPT_SSL | PROTOPT_STREAM /* flags */
|
---|
93 | };
|
---|
94 |
|
---|
95 | static const MSH3_REQUEST_IF msh3_request_if = {
|
---|
96 | msh3_header_received,
|
---|
97 | msh3_data_received,
|
---|
98 | msh3_complete,
|
---|
99 | msh3_shutdown
|
---|
100 | };
|
---|
101 |
|
---|
102 | void Curl_quic_ver(char *p, size_t len)
|
---|
103 | {
|
---|
104 | uint32_t v[4];
|
---|
105 | MsH3Version(v);
|
---|
106 | (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]);
|
---|
107 | }
|
---|
108 |
|
---|
109 | CURLcode Curl_quic_connect(struct Curl_easy *data,
|
---|
110 | struct connectdata *conn,
|
---|
111 | curl_socket_t sockfd,
|
---|
112 | int sockindex,
|
---|
113 | const struct sockaddr *addr,
|
---|
114 | socklen_t addrlen)
|
---|
115 | {
|
---|
116 | struct quicsocket *qs = &conn->hequic[sockindex];
|
---|
117 | bool insecure = !conn->ssl_config.verifypeer;
|
---|
118 | memset(qs, 0, sizeof(*qs));
|
---|
119 |
|
---|
120 | (void)sockfd;
|
---|
121 | (void)addr; /* TODO - Pass address along */
|
---|
122 | (void)addrlen;
|
---|
123 |
|
---|
124 | H3BUGF(infof(data, "creating new api/connection"));
|
---|
125 |
|
---|
126 | qs->api = MsH3ApiOpen();
|
---|
127 | if(!qs->api) {
|
---|
128 | failf(data, "can't create msh3 api");
|
---|
129 | return CURLE_FAILED_INIT;
|
---|
130 | }
|
---|
131 |
|
---|
132 | qs->conn = MsH3ConnectionOpen(qs->api,
|
---|
133 | conn->host.name,
|
---|
134 | (uint16_t)conn->remote_port,
|
---|
135 | insecure);
|
---|
136 | if(!qs->conn) {
|
---|
137 | failf(data, "can't create msh3 connection");
|
---|
138 | if(qs->api) {
|
---|
139 | MsH3ApiClose(qs->api);
|
---|
140 | }
|
---|
141 | return CURLE_FAILED_INIT;
|
---|
142 | }
|
---|
143 |
|
---|
144 | return CURLE_OK;
|
---|
145 | }
|
---|
146 |
|
---|
147 | CURLcode Curl_quic_is_connected(struct Curl_easy *data,
|
---|
148 | struct connectdata *conn,
|
---|
149 | int sockindex,
|
---|
150 | bool *connected)
|
---|
151 | {
|
---|
152 | struct quicsocket *qs = &conn->hequic[sockindex];
|
---|
153 | MSH3_CONNECTION_STATE state;
|
---|
154 |
|
---|
155 | state = MsH3ConnectionGetState(qs->conn, false);
|
---|
156 | if(state == MSH3_CONN_HANDSHAKE_FAILED || state == MSH3_CONN_DISCONNECTED) {
|
---|
157 | failf(data, "failed to connect, state=%u", (uint32_t)state);
|
---|
158 | return CURLE_COULDNT_CONNECT;
|
---|
159 | }
|
---|
160 |
|
---|
161 | if(state == MSH3_CONN_CONNECTED) {
|
---|
162 | H3BUGF(infof(data, "connection connected"));
|
---|
163 | *connected = true;
|
---|
164 | conn->quic = qs;
|
---|
165 | conn->recv[sockindex] = msh3_stream_recv;
|
---|
166 | conn->send[sockindex] = msh3_stream_send;
|
---|
167 | conn->handler = &msh3_curl_handler_http3;
|
---|
168 | conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
|
---|
169 | conn->httpversion = 30;
|
---|
170 | conn->bundle->multiuse = BUNDLE_MULTIPLEX;
|
---|
171 | /* TODO - Clean up other happy-eyeballs connection(s)? */
|
---|
172 | }
|
---|
173 |
|
---|
174 | return CURLE_OK;
|
---|
175 | }
|
---|
176 |
|
---|
177 | static int msh3_getsock(struct Curl_easy *data,
|
---|
178 | struct connectdata *conn, curl_socket_t *socks)
|
---|
179 | {
|
---|
180 | struct HTTP *stream = data->req.p.http;
|
---|
181 | int bitmap = GETSOCK_BLANK;
|
---|
182 |
|
---|
183 | socks[0] = conn->sock[FIRSTSOCKET];
|
---|
184 |
|
---|
185 | if(stream->recv_error) {
|
---|
186 | bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
|
---|
187 | data->state.drain++;
|
---|
188 | }
|
---|
189 | else if(stream->recv_header_len || stream->recv_data_len) {
|
---|
190 | bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
|
---|
191 | data->state.drain++;
|
---|
192 | }
|
---|
193 |
|
---|
194 | H3BUGF(infof(data, "msh3_getsock %u", (uint32_t)data->state.drain));
|
---|
195 |
|
---|
196 | return bitmap;
|
---|
197 | }
|
---|
198 |
|
---|
199 | static CURLcode msh3_do_it(struct Curl_easy *data, bool *done)
|
---|
200 | {
|
---|
201 | struct HTTP *stream = data->req.p.http;
|
---|
202 | H3BUGF(infof(data, "msh3_do_it"));
|
---|
203 | stream->recv_buf = malloc(MSH3_REQ_INIT_BUF_LEN);
|
---|
204 | if(!stream->recv_buf) {
|
---|
205 | return CURLE_OUT_OF_MEMORY;
|
---|
206 | }
|
---|
207 | stream->req = ZERO_NULL;
|
---|
208 | msh3_lock_initialize(&stream->recv_lock);
|
---|
209 | stream->recv_buf_alloc = MSH3_REQ_INIT_BUF_LEN;
|
---|
210 | stream->recv_header_len = 0;
|
---|
211 | stream->recv_header_complete = false;
|
---|
212 | stream->recv_data_len = 0;
|
---|
213 | stream->recv_data_complete = false;
|
---|
214 | stream->recv_error = CURLE_OK;
|
---|
215 | return Curl_http(data, done);
|
---|
216 | }
|
---|
217 |
|
---|
218 | static unsigned int msh3_conncheck(struct Curl_easy *data,
|
---|
219 | struct connectdata *conn,
|
---|
220 | unsigned int checks_to_perform)
|
---|
221 | {
|
---|
222 | (void)data;
|
---|
223 | (void)conn;
|
---|
224 | (void)checks_to_perform;
|
---|
225 | H3BUGF(infof(data, "msh3_conncheck"));
|
---|
226 | return CONNRESULT_NONE;
|
---|
227 | }
|
---|
228 |
|
---|
229 | static void disconnect(struct quicsocket *qs)
|
---|
230 | {
|
---|
231 | if(qs->conn) {
|
---|
232 | MsH3ConnectionClose(qs->conn);
|
---|
233 | qs->conn = ZERO_NULL;
|
---|
234 | }
|
---|
235 | if(qs->api) {
|
---|
236 | MsH3ApiClose(qs->api);
|
---|
237 | qs->api = ZERO_NULL;
|
---|
238 | }
|
---|
239 | }
|
---|
240 |
|
---|
241 | static CURLcode msh3_disconnect(struct Curl_easy *data,
|
---|
242 | struct connectdata *conn, bool dead_connection)
|
---|
243 | {
|
---|
244 | (void)data;
|
---|
245 | (void)dead_connection;
|
---|
246 | H3BUGF(infof(data, "disconnecting (msh3)"));
|
---|
247 | disconnect(conn->quic);
|
---|
248 | return CURLE_OK;
|
---|
249 | }
|
---|
250 |
|
---|
251 | void Curl_quic_disconnect(struct Curl_easy *data, struct connectdata *conn,
|
---|
252 | int tempindex)
|
---|
253 | {
|
---|
254 | (void)data;
|
---|
255 | if(conn->transport == TRNSPRT_QUIC) {
|
---|
256 | H3BUGF(infof(data, "disconnecting QUIC index %u", tempindex));
|
---|
257 | disconnect(&conn->hequic[tempindex]);
|
---|
258 | }
|
---|
259 | }
|
---|
260 |
|
---|
261 | /* Requires stream->recv_lock to be held */
|
---|
262 | static bool msh3request_ensure_room(struct HTTP *stream, size_t len)
|
---|
263 | {
|
---|
264 | uint8_t *new_recv_buf;
|
---|
265 | const size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
|
---|
266 | if(cur_recv_len + len > stream->recv_buf_alloc) {
|
---|
267 | size_t new_recv_buf_alloc_len = stream->recv_buf_alloc;
|
---|
268 | do {
|
---|
269 | new_recv_buf_alloc_len <<= 1; /* TODO - handle overflow */
|
---|
270 | } while(cur_recv_len + len > new_recv_buf_alloc_len);
|
---|
271 | new_recv_buf = malloc(new_recv_buf_alloc_len);
|
---|
272 | if(!new_recv_buf) {
|
---|
273 | return false;
|
---|
274 | }
|
---|
275 | if(cur_recv_len) {
|
---|
276 | memcpy(new_recv_buf, stream->recv_buf, cur_recv_len);
|
---|
277 | }
|
---|
278 | stream->recv_buf_alloc = new_recv_buf_alloc_len;
|
---|
279 | free(stream->recv_buf);
|
---|
280 | stream->recv_buf = new_recv_buf;
|
---|
281 | }
|
---|
282 | return true;
|
---|
283 | }
|
---|
284 |
|
---|
285 | static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
|
---|
286 | void *IfContext,
|
---|
287 | const MSH3_HEADER *Header)
|
---|
288 | {
|
---|
289 | struct HTTP *stream = IfContext;
|
---|
290 | size_t total_len;
|
---|
291 | (void)Request;
|
---|
292 |
|
---|
293 | if(stream->recv_header_complete) {
|
---|
294 | H3BUGF(printf("* ignoring header after data\n"));
|
---|
295 | return;
|
---|
296 | }
|
---|
297 |
|
---|
298 | msh3_lock_acquire(&stream->recv_lock);
|
---|
299 |
|
---|
300 | if((Header->NameLength == 7) &&
|
---|
301 | !strncmp(H2H3_PSEUDO_STATUS, (char *)Header->Name, 7)) {
|
---|
302 | total_len = 9 + Header->ValueLength;
|
---|
303 | if(!msh3request_ensure_room(stream, total_len)) {
|
---|
304 | /* TODO - handle error */
|
---|
305 | goto release_lock;
|
---|
306 | }
|
---|
307 | msnprintf((char *)stream->recv_buf + stream->recv_header_len,
|
---|
308 | stream->recv_buf_alloc - stream->recv_header_len,
|
---|
309 | "HTTP/3 %.*s\n", (int)Header->ValueLength, Header->Value);
|
---|
310 | }
|
---|
311 | else {
|
---|
312 | total_len = Header->NameLength + 4 + Header->ValueLength;
|
---|
313 | if(!msh3request_ensure_room(stream, total_len)) {
|
---|
314 | /* TODO - handle error */
|
---|
315 | goto release_lock;
|
---|
316 | }
|
---|
317 | msnprintf((char *)stream->recv_buf + stream->recv_header_len,
|
---|
318 | stream->recv_buf_alloc - stream->recv_header_len,
|
---|
319 | "%.*s: %.*s\n",
|
---|
320 | (int)Header->NameLength, Header->Name,
|
---|
321 | (int)Header->ValueLength, Header->Value);
|
---|
322 | }
|
---|
323 |
|
---|
324 | stream->recv_header_len += total_len - 1; /* don't include null-terminator */
|
---|
325 |
|
---|
326 | release_lock:
|
---|
327 | msh3_lock_release(&stream->recv_lock);
|
---|
328 | }
|
---|
329 |
|
---|
330 | static void MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
|
---|
331 | void *IfContext, uint32_t Length,
|
---|
332 | const uint8_t *Data)
|
---|
333 | {
|
---|
334 | struct HTTP *stream = IfContext;
|
---|
335 | size_t cur_recv_len = stream->recv_header_len + stream->recv_data_len;
|
---|
336 | (void)Request;
|
---|
337 | H3BUGF(printf("* msh3_data_received %u. %zu buffered, %zu allocated\n",
|
---|
338 | Length, cur_recv_len, stream->recv_buf_alloc));
|
---|
339 | msh3_lock_acquire(&stream->recv_lock);
|
---|
340 | if(!stream->recv_header_complete) {
|
---|
341 | H3BUGF(printf("* Headers complete!\n"));
|
---|
342 | if(!msh3request_ensure_room(stream, 2)) {
|
---|
343 | /* TODO - handle error */
|
---|
344 | goto release_lock;
|
---|
345 | }
|
---|
346 | stream->recv_buf[stream->recv_header_len++] = '\r';
|
---|
347 | stream->recv_buf[stream->recv_header_len++] = '\n';
|
---|
348 | stream->recv_header_complete = true;
|
---|
349 | cur_recv_len += 2;
|
---|
350 | }
|
---|
351 | if(!msh3request_ensure_room(stream, Length)) {
|
---|
352 | /* TODO - handle error */
|
---|
353 | goto release_lock;
|
---|
354 | }
|
---|
355 | memcpy(stream->recv_buf + cur_recv_len, Data, Length);
|
---|
356 | stream->recv_data_len += (size_t)Length;
|
---|
357 | release_lock:
|
---|
358 | msh3_lock_release(&stream->recv_lock);
|
---|
359 | }
|
---|
360 |
|
---|
361 | static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
|
---|
362 | bool Aborted, uint64_t AbortError)
|
---|
363 | {
|
---|
364 | struct HTTP *stream = IfContext;
|
---|
365 | (void)Request;
|
---|
366 | (void)AbortError;
|
---|
367 | H3BUGF(printf("* msh3_complete, aborted=%s\n", Aborted ? "true" : "false"));
|
---|
368 | msh3_lock_acquire(&stream->recv_lock);
|
---|
369 | if(Aborted) {
|
---|
370 | stream->recv_error = CURLE_HTTP3; /* TODO - how do we pass AbortError? */
|
---|
371 | }
|
---|
372 | stream->recv_header_complete = true;
|
---|
373 | stream->recv_data_complete = true;
|
---|
374 | msh3_lock_release(&stream->recv_lock);
|
---|
375 | }
|
---|
376 |
|
---|
377 | static void MSH3_CALL msh3_shutdown(MSH3_REQUEST *Request, void *IfContext)
|
---|
378 | {
|
---|
379 | struct HTTP *stream = IfContext;
|
---|
380 | (void)Request;
|
---|
381 | (void)stream;
|
---|
382 | }
|
---|
383 |
|
---|
384 | static ssize_t msh3_stream_send(struct Curl_easy *data,
|
---|
385 | int sockindex,
|
---|
386 | const void *mem,
|
---|
387 | size_t len,
|
---|
388 | CURLcode *curlcode)
|
---|
389 | {
|
---|
390 | struct connectdata *conn = data->conn;
|
---|
391 | struct HTTP *stream = data->req.p.http;
|
---|
392 | struct quicsocket *qs = conn->quic;
|
---|
393 | struct h2h3req *hreq;
|
---|
394 |
|
---|
395 | (void)sockindex;
|
---|
396 | /* Sizes must match for cast below to work" */
|
---|
397 | DEBUGASSERT(sizeof(MSH3_HEADER) == sizeof(struct h2h3pseudo));
|
---|
398 |
|
---|
399 | H3BUGF(infof(data, "msh3_stream_send %zu", len));
|
---|
400 |
|
---|
401 | if(!stream->req) {
|
---|
402 | *curlcode = Curl_pseudo_headers(data, mem, len, &hreq);
|
---|
403 | if(*curlcode) {
|
---|
404 | failf(data, "Curl_pseudo_headers failed");
|
---|
405 | return -1;
|
---|
406 | }
|
---|
407 | H3BUGF(infof(data, "starting request with %zu headers", hreq->entries));
|
---|
408 | stream->req = MsH3RequestOpen(qs->conn, &msh3_request_if, stream,
|
---|
409 | (MSH3_HEADER*)hreq->header, hreq->entries);
|
---|
410 | Curl_pseudo_free(hreq);
|
---|
411 | if(!stream->req) {
|
---|
412 | failf(data, "request open failed");
|
---|
413 | *curlcode = CURLE_SEND_ERROR;
|
---|
414 | return -1;
|
---|
415 | }
|
---|
416 | *curlcode = CURLE_OK;
|
---|
417 | return len;
|
---|
418 | }
|
---|
419 | H3BUGF(infof(data, "send %zd body bytes on request %p", len,
|
---|
420 | (void *)stream->req));
|
---|
421 | *curlcode = CURLE_SEND_ERROR;
|
---|
422 | return -1;
|
---|
423 | }
|
---|
424 |
|
---|
425 | static ssize_t msh3_stream_recv(struct Curl_easy *data,
|
---|
426 | int sockindex,
|
---|
427 | char *buf,
|
---|
428 | size_t buffersize,
|
---|
429 | CURLcode *curlcode)
|
---|
430 | {
|
---|
431 | struct HTTP *stream = data->req.p.http;
|
---|
432 | size_t outsize = 0;
|
---|
433 | (void)sockindex;
|
---|
434 | H3BUGF(infof(data, "msh3_stream_recv %zu", buffersize));
|
---|
435 |
|
---|
436 | if(stream->recv_error) {
|
---|
437 | failf(data, "request aborted");
|
---|
438 | *curlcode = stream->recv_error;
|
---|
439 | return -1;
|
---|
440 | }
|
---|
441 |
|
---|
442 | msh3_lock_acquire(&stream->recv_lock);
|
---|
443 |
|
---|
444 | if(stream->recv_header_len) {
|
---|
445 | outsize = buffersize;
|
---|
446 | if(stream->recv_header_len < outsize) {
|
---|
447 | outsize = stream->recv_header_len;
|
---|
448 | }
|
---|
449 | memcpy(buf, stream->recv_buf, outsize);
|
---|
450 | if(outsize < stream->recv_header_len + stream->recv_data_len) {
|
---|
451 | memmove(stream->recv_buf, stream->recv_buf + outsize,
|
---|
452 | stream->recv_header_len + stream->recv_data_len - outsize);
|
---|
453 | }
|
---|
454 | stream->recv_header_len -= outsize;
|
---|
455 | H3BUGF(infof(data, "returned %zu bytes of headers", outsize));
|
---|
456 | }
|
---|
457 | else if(stream->recv_data_len) {
|
---|
458 | outsize = buffersize;
|
---|
459 | if(stream->recv_data_len < outsize) {
|
---|
460 | outsize = stream->recv_data_len;
|
---|
461 | }
|
---|
462 | memcpy(buf, stream->recv_buf, outsize);
|
---|
463 | if(outsize < stream->recv_data_len) {
|
---|
464 | memmove(stream->recv_buf, stream->recv_buf + outsize,
|
---|
465 | stream->recv_data_len - outsize);
|
---|
466 | }
|
---|
467 | stream->recv_data_len -= outsize;
|
---|
468 | H3BUGF(infof(data, "returned %zu bytes of data", outsize));
|
---|
469 | }
|
---|
470 | else if(stream->recv_data_complete) {
|
---|
471 | H3BUGF(infof(data, "receive complete"));
|
---|
472 | }
|
---|
473 |
|
---|
474 | msh3_lock_release(&stream->recv_lock);
|
---|
475 |
|
---|
476 | return (ssize_t)outsize;
|
---|
477 | }
|
---|
478 |
|
---|
479 | CURLcode Curl_quic_done_sending(struct Curl_easy *data)
|
---|
480 | {
|
---|
481 | struct connectdata *conn = data->conn;
|
---|
482 | H3BUGF(infof(data, "Curl_quic_done_sending"));
|
---|
483 | if(conn->handler == &msh3_curl_handler_http3) {
|
---|
484 | struct HTTP *stream = data->req.p.http;
|
---|
485 | stream->upload_done = TRUE;
|
---|
486 | }
|
---|
487 |
|
---|
488 | return CURLE_OK;
|
---|
489 | }
|
---|
490 |
|
---|
491 | void Curl_quic_done(struct Curl_easy *data, bool premature)
|
---|
492 | {
|
---|
493 | struct HTTP *stream = data->req.p.http;
|
---|
494 | (void)premature;
|
---|
495 | H3BUGF(infof(data, "Curl_quic_done"));
|
---|
496 | if(stream) {
|
---|
497 | if(stream->recv_buf) {
|
---|
498 | Curl_safefree(stream->recv_buf);
|
---|
499 | msh3_lock_uninitialize(&stream->recv_lock);
|
---|
500 | }
|
---|
501 | if(stream->req) {
|
---|
502 | MsH3RequestClose(stream->req);
|
---|
503 | stream->req = ZERO_NULL;
|
---|
504 | }
|
---|
505 | }
|
---|
506 | }
|
---|
507 |
|
---|
508 | bool Curl_quic_data_pending(const struct Curl_easy *data)
|
---|
509 | {
|
---|
510 | struct HTTP *stream = data->req.p.http;
|
---|
511 | H3BUGF(infof((struct Curl_easy *)data, "Curl_quic_data_pending"));
|
---|
512 | return stream->recv_header_len || stream->recv_data_len;
|
---|
513 | }
|
---|
514 |
|
---|
515 | /*
|
---|
516 | * Called from transfer.c:Curl_readwrite when neither HTTP level read
|
---|
517 | * nor write is performed. It is a good place to handle timer expiry
|
---|
518 | * for QUIC transport.
|
---|
519 | */
|
---|
520 | CURLcode Curl_quic_idle(struct Curl_easy *data)
|
---|
521 | {
|
---|
522 | (void)data;
|
---|
523 | H3BUGF(infof(data, "Curl_quic_idle"));
|
---|
524 | return CURLE_OK;
|
---|
525 | }
|
---|
526 |
|
---|
527 | #endif /* USE_MSH3 */
|
---|