VirtualBox

source: vbox/trunk/src/libs/curl-8.7.1/lib/vquic/curl_msh3.c@ 106542

最後變更 在這個檔案從106542是 104083,由 vboxsync 提交於 11 月 前

curl-8.7.1: Applied and adjusted our curl changes to 8.4.0. bugref:10639

檔案大小: 29.1 KB
 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 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 "curl_trc.h"
34#include "cfilters.h"
35#include "cf-socket.h"
36#include "connect.h"
37#include "progress.h"
38#include "http1.h"
39#include "curl_msh3.h"
40#include "socketpair.h"
41#include "vtls/vtls.h"
42#include "vquic/vquic.h"
43
44/* The last 3 #include files should be in this order */
45#include "curl_printf.h"
46#include "curl_memory.h"
47#include "memdebug.h"
48
49#ifdef CURL_DISABLE_SOCKETPAIR
50#error "MSH3 cannot be build with CURL_DISABLE_SOCKETPAIR set"
51#endif
52
53#define H3_STREAM_WINDOW_SIZE (128 * 1024)
54#define H3_STREAM_CHUNK_SIZE (16 * 1024)
55#define H3_STREAM_RECV_CHUNKS \
56 (H3_STREAM_WINDOW_SIZE / H3_STREAM_CHUNK_SIZE)
57
58#ifdef _WIN32
59#define msh3_lock CRITICAL_SECTION
60#define msh3_lock_initialize(lock) InitializeCriticalSection(lock)
61#define msh3_lock_uninitialize(lock) DeleteCriticalSection(lock)
62#define msh3_lock_acquire(lock) EnterCriticalSection(lock)
63#define msh3_lock_release(lock) LeaveCriticalSection(lock)
64#else /* !_WIN32 */
65#include <pthread.h>
66#define msh3_lock pthread_mutex_t
67#define msh3_lock_initialize(lock) do { \
68 pthread_mutexattr_t attr; \
69 pthread_mutexattr_init(&attr); \
70 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \
71 pthread_mutex_init(lock, &attr); \
72 pthread_mutexattr_destroy(&attr); \
73}while(0)
74#define msh3_lock_uninitialize(lock) pthread_mutex_destroy(lock)
75#define msh3_lock_acquire(lock) pthread_mutex_lock(lock)
76#define msh3_lock_release(lock) pthread_mutex_unlock(lock)
77#endif /* _WIN32 */
78
79
80static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection,
81 void *IfContext);
82static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection,
83 void *IfContext);
84static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection,
85 void *IfContext,
86 MSH3_REQUEST *Request);
87static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
88 void *IfContext,
89 const MSH3_HEADER *Header);
90static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
91 void *IfContext, uint32_t *Length,
92 const uint8_t *Data);
93static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
94 bool Aborted, uint64_t AbortError);
95static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request,
96 void *IfContext);
97static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request,
98 void *IfContext, void *SendContext);
99
100
101void Curl_msh3_ver(char *p, size_t len)
102{
103 uint32_t v[4];
104 MsH3Version(v);
105 (void)msnprintf(p, len, "msh3/%d.%d.%d.%d", v[0], v[1], v[2], v[3]);
106}
107
108#define SP_LOCAL 0
109#define SP_REMOTE 1
110
111struct cf_msh3_ctx {
112 MSH3_API *api;
113 MSH3_CONNECTION *qconn;
114 struct Curl_sockaddr_ex addr;
115 curl_socket_t sock[2]; /* fake socket pair until we get support in msh3 */
116 char l_ip[MAX_IPADR_LEN]; /* local IP as string */
117 int l_port; /* local port number */
118 struct cf_call_data call_data;
119 struct curltime connect_started; /* time the current attempt started */
120 struct curltime handshake_at; /* time connect handshake finished */
121 /* Flags written by msh3/msquic thread */
122 bool handshake_complete;
123 bool handshake_succeeded;
124 bool connected;
125 /* Flags written by curl thread */
126 BIT(verbose);
127 BIT(active);
128};
129
130/* How to access `call_data` from a cf_msh3 filter */
131#undef CF_CTX_CALL_DATA
132#define CF_CTX_CALL_DATA(cf) \
133 ((struct cf_msh3_ctx *)(cf)->ctx)->call_data
134
135/**
136 * All about the H3 internals of a stream
137 */
138struct stream_ctx {
139 struct MSH3_REQUEST *req;
140 struct bufq recvbuf; /* h3 response */
141#ifdef _WIN32
142 CRITICAL_SECTION recv_lock;
143#else /* !_WIN32 */
144 pthread_mutex_t recv_lock;
145#endif /* _WIN32 */
146 uint64_t error3; /* HTTP/3 stream error code */
147 int status_code; /* HTTP status code */
148 CURLcode recv_error;
149 bool closed;
150 bool reset;
151 bool upload_done;
152 bool firstheader; /* FALSE until headers arrive */
153 bool recv_header_complete;
154};
155
156#define H3_STREAM_CTX(d) ((struct stream_ctx *)(((d) && (d)->req.p.http)? \
157 ((struct HTTP *)(d)->req.p.http)->h3_ctx \
158 : NULL))
159#define H3_STREAM_LCTX(d) ((struct HTTP *)(d)->req.p.http)->h3_ctx
160#define H3_STREAM_ID(d) (H3_STREAM_CTX(d)? \
161 H3_STREAM_CTX(d)->id : -2)
162
163
164static CURLcode h3_data_setup(struct Curl_cfilter *cf,
165 struct Curl_easy *data)
166{
167 struct stream_ctx *stream = H3_STREAM_CTX(data);
168
169 if(stream)
170 return CURLE_OK;
171
172 stream = calloc(1, sizeof(*stream));
173 if(!stream)
174 return CURLE_OUT_OF_MEMORY;
175
176 H3_STREAM_LCTX(data) = stream;
177 stream->req = ZERO_NULL;
178 msh3_lock_initialize(&stream->recv_lock);
179 Curl_bufq_init2(&stream->recvbuf, H3_STREAM_CHUNK_SIZE,
180 H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
181 CURL_TRC_CF(data, cf, "data setup");
182 return CURLE_OK;
183}
184
185static void h3_data_done(struct Curl_cfilter *cf, struct Curl_easy *data)
186{
187 struct stream_ctx *stream = H3_STREAM_CTX(data);
188
189 (void)cf;
190 if(stream) {
191 CURL_TRC_CF(data, cf, "easy handle is done");
192 Curl_bufq_free(&stream->recvbuf);
193 free(stream);
194 H3_STREAM_LCTX(data) = NULL;
195 }
196}
197
198static void drain_stream_from_other_thread(struct Curl_easy *data,
199 struct stream_ctx *stream)
200{
201 unsigned char bits;
202
203 /* risky */
204 bits = CURL_CSELECT_IN;
205 if(stream && !stream->upload_done)
206 bits |= CURL_CSELECT_OUT;
207 if(data->state.select_bits != bits) {
208 data->state.select_bits = bits;
209 /* cannot expire from other thread */
210 }
211}
212
213static void drain_stream(struct Curl_cfilter *cf,
214 struct Curl_easy *data)
215{
216 struct stream_ctx *stream = H3_STREAM_CTX(data);
217 unsigned char bits;
218
219 (void)cf;
220 bits = CURL_CSELECT_IN;
221 if(stream && !stream->upload_done)
222 bits |= CURL_CSELECT_OUT;
223 if(data->state.select_bits != bits) {
224 data->state.select_bits = bits;
225 Curl_expire(data, 0, EXPIRE_RUN_NOW);
226 }
227}
228
229static const MSH3_CONNECTION_IF msh3_conn_if = {
230 msh3_conn_connected,
231 msh3_conn_shutdown_complete,
232 msh3_conn_new_request
233};
234
235static void MSH3_CALL msh3_conn_connected(MSH3_CONNECTION *Connection,
236 void *IfContext)
237{
238 struct Curl_cfilter *cf = IfContext;
239 struct cf_msh3_ctx *ctx = cf->ctx;
240 struct Curl_easy *data = CF_DATA_CURRENT(cf);
241 (void)Connection;
242
243 CURL_TRC_CF(data, cf, "[MSH3] connected");
244 ctx->handshake_succeeded = true;
245 ctx->connected = true;
246 ctx->handshake_complete = true;
247}
248
249static void MSH3_CALL msh3_conn_shutdown_complete(MSH3_CONNECTION *Connection,
250 void *IfContext)
251{
252 struct Curl_cfilter *cf = IfContext;
253 struct cf_msh3_ctx *ctx = cf->ctx;
254 struct Curl_easy *data = CF_DATA_CURRENT(cf);
255
256 (void)Connection;
257 CURL_TRC_CF(data, cf, "[MSH3] shutdown complete");
258 ctx->connected = false;
259 ctx->handshake_complete = true;
260}
261
262static void MSH3_CALL msh3_conn_new_request(MSH3_CONNECTION *Connection,
263 void *IfContext,
264 MSH3_REQUEST *Request)
265{
266 (void)Connection;
267 (void)IfContext;
268 (void)Request;
269}
270
271static const MSH3_REQUEST_IF msh3_request_if = {
272 msh3_header_received,
273 msh3_data_received,
274 msh3_complete,
275 msh3_shutdown_complete,
276 msh3_data_sent
277};
278
279/* Decode HTTP status code. Returns -1 if no valid status code was
280 decoded. (duplicate from http2.c) */
281static int decode_status_code(const char *value, size_t len)
282{
283 int i;
284 int res;
285
286 if(len != 3) {
287 return -1;
288 }
289
290 res = 0;
291
292 for(i = 0; i < 3; ++i) {
293 char c = value[i];
294
295 if(c < '0' || c > '9') {
296 return -1;
297 }
298
299 res *= 10;
300 res += c - '0';
301 }
302
303 return res;
304}
305
306/*
307 * write_resp_raw() copies response data in raw format to the `data`'s
308 * receive buffer. If not enough space is available, it appends to the
309 * `data`'s overflow buffer.
310 */
311static CURLcode write_resp_raw(struct Curl_easy *data,
312 const void *mem, size_t memlen)
313{
314 struct stream_ctx *stream = H3_STREAM_CTX(data);
315 CURLcode result = CURLE_OK;
316 ssize_t nwritten;
317
318 if(!stream)
319 return CURLE_RECV_ERROR;
320
321 nwritten = Curl_bufq_write(&stream->recvbuf, mem, memlen, &result);
322 if(nwritten < 0) {
323 return result;
324 }
325
326 if((size_t)nwritten < memlen) {
327 /* This MUST not happen. Our recbuf is dimensioned to hold the
328 * full max_stream_window and then some for this very reason. */
329 DEBUGASSERT(0);
330 return CURLE_RECV_ERROR;
331 }
332 return result;
333}
334
335static void MSH3_CALL msh3_header_received(MSH3_REQUEST *Request,
336 void *userp,
337 const MSH3_HEADER *hd)
338{
339 struct Curl_easy *data = userp;
340 struct stream_ctx *stream = H3_STREAM_CTX(data);
341 CURLcode result;
342 (void)Request;
343
344 if(!stream || stream->recv_header_complete) {
345 return;
346 }
347
348 msh3_lock_acquire(&stream->recv_lock);
349
350 if((hd->NameLength == 7) &&
351 !strncmp(HTTP_PSEUDO_STATUS, (char *)hd->Name, 7)) {
352 char line[14]; /* status line is always 13 characters long */
353 size_t ncopy;
354
355 DEBUGASSERT(!stream->firstheader);
356 stream->status_code = decode_status_code(hd->Value, hd->ValueLength);
357 DEBUGASSERT(stream->status_code != -1);
358 ncopy = msnprintf(line, sizeof(line), "HTTP/3 %03d \r\n",
359 stream->status_code);
360 result = write_resp_raw(data, line, ncopy);
361 if(result)
362 stream->recv_error = result;
363 stream->firstheader = TRUE;
364 }
365 else {
366 /* store as an HTTP1-style header */
367 DEBUGASSERT(stream->firstheader);
368 result = write_resp_raw(data, hd->Name, hd->NameLength);
369 if(!result)
370 result = write_resp_raw(data, ": ", 2);
371 if(!result)
372 result = write_resp_raw(data, hd->Value, hd->ValueLength);
373 if(!result)
374 result = write_resp_raw(data, "\r\n", 2);
375 if(result) {
376 stream->recv_error = result;
377 }
378 }
379
380 drain_stream_from_other_thread(data, stream);
381 msh3_lock_release(&stream->recv_lock);
382}
383
384static bool MSH3_CALL msh3_data_received(MSH3_REQUEST *Request,
385 void *IfContext, uint32_t *buflen,
386 const uint8_t *buf)
387{
388 struct Curl_easy *data = IfContext;
389 struct stream_ctx *stream = H3_STREAM_CTX(data);
390 CURLcode result;
391 bool rv = FALSE;
392
393 /* TODO: we would like to limit the amount of data we are buffer here.
394 * There seems to be no mechanism in msh3 to adjust flow control and
395 * it is undocumented what happens if we return FALSE here or less
396 * length (buflen is an inout parameter).
397 */
398 (void)Request;
399 if(!stream)
400 return FALSE;
401
402 msh3_lock_acquire(&stream->recv_lock);
403
404 if(!stream->recv_header_complete) {
405 result = write_resp_raw(data, "\r\n", 2);
406 if(result) {
407 stream->recv_error = result;
408 goto out;
409 }
410 stream->recv_header_complete = true;
411 }
412
413 result = write_resp_raw(data, buf, *buflen);
414 if(result) {
415 stream->recv_error = result;
416 }
417 rv = TRUE;
418
419out:
420 msh3_lock_release(&stream->recv_lock);
421 return rv;
422}
423
424static void MSH3_CALL msh3_complete(MSH3_REQUEST *Request, void *IfContext,
425 bool aborted, uint64_t error)
426{
427 struct Curl_easy *data = IfContext;
428 struct stream_ctx *stream = H3_STREAM_CTX(data);
429
430 (void)Request;
431 if(!stream)
432 return;
433 msh3_lock_acquire(&stream->recv_lock);
434 stream->closed = TRUE;
435 stream->recv_header_complete = true;
436 if(error)
437 stream->error3 = error;
438 if(aborted)
439 stream->reset = TRUE;
440 msh3_lock_release(&stream->recv_lock);
441}
442
443static void MSH3_CALL msh3_shutdown_complete(MSH3_REQUEST *Request,
444 void *IfContext)
445{
446 struct Curl_easy *data = IfContext;
447 struct stream_ctx *stream = H3_STREAM_CTX(data);
448
449 if(!stream)
450 return;
451 (void)Request;
452 (void)stream;
453}
454
455static void MSH3_CALL msh3_data_sent(MSH3_REQUEST *Request,
456 void *IfContext, void *SendContext)
457{
458 struct Curl_easy *data = IfContext;
459 struct stream_ctx *stream = H3_STREAM_CTX(data);
460 if(!stream)
461 return;
462 (void)Request;
463 (void)stream;
464 (void)SendContext;
465}
466
467static ssize_t recv_closed_stream(struct Curl_cfilter *cf,
468 struct Curl_easy *data,
469 CURLcode *err)
470{
471 struct stream_ctx *stream = H3_STREAM_CTX(data);
472 ssize_t nread = -1;
473
474 if(!stream) {
475 *err = CURLE_RECV_ERROR;
476 return -1;
477 }
478 (void)cf;
479 if(stream->reset) {
480 failf(data, "HTTP/3 stream reset by server");
481 *err = CURLE_PARTIAL_FILE;
482 CURL_TRC_CF(data, cf, "cf_recv, was reset -> %d", *err);
483 goto out;
484 }
485 else if(stream->error3) {
486 failf(data, "HTTP/3 stream was not closed cleanly: (error %zd)",
487 (ssize_t)stream->error3);
488 *err = CURLE_HTTP3;
489 CURL_TRC_CF(data, cf, "cf_recv, closed uncleanly -> %d", *err);
490 goto out;
491 }
492 else {
493 CURL_TRC_CF(data, cf, "cf_recv, closed ok -> %d", *err);
494 }
495 *err = CURLE_OK;
496 nread = 0;
497
498out:
499 return nread;
500}
501
502static void set_quic_expire(struct Curl_cfilter *cf, struct Curl_easy *data)
503{
504 struct stream_ctx *stream = H3_STREAM_CTX(data);
505
506 /* we have no indication from msh3 when it would be a good time
507 * to juggle the connection again. So, we compromise by calling
508 * us again every some milliseconds. */
509 (void)cf;
510 if(stream && stream->req && !stream->closed) {
511 Curl_expire(data, 10, EXPIRE_QUIC);
512 }
513 else {
514 Curl_expire(data, 50, EXPIRE_QUIC);
515 }
516}
517
518static ssize_t cf_msh3_recv(struct Curl_cfilter *cf, struct Curl_easy *data,
519 char *buf, size_t len, CURLcode *err)
520{
521 struct stream_ctx *stream = H3_STREAM_CTX(data);
522 ssize_t nread = -1;
523 struct cf_call_data save;
524
525 (void)cf;
526 if(!stream) {
527 *err = CURLE_RECV_ERROR;
528 return -1;
529 }
530 CF_DATA_SAVE(save, cf, data);
531 CURL_TRC_CF(data, cf, "req: recv with %zu byte buffer", len);
532
533 msh3_lock_acquire(&stream->recv_lock);
534
535 if(stream->recv_error) {
536 failf(data, "request aborted");
537 *err = stream->recv_error;
538 goto out;
539 }
540
541 *err = CURLE_OK;
542
543 if(!Curl_bufq_is_empty(&stream->recvbuf)) {
544 nread = Curl_bufq_read(&stream->recvbuf,
545 (unsigned char *)buf, len, err);
546 CURL_TRC_CF(data, cf, "read recvbuf(len=%zu) -> %zd, %d",
547 len, nread, *err);
548 if(nread < 0)
549 goto out;
550 if(stream->closed)
551 drain_stream(cf, data);
552 }
553 else if(stream->closed) {
554 nread = recv_closed_stream(cf, data, err);
555 goto out;
556 }
557 else {
558 CURL_TRC_CF(data, cf, "req: nothing here, call again");
559 *err = CURLE_AGAIN;
560 }
561
562out:
563 msh3_lock_release(&stream->recv_lock);
564 set_quic_expire(cf, data);
565 CF_DATA_RESTORE(cf, save);
566 return nread;
567}
568
569static ssize_t cf_msh3_send(struct Curl_cfilter *cf, struct Curl_easy *data,
570 const void *buf, size_t len, CURLcode *err)
571{
572 struct cf_msh3_ctx *ctx = cf->ctx;
573 struct stream_ctx *stream = H3_STREAM_CTX(data);
574 struct h1_req_parser h1;
575 struct dynhds h2_headers;
576 MSH3_HEADER *nva = NULL;
577 size_t nheader, i;
578 ssize_t nwritten = -1;
579 struct cf_call_data save;
580 bool eos;
581
582 CF_DATA_SAVE(save, cf, data);
583
584 Curl_h1_req_parse_init(&h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
585 Curl_dynhds_init(&h2_headers, 0, DYN_HTTP_REQUEST);
586
587 /* Sizes must match for cast below to work" */
588 DEBUGASSERT(stream);
589 CURL_TRC_CF(data, cf, "req: send %zu bytes", len);
590
591 if(!stream->req) {
592 /* The first send on the request contains the headers and possibly some
593 data. Parse out the headers and create the request, then if there is
594 any data left over go ahead and send it too. */
595 nwritten = Curl_h1_req_parse_read(&h1, buf, len, NULL, 0, err);
596 if(nwritten < 0)
597 goto out;
598 DEBUGASSERT(h1.done);
599 DEBUGASSERT(h1.req);
600
601 *err = Curl_http_req_to_h2(&h2_headers, h1.req, data);
602 if(*err) {
603 nwritten = -1;
604 goto out;
605 }
606
607 nheader = Curl_dynhds_count(&h2_headers);
608 nva = malloc(sizeof(MSH3_HEADER) * nheader);
609 if(!nva) {
610 *err = CURLE_OUT_OF_MEMORY;
611 nwritten = -1;
612 goto out;
613 }
614
615 for(i = 0; i < nheader; ++i) {
616 struct dynhds_entry *e = Curl_dynhds_getn(&h2_headers, i);
617 nva[i].Name = e->name;
618 nva[i].NameLength = e->namelen;
619 nva[i].Value = e->value;
620 nva[i].ValueLength = e->valuelen;
621 }
622
623 switch(data->state.httpreq) {
624 case HTTPREQ_POST:
625 case HTTPREQ_POST_FORM:
626 case HTTPREQ_POST_MIME:
627 case HTTPREQ_PUT:
628 /* known request body size or -1 */
629 eos = FALSE;
630 break;
631 default:
632 /* there is not request body */
633 eos = TRUE;
634 stream->upload_done = TRUE;
635 break;
636 }
637
638 CURL_TRC_CF(data, cf, "req: send %zu headers", nheader);
639 stream->req = MsH3RequestOpen(ctx->qconn, &msh3_request_if, data,
640 nva, nheader,
641 eos ? MSH3_REQUEST_FLAG_FIN :
642 MSH3_REQUEST_FLAG_NONE);
643 if(!stream->req) {
644 failf(data, "request open failed");
645 *err = CURLE_SEND_ERROR;
646 goto out;
647 }
648 *err = CURLE_OK;
649 nwritten = len;
650 goto out;
651 }
652 else {
653 /* request is open */
654 CURL_TRC_CF(data, cf, "req: send %zu body bytes", len);
655 if(len > 0xFFFFFFFF) {
656 len = 0xFFFFFFFF;
657 }
658
659 if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_NONE, buf,
660 (uint32_t)len, stream)) {
661 *err = CURLE_SEND_ERROR;
662 goto out;
663 }
664
665 /* TODO - msh3/msquic will hold onto this memory until the send complete
666 event. How do we make sure curl doesn't free it until then? */
667 *err = CURLE_OK;
668 nwritten = len;
669 }
670
671out:
672 set_quic_expire(cf, data);
673 free(nva);
674 Curl_h1_req_parse_free(&h1);
675 Curl_dynhds_free(&h2_headers);
676 CF_DATA_RESTORE(cf, save);
677 return nwritten;
678}
679
680static void cf_msh3_adjust_pollset(struct Curl_cfilter *cf,
681 struct Curl_easy *data,
682 struct easy_pollset *ps)
683{
684 struct cf_msh3_ctx *ctx = cf->ctx;
685 struct stream_ctx *stream = H3_STREAM_CTX(data);
686 struct cf_call_data save;
687
688 CF_DATA_SAVE(save, cf, data);
689 if(stream && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) {
690 if(stream->recv_error) {
691 Curl_pollset_add_in(data, ps, ctx->sock[SP_LOCAL]);
692 drain_stream(cf, data);
693 }
694 else if(stream->req) {
695 Curl_pollset_add_out(data, ps, ctx->sock[SP_LOCAL]);
696 drain_stream(cf, data);
697 }
698 }
699}
700
701static bool cf_msh3_data_pending(struct Curl_cfilter *cf,
702 const struct Curl_easy *data)
703{
704 struct stream_ctx *stream = H3_STREAM_CTX(data);
705 struct cf_call_data save;
706 bool pending = FALSE;
707
708 CF_DATA_SAVE(save, cf, data);
709
710 (void)cf;
711 if(stream && stream->req) {
712 msh3_lock_acquire(&stream->recv_lock);
713 CURL_TRC_CF((struct Curl_easy *)data, cf, "data pending = %zu",
714 Curl_bufq_len(&stream->recvbuf));
715 pending = !Curl_bufq_is_empty(&stream->recvbuf);
716 msh3_lock_release(&stream->recv_lock);
717 if(pending)
718 drain_stream(cf, (struct Curl_easy *)data);
719 }
720
721 CF_DATA_RESTORE(cf, save);
722 return pending;
723}
724
725static CURLcode h3_data_pause(struct Curl_cfilter *cf,
726 struct Curl_easy *data,
727 bool pause)
728{
729 if(!pause) {
730 drain_stream(cf, data);
731 Curl_expire(data, 0, EXPIRE_RUN_NOW);
732 }
733 return CURLE_OK;
734}
735
736static CURLcode cf_msh3_data_event(struct Curl_cfilter *cf,
737 struct Curl_easy *data,
738 int event, int arg1, void *arg2)
739{
740 struct stream_ctx *stream = H3_STREAM_CTX(data);
741 struct cf_call_data save;
742 CURLcode result = CURLE_OK;
743
744 CF_DATA_SAVE(save, cf, data);
745
746 (void)arg1;
747 (void)arg2;
748 switch(event) {
749 case CF_CTRL_DATA_SETUP:
750 result = h3_data_setup(cf, data);
751 break;
752 case CF_CTRL_DATA_PAUSE:
753 result = h3_data_pause(cf, data, (arg1 != 0));
754 break;
755 case CF_CTRL_DATA_DONE:
756 h3_data_done(cf, data);
757 break;
758 case CF_CTRL_DATA_DONE_SEND:
759 CURL_TRC_CF(data, cf, "req: send done");
760 if(stream) {
761 stream->upload_done = TRUE;
762 if(stream->req) {
763 char buf[1];
764 if(!MsH3RequestSend(stream->req, MSH3_REQUEST_FLAG_FIN,
765 buf, 0, data)) {
766 result = CURLE_SEND_ERROR;
767 }
768 }
769 }
770 break;
771 default:
772 break;
773 }
774
775 CF_DATA_RESTORE(cf, save);
776 return result;
777}
778
779static CURLcode cf_connect_start(struct Curl_cfilter *cf,
780 struct Curl_easy *data)
781{
782 struct cf_msh3_ctx *ctx = cf->ctx;
783 struct ssl_primary_config *conn_config;
784 MSH3_ADDR addr = {0};
785 CURLcode result;
786 bool verify;
787
788 conn_config = Curl_ssl_cf_get_primary_config(cf);
789 if(!conn_config)
790 return CURLE_FAILED_INIT;
791 verify = !!conn_config->verifypeer;
792
793 memcpy(&addr, &ctx->addr.sa_addr, ctx->addr.addrlen);
794 MSH3_SET_PORT(&addr, (uint16_t)cf->conn->remote_port);
795
796 if(verify && (conn_config->CAfile || conn_config->CApath)) {
797 /* TODO: need a way to provide trust anchors to MSH3 */
798#ifdef DEBUGBUILD
799 /* we need this for our test cases to run */
800 CURL_TRC_CF(data, cf, "non-standard CA not supported, "
801 "switching off verifypeer in DEBUG mode");
802 verify = 0;
803#else
804 CURL_TRC_CF(data, cf, "non-standard CA not supported, "
805 "attempting with built-in verification");
806#endif
807 }
808
809 CURL_TRC_CF(data, cf, "connecting to %s:%d (verify=%d)",
810 cf->conn->host.name, (int)cf->conn->remote_port, verify);
811
812 ctx->api = MsH3ApiOpen();
813 if(!ctx->api) {
814 failf(data, "can't create msh3 api");
815 return CURLE_FAILED_INIT;
816 }
817
818 ctx->qconn = MsH3ConnectionOpen(ctx->api,
819 &msh3_conn_if,
820 cf,
821 cf->conn->host.name,
822 &addr,
823 !verify);
824 if(!ctx->qconn) {
825 failf(data, "can't create msh3 connection");
826 if(ctx->api) {
827 MsH3ApiClose(ctx->api);
828 ctx->api = NULL;
829 }
830 return CURLE_FAILED_INIT;
831 }
832
833 result = h3_data_setup(cf, data);
834 if(result)
835 return result;
836
837 return CURLE_OK;
838}
839
840static CURLcode cf_msh3_connect(struct Curl_cfilter *cf,
841 struct Curl_easy *data,
842 bool blocking, bool *done)
843{
844 struct cf_msh3_ctx *ctx = cf->ctx;
845 struct cf_call_data save;
846 CURLcode result = CURLE_OK;
847
848 (void)blocking;
849 if(cf->connected) {
850 *done = TRUE;
851 return CURLE_OK;
852 }
853
854 CF_DATA_SAVE(save, cf, data);
855
856 if(ctx->sock[SP_LOCAL] == CURL_SOCKET_BAD) {
857 if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &ctx->sock[0]) < 0) {
858 ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
859 ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
860 return CURLE_COULDNT_CONNECT;
861 }
862 }
863
864 *done = FALSE;
865 if(!ctx->qconn) {
866 ctx->connect_started = Curl_now();
867 result = cf_connect_start(cf, data);
868 if(result)
869 goto out;
870 }
871
872 if(ctx->handshake_complete) {
873 ctx->handshake_at = Curl_now();
874 if(ctx->handshake_succeeded) {
875 CURL_TRC_CF(data, cf, "handshake succeeded");
876 cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
877 cf->conn->httpversion = 30;
878 cf->conn->bundle->multiuse = BUNDLE_MULTIPLEX;
879 cf->connected = TRUE;
880 cf->conn->alpn = CURL_HTTP_VERSION_3;
881 *done = TRUE;
882 connkeep(cf->conn, "HTTP/3 default");
883 Curl_pgrsTime(data, TIMER_APPCONNECT);
884 }
885 else {
886 failf(data, "failed to connect, handshake failed");
887 result = CURLE_COULDNT_CONNECT;
888 }
889 }
890
891out:
892 CF_DATA_RESTORE(cf, save);
893 return result;
894}
895
896static void cf_msh3_close(struct Curl_cfilter *cf, struct Curl_easy *data)
897{
898 struct cf_msh3_ctx *ctx = cf->ctx;
899 struct cf_call_data save;
900
901 (void)data;
902 CF_DATA_SAVE(save, cf, data);
903
904 if(ctx) {
905 CURL_TRC_CF(data, cf, "destroying");
906 if(ctx->qconn) {
907 MsH3ConnectionClose(ctx->qconn);
908 ctx->qconn = NULL;
909 }
910 if(ctx->api) {
911 MsH3ApiClose(ctx->api);
912 ctx->api = NULL;
913 }
914
915 if(ctx->active) {
916 /* We share our socket at cf->conn->sock[cf->sockindex] when active.
917 * If it is no longer there, someone has stolen (and hopefully
918 * closed it) and we just forget about it.
919 */
920 ctx->active = FALSE;
921 if(ctx->sock[SP_LOCAL] == cf->conn->sock[cf->sockindex]) {
922 CURL_TRC_CF(data, cf, "cf_msh3_close(%d) active",
923 (int)ctx->sock[SP_LOCAL]);
924 cf->conn->sock[cf->sockindex] = CURL_SOCKET_BAD;
925 }
926 else {
927 CURL_TRC_CF(data, cf, "cf_socket_close(%d) no longer at "
928 "conn->sock[], discarding", (int)ctx->sock[SP_LOCAL]);
929 ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
930 }
931 if(cf->sockindex == FIRSTSOCKET)
932 cf->conn->remote_addr = NULL;
933 }
934 if(ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD) {
935 sclose(ctx->sock[SP_LOCAL]);
936 }
937 if(ctx->sock[SP_REMOTE] != CURL_SOCKET_BAD) {
938 sclose(ctx->sock[SP_REMOTE]);
939 }
940 ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
941 ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
942 }
943 CF_DATA_RESTORE(cf, save);
944}
945
946static void cf_msh3_destroy(struct Curl_cfilter *cf, struct Curl_easy *data)
947{
948 struct cf_call_data save;
949
950 CF_DATA_SAVE(save, cf, data);
951 cf_msh3_close(cf, data);
952 free(cf->ctx);
953 cf->ctx = NULL;
954 /* no CF_DATA_RESTORE(cf, save); its gone */
955
956}
957
958static CURLcode cf_msh3_query(struct Curl_cfilter *cf,
959 struct Curl_easy *data,
960 int query, int *pres1, void *pres2)
961{
962 struct cf_msh3_ctx *ctx = cf->ctx;
963
964 switch(query) {
965 case CF_QUERY_MAX_CONCURRENT: {
966 /* TODO: we do not have access to this so far, fake it */
967 (void)ctx;
968 *pres1 = 100;
969 return CURLE_OK;
970 }
971 case CF_QUERY_TIMER_CONNECT: {
972 struct curltime *when = pres2;
973 /* we do not know when the first byte arrived */
974 if(cf->connected)
975 *when = ctx->handshake_at;
976 return CURLE_OK;
977 }
978 case CF_QUERY_TIMER_APPCONNECT: {
979 struct curltime *when = pres2;
980 if(cf->connected)
981 *when = ctx->handshake_at;
982 return CURLE_OK;
983 }
984 default:
985 break;
986 }
987 return cf->next?
988 cf->next->cft->query(cf->next, data, query, pres1, pres2) :
989 CURLE_UNKNOWN_OPTION;
990}
991
992static bool cf_msh3_conn_is_alive(struct Curl_cfilter *cf,
993 struct Curl_easy *data,
994 bool *input_pending)
995{
996 struct cf_msh3_ctx *ctx = cf->ctx;
997
998 (void)data;
999 *input_pending = FALSE;
1000 return ctx && ctx->sock[SP_LOCAL] != CURL_SOCKET_BAD && ctx->qconn &&
1001 ctx->connected;
1002}
1003
1004struct Curl_cftype Curl_cft_http3 = {
1005 "HTTP/3",
1006 CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
1007 0,
1008 cf_msh3_destroy,
1009 cf_msh3_connect,
1010 cf_msh3_close,
1011 Curl_cf_def_get_host,
1012 cf_msh3_adjust_pollset,
1013 cf_msh3_data_pending,
1014 cf_msh3_send,
1015 cf_msh3_recv,
1016 cf_msh3_data_event,
1017 cf_msh3_conn_is_alive,
1018 Curl_cf_def_conn_keep_alive,
1019 cf_msh3_query,
1020};
1021
1022CURLcode Curl_cf_msh3_create(struct Curl_cfilter **pcf,
1023 struct Curl_easy *data,
1024 struct connectdata *conn,
1025 const struct Curl_addrinfo *ai)
1026{
1027 struct cf_msh3_ctx *ctx = NULL;
1028 struct Curl_cfilter *cf = NULL;
1029 CURLcode result;
1030
1031 (void)data;
1032 (void)conn;
1033 (void)ai; /* TODO: msh3 resolves itself? */
1034 ctx = calloc(1, sizeof(*ctx));
1035 if(!ctx) {
1036 result = CURLE_OUT_OF_MEMORY;
1037 goto out;
1038 }
1039 Curl_sock_assign_addr(&ctx->addr, ai, TRNSPRT_QUIC);
1040 ctx->sock[SP_LOCAL] = CURL_SOCKET_BAD;
1041 ctx->sock[SP_REMOTE] = CURL_SOCKET_BAD;
1042
1043 result = Curl_cf_create(&cf, &Curl_cft_http3, ctx);
1044
1045out:
1046 *pcf = (!result)? cf : NULL;
1047 if(result) {
1048 Curl_safefree(cf);
1049 Curl_safefree(ctx);
1050 }
1051
1052 return result;
1053}
1054
1055bool Curl_conn_is_msh3(const struct Curl_easy *data,
1056 const struct connectdata *conn,
1057 int sockindex)
1058{
1059 struct Curl_cfilter *cf = conn? conn->cfilter[sockindex] : NULL;
1060
1061 (void)data;
1062 for(; cf; cf = cf->next) {
1063 if(cf->cft == &Curl_cft_http3)
1064 return TRUE;
1065 if(cf->cft->flags & CF_TYPE_IP_CONNECT)
1066 return FALSE;
1067 }
1068 return FALSE;
1069}
1070
1071#endif /* USE_MSH3 */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette