Annotation of embedaddon/curl/lib/vquic/quiche.c, revision 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>