Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/curl/lib/vquic/quiche.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /*************************************************************************** | |
| 2 * _ _ ____ _ | |
| 3 * Project ___| | | | _ \| | | |
| 4 * / __| | | | |_) | | | |
| 5 * | (__| |_| | _ <| |___ | |
| 6 * \___|\___/|_| \_\_____| | |
| 7 * | |
| 8 * Copyright (C) 1998 - 2019, 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_FALSE | |
| 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_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 #if 0 | |
| 207 /* store the used address as a string */ | |
| 208 if(!Curl_addr2string((struct sockaddr*)addr, | |
| 209 conn->primary_ip, &conn->primary_port)) { | |
| 210 char buffer[STRERROR_LEN]; | |
| 211 failf(data, "ssrem inet_ntop() failed with errno %d: %s", | |
| 212 errno, Curl_strerror(errno, buffer, sizeof(buffer))); | |
| 213 return CURLE_BAD_FUNCTION_ARGUMENT; | |
| 214 } | |
| 215 memcpy(conn->ip_addr_str, conn->primary_ip, MAX_IPADR_LEN); | |
| 216 #endif | |
| 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(0, 1024, 0, 0); | |
| 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) && ((errno == EAGAIN) || (errno == 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 { | |
| 383 msnprintf(headers->dest, | |
| 384 headers->destlen, "%.*s: %.*s\n", | |
| 385 (int)name_len, name, (int) value_len, value); | |
| 386 } | |
| 387 olen = strlen(headers->dest); | |
| 388 headers->destlen -= olen; | |
| 389 headers->nlen += olen; | |
| 390 headers->dest += olen; | |
| 391 return 0; | |
| 392 } | |
| 393 | |
| 394 static ssize_t h3_stream_recv(struct connectdata *conn, | |
| 395 int sockindex, | |
| 396 char *buf, | |
| 397 size_t buffersize, | |
| 398 CURLcode *curlcode) | |
| 399 { | |
| 400 ssize_t recvd = -1; | |
| 401 ssize_t rcode; | |
| 402 struct quicsocket *qs = conn->quic; | |
| 403 curl_socket_t sockfd = conn->sock[sockindex]; | |
| 404 quiche_h3_event *ev; | |
| 405 int rc; | |
| 406 struct h3h1header headers; | |
| 407 struct HTTP *stream = conn->data->req.protop; | |
| 408 headers.dest = buf; | |
| 409 headers.destlen = buffersize; | |
| 410 headers.nlen = 0; | |
| 411 | |
| 412 if(process_ingress(conn, sockfd, qs)) { | |
| 413 infof(conn->data, "h3_stream_recv returns on ingress\n"); | |
| 414 *curlcode = CURLE_RECV_ERROR; | |
| 415 return -1; | |
| 416 } | |
| 417 | |
| 418 while(recvd < 0) { | |
| 419 int64_t s = quiche_h3_conn_poll(qs->h3c, qs->conn, &ev); | |
| 420 if(s < 0) | |
| 421 /* nothing more to do */ | |
| 422 break; | |
| 423 | |
| 424 if(s != stream->stream3_id) { | |
| 425 /* another transfer, ignore for now */ | |
| 426 infof(conn->data, "Got h3 for stream %u, expects %u\n", | |
| 427 s, stream->stream3_id); | |
| 428 continue; | |
| 429 } | |
| 430 | |
| 431 switch(quiche_h3_event_type(ev)) { | |
| 432 case QUICHE_H3_EVENT_HEADERS: | |
| 433 rc = quiche_h3_event_for_each_header(ev, cb_each_header, &headers); | |
| 434 if(rc) { | |
| 435 /* what do we do about this? */ | |
| 436 } | |
| 437 recvd = headers.nlen; | |
| 438 break; | |
| 439 case QUICHE_H3_EVENT_DATA: | |
| 440 if(!stream->firstbody) { | |
| 441 /* add a header-body separator CRLF */ | |
| 442 buf[0] = '\r'; | |
| 443 buf[1] = '\n'; | |
| 444 buf += 2; | |
| 445 buffersize -= 2; | |
| 446 stream->firstbody = TRUE; | |
| 447 recvd = 2; /* two bytes already */ | |
| 448 } | |
| 449 else | |
| 450 recvd = 0; | |
| 451 rcode = quiche_h3_recv_body(qs->h3c, qs->conn, s, (unsigned char *)buf, | |
| 452 buffersize); | |
| 453 if(rcode <= 0) { | |
| 454 recvd = -1; | |
| 455 break; | |
| 456 } | |
| 457 recvd += rcode; | |
| 458 break; | |
| 459 | |
| 460 case QUICHE_H3_EVENT_FINISHED: | |
| 461 if(quiche_conn_close(qs->conn, true, 0, NULL, 0) < 0) { | |
| 462 ; | |
| 463 } | |
| 464 recvd = 0; /* end of stream */ | |
| 465 break; | |
| 466 default: | |
| 467 break; | |
| 468 } | |
| 469 | |
| 470 quiche_h3_event_free(ev); | |
| 471 } | |
| 472 if(flush_egress(conn, sockfd, qs)) { | |
| 473 *curlcode = CURLE_SEND_ERROR; | |
| 474 return -1; | |
| 475 } | |
| 476 | |
| 477 *curlcode = (-1 == recvd)? CURLE_AGAIN : CURLE_OK; | |
| 478 if(recvd >= 0) | |
| 479 /* Get this called again to drain the event queue */ | |
| 480 Curl_expire(conn->data, 0, EXPIRE_QUIC); | |
| 481 return recvd; | |
| 482 } | |
| 483 | |
| 484 static ssize_t h3_stream_send(struct connectdata *conn, | |
| 485 int sockindex, | |
| 486 const void *mem, | |
| 487 size_t len, | |
| 488 CURLcode *curlcode) | |
| 489 { | |
| 490 ssize_t sent; | |
| 491 struct quicsocket *qs = conn->quic; | |
| 492 curl_socket_t sockfd = conn->sock[sockindex]; | |
| 493 struct HTTP *stream = conn->data->req.protop; | |
| 494 | |
| 495 if(!stream->h3req) { | |
| 496 CURLcode result = http_request(conn, mem, len); | |
| 497 if(result) { | |
| 498 *curlcode = CURLE_SEND_ERROR; | |
| 499 return -1; | |
| 500 } | |
| 501 sent = len; | |
| 502 } | |
| 503 else { | |
| 504 H3BUGF(infof(conn->data, "Pass on %zd body bytes to quiche\n", | |
| 505 len)); | |
| 506 sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id, | |
| 507 (uint8_t *)mem, len, FALSE); | |
| 508 if(sent < 0) { | |
| 509 *curlcode = CURLE_SEND_ERROR; | |
| 510 return -1; | |
| 511 } | |
| 512 } | |
| 513 | |
| 514 if(flush_egress(conn, sockfd, qs)) { | |
| 515 *curlcode = CURLE_SEND_ERROR; | |
| 516 return -1; | |
| 517 } | |
| 518 | |
| 519 *curlcode = CURLE_OK; | |
| 520 return sent; | |
| 521 } | |
| 522 | |
| 523 /* | |
| 524 * Store quiche version info in this buffer, Prefix with a space. Return total | |
| 525 * length written. | |
| 526 */ | |
| 527 int Curl_quic_ver(char *p, size_t len) | |
| 528 { | |
| 529 return msnprintf(p, len, " quiche/%s", quiche_version()); | |
| 530 } | |
| 531 | |
| 532 /* Index where :authority header field will appear in request header | |
| 533 field list. */ | |
| 534 #define AUTHORITY_DST_IDX 3 | |
| 535 | |
| 536 static CURLcode http_request(struct connectdata *conn, const void *mem, | |
| 537 size_t len) | |
| 538 { | |
| 539 /* | |
| 540 */ | |
| 541 struct HTTP *stream = conn->data->req.protop; | |
| 542 size_t nheader; | |
| 543 size_t i; | |
| 544 size_t authority_idx; | |
| 545 char *hdbuf = (char *)mem; | |
| 546 char *end, *line_end; | |
| 547 int64_t stream3_id; | |
| 548 quiche_h3_header *nva = NULL; | |
| 549 struct quicsocket *qs = conn->quic; | |
| 550 CURLcode result = CURLE_OK; | |
| 551 struct Curl_easy *data = conn->data; | |
| 552 | |
| 553 stream->h3req = TRUE; /* senf off! */ | |
| 554 | |
| 555 /* Calculate number of headers contained in [mem, mem + len). Assumes a | |
| 556 correctly generated HTTP header field block. */ | |
| 557 nheader = 0; | |
| 558 for(i = 1; i < len; ++i) { | |
| 559 if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') { | |
| 560 ++nheader; | |
| 561 ++i; | |
| 562 } | |
| 563 } | |
| 564 if(nheader < 2) | |
| 565 goto fail; | |
| 566 | |
| 567 /* We counted additional 2 \r\n in the first and last line. We need 3 | |
| 568 new headers: :method, :path and :scheme. Therefore we need one | |
| 569 more space. */ | |
| 570 nheader += 1; | |
| 571 nva = malloc(sizeof(quiche_h3_header) * nheader); | |
| 572 if(!nva) { | |
| 573 result = CURLE_OUT_OF_MEMORY; | |
| 574 goto fail; | |
| 575 } | |
| 576 | |
| 577 /* Extract :method, :path from request line | |
| 578 We do line endings with CRLF so checking for CR is enough */ | |
| 579 line_end = memchr(hdbuf, '\r', len); | |
| 580 if(!line_end) { | |
| 581 result = CURLE_BAD_FUNCTION_ARGUMENT; /* internal error */ | |
| 582 goto fail; | |
| 583 } | |
| 584 | |
| 585 /* Method does not contain spaces */ | |
| 586 end = memchr(hdbuf, ' ', line_end - hdbuf); | |
| 587 if(!end || end == hdbuf) | |
| 588 goto fail; | |
| 589 nva[0].name = (unsigned char *)":method"; | |
| 590 nva[0].name_len = strlen((char *)nva[0].name); | |
| 591 nva[0].value = (unsigned char *)hdbuf; | |
| 592 nva[0].value_len = (size_t)(end - hdbuf); | |
| 593 | |
| 594 hdbuf = end + 1; | |
| 595 | |
| 596 /* Path may contain spaces so scan backwards */ | |
| 597 end = NULL; | |
| 598 for(i = (size_t)(line_end - hdbuf); i; --i) { | |
| 599 if(hdbuf[i - 1] == ' ') { | |
| 600 end = &hdbuf[i - 1]; | |
| 601 break; | |
| 602 } | |
| 603 } | |
| 604 if(!end || end == hdbuf) | |
| 605 goto fail; | |
| 606 nva[1].name = (unsigned char *)":path"; | |
| 607 nva[1].name_len = strlen((char *)nva[1].name); | |
| 608 nva[1].value = (unsigned char *)hdbuf; | |
| 609 nva[1].value_len = (size_t)(end - hdbuf); | |
| 610 | |
| 611 nva[2].name = (unsigned char *)":scheme"; | |
| 612 nva[2].name_len = strlen((char *)nva[2].name); | |
| 613 if(conn->handler->flags & PROTOPT_SSL) | |
| 614 nva[2].value = (unsigned char *)"https"; | |
| 615 else | |
| 616 nva[2].value = (unsigned char *)"http"; | |
| 617 nva[2].value_len = strlen((char *)nva[2].value); | |
| 618 | |
| 619 | |
| 620 authority_idx = 0; | |
| 621 i = 3; | |
| 622 while(i < nheader) { | |
| 623 size_t hlen; | |
| 624 | |
| 625 hdbuf = line_end + 2; | |
| 626 | |
| 627 /* check for next CR, but only within the piece of data left in the given | |
| 628 buffer */ | |
| 629 line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem)); | |
| 630 if(!line_end || (line_end == hdbuf)) | |
| 631 goto fail; | |
| 632 | |
| 633 /* header continuation lines are not supported */ | |
| 634 if(*hdbuf == ' ' || *hdbuf == '\t') | |
| 635 goto fail; | |
| 636 | |
| 637 for(end = hdbuf; end < line_end && *end != ':'; ++end) | |
| 638 ; | |
| 639 if(end == hdbuf || end == line_end) | |
| 640 goto fail; | |
| 641 hlen = end - hdbuf; | |
| 642 | |
| 643 if(hlen == 4 && strncasecompare("host", hdbuf, 4)) { | |
| 644 authority_idx = i; | |
| 645 nva[i].name = (unsigned char *)":authority"; | |
| 646 nva[i].name_len = strlen((char *)nva[i].name); | |
| 647 } | |
| 648 else { | |
| 649 nva[i].name = (unsigned char *)hdbuf; | |
| 650 nva[i].name_len = (size_t)(end - hdbuf); | |
| 651 } | |
| 652 hdbuf = end + 1; | |
| 653 while(*hdbuf == ' ' || *hdbuf == '\t') | |
| 654 ++hdbuf; | |
| 655 end = line_end; | |
| 656 | |
| 657 #if 0 /* This should probably go in more or less like this */ | |
| 658 switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf, | |
| 659 end - hdbuf)) { | |
| 660 case HEADERINST_IGNORE: | |
| 661 /* skip header fields prohibited by HTTP/2 specification. */ | |
| 662 --nheader; | |
| 663 continue; | |
| 664 case HEADERINST_TE_TRAILERS: | |
| 665 nva[i].value = (uint8_t*)"trailers"; | |
| 666 nva[i].value_len = sizeof("trailers") - 1; | |
| 667 break; | |
| 668 default: | |
| 669 nva[i].value = (unsigned char *)hdbuf; | |
| 670 nva[i].value_len = (size_t)(end - hdbuf); | |
| 671 } | |
| 672 #endif | |
| 673 nva[i].value = (unsigned char *)hdbuf; | |
| 674 nva[i].value_len = (size_t)(end - hdbuf); | |
| 675 | |
| 676 ++i; | |
| 677 } | |
| 678 | |
| 679 /* :authority must come before non-pseudo header fields */ | |
| 680 if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) { | |
| 681 quiche_h3_header authority = nva[authority_idx]; | |
| 682 for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) { | |
| 683 nva[i] = nva[i - 1]; | |
| 684 } | |
| 685 nva[i] = authority; | |
| 686 } | |
| 687 | |
| 688 /* Warn stream may be rejected if cumulative length of headers is too | |
| 689 large. */ | |
| 690 #define MAX_ACC 60000 /* <64KB to account for some overhead */ | |
| 691 { | |
| 692 size_t acc = 0; | |
| 693 | |
| 694 for(i = 0; i < nheader; ++i) { | |
| 695 acc += nva[i].name_len + nva[i].value_len; | |
| 696 | |
| 697 H3BUGF(infof(data, "h3 [%.*s: %.*s]\n", | |
| 698 nva[i].name_len, nva[i].name, | |
| 699 nva[i].value_len, nva[i].value)); | |
| 700 } | |
| 701 | |
| 702 if(acc > MAX_ACC) { | |
| 703 infof(data, "http_request: Warning: The cumulative length of all " | |
| 704 "headers exceeds %zu bytes and that could cause the " | |
| 705 "stream to be rejected.\n", MAX_ACC); | |
| 706 } | |
| 707 } | |
| 708 | |
| 709 switch(data->set.httpreq) { | |
| 710 case HTTPREQ_POST: | |
| 711 case HTTPREQ_POST_FORM: | |
| 712 case HTTPREQ_POST_MIME: | |
| 713 case HTTPREQ_PUT: | |
| 714 if(data->state.infilesize != -1) | |
| 715 stream->upload_left = data->state.infilesize; | |
| 716 else | |
| 717 /* data sending without specifying the data amount up front */ | |
| 718 stream->upload_left = -1; /* unknown, but not zero */ | |
| 719 | |
| 720 stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader, | |
| 721 stream->upload_left ? FALSE: TRUE); | |
| 722 if((stream3_id >= 0) && data->set.postfields) { | |
| 723 ssize_t sent = quiche_h3_send_body(qs->h3c, qs->conn, stream3_id, | |
| 724 (uint8_t *)data->set.postfields, | |
| 725 stream->upload_left, TRUE); | |
| 726 if(sent <= 0) { | |
| 727 failf(data, "quiche_h3_send_body failed!"); | |
| 728 result = CURLE_SEND_ERROR; | |
| 729 } | |
| 730 stream->upload_left = 0; /* nothing left to send */ | |
| 731 } | |
| 732 break; | |
| 733 default: | |
| 734 stream3_id = quiche_h3_send_request(qs->h3c, qs->conn, nva, nheader, | |
| 735 TRUE); | |
| 736 break; | |
| 737 } | |
| 738 | |
| 739 Curl_safefree(nva); | |
| 740 | |
| 741 if(stream3_id < 0) { | |
| 742 H3BUGF(infof(data, "quiche_h3_send_request returned %d\n", | |
| 743 stream3_id)); | |
| 744 result = CURLE_SEND_ERROR; | |
| 745 goto fail; | |
| 746 } | |
| 747 | |
| 748 infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)\n", | |
| 749 stream3_id, (void *)data); | |
| 750 stream->stream3_id = stream3_id; | |
| 751 | |
| 752 return CURLE_OK; | |
| 753 | |
| 754 fail: | |
| 755 free(nva); | |
| 756 return result; | |
| 757 } | |
| 758 | |
| 759 /* | |
| 760 * Called from transfer.c:done_sending when we stop HTTP/3 uploading. | |
| 761 */ | |
| 762 CURLcode Curl_quic_done_sending(struct connectdata *conn) | |
| 763 { | |
| 764 if(conn->handler == &Curl_handler_http3) { | |
| 765 /* only for HTTP/3 transfers */ | |
| 766 ssize_t sent; | |
| 767 struct HTTP *stream = conn->data->req.protop; | |
| 768 struct quicsocket *qs = conn->quic; | |
| 769 fprintf(stderr, "!!! Curl_quic_done_sending\n"); | |
| 770 stream->upload_done = TRUE; | |
| 771 sent = quiche_h3_send_body(qs->h3c, qs->conn, stream->stream3_id, | |
| 772 NULL, 0, TRUE); | |
| 773 if(sent < 0) | |
| 774 return CURLE_SEND_ERROR; | |
| 775 } | |
| 776 | |
| 777 return CURLE_OK; | |
| 778 } | |
| 779 | |
| 780 #endif |
