Return to quiche.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / lib / vquic |
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