Annotation of embedaddon/curl/lib/vquic/quiche.c, revision 1.1.1.1

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>