1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2021, 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 ***************************************************************************/
22
23#include "curl_setup.h"
24
25#ifdef USE_QUICHE
26#include <quiche.h>
27#include <openssl/err.h>
28#include "urldata.h"
29#include "sendf.h"
30#include "strdup.h"
31#include "rand.h"
32#include "quic.h"
33#include "strcase.h"
34#include "multiif.h"
35#include "connect.h"
36#include "strerror.h"
37#include "vquic.h"
38
39/* The last 3 #include files should be in this order */
40#include "curl_printf.h"
41#include "curl_memory.h"
42#include "memdebug.h"
43
44#define DEBUG_HTTP3
45/* #define DEBUG_QUICHE */
46#ifdef DEBUG_HTTP3
47#define H3BUGF(x) x
48#else
49#define H3BUGF(x) do { } while(0)
50#endif
51
52#define QUIC_MAX_STREAMS (256*1024)
53#define QUIC_MAX_DATA (1*1024*1024)
54#define QUIC_IDLE_TIMEOUT (60 * 1000) /* milliseconds */
55
56static CURLcode process_ingress(struct Curl_easy *data,
57 curl_socket_t sockfd,
58 struct quicsocket *qs);
59
60static CURLcode flush_egress(struct Curl_easy *data, curl_socket_t sockfd,
61 struct quicsocket *qs);
62
63static CURLcode http_request(struct Curl_easy *data, const void *mem,
64 size_t len);
65static Curl_recv h3_stream_recv;
66static Curl_send h3_stream_send;
67
68static int quiche_getsock(struct Curl_easy *data,
69 struct connectdata *conn, curl_socket_t *socks)
70{
71 struct SingleRequest *k = &data->req;
72 int bitmap = GETSOCK_BLANK;
73
74 socks[0] = conn->sock[FIRSTSOCKET];
75
76 /* in a HTTP/2 connection we can basically always get a frame so we should
77 always be ready for one */
78 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
79
80 /* we're still uploading or the HTTP/2 layer wants to send data */
81 if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND)
82 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
83
84 return bitmap;
85}
86
87static CURLcode qs_disconnect(struct Curl_easy *data,
88 struct quicsocket *qs)
89{
90 DEBUGASSERT(qs);
91 if(qs->conn) {
92 (void)quiche_conn_close(qs->conn, TRUE, 0, NULL, 0);
93 /* flushing the egress is not a failsafe way to deliver all the
94 outstanding packets, but we also don't want to get stuck here... */
95 (void)flush_egress(data, qs->sockfd, qs);
96 quiche_conn_free(qs->conn);
97 qs->conn = NULL;
98 }
99 if(qs->h3config)
100 quiche_h3_config_free(qs->h3config);
101 if(qs->h3c)
102 quiche_h3_conn_free(qs->h3c);
103 if(qs->cfg) {
104 quiche_config_free(qs->cfg);
105 qs->cfg = NULL;
106 }
107 return CURLE_OK;
108}
109
110static CURLcode quiche_disconnect(struct Curl_easy *data,
111 struct connectdata *conn,
112 bool dead_connection)
113{
114 struct quicsocket *qs = conn->quic;
115 (void)dead_connection;
116 return qs_disconnect(data, qs);
117}
118
119void Curl_quic_disconnect(struct Curl_easy *data,
120 struct connectdata *conn,
121 int tempindex)
122{
123 if(conn->transport == TRNSPRT_QUIC)
124 qs_disconnect(data, &conn->hequic[tempindex]);
125}
126
127static unsigned int quiche_conncheck(struct Curl_easy *data,
128 struct connectdata *conn,
129 unsigned int checks_to_perform)
130{
131 (void)data;
132 (void)conn;
133 (void)checks_to_perform;
134 return CONNRESULT_NONE;
135}
136
137static CURLcode quiche_do(struct Curl_easy *data, bool *done)
138{
139 struct HTTP *stream = data->req.p.http;
140 stream->h3req = FALSE; /* not sent */
141 return Curl_http(data, done);
142}
143
144static const struct Curl_handler Curl_handler_http3 = {
145 "HTTPS", /* scheme */
146 ZERO_NULL, /* setup_connection */
147 quiche_do, /* do_it */
148 Curl_http_done, /* done */
149 ZERO_NULL, /* do_more */
150 ZERO_NULL, /* connect_it */
151 ZERO_NULL, /* connecting */
152 ZERO_NULL, /* doing */
153 quiche_getsock, /* proto_getsock */
154 quiche_getsock, /* doing_getsock */
155 ZERO_NULL, /* domore_getsock */
156 quiche_getsock, /* perform_getsock */
157 quiche_disconnect, /* disconnect */
158 ZERO_NULL, /* readwrite */
159 quiche_conncheck, /* connection_check */
160 ZERO_NULL, /* attach connection */
161 PORT_HTTP, /* defport */
162 CURLPROTO_HTTPS, /* protocol */
163 CURLPROTO_HTTP, /* family */
164 PROTOPT_SSL | PROTOPT_STREAM /* flags */
165};
166
167#ifdef DEBUG_QUICHE
168static void quiche_debug_log(const char *line, void *argp)
169{
170 (void)argp;
171 fprintf(stderr, "%s\n", line);
172}
173#endif
174
175CURLcode Curl_quic_connect(struct Curl_easy *data,
176 struct connectdata *conn, curl_socket_t sockfd,
177 int sockindex,
178 const struct sockaddr *addr, socklen_t addrlen)
179{
180 CURLcode result;
181 struct quicsocket *qs = &conn->hequic[sockindex];
182 char *keylog_file = NULL;
183 char ipbuf[40];
184 int port;
185
186#ifdef DEBUG_QUICHE
187 /* initialize debug log callback only once */
188 static int debug_log_init = 0;
189 if(!debug_log_init) {
190 quiche_enable_debug_logging(quiche_debug_log, NULL);
191 debug_log_init = 1;
192 }
193#endif
194
195 (void)addr;
196 (void)addrlen;
197
198 qs->sockfd = sockfd;
199 qs->cfg = quiche_config_new(QUICHE_PROTOCOL_VERSION);
200 if(!qs->cfg) {
201 failf(data, "can't create quiche config");
202 return CURLE_FAILED_INIT;
203 }
204
205 quiche_config_set_max_idle_timeout(qs->cfg, QUIC_IDLE_TIMEOUT);
206 quiche_config_set_initial_max_data(qs->cfg, QUIC_MAX_DATA);
207 quiche_config_set_initial_max_stream_data_bidi_local(qs->cfg, QUIC_MAX_DATA);
208 quiche_config_set_initial_max_stream_data_bidi_remote(qs->cfg,
209 QUIC_MAX_DATA);
210 quiche_config_set_initial_max_stream_data_uni(qs->cfg, QUIC_MAX_DATA);
211 quiche_config_set_initial_max_streams_bidi(qs->cfg, QUIC_MAX_STREAMS);
212 quiche_config_set_initial_max_streams_uni(qs->cfg, QUIC_MAX_STREAMS);
213 quiche_config_set_application_protos(qs->cfg,
214 (uint8_t *)
215 QUICHE_H3_APPLICATION_PROTOCOL,
216 sizeof(QUICHE_H3_APPLICATION_PROTOCOL)
217 - 1);
218
219 result = Curl_rand(data, qs->scid, sizeof(qs->scid));
220 if(result)
221 return result;
222
223 keylog_file = getenv("SSLKEYLOGFILE");
224
225 if(keylog_file)
226 quiche_config_log_keys(qs->cfg);
227
228 qs->conn = quiche_connect(conn->host.name, (const uint8_t *) qs->scid,
229 sizeof(qs->scid), addr, addrlen, qs->cfg);
230 if(!qs->conn) {
231 failf(data, "can't create quiche connection");
232 return CURLE_OUT_OF_MEMORY;
233 }
234
235 if(keylog_file)
236 quiche_conn_set_keylog_path(qs->conn, keylog_file);
237
238 /* Known to not work on Windows */
239#if !defined(WIN32) && defined(HAVE_QUICHE_CONN_SET_QLOG_FD)
240 {
241 int qfd;
242 (void)Curl_qlogdir(data, qs->scid, sizeof(qs->scid), &qfd);
243 if(qfd != -1)
244 quiche_conn_set_qlog_fd(qs->conn, qfd,
245 "qlog title", "curl qlog");
246 }
247#endif
248
249 result = flush_egress(data, sockfd, qs);
250 if(result)
251 return result;
252
253 /* extract the used address as a string */
254 if(!Curl_addr2string((struct sockaddr*)addr, addrlen, ipbuf, &port)) {
255 char buffer[STRERROR_LEN];
256 failf(data, "ssrem inet_ntop() failed with errno %d: %s",
257 SOCKERRNO, Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
258 return CURLE_BAD_FUNCTION_ARGUMENT;
259 }
260
261 infof(data, "Connect socket %d over QUIC to %s:%ld",
262 sockfd, ipbuf, port);
263
264 Curl_persistconninfo(data, conn, NULL, -1);
265
266 /* for connection reuse purposes: */
267 conn->ssl[FIRSTSOCKET].state = ssl_connection_complete;
268
269 {
270 unsigned char alpn_protocols[] = QUICHE_H3_APPLICATION_PROTOCOL;
271 unsigned alpn_len, offset = 0;
272
273 /* Replace each ALPN length prefix by a comma. */
274 while(offset < sizeof(alpn_protocols) - 1) {
275 alpn_len = alpn_protocols[offset];
276 alpn_protocols[offset] = ',';
277 offset += 1 + alpn_len;
278 }
279
280 infof(data, "Sent QUIC client Initial, ALPN: %s",
281 alpn_protocols + 1);
282 }
283
284 return CURLE_OK;
285}
286
287static CURLcode quiche_has_connected(struct connectdata *conn,
288 int sockindex,
289 int tempindex)
290{
291 CURLcode result;
292 struct quicsocket *qs = conn->quic = &conn->hequic[tempindex];
293
294 conn->recv[sockindex] = h3_stream_recv;
295 conn->send[sockindex] = h3_stream_send;
296 conn->handler = &Curl_handler_http3;
297 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
298 conn->httpversion = 30;
299 conn->bundle->multiuse = BUNDLE_MULTIPLEX;
300
301 qs->h3config = quiche_h3_config_new();
302 if(!qs->h3config)
303 return CURLE_OUT_OF_MEMORY;
304
305 /* Create a new HTTP/3 connection on the QUIC connection. */
306 qs->h3c = quiche_h3_conn_new_with_transport(qs->conn, qs->h3config);
307 if(!qs->h3c) {
308 result = CURLE_OUT_OF_MEMORY;
309 goto fail;
310 }
311 if(conn->hequic[1-tempindex].cfg) {
312 qs = &conn->hequic[1-tempindex];
313 quiche_config_free(qs->cfg);
314 quiche_conn_free(qs->conn);
315 qs->cfg = NULL;
316 qs->conn = NULL;
317 }
318 return CURLE_OK;
319 fail:
320 quiche_h3_config_free(qs->h3config);
321 quiche_h3_conn_free(qs->h3c);
322 return result;
323}
324
325/*
326 * This function gets polled to check if this QUIC connection has connected.
327 */
328CURLcode Curl_quic_is_connected(struct Curl_easy *data,
329 struct connectdata *conn,
330 int sockindex,
331 bool *done)
332{
333 CURLcode result;
334 struct quicsocket *qs = &conn->hequic[sockindex];
335 curl_socket_t sockfd = conn->tempsock[sockindex];
336
337 result = process_ingress(data, sockfd, qs);
338 if(result)
339 goto error;
340
341 result = flush_egress(data, sockfd, qs);
342 if(result)
343 goto error;
344
345 if(quiche_conn_is_established(qs->conn)) {
346 *done = TRUE;
347 result = quiche_has_connected(conn, 0, sockindex);
348 DEBUGF(infof(data, "quiche established connection!"));
349 }
350
351 return result;
352 error:
353 qs_disconnect(data, qs);
354 return result;
355}
356
357static CURLcode process_ingress(struct Curl_easy *data, int sockfd,
358 struct quicsocket *qs)
359{
360 ssize_t recvd;
361 uint8_t *buf = (uint8_t *)data->state.buffer;
362 size_t bufsize = data->set.buffer_size;
363 struct sockaddr_storage from;
364 socklen_t from_len;
365 quiche_recv_info recv_info;
366
367 DEBUGASSERT(qs->conn);
368
369 /* in case the timeout expired */
370 quiche_conn_on_timeout(qs->conn);
371
372 do {
373 from_len = sizeof(from);
374
375 recvd = recvfrom(sockfd, buf, bufsize, 0,
376 (struct sockaddr *)&from, &from_len);
377
378 if((recvd < 0) && ((SOCKERRNO == EAGAIN) || (SOCKERRNO == EWOULDBLOCK)))
379 break;
380
381 if(recvd < 0) {
382 failf(data, "quiche: recvfrom() unexpectedly returned %zd "
383 "(errno: %d, socket %d)", recvd, SOCKERRNO, sockfd);
384 return CURLE_RECV_ERROR;
385 }
386
387 recv_info.from = (struct sockaddr *) &from;
388 recv_info.from_len = from_len;
389
390 recvd = quiche_conn_recv(qs->conn, buf, recvd, &recv_info);
391 if(recvd == QUICHE_ERR_DONE)
392 break;
393
394 if(recvd < 0) {
395 failf(data, "quiche_conn_recv() == %zd", recvd);
396 return CURLE_RECV_ERROR;
397 }
398 } while(1);
399
400 return CURLE_OK;
401}
402
403/*
404 * flush_egress drains the buffers and sends off data.
405 * Calls failf() on errors.
406 */
407static CURLcode flush_egress(struct Curl_easy *data, int sockfd,
408 struct quicsocket *qs)
409{
410 ssize_t sent;
411 uint8_t out[1200];
412 int64_t timeout_ns;
413 quiche_send_info send_info;
414
415 do {
416 sent = quiche_conn_send(qs->conn, out, sizeof(out), &send_info);
417 if(sent == QUICHE_ERR_DONE)
418 break;
419
420 if(sent < 0) {
421 failf(data, "quiche_conn_send returned %zd", sent);
422 return CURLE_SEND_ERROR;
423 }
424
425 sent = send(sockfd, out, sent, 0);
426 if(sent < 0) {
427 failf(data, "send() returned %zd", sent);
428 return CURLE_SEND_ERROR;
429 }
430 } while(1);
431
432 /* time until the next timeout event, as nanoseconds. */
433 timeout_ns = quiche_conn_timeout_as_nanos(qs->conn);
434 if(timeout_ns)
435 /* expire uses milliseconds */
436 Curl_expire(data, (timeout_ns + 999999) / 1000000, EXPIRE_QUIC);
437
438 return CURLE_OK;
439}
440
441struct h3h1header {
442 char *dest;
443 size_t destlen; /* left to use */
444 size_t nlen; /* used */
445};
446
447static int cb_each_header(uint8_t *name, size_t name_len,
448 uint8_t *value, size_t value_len,
449 void *argp)
450{
451 struct h3h1header *headers = (struct h3h1header *)argp;
452 size_t olen = 0;
453
454 if((name_len == 7) && !strncmp(":status", (char *)name, 7)) {
455 msnprintf(headers->dest,
456 headers->destlen, "HTTP/3 %.*s\n",
457 (int) value_len, value);
458 }
459 else if(!headers->nlen) {
460 return CURLE_HTTP3;
461 }
462 else {
463 msnprintf(headers->dest,
464 headers->destlen, "%.*s: %.*s\n",
465 (int)name_len, name, (int) value_len, value);
466 }
467 olen = strlen(headers->dest);
468 headers->destlen -= olen;
469 headers->nlen += olen;
470 headers->dest += olen;
471 return 0;
472}
473
474static ssize_t h3_stream_recv(struct Curl_easy *data,
475 int sockindex,
476 char *buf,
477 size_t buffersize,
478 CURLcode *curlcode)
479{
480 ssize_t recvd = -1;
481 ssize_t rcode;
482 struct connectdata *conn = data->conn;
483 struct quicsocket *qs = conn->quic;
484 curl_socket_t sockfd = conn->sock[sockindex];
485 quiche_h3_event *ev;
486 int rc;
487 struct h3h1header headers;
488 struct HTTP *stream = data->req.p.http;
489 headers.dest = buf;
490 headers.destlen = buffersize;
491 headers.nlen = 0;
492
493 if(process_ingress(data, sockfd, qs)) {
494 infof(data, "h3_stream_recv returns on ingress");
495 *curlcode = CURLE_RECV_ERROR;
496 return -1;
497 }
498
499 while(recvd < 0) {
500 int64_t s = quiche_h3_conn_poll(qs->h3c, qs->conn, &ev);
501 if(s < 0)
502 /* nothing more to do */
503 break;
504
505 if(s != stream->stream3_id) {
506 /* another transfer, ignore for now */
507 infof(data, "Got h3 for stream %u, expects %u",
508 s, stream->stream3_id);
509 continue;
510 }
511
512 switch(quiche_h3_event_type(ev)) {
513 case QUICHE_H3_EVENT_HEADERS:
514 rc = quiche_h3_event_for_each_header(ev, cb_each_header, &headers);
515 if(rc) {
516 *curlcode = rc;
517 failf(data, "Error in HTTP/3 response header");
518 break;
519 }
520 recvd = headers.nlen;
521 break;
522 case QUICHE_H3_EVENT_DATA:
523 if(!stream->firstbody) {
524 /* add a header-body separator CRLF */
525 buf[0] = '\r';
526 buf[1] = '\n';
527 buf += 2;
528 buffersize -= 2;
529 stream->firstbody = TRUE;
530 recvd = 2; /* two bytes already */
531 }
532 else
533 recvd = 0;
534 rcode = quiche_h3_recv_body(qs->h3c, qs->conn, s, (unsigned char *)buf,
535 buffersize);
536 if(rcode <= 0) {
537 recvd = -1;
538 break;
539 }
540 recvd += rcode;
541 break;
542
543 case QUICHE_H3_EVENT_FINISHED:
544 streamclose(conn, "End of stream");
545 recvd = 0; /* end of stream */
546 break;
547 default:
548 break;
549 }
550
551 quiche_h3_event_free(ev);
552 }
553 if(flush_egress(data, sockfd, qs)) {
554 *curlcode = CURLE_SEND_ERROR;
555 return -1;
556 }
557
558 *curlcode = (-1 == recvd)? CURLE_AGAIN : CURLE_OK;
559 if(recvd >= 0)
560 /* Get this called again to drain the event queue */
561 Curl_expire(data, 0, EXPIRE_QUIC);
562
563 data->state.drain = (recvd >= 0) ? 1 : 0;
564 return recvd;
565}
566
567static ssize_t h3_stream_send(struct Curl_easy *data,
568 int sockindex,
569 const void *mem,
570 size_t len,
571 CURLcode *curlcode)
572{
573 ssize_t sent;
574 struct connectdata *conn = data->conn;
575 struct quicsocket *qs = conn->quic;
576 curl_socket_t sockfd = conn->sock[sockindex];
577 struct HTTP *stream = data->req.p.http;
578
579 if(!stream->h3req) {
580 CURLcode result = http_request(data, mem, len);
581 if(result) {
582 *curlcode = CURLE_SEND_ERROR;
583 return -1;
584 }
585 sent = len;
586 }
587 else {
588 H3BUGF(infof(data, "Pass on %zd body bytes to quiche", len));
589 sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id,
590 (uint8_t *)mem, len, FALSE);
591 if(sent < 0) {
592 *curlcode = CURLE_SEND_ERROR;
593 return -1;
594 }
595 }
596
597 if(flush_egress(data, sockfd, qs)) {
598 *curlcode = CURLE_SEND_ERROR;
599 return -1;
600 }
601
602 *curlcode = CURLE_OK;
603 return sent;
604}
605
606/*
607 * Store quiche version info in this buffer.
608 */
609void Curl_quic_ver(char *p, size_t len)
610{
611 (void)msnprintf(p, len, "quiche/%s", quiche_version());
612}
613
614/* Index where :authority header field will appear in request header
615 field list. */
616#define AUTHORITY_DST_IDX 3
617
618static CURLcode http_request(struct Curl_easy *data, const void *mem,
619 size_t len)
620{
621 /*
622 */
623 struct connectdata *conn = data->conn;
624 struct HTTP *stream = data->req.p.http;
625 size_t nheader;
626 size_t i;
627 size_t authority_idx;
628 char *hdbuf = (char *)mem;
629 char *end, *line_end;
630 int64_t stream3_id;
631 quiche_h3_header *nva = NULL;
632 struct quicsocket *qs = conn->quic;
633 CURLcode result = CURLE_OK;
634
635 stream->h3req = TRUE; /* senf off! */
636
637 /* Calculate number of headers contained in [mem, mem + len). Assumes a
638 correctly generated HTTP header field block. */
639 nheader = 0;
640 for(i = 1; i < len; ++i) {
641 if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') {
642 ++nheader;
643 ++i;
644 }
645 }
646 if(nheader < 2)
647 goto fail;
648
649 /* We counted additional 2 \r\n in the first and last line. We need 3
650 new headers: :method, :path and :scheme. Therefore we need one
651 more space. */
652 nheader += 1;
653 nva = malloc(sizeof(quiche_h3_header) * nheader);
654 if(!nva) {
655 result = CURLE_OUT_OF_MEMORY;
656 goto fail;
657 }
658
659 /* Extract :method, :path from request line
660 We do line endings with CRLF so checking for CR is enough */
661 line_end = memchr(hdbuf, '\r', len);
662 if(!line_end) {
663 result = CURLE_BAD_FUNCTION_ARGUMENT; /* internal error */
664 goto fail;
665 }
666
667 /* Method does not contain spaces */
668 end = memchr(hdbuf, ' ', line_end - hdbuf);
669 if(!end || end == hdbuf)
670 goto fail;
671 nva[0].name = (unsigned char *)":method";
672 nva[0].name_len = strlen((char *)nva[0].name);
673 nva[0].value = (unsigned char *)hdbuf;
674 nva[0].value_len = (size_t)(end - hdbuf);
675
676 hdbuf = end + 1;
677
678 /* Path may contain spaces so scan backwards */
679 end = NULL;
680 for(i = (size_t)(line_end - hdbuf); i; --i) {
681 if(hdbuf[i - 1] == ' ') {
682 end = &hdbuf[i - 1];
683 break;
684 }
685 }
686 if(!end || end == hdbuf)
687 goto fail;
688 nva[1].name = (unsigned char *)":path";
689 nva[1].name_len = strlen((char *)nva[1].name);
690 nva[1].value = (unsigned char *)hdbuf;
691 nva[1].value_len = (size_t)(end - hdbuf);
692
693 nva[2].name = (unsigned char *)":scheme";
694 nva[2].name_len = strlen((char *)nva[2].name);
695 if(conn->handler->flags & PROTOPT_SSL)
696 nva[2].value = (unsigned char *)"https";
697 else
698 nva[2].value = (unsigned char *)"http";
699 nva[2].value_len = strlen((char *)nva[2].value);
700
701
702 authority_idx = 0;
703 i = 3;
704 while(i < nheader) {
705 size_t hlen;
706
707 hdbuf = line_end + 2;
708
709 /* check for next CR, but only within the piece of data left in the given
710 buffer */
711 line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem));
712 if(!line_end || (line_end == hdbuf))
713 goto fail;
714
715 /* header continuation lines are not supported */
716 if(*hdbuf == ' ' || *hdbuf == '\t')
717 goto fail;
718
719 for(end = hdbuf; end < line_end && *end != ':'; ++end)
720 ;
721 if(end == hdbuf || end == line_end)
722 goto fail;
723 hlen = end - hdbuf;
724
725 if(hlen == 4 && strncasecompare("host", hdbuf, 4)) {
726 authority_idx = i;
727 nva[i].name = (unsigned char *)":authority";
728 nva[i].name_len = strlen((char *)nva[i].name);
729 }
730 else {
731 nva[i].name_len = (size_t)(end - hdbuf);
732 /* Lower case the header name for HTTP/3 */
733 Curl_strntolower((char *)hdbuf, hdbuf, nva[i].name_len);
734 nva[i].name = (unsigned char *)hdbuf;
735 }
736 hdbuf = end + 1;
737 while(*hdbuf == ' ' || *hdbuf == '\t')
738 ++hdbuf;
739 end = line_end;
740
741#if 0 /* This should probably go in more or less like this */
742 switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf,
743 end - hdbuf)) {
744 case HEADERINST_IGNORE:
745 /* skip header fields prohibited by HTTP/2 specification. */
746 --nheader;
747 continue;
748 case HEADERINST_TE_TRAILERS:
749 nva[i].value = (uint8_t*)"trailers";
750 nva[i].value_len = sizeof("trailers") - 1;
751 break;
752 default:
753 nva[i].value = (unsigned char *)hdbuf;
754 nva[i].value_len = (size_t)(end - hdbuf);
755 }
756#endif
757 nva[i].value = (unsigned char *)hdbuf;
758 nva[i].value_len = (size_t)(end - hdbuf);
759
760 ++i;
761 }
762
763 /* :authority must come before non-pseudo header fields */
764 if(authority_idx && authority_idx != AUTHORITY_DST_IDX) {
765 quiche_h3_header authority = nva[authority_idx];
766 for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) {
767 nva[i] = nva[i - 1];
768 }
769 nva[i] = authority;
770 }
771
772 /* Warn stream may be rejected if cumulative length of headers is too
773 large. */
774#define MAX_ACC 60000 /* <64KB to account for some overhead */
775 {
776 size_t acc = 0;
777
778 for(i = 0; i < nheader; ++i) {
779 acc += nva[i].name_len + nva[i].value_len;
780
781 H3BUGF(infof(data, "h3 [%.*s: %.*s]",
782 nva[i].name_len, nva[i].name,
783 nva[i].value_len, nva[i].value));
784 }
785
786 if(acc > MAX_ACC) {
787 infof(data, "http_request: Warning: The cumulative length of all "
788 "headers exceeds %d bytes and that could cause the "
789 "stream to be rejected.", MAX_ACC);
790 }
791 }
792
793 switch(data->state.httpreq) {
794 case HTTPREQ_POST:
795 case HTTPREQ_POST_FORM:
796 case HTTPREQ_POST_MIME:
797 case HTTPREQ_PUT:
798 if(data->state.infilesize != -1)
799 stream->upload_left = data->state.infilesize;
800 else
801 /* data sending without specifying the data amount up front */
802 stream->upload_left = -1; /* unknown, but not zero */
803
804 stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader,
805 stream->upload_left ? FALSE: TRUE);
806 if((stream3_id >= 0) && data->set.postfields) {
807 ssize_t sent = quiche_h3_send_body(qs->h3c, qs->conn, stream3_id,
808 (uint8_t *)data->set.postfields,
809 stream->upload_left, TRUE);
810 if(sent <= 0) {
811 failf(data, "quiche_h3_send_body failed!");
812 result = CURLE_SEND_ERROR;
813 }
814 stream->upload_left = 0; /* nothing left to send */
815 }
816 break;
817 default:
818 stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader,
819 TRUE);
820 break;
821 }
822
823 Curl_safefree(nva);
824
825 if(stream3_id < 0) {
826 H3BUGF(infof(data, "quiche_h3_send_request returned %d",
827 stream3_id));
828 result = CURLE_SEND_ERROR;
829 goto fail;
830 }
831
832 infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)",
833 stream3_id, (void *)data);
834 stream->stream3_id = stream3_id;
835
836 return CURLE_OK;
837
838fail:
839 free(nva);
840 return result;
841}
842
843/*
844 * Called from transfer.c:done_sending when we stop HTTP/3 uploading.
845 */
846CURLcode Curl_quic_done_sending(struct Curl_easy *data)
847{
848 struct connectdata *conn = data->conn;
849 DEBUGASSERT(conn);
850 if(conn->handler == &Curl_handler_http3) {
851 /* only for HTTP/3 transfers */
852 ssize_t sent;
853 struct HTTP *stream = data->req.p.http;
854 struct quicsocket *qs = conn->quic;
855 stream->upload_done = TRUE;
856 sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id,
857 NULL, 0, TRUE);
858 if(sent < 0)
859 return CURLE_SEND_ERROR;
860 }
861
862 return CURLE_OK;
863}
864
865/*
866 * Called from http.c:Curl_http_done when a request completes.
867 */
868void Curl_quic_done(struct Curl_easy *data, bool premature)
869{
870 (void)data;
871 (void)premature;
872}
873
874/*
875 * Called from transfer.c:data_pending to know if we should keep looping
876 * to receive more data from the connection.
877 */
878bool Curl_quic_data_pending(const struct Curl_easy *data)
879{
880 (void)data;
881 return FALSE;
882}
883
884#endif
885