• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
56 static CURLcode process_ingress(struct Curl_easy *data,
57                                 curl_socket_t sockfd,
58                                 struct quicsocket *qs);
59 
60 static CURLcode flush_egress(struct Curl_easy *data, curl_socket_t sockfd,
61                              struct quicsocket *qs);
62 
63 static CURLcode http_request(struct Curl_easy *data, const void *mem,
64                              size_t len);
65 static Curl_recv h3_stream_recv;
66 static Curl_send h3_stream_send;
67 
quiche_getsock(struct Curl_easy * data,struct connectdata * conn,curl_socket_t * socks)68 static 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 
qs_disconnect(struct Curl_easy * data,struct quicsocket * qs)87 static 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 
quiche_disconnect(struct Curl_easy * data,struct connectdata * conn,bool dead_connection)110 static 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 
Curl_quic_disconnect(struct Curl_easy * data,struct connectdata * conn,int tempindex)119 void 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 
quiche_conncheck(struct Curl_easy * data,struct connectdata * conn,unsigned int checks_to_perform)127 static 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 
quiche_do(struct Curl_easy * data,bool * done)137 static 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 
144 static 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
quiche_debug_log(const char * line,void * argp)168 static void quiche_debug_log(const char *line, void *argp)
169 {
170   (void)argp;
171   fprintf(stderr, "%s\n", line);
172 }
173 #endif
174 
Curl_quic_connect(struct Curl_easy * data,struct connectdata * conn,curl_socket_t sockfd,int sockindex,const struct sockaddr * addr,socklen_t addrlen)175 CURLcode 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 
quiche_has_connected(struct connectdata * conn,int sockindex,int tempindex)287 static 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  */
Curl_quic_is_connected(struct Curl_easy * data,struct connectdata * conn,int sockindex,bool * done)328 CURLcode 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 
process_ingress(struct Curl_easy * data,int sockfd,struct quicsocket * qs)357 static 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  */
flush_egress(struct Curl_easy * data,int sockfd,struct quicsocket * qs)407 static 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 
441 struct h3h1header {
442   char *dest;
443   size_t destlen; /* left to use */
444   size_t nlen; /* used */
445 };
446 
cb_each_header(uint8_t * name,size_t name_len,uint8_t * value,size_t value_len,void * argp)447 static 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 
h3_stream_recv(struct Curl_easy * data,int sockindex,char * buf,size_t buffersize,CURLcode * curlcode)474 static 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 
h3_stream_send(struct Curl_easy * data,int sockindex,const void * mem,size_t len,CURLcode * curlcode)567 static 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  */
Curl_quic_ver(char * p,size_t len)609 void 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 
http_request(struct Curl_easy * data,const void * mem,size_t len)618 static 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 
838 fail:
839   free(nva);
840   return result;
841 }
842 
843 /*
844  * Called from transfer.c:done_sending when we stop HTTP/3 uploading.
845  */
Curl_quic_done_sending(struct Curl_easy * data)846 CURLcode 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  */
Curl_quic_done(struct Curl_easy * data,bool premature)868 void 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  */
Curl_quic_data_pending(const struct Curl_easy * data)878 bool Curl_quic_data_pending(const struct Curl_easy *data)
879 {
880   (void)data;
881   return FALSE;
882 }
883 
884 #endif
885