Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/curl/lib/vquic/ngtcp2.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_NGTCP2 | |
| 26 #include <ngtcp2/ngtcp2.h> | |
| 27 #include <ngtcp2/ngtcp2_crypto.h> | |
| 28 #include <nghttp3/nghttp3.h> | |
| 29 #include <openssl/err.h> | |
| 30 #include "urldata.h" | |
| 31 #include "sendf.h" | |
| 32 #include "strdup.h" | |
| 33 #include "rand.h" | |
| 34 #include "ngtcp2.h" | |
| 35 #include "multiif.h" | |
| 36 #include "strcase.h" | |
| 37 #include "connect.h" | |
| 38 #include "strerror.h" | |
| 39 | |
| 40 /* The last 3 #include files should be in this order */ | |
| 41 #include "curl_printf.h" | |
| 42 #include "curl_memory.h" | |
| 43 #include "memdebug.h" | |
| 44 | |
| 45 /* #define DEBUG_NGTCP2 */ | |
| 46 #define DEBUG_HTTP3 | |
| 47 #ifdef DEBUG_HTTP3 | |
| 48 #define H3BUGF(x) x | |
| 49 #else | |
| 50 #define H3BUGF(x) do { } WHILE_FALSE | |
| 51 #endif | |
| 52 | |
| 53 /* | |
| 54 * This holds outgoing HTTP/3 stream data that is used by nghttp3 until acked. | |
| 55 * It is used as a circular buffer. Add new bytes at the end until it reaches | |
| 56 * the far end, then start over at index 0 again. | |
| 57 */ | |
| 58 | |
| 59 #define H3_SEND_SIZE (20*1024) | |
| 60 struct h3out { | |
| 61 uint8_t buf[H3_SEND_SIZE]; | |
| 62 size_t used; /* number of bytes used in the buffer */ | |
| 63 size_t windex; /* index in the buffer where to start writing the next | |
| 64 data block */ | |
| 65 }; | |
| 66 | |
| 67 #define QUIC_MAX_STREAMS (256*1024) | |
| 68 #define QUIC_MAX_DATA (1*1024*1024) | |
| 69 #define QUIC_IDLE_TIMEOUT 60000 /* milliseconds */ | |
| 70 #define QUIC_CIPHERS \ | |
| 71 "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_" \ | |
| 72 "POLY1305_SHA256:TLS_AES_128_CCM_SHA256" | |
| 73 #define QUIC_GROUPS "P-256:X25519:P-384:P-521" | |
| 74 | |
| 75 static CURLcode ng_process_ingress(struct connectdata *conn, | |
| 76 curl_socket_t sockfd, | |
| 77 struct quicsocket *qs); | |
| 78 static CURLcode ng_flush_egress(struct connectdata *conn, int sockfd, | |
| 79 struct quicsocket *qs); | |
| 80 static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id, | |
| 81 size_t datalen, void *user_data, | |
| 82 void *stream_user_data); | |
| 83 | |
| 84 static ngtcp2_tstamp timestamp(void) | |
| 85 { | |
| 86 struct curltime ct = Curl_now(); | |
| 87 return ct.tv_sec * NGTCP2_SECONDS + ct.tv_usec * NGTCP2_MICROSECONDS; | |
| 88 } | |
| 89 | |
| 90 #ifdef DEBUG_NGTCP2 | |
| 91 static void quic_printf(void *user_data, const char *fmt, ...) | |
| 92 { | |
| 93 va_list ap; | |
| 94 (void)user_data; /* TODO, use this to do infof() instead long-term */ | |
| 95 va_start(ap, fmt); | |
| 96 vfprintf(stderr, fmt, ap); | |
| 97 va_end(ap); | |
| 98 fprintf(stderr, "\n"); | |
| 99 } | |
| 100 #endif | |
| 101 | |
| 102 static ngtcp2_crypto_level | |
| 103 quic_from_ossl_level(OSSL_ENCRYPTION_LEVEL ossl_level) | |
| 104 { | |
| 105 switch(ossl_level) { | |
| 106 case ssl_encryption_initial: | |
| 107 return NGTCP2_CRYPTO_LEVEL_INITIAL; | |
| 108 case ssl_encryption_early_data: | |
| 109 return NGTCP2_CRYPTO_LEVEL_EARLY; | |
| 110 case ssl_encryption_handshake: | |
| 111 return NGTCP2_CRYPTO_LEVEL_HANDSHAKE; | |
| 112 case ssl_encryption_application: | |
| 113 return NGTCP2_CRYPTO_LEVEL_APP; | |
| 114 default: | |
| 115 assert(0); | |
| 116 } | |
| 117 } | |
| 118 | |
| 119 static int setup_initial_crypto_context(struct quicsocket *qs) | |
| 120 { | |
| 121 const ngtcp2_cid *dcid = ngtcp2_conn_get_dcid(qs->qconn); | |
| 122 | |
| 123 if(ngtcp2_crypto_derive_and_install_initial_key( | |
| 124 qs->qconn, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, dcid, | |
| 125 NGTCP2_CRYPTO_SIDE_CLIENT) != 0) | |
| 126 return -1; | |
| 127 | |
| 128 return 0; | |
| 129 } | |
| 130 | |
| 131 static void quic_settings(ngtcp2_settings *s, | |
| 132 uint64_t stream_buffer_size) | |
| 133 { | |
| 134 ngtcp2_settings_default(s); | |
| 135 #ifdef DEBUG_NGTCP2 | |
| 136 s->log_printf = quic_printf; | |
| 137 #else | |
| 138 s->log_printf = NULL; | |
| 139 #endif | |
| 140 s->initial_ts = timestamp(); | |
| 141 s->max_stream_data_bidi_local = stream_buffer_size; | |
| 142 s->max_stream_data_bidi_remote = QUIC_MAX_STREAMS; | |
| 143 s->max_stream_data_uni = QUIC_MAX_STREAMS; | |
| 144 s->max_data = QUIC_MAX_DATA; | |
| 145 s->max_streams_bidi = 1; | |
| 146 s->max_streams_uni = 3; | |
| 147 s->idle_timeout = QUIC_IDLE_TIMEOUT; | |
| 148 } | |
| 149 | |
| 150 static FILE *keylog_file; /* not thread-safe */ | |
| 151 static void keylog_callback(const SSL *ssl, const char *line) | |
| 152 { | |
| 153 (void)ssl; | |
| 154 fputs(line, keylog_file); | |
| 155 fputc('\n', keylog_file); | |
| 156 fflush(keylog_file); | |
| 157 } | |
| 158 | |
| 159 static int init_ngh3_conn(struct quicsocket *qs); | |
| 160 | |
| 161 static int quic_set_encryption_secrets(SSL *ssl, | |
| 162 OSSL_ENCRYPTION_LEVEL ossl_level, | |
| 163 const uint8_t *rx_secret, | |
| 164 const uint8_t *tx_secret, | |
| 165 size_t secretlen) | |
| 166 { | |
| 167 struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl); | |
| 168 int level = quic_from_ossl_level(ossl_level); | |
| 169 | |
| 170 if(ngtcp2_crypto_derive_and_install_key( | |
| 171 qs->qconn, ssl, NULL, NULL, NULL, NULL, NULL, NULL, level, rx_secret, | |
| 172 tx_secret, secretlen, NGTCP2_CRYPTO_SIDE_CLIENT) != 0) | |
| 173 return 0; | |
| 174 | |
| 175 if(level == NGTCP2_CRYPTO_LEVEL_APP && init_ngh3_conn(qs) != CURLE_OK) | |
| 176 return 0; | |
| 177 | |
| 178 return 1; | |
| 179 } | |
| 180 | |
| 181 static int quic_add_handshake_data(SSL *ssl, OSSL_ENCRYPTION_LEVEL ossl_level, | |
| 182 const uint8_t *data, size_t len) | |
| 183 { | |
| 184 struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl); | |
| 185 struct quic_handshake *crypto_data; | |
| 186 ngtcp2_crypto_level level = quic_from_ossl_level(ossl_level); | |
| 187 int rv; | |
| 188 | |
| 189 crypto_data = &qs->client_crypto_data[level]; | |
| 190 if(crypto_data->buf == NULL) { | |
| 191 crypto_data->buf = malloc(4096); | |
| 192 crypto_data->alloclen = 4096; | |
| 193 /* TODO Explode if malloc failed */ | |
| 194 } | |
| 195 | |
| 196 /* TODO Just pretend that handshake does not grow more than 4KiB for | |
| 197 now */ | |
| 198 assert(crypto_data->len + len <= crypto_data->alloclen); | |
| 199 | |
| 200 memcpy(&crypto_data->buf[crypto_data->len], data, len); | |
| 201 crypto_data->len += len; | |
| 202 | |
| 203 rv = ngtcp2_conn_submit_crypto_data( | |
| 204 qs->qconn, level, (uint8_t *)(&crypto_data->buf[crypto_data->len] - len), | |
| 205 len); | |
| 206 if(rv) { | |
| 207 fprintf(stderr, "write_client_handshake failed\n"); | |
| 208 } | |
| 209 assert(0 == rv); | |
| 210 | |
| 211 return 1; | |
| 212 } | |
| 213 | |
| 214 static int quic_flush_flight(SSL *ssl) | |
| 215 { | |
| 216 (void)ssl; | |
| 217 return 1; | |
| 218 } | |
| 219 | |
| 220 static int quic_send_alert(SSL *ssl, enum ssl_encryption_level_t level, | |
| 221 uint8_t alert) | |
| 222 { | |
| 223 struct quicsocket *qs = (struct quicsocket *)SSL_get_app_data(ssl); | |
| 224 (void)level; | |
| 225 | |
| 226 qs->tls_alert = alert; | |
| 227 return 1; | |
| 228 } | |
| 229 | |
| 230 static SSL_QUIC_METHOD quic_method = {quic_set_encryption_secrets, | |
| 231 quic_add_handshake_data, | |
| 232 quic_flush_flight, quic_send_alert}; | |
| 233 | |
| 234 static SSL_CTX *quic_ssl_ctx(struct Curl_easy *data) | |
| 235 { | |
| 236 SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); | |
| 237 const char *keylog_filename; | |
| 238 | |
| 239 SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_3_VERSION); | |
| 240 SSL_CTX_set_max_proto_version(ssl_ctx, TLS1_3_VERSION); | |
| 241 | |
| 242 SSL_CTX_set_default_verify_paths(ssl_ctx); | |
| 243 | |
| 244 if(SSL_CTX_set_ciphersuites(ssl_ctx, QUIC_CIPHERS) != 1) { | |
| 245 failf(data, "SSL_CTX_set_ciphersuites: %s", | |
| 246 ERR_error_string(ERR_get_error(), NULL)); | |
| 247 return NULL; | |
| 248 } | |
| 249 | |
| 250 if(SSL_CTX_set1_groups_list(ssl_ctx, QUIC_GROUPS) != 1) { | |
| 251 failf(data, "SSL_CTX_set1_groups_list failed"); | |
| 252 return NULL; | |
| 253 } | |
| 254 | |
| 255 SSL_CTX_set_quic_method(ssl_ctx, &quic_method); | |
| 256 | |
| 257 keylog_filename = getenv("SSLKEYLOGFILE"); | |
| 258 if(keylog_filename) { | |
| 259 keylog_file = fopen(keylog_filename, "wb"); | |
| 260 if(keylog_file) { | |
| 261 SSL_CTX_set_keylog_callback(ssl_ctx, keylog_callback); | |
| 262 } | |
| 263 } | |
| 264 | |
| 265 return ssl_ctx; | |
| 266 } | |
| 267 | |
| 268 /** SSL callbacks ***/ | |
| 269 | |
| 270 static int quic_init_ssl(struct quicsocket *qs) | |
| 271 { | |
| 272 const uint8_t *alpn = NULL; | |
| 273 size_t alpnlen = 0; | |
| 274 /* this will need some attention when HTTPS proxy over QUIC get fixed */ | |
| 275 const char * const hostname = qs->conn->host.name; | |
| 276 | |
| 277 if(qs->ssl) | |
| 278 SSL_free(qs->ssl); | |
| 279 | |
| 280 qs->ssl = SSL_new(qs->sslctx); | |
| 281 | |
| 282 SSL_set_app_data(qs->ssl, qs); | |
| 283 SSL_set_connect_state(qs->ssl); | |
| 284 | |
| 285 switch(qs->version) { | |
| 286 #ifdef NGTCP2_PROTO_VER | |
| 287 case NGTCP2_PROTO_VER: | |
| 288 alpn = (const uint8_t *)NGTCP2_ALPN_H3; | |
| 289 alpnlen = sizeof(NGTCP2_ALPN_H3) - 1; | |
| 290 break; | |
| 291 #endif | |
| 292 } | |
| 293 if(alpn) | |
| 294 SSL_set_alpn_protos(qs->ssl, alpn, (int)alpnlen); | |
| 295 | |
| 296 /* set SNI */ | |
| 297 SSL_set_tlsext_host_name(qs->ssl, hostname); | |
| 298 return 0; | |
| 299 } | |
| 300 | |
| 301 static int cb_initial(ngtcp2_conn *quic, void *user_data) | |
| 302 { | |
| 303 struct quicsocket *qs = (struct quicsocket *)user_data; | |
| 304 | |
| 305 if(ngtcp2_crypto_read_write_crypto_data( | |
| 306 quic, qs->ssl, NGTCP2_CRYPTO_LEVEL_INITIAL, NULL, 0) != 0) | |
| 307 return NGTCP2_ERR_CALLBACK_FAILURE; | |
| 308 | |
| 309 return 0; | |
| 310 } | |
| 311 | |
| 312 static int | |
| 313 cb_recv_crypto_data(ngtcp2_conn *tconn, ngtcp2_crypto_level crypto_level, | |
| 314 uint64_t offset, | |
| 315 const uint8_t *data, size_t datalen, | |
| 316 void *user_data) | |
| 317 { | |
| 318 struct quicsocket *qs = (struct quicsocket *)user_data; | |
| 319 (void)offset; | |
| 320 | |
| 321 if(ngtcp2_crypto_read_write_crypto_data(tconn, qs->ssl, crypto_level, data, | |
| 322 datalen) != 0) | |
| 323 return NGTCP2_ERR_CRYPTO; | |
| 324 | |
| 325 return 0; | |
| 326 } | |
| 327 | |
| 328 static int cb_handshake_completed(ngtcp2_conn *tconn, void *user_data) | |
| 329 { | |
| 330 struct quicsocket *qs = (struct quicsocket *)user_data; | |
| 331 (void)tconn; | |
| 332 infof(qs->conn->data, "QUIC handshake is completed\n"); | |
| 333 | |
| 334 return 0; | |
| 335 } | |
| 336 | |
| 337 static int cb_recv_stream_data(ngtcp2_conn *tconn, int64_t stream_id, | |
| 338 int fin, uint64_t offset, | |
| 339 const uint8_t *buf, size_t buflen, | |
| 340 void *user_data, void *stream_user_data) | |
| 341 { | |
| 342 struct quicsocket *qs = (struct quicsocket *)user_data; | |
| 343 ssize_t nconsumed; | |
| 344 (void)offset; | |
| 345 (void)stream_user_data; | |
| 346 | |
| 347 infof(qs->conn->data, "Received %ld bytes data on stream %u\n", | |
| 348 buflen, stream_id); | |
| 349 | |
| 350 nconsumed = | |
| 351 nghttp3_conn_read_stream(qs->h3conn, stream_id, buf, buflen, fin); | |
| 352 if(nconsumed < 0) { | |
| 353 failf(qs->conn->data, "nghttp3_conn_read_stream returned error: %s\n", | |
| 354 nghttp3_strerror((int)nconsumed)); | |
| 355 return NGTCP2_ERR_CALLBACK_FAILURE; | |
| 356 } | |
| 357 | |
| 358 ngtcp2_conn_extend_max_stream_offset(tconn, stream_id, nconsumed); | |
| 359 ngtcp2_conn_extend_max_offset(tconn, nconsumed); | |
| 360 | |
| 361 return 0; | |
| 362 } | |
| 363 | |
| 364 static int | |
| 365 cb_acked_stream_data_offset(ngtcp2_conn *tconn, int64_t stream_id, | |
| 366 uint64_t offset, size_t datalen, void *user_data, | |
| 367 void *stream_user_data) | |
| 368 { | |
| 369 struct quicsocket *qs = (struct quicsocket *)user_data; | |
| 370 int rv; | |
| 371 (void)stream_id; | |
| 372 (void)tconn; | |
| 373 (void)offset; | |
| 374 (void)datalen; | |
| 375 (void)stream_user_data; | |
| 376 | |
| 377 rv = nghttp3_conn_add_ack_offset(qs->h3conn, stream_id, datalen); | |
| 378 if(rv != 0) { | |
| 379 failf(qs->conn->data, "nghttp3_conn_add_ack_offset returned error: %s\n", | |
| 380 nghttp3_strerror(rv)); | |
| 381 return NGTCP2_ERR_CALLBACK_FAILURE; | |
| 382 } | |
| 383 | |
| 384 return 0; | |
| 385 } | |
| 386 | |
| 387 static int cb_stream_close(ngtcp2_conn *tconn, int64_t stream_id, | |
| 388 uint64_t app_error_code, | |
| 389 void *user_data, void *stream_user_data) | |
| 390 { | |
| 391 struct quicsocket *qs = (struct quicsocket *)user_data; | |
| 392 int rv; | |
| 393 (void)tconn; | |
| 394 (void)stream_user_data; | |
| 395 /* stream is closed... */ | |
| 396 | |
| 397 rv = nghttp3_conn_close_stream(qs->h3conn, stream_id, | |
| 398 app_error_code); | |
| 399 if(rv != 0) { | |
| 400 failf(qs->conn->data, "nghttp3_conn_close_stream returned error: %s\n", | |
| 401 nghttp3_strerror(rv)); | |
| 402 return NGTCP2_ERR_CALLBACK_FAILURE; | |
| 403 } | |
| 404 | |
| 405 return 0; | |
| 406 } | |
| 407 | |
| 408 static int cb_stream_reset(ngtcp2_conn *tconn, int64_t stream_id, | |
| 409 uint64_t final_size, uint64_t app_error_code, | |
| 410 void *user_data, void *stream_user_data) | |
| 411 { | |
| 412 struct quicsocket *qs = (struct quicsocket *)user_data; | |
| 413 int rv; | |
| 414 (void)tconn; | |
| 415 (void)final_size; | |
| 416 (void)app_error_code; | |
| 417 (void)stream_user_data; | |
| 418 | |
| 419 rv = nghttp3_conn_reset_stream(qs->h3conn, stream_id); | |
| 420 if(rv != 0) { | |
| 421 failf(qs->conn->data, "nghttp3_conn_reset_stream returned error: %s\n", | |
| 422 nghttp3_strerror(rv)); | |
| 423 return NGTCP2_ERR_CALLBACK_FAILURE; | |
| 424 } | |
| 425 | |
| 426 return 0; | |
| 427 } | |
| 428 | |
| 429 static int cb_recv_retry(ngtcp2_conn *tconn, const ngtcp2_pkt_hd *hd, | |
| 430 const ngtcp2_pkt_retry *retry, void *user_data) | |
| 431 { | |
| 432 /* Re-generate handshake secrets here because connection ID might change. */ | |
| 433 struct quicsocket *qs = (struct quicsocket *)user_data; | |
| 434 (void)tconn; | |
| 435 (void)hd; | |
| 436 (void)retry; | |
| 437 | |
| 438 setup_initial_crypto_context(qs); | |
| 439 | |
| 440 return 0; | |
| 441 } | |
| 442 | |
| 443 static int cb_extend_max_local_streams_bidi(ngtcp2_conn *tconn, | |
| 444 uint64_t max_streams, | |
| 445 void *user_data) | |
| 446 { | |
| 447 (void)tconn; | |
| 448 (void)max_streams; | |
| 449 (void)user_data; | |
| 450 | |
| 451 return 0; | |
| 452 } | |
| 453 | |
| 454 static int cb_extend_max_stream_data(ngtcp2_conn *tconn, int64_t stream_id, | |
| 455 uint64_t max_data, void *user_data, | |
| 456 void *stream_user_data) | |
| 457 { | |
| 458 struct quicsocket *qs = (struct quicsocket *)user_data; | |
| 459 int rv; | |
| 460 (void)tconn; | |
| 461 (void)max_data; | |
| 462 (void)stream_user_data; | |
| 463 | |
| 464 rv = nghttp3_conn_unblock_stream(qs->h3conn, stream_id); | |
| 465 if(rv != 0) { | |
| 466 failf(qs->conn->data, "nghttp3_conn_unblock_stream returned error: %s\n", | |
| 467 nghttp3_strerror(rv)); | |
| 468 return NGTCP2_ERR_CALLBACK_FAILURE; | |
| 469 } | |
| 470 | |
| 471 return 0; | |
| 472 } | |
| 473 | |
| 474 static int cb_get_new_connection_id(ngtcp2_conn *tconn, ngtcp2_cid *cid, | |
| 475 uint8_t *token, size_t cidlen, | |
| 476 void *user_data) | |
| 477 { | |
| 478 struct quicsocket *qs = (struct quicsocket *)user_data; | |
| 479 CURLcode result; | |
| 480 (void)tconn; | |
| 481 | |
| 482 result = Curl_rand(qs->conn->data, cid->data, cidlen); | |
| 483 if(result) | |
| 484 return NGTCP2_ERR_CALLBACK_FAILURE; | |
| 485 cid->datalen = cidlen; | |
| 486 | |
| 487 result = Curl_rand(qs->conn->data, token, NGTCP2_STATELESS_RESET_TOKENLEN); | |
| 488 if(result) | |
| 489 return NGTCP2_ERR_CALLBACK_FAILURE; | |
| 490 | |
| 491 return 0; | |
| 492 } | |
| 493 | |
| 494 static ngtcp2_conn_callbacks ng_callbacks = { | |
| 495 cb_initial, | |
| 496 NULL, /* recv_client_initial */ | |
| 497 cb_recv_crypto_data, | |
| 498 cb_handshake_completed, | |
| 499 NULL, /* recv_version_negotiation */ | |
| 500 ngtcp2_crypto_encrypt_cb, | |
| 501 ngtcp2_crypto_decrypt_cb, | |
| 502 ngtcp2_crypto_hp_mask_cb, | |
| 503 cb_recv_stream_data, | |
| 504 NULL, /* acked_crypto_offset */ | |
| 505 cb_acked_stream_data_offset, | |
| 506 NULL, /* stream_open */ | |
| 507 cb_stream_close, | |
| 508 NULL, /* recv_stateless_reset */ | |
| 509 cb_recv_retry, | |
| 510 cb_extend_max_local_streams_bidi, | |
| 511 NULL, /* extend_max_local_streams_uni */ | |
| 512 NULL, /* rand */ | |
| 513 cb_get_new_connection_id, | |
| 514 NULL, /* remove_connection_id */ | |
| 515 NULL, /* update_key */ | |
| 516 NULL, /* path_validation */ | |
| 517 NULL, /* select_preferred_addr */ | |
| 518 cb_stream_reset, | |
| 519 NULL, /* extend_max_remote_streams_bidi */ | |
| 520 NULL, /* extend_max_remote_streams_uni */ | |
| 521 cb_extend_max_stream_data, | |
| 522 }; | |
| 523 | |
| 524 /* | |
| 525 * Might be called twice for happy eyeballs. | |
| 526 */ | |
| 527 CURLcode Curl_quic_connect(struct connectdata *conn, | |
| 528 curl_socket_t sockfd, | |
| 529 int sockindex, | |
| 530 const struct sockaddr *addr, | |
| 531 socklen_t addrlen) | |
| 532 { | |
| 533 int rc; | |
| 534 int rv; | |
| 535 CURLcode result; | |
| 536 ngtcp2_path path; /* TODO: this must be initialized properly */ | |
| 537 struct Curl_easy *data = conn->data; | |
| 538 struct quicsocket *qs = &conn->hequic[sockindex]; | |
| 539 char ipbuf[40]; | |
| 540 long port; | |
| 541 uint8_t paramsbuf[64]; | |
| 542 ngtcp2_transport_params params; | |
| 543 ssize_t nwrite; | |
| 544 | |
| 545 qs->conn = conn; | |
| 546 | |
| 547 /* extract the used address as a string */ | |
| 548 if(!Curl_addr2string((struct sockaddr*)addr, addrlen, ipbuf, &port)) { | |
| 549 char buffer[STRERROR_LEN]; | |
| 550 failf(data, "ssrem inet_ntop() failed with errno %d: %s", | |
| 551 errno, Curl_strerror(errno, buffer, sizeof(buffer))); | |
| 552 return CURLE_BAD_FUNCTION_ARGUMENT; | |
| 553 } | |
| 554 | |
| 555 infof(data, "Connect socket %d over QUIC to %s:%ld\n", | |
| 556 sockfd, ipbuf, port); | |
| 557 | |
| 558 qs->version = NGTCP2_PROTO_VER; | |
| 559 qs->sslctx = quic_ssl_ctx(data); | |
| 560 if(!qs->sslctx) | |
| 561 return CURLE_FAILED_INIT; /* TODO: better return code */ | |
| 562 | |
| 563 if(quic_init_ssl(qs)) | |
| 564 return CURLE_FAILED_INIT; /* TODO: better return code */ | |
| 565 | |
| 566 qs->dcid.datalen = NGTCP2_MAX_CIDLEN; | |
| 567 result = Curl_rand(data, qs->dcid.data, NGTCP2_MAX_CIDLEN); | |
| 568 if(result) | |
| 569 return result; | |
| 570 | |
| 571 qs->scid.datalen = NGTCP2_MAX_CIDLEN; | |
| 572 result = Curl_rand(data, qs->scid.data, NGTCP2_MAX_CIDLEN); | |
| 573 if(result) | |
| 574 return result; | |
| 575 | |
| 576 quic_settings(&qs->settings, data->set.buffer_size); | |
| 577 | |
| 578 qs->local_addrlen = sizeof(qs->local_addr); | |
| 579 rv = getsockname(sockfd, (struct sockaddr *)&qs->local_addr, | |
| 580 &qs->local_addrlen); | |
| 581 if(rv == -1) | |
| 582 return CURLE_FAILED_INIT; | |
| 583 | |
| 584 ngtcp2_addr_init(&path.local, (uint8_t *)&qs->local_addr, qs->local_addrlen, | |
| 585 NULL); | |
| 586 ngtcp2_addr_init(&path.remote, (uint8_t*)addr, addrlen, NULL); | |
| 587 | |
| 588 #ifdef NGTCP2_PROTO_VER | |
| 589 #define QUICVER NGTCP2_PROTO_VER | |
| 590 #else | |
| 591 #error "unsupported ngtcp2 version" | |
| 592 #endif | |
| 593 rc = ngtcp2_conn_client_new(&qs->qconn, &qs->dcid, &qs->scid, &path, QUICVER, | |
| 594 &ng_callbacks, &qs->settings, NULL, qs); | |
| 595 if(rc) | |
| 596 return CURLE_FAILED_INIT; /* TODO: create a QUIC error code */ | |
| 597 | |
| 598 ngtcp2_conn_get_local_transport_params(qs->qconn, ¶ms); | |
| 599 nwrite = ngtcp2_encode_transport_params( | |
| 600 paramsbuf, sizeof(paramsbuf), NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, | |
| 601 ¶ms); | |
| 602 if(nwrite < 0) { | |
| 603 fprintf(stderr, "ngtcp2_encode_transport_params: %s\n", | |
| 604 ngtcp2_strerror((int)nwrite)); | |
| 605 | |
| 606 return CURLE_FAILED_INIT; | |
| 607 } | |
| 608 | |
| 609 if(!SSL_set_quic_transport_params(qs->ssl, paramsbuf, nwrite)) | |
| 610 return CURLE_FAILED_INIT; | |
| 611 | |
| 612 rc = setup_initial_crypto_context(qs); | |
| 613 if(rc) | |
| 614 return CURLE_FAILED_INIT; /* TODO: better return code */ | |
| 615 | |
| 616 return CURLE_OK; | |
| 617 } | |
| 618 | |
| 619 /* | |
| 620 * Store ngtp2 version info in this buffer, Prefix with a space. Return total | |
| 621 * length written. | |
| 622 */ | |
| 623 int Curl_quic_ver(char *p, size_t len) | |
| 624 { | |
| 625 ngtcp2_info *ng2 = ngtcp2_version(0); | |
| 626 nghttp3_info *ht3 = nghttp3_version(0); | |
| 627 return msnprintf(p, len, " ngtcp2/%s nghttp3/%s", | |
| 628 ng2->version_str, ht3->version_str); | |
| 629 } | |
| 630 | |
| 631 static int ng_getsock(struct connectdata *conn, curl_socket_t *socks) | |
| 632 { | |
| 633 struct SingleRequest *k = &conn->data->req; | |
| 634 int bitmap = GETSOCK_BLANK; | |
| 635 | |
| 636 socks[0] = conn->sock[FIRSTSOCKET]; | |
| 637 | |
| 638 /* in a HTTP/2 connection we can basically always get a frame so we should | |
| 639 always be ready for one */ | |
| 640 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); | |
| 641 | |
| 642 /* we're still uploading or the HTTP/2 layer wants to send data */ | |
| 643 if((k->keepon & (KEEP_SEND|KEEP_SEND_PAUSE)) == KEEP_SEND) | |
| 644 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); | |
| 645 | |
| 646 return bitmap; | |
| 647 } | |
| 648 | |
| 649 static int ng_perform_getsock(const struct connectdata *conn, | |
| 650 curl_socket_t *socks) | |
| 651 { | |
| 652 return ng_getsock((struct connectdata *)conn, socks); | |
| 653 } | |
| 654 | |
| 655 static CURLcode ng_disconnect(struct connectdata *conn, | |
| 656 bool dead_connection) | |
| 657 { | |
| 658 (void)conn; | |
| 659 (void)dead_connection; | |
| 660 return CURLE_OK; | |
| 661 } | |
| 662 | |
| 663 static unsigned int ng_conncheck(struct connectdata *conn, | |
| 664 unsigned int checks_to_perform) | |
| 665 { | |
| 666 (void)conn; | |
| 667 (void)checks_to_perform; | |
| 668 return CONNRESULT_NONE; | |
| 669 } | |
| 670 | |
| 671 static const struct Curl_handler Curl_handler_http3 = { | |
| 672 "HTTPS", /* scheme */ | |
| 673 ZERO_NULL, /* setup_connection */ | |
| 674 Curl_http, /* do_it */ | |
| 675 Curl_http_done, /* done */ | |
| 676 ZERO_NULL, /* do_more */ | |
| 677 ZERO_NULL, /* connect_it */ | |
| 678 ZERO_NULL, /* connecting */ | |
| 679 ZERO_NULL, /* doing */ | |
| 680 ng_getsock, /* proto_getsock */ | |
| 681 ng_getsock, /* doing_getsock */ | |
| 682 ZERO_NULL, /* domore_getsock */ | |
| 683 ng_perform_getsock, /* perform_getsock */ | |
| 684 ng_disconnect, /* disconnect */ | |
| 685 ZERO_NULL, /* readwrite */ | |
| 686 ng_conncheck, /* connection_check */ | |
| 687 PORT_HTTP, /* defport */ | |
| 688 CURLPROTO_HTTPS, /* protocol */ | |
| 689 PROTOPT_SSL | PROTOPT_STREAM /* flags */ | |
| 690 }; | |
| 691 | |
| 692 static int cb_h3_stream_close(nghttp3_conn *conn, int64_t stream_id, | |
| 693 uint64_t app_error_code, void *user_data, | |
| 694 void *stream_user_data) | |
| 695 { | |
| 696 struct Curl_easy *data = stream_user_data; | |
| 697 struct HTTP *stream = data->req.protop; | |
| 698 (void)conn; | |
| 699 (void)stream_id; | |
| 700 (void)app_error_code; | |
| 701 (void)user_data; | |
| 702 fprintf(stderr, "cb_h3_stream_close CALLED\n"); | |
| 703 | |
| 704 stream->closed = TRUE; | |
| 705 Curl_expire(data, 0, EXPIRE_QUIC); | |
| 706 return 0; | |
| 707 } | |
| 708 | |
| 709 static int cb_h3_recv_data(nghttp3_conn *conn, int64_t stream_id, | |
| 710 const uint8_t *buf, size_t buflen, | |
| 711 void *user_data, void *stream_user_data) | |
| 712 { | |
| 713 struct quicsocket *qs = user_data; | |
| 714 size_t ncopy; | |
| 715 struct Curl_easy *data = stream_user_data; | |
| 716 struct HTTP *stream = data->req.protop; | |
| 717 (void)conn; | |
| 718 fprintf(stderr, "cb_h3_recv_data CALLED with %d bytes\n", buflen); | |
| 719 | |
| 720 /* TODO: this needs to be handled properly */ | |
| 721 DEBUGASSERT(buflen <= stream->len); | |
| 722 | |
| 723 ncopy = CURLMIN(stream->len, buflen); | |
| 724 memcpy(stream->mem, buf, ncopy); | |
| 725 stream->len -= ncopy; | |
| 726 stream->memlen += ncopy; | |
| 727 #if 0 /* extra debugging of incoming h3 data */ | |
| 728 fprintf(stderr, "!! Copies %zd bytes to %p (total %zd)\n", | |
| 729 ncopy, stream->mem, stream->memlen); | |
| 730 { | |
| 731 size_t i; | |
| 732 for(i = 0; i < ncopy; i++) { | |
| 733 fprintf(stderr, "!! data[%d]: %02x '%c'\n", i, buf[i], buf[i]); | |
| 734 } | |
| 735 } | |
| 736 #endif | |
| 737 stream->mem += ncopy; | |
| 738 | |
| 739 ngtcp2_conn_extend_max_stream_offset(qs->qconn, stream_id, buflen); | |
| 740 ngtcp2_conn_extend_max_offset(qs->qconn, buflen); | |
| 741 | |
| 742 return 0; | |
| 743 } | |
| 744 | |
| 745 static int cb_h3_deferred_consume(nghttp3_conn *conn, int64_t stream_id, | |
| 746 size_t consumed, void *user_data, | |
| 747 void *stream_user_data) | |
| 748 { | |
| 749 struct quicsocket *qs = user_data; | |
| 750 (void)conn; | |
| 751 (void)stream_user_data; | |
| 752 fprintf(stderr, "cb_h3_deferred_consume CALLED\n"); | |
| 753 | |
| 754 ngtcp2_conn_extend_max_stream_offset(qs->qconn, stream_id, consumed); | |
| 755 ngtcp2_conn_extend_max_offset(qs->qconn, consumed); | |
| 756 | |
| 757 return 0; | |
| 758 } | |
| 759 | |
| 760 /* Decode HTTP status code. Returns -1 if no valid status code was | |
| 761 decoded. (duplicate from http2.c) */ | |
| 762 static int decode_status_code(const uint8_t *value, size_t len) | |
| 763 { | |
| 764 int i; | |
| 765 int res; | |
| 766 | |
| 767 if(len != 3) { | |
| 768 return -1; | |
| 769 } | |
| 770 | |
| 771 res = 0; | |
| 772 | |
| 773 for(i = 0; i < 3; ++i) { | |
| 774 char c = value[i]; | |
| 775 | |
| 776 if(c < '0' || c > '9') { | |
| 777 return -1; | |
| 778 } | |
| 779 | |
| 780 res *= 10; | |
| 781 res += c - '0'; | |
| 782 } | |
| 783 | |
| 784 return res; | |
| 785 } | |
| 786 | |
| 787 static int cb_h3_end_headers(nghttp3_conn *conn, int64_t stream_id, | |
| 788 void *user_data, void *stream_user_data) | |
| 789 { | |
| 790 struct Curl_easy *data = stream_user_data; | |
| 791 struct HTTP *stream = data->req.protop; | |
| 792 (void)conn; | |
| 793 (void)stream_id; | |
| 794 (void)user_data; | |
| 795 | |
| 796 if(stream->memlen >= 2) { | |
| 797 memcpy(stream->mem, "\r\n", 2); | |
| 798 stream->len -= 2; | |
| 799 stream->memlen += 2; | |
| 800 stream->mem += 2; | |
| 801 } | |
| 802 return 0; | |
| 803 } | |
| 804 | |
| 805 static int cb_h3_recv_header(nghttp3_conn *conn, int64_t stream_id, | |
| 806 int32_t token, nghttp3_rcbuf *name, | |
| 807 nghttp3_rcbuf *value, uint8_t flags, | |
| 808 void *user_data, void *stream_user_data) | |
| 809 { | |
| 810 nghttp3_vec h3name = nghttp3_rcbuf_get_buf(name); | |
| 811 nghttp3_vec h3val = nghttp3_rcbuf_get_buf(value); | |
| 812 struct Curl_easy *data = stream_user_data; | |
| 813 struct HTTP *stream = data->req.protop; | |
| 814 size_t ncopy; | |
| 815 (void)conn; | |
| 816 (void)stream_id; | |
| 817 (void)token; | |
| 818 (void)flags; | |
| 819 (void)user_data; | |
| 820 | |
| 821 fprintf(stderr, "cb_h3_recv_header called!\n"); | |
| 822 | |
| 823 if(h3name.len == sizeof(":status") - 1 && | |
| 824 !memcmp(":status", h3name.base, h3name.len)) { | |
| 825 int status = decode_status_code(h3val.base, h3val.len); | |
| 826 DEBUGASSERT(status != -1); | |
| 827 msnprintf(stream->mem, stream->len, "HTTP/3 %03d \r\n", status); | |
| 828 } | |
| 829 else { | |
| 830 /* store as a HTTP1-style header */ | |
| 831 msnprintf(stream->mem, stream->len, "%.*s: %.*s\n", | |
| 832 h3name.len, h3name.base, h3val.len, h3val.base); | |
| 833 } | |
| 834 | |
| 835 ncopy = strlen(stream->mem); | |
| 836 stream->len -= ncopy; | |
| 837 stream->memlen += ncopy; | |
| 838 stream->mem += ncopy; | |
| 839 return 0; | |
| 840 } | |
| 841 | |
| 842 static int cb_h3_send_stop_sending(nghttp3_conn *conn, int64_t stream_id, | |
| 843 uint64_t app_error_code, | |
| 844 void *user_data, | |
| 845 void *stream_user_data) | |
| 846 { | |
| 847 (void)conn; | |
| 848 (void)stream_id; | |
| 849 (void)app_error_code; | |
| 850 (void)user_data; | |
| 851 (void)stream_user_data; | |
| 852 fprintf(stderr, "cb_h3_send_stop_sending CALLED\n"); | |
| 853 return 0; | |
| 854 } | |
| 855 | |
| 856 static nghttp3_conn_callbacks ngh3_callbacks = { | |
| 857 cb_h3_acked_stream_data, /* acked_stream_data */ | |
| 858 cb_h3_stream_close, | |
| 859 cb_h3_recv_data, | |
| 860 cb_h3_deferred_consume, | |
| 861 NULL, /* begin_headers */ | |
| 862 cb_h3_recv_header, | |
| 863 cb_h3_end_headers, | |
| 864 NULL, /* begin_trailers */ | |
| 865 cb_h3_recv_header, | |
| 866 NULL, /* end_trailers */ | |
| 867 NULL, /* http_begin_push_promise */ | |
| 868 NULL, /* http_recv_push_promise */ | |
| 869 NULL, /* http_end_push_promise */ | |
| 870 NULL, /* http_cancel_push */ | |
| 871 cb_h3_send_stop_sending, | |
| 872 NULL, /* push_stream */ | |
| 873 NULL, /* end_stream */ | |
| 874 }; | |
| 875 | |
| 876 static int init_ngh3_conn(struct quicsocket *qs) | |
| 877 { | |
| 878 CURLcode result; | |
| 879 int rc; | |
| 880 int64_t ctrl_stream_id, qpack_enc_stream_id, qpack_dec_stream_id; | |
| 881 | |
| 882 if(ngtcp2_conn_get_max_local_streams_uni(qs->qconn) < 3) { | |
| 883 failf(qs->conn->data, "too few available QUIC streams"); | |
| 884 return CURLE_FAILED_INIT; | |
| 885 } | |
| 886 | |
| 887 nghttp3_conn_settings_default(&qs->h3settings); | |
| 888 | |
| 889 rc = nghttp3_conn_client_new(&qs->h3conn, | |
| 890 &ngh3_callbacks, | |
| 891 &qs->h3settings, | |
| 892 nghttp3_mem_default(), | |
| 893 qs); | |
| 894 if(rc) { | |
| 895 result = CURLE_OUT_OF_MEMORY; | |
| 896 goto fail; | |
| 897 } | |
| 898 | |
| 899 rc = ngtcp2_conn_open_uni_stream(qs->qconn, &ctrl_stream_id, NULL); | |
| 900 if(rc) { | |
| 901 result = CURLE_FAILED_INIT; | |
| 902 goto fail; | |
| 903 } | |
| 904 | |
| 905 rc = nghttp3_conn_bind_control_stream(qs->h3conn, ctrl_stream_id); | |
| 906 if(rc) { | |
| 907 result = CURLE_FAILED_INIT; | |
| 908 goto fail; | |
| 909 } | |
| 910 | |
| 911 rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_enc_stream_id, NULL); | |
| 912 if(rc) { | |
| 913 result = CURLE_FAILED_INIT; | |
| 914 goto fail; | |
| 915 } | |
| 916 | |
| 917 rc = ngtcp2_conn_open_uni_stream(qs->qconn, &qpack_dec_stream_id, NULL); | |
| 918 if(rc) { | |
| 919 result = CURLE_FAILED_INIT; | |
| 920 goto fail; | |
| 921 } | |
| 922 | |
| 923 rc = nghttp3_conn_bind_qpack_streams(qs->h3conn, qpack_enc_stream_id, | |
| 924 qpack_dec_stream_id); | |
| 925 if(rc) { | |
| 926 result = CURLE_FAILED_INIT; | |
| 927 goto fail; | |
| 928 } | |
| 929 | |
| 930 return CURLE_OK; | |
| 931 fail: | |
| 932 | |
| 933 return result; | |
| 934 } | |
| 935 | |
| 936 static Curl_recv ngh3_stream_recv; | |
| 937 static Curl_send ngh3_stream_send; | |
| 938 | |
| 939 /* incoming data frames on the h3 stream */ | |
| 940 static ssize_t ngh3_stream_recv(struct connectdata *conn, | |
| 941 int sockindex, | |
| 942 char *buf, | |
| 943 size_t buffersize, | |
| 944 CURLcode *curlcode) | |
| 945 { | |
| 946 curl_socket_t sockfd = conn->sock[sockindex]; | |
| 947 struct HTTP *stream = conn->data->req.protop; | |
| 948 struct quicsocket *qs = conn->quic; | |
| 949 | |
| 950 fprintf(stderr, "ngh3_stream_recv CALLED (easy %p, socket %d)\n", | |
| 951 conn->data, sockfd); | |
| 952 | |
| 953 if(!stream->memlen) { | |
| 954 /* remember where to store incoming data for this stream and how big the | |
| 955 buffer is */ | |
| 956 stream->mem = buf; | |
| 957 stream->len = buffersize; | |
| 958 } | |
| 959 /* else, there's data in the buffer already */ | |
| 960 | |
| 961 if(ng_process_ingress(conn, sockfd, qs)) { | |
| 962 *curlcode = CURLE_RECV_ERROR; | |
| 963 return -1; | |
| 964 } | |
| 965 if(ng_flush_egress(conn, sockfd, qs)) { | |
| 966 *curlcode = CURLE_SEND_ERROR; | |
| 967 return -1; | |
| 968 } | |
| 969 | |
| 970 if(stream->memlen) { | |
| 971 ssize_t memlen = stream->memlen; | |
| 972 /* data arrived */ | |
| 973 *curlcode = CURLE_OK; | |
| 974 /* reset to allow more data to come */ | |
| 975 stream->memlen = 0; | |
| 976 stream->mem = buf; | |
| 977 stream->len = buffersize; | |
| 978 H3BUGF(infof(conn->data, "!! ngh3_stream_recv returns %zd bytes at %p\n", | |
| 979 memlen, buf)); | |
| 980 return memlen; | |
| 981 } | |
| 982 | |
| 983 if(stream->closed) { | |
| 984 *curlcode = CURLE_OK; | |
| 985 return 0; | |
| 986 } | |
| 987 | |
| 988 infof(conn->data, "ngh3_stream_recv returns 0 bytes and EAGAIN\n"); | |
| 989 *curlcode = CURLE_AGAIN; | |
| 990 return -1; | |
| 991 } | |
| 992 | |
| 993 /* this amount of data has now been acked on this stream */ | |
| 994 static int cb_h3_acked_stream_data(nghttp3_conn *conn, int64_t stream_id, | |
| 995 size_t datalen, void *user_data, | |
| 996 void *stream_user_data) | |
| 997 { | |
| 998 struct Curl_easy *data = stream_user_data; | |
| 999 struct HTTP *stream = data->req.protop; | |
| 1000 (void)conn; | |
| 1001 (void)stream_id; | |
| 1002 (void)user_data; | |
| 1003 | |
| 1004 if(!data->set.postfields) { | |
| 1005 stream->h3out->used -= datalen; | |
| 1006 fprintf(stderr, "cb_h3_acked_stream_data, %zd bytes, %zd left unacked\n", | |
| 1007 datalen, stream->h3out->used); | |
| 1008 DEBUGASSERT(stream->h3out->used < H3_SEND_SIZE); | |
| 1009 } | |
| 1010 return 0; | |
| 1011 } | |
| 1012 | |
| 1013 static int cb_h3_readfunction(nghttp3_conn *conn, int64_t stream_id, | |
| 1014 const uint8_t **pdata, | |
| 1015 size_t *pdatalen, uint32_t *pflags, | |
| 1016 void *user_data, void *stream_user_data) | |
| 1017 { | |
| 1018 struct Curl_easy *data = stream_user_data; | |
| 1019 size_t nread; | |
| 1020 struct HTTP *stream = data->req.protop; | |
| 1021 (void)conn; | |
| 1022 (void)stream_id; | |
| 1023 (void)user_data; | |
| 1024 | |
| 1025 if(data->set.postfields) { | |
| 1026 *pdata = data->set.postfields; | |
| 1027 *pdatalen = data->state.infilesize; | |
| 1028 *pflags = NGHTTP3_DATA_FLAG_EOF; | |
| 1029 return 0; | |
| 1030 } | |
| 1031 | |
| 1032 nread = CURLMIN(stream->upload_len, H3_SEND_SIZE - stream->h3out->used); | |
| 1033 if(nread > 0) { | |
| 1034 /* nghttp3 wants us to hold on to the data until it tells us it is okay to | |
| 1035 delete it. Append the data at the end of the h3out buffer. Since we can | |
| 1036 only return consecutive data, copy the amount that fits and the next | |
| 1037 part comes in next invoke. */ | |
| 1038 struct h3out *out = stream->h3out; | |
| 1039 if(nread + out->windex > H3_SEND_SIZE) | |
| 1040 nread = H3_SEND_SIZE - out->windex; | |
| 1041 | |
| 1042 memcpy(&out->buf[out->windex], stream->upload_mem, nread); | |
| 1043 out->windex += nread; | |
| 1044 out->used += nread; | |
| 1045 | |
| 1046 /* that's the chunk we return to nghttp3 */ | |
| 1047 *pdata = &out->buf[out->windex]; | |
| 1048 *pdatalen = nread; | |
| 1049 | |
| 1050 if(out->windex == H3_SEND_SIZE) | |
| 1051 out->windex = 0; /* wrap */ | |
| 1052 stream->upload_mem += nread; | |
| 1053 stream->upload_len -= nread; | |
| 1054 if(data->state.infilesize != -1) { | |
| 1055 stream->upload_left -= nread; | |
| 1056 if(!stream->upload_left) | |
| 1057 *pflags = NGHTTP3_DATA_FLAG_EOF; | |
| 1058 } | |
| 1059 fprintf(stderr, "cb_h3_readfunction %zd bytes%s (at %zd unacked)\n", | |
| 1060 nread, *pflags == NGHTTP3_DATA_FLAG_EOF?" EOF":"", | |
| 1061 out->used); | |
| 1062 } | |
| 1063 if(stream->upload_done && !stream->upload_len && | |
| 1064 (stream->upload_left <= 0)) { | |
| 1065 H3BUGF(infof(data, "!!!!!!!!! cb_h3_readfunction sets EOF\n")); | |
| 1066 *pdata = NULL; | |
| 1067 *pdatalen = 0; | |
| 1068 *pflags = NGHTTP3_DATA_FLAG_EOF; | |
| 1069 } | |
| 1070 else if(!nread) { | |
| 1071 *pdatalen = 0; | |
| 1072 return NGHTTP3_ERR_WOULDBLOCK; | |
| 1073 } | |
| 1074 return 0; | |
| 1075 } | |
| 1076 | |
| 1077 /* Index where :authority header field will appear in request header | |
| 1078 field list. */ | |
| 1079 #define AUTHORITY_DST_IDX 3 | |
| 1080 | |
| 1081 static CURLcode http_request(struct connectdata *conn, const void *mem, | |
| 1082 size_t len) | |
| 1083 { | |
| 1084 struct HTTP *stream = conn->data->req.protop; | |
| 1085 size_t nheader; | |
| 1086 size_t i; | |
| 1087 size_t authority_idx; | |
| 1088 char *hdbuf = (char *)mem; | |
| 1089 char *end, *line_end; | |
| 1090 struct quicsocket *qs = conn->quic; | |
| 1091 CURLcode result = CURLE_OK; | |
| 1092 struct Curl_easy *data = conn->data; | |
| 1093 nghttp3_nv *nva = NULL; | |
| 1094 int64_t stream3_id; | |
| 1095 int rc; | |
| 1096 struct h3out *h3out = NULL; | |
| 1097 | |
| 1098 rc = ngtcp2_conn_open_bidi_stream(qs->qconn, &stream3_id, NULL); | |
| 1099 if(rc) { | |
| 1100 failf(conn->data, "can get bidi streams"); | |
| 1101 result = CURLE_SEND_ERROR; | |
| 1102 goto fail; | |
| 1103 } | |
| 1104 | |
| 1105 stream->stream3_id = stream3_id; | |
| 1106 stream->h3req = TRUE; /* senf off! */ | |
| 1107 | |
| 1108 /* Calculate number of headers contained in [mem, mem + len). Assumes a | |
| 1109 correctly generated HTTP header field block. */ | |
| 1110 nheader = 0; | |
| 1111 for(i = 1; i < len; ++i) { | |
| 1112 if(hdbuf[i] == '\n' && hdbuf[i - 1] == '\r') { | |
| 1113 ++nheader; | |
| 1114 ++i; | |
| 1115 } | |
| 1116 } | |
| 1117 if(nheader < 2) | |
| 1118 goto fail; | |
| 1119 | |
| 1120 /* We counted additional 2 \r\n in the first and last line. We need 3 | |
| 1121 new headers: :method, :path and :scheme. Therefore we need one | |
| 1122 more space. */ | |
| 1123 nheader += 1; | |
| 1124 nva = malloc(sizeof(nghttp3_nv) * nheader); | |
| 1125 if(!nva) { | |
| 1126 result = CURLE_OUT_OF_MEMORY; | |
| 1127 goto fail; | |
| 1128 } | |
| 1129 | |
| 1130 /* Extract :method, :path from request line | |
| 1131 We do line endings with CRLF so checking for CR is enough */ | |
| 1132 line_end = memchr(hdbuf, '\r', len); | |
| 1133 if(!line_end) { | |
| 1134 result = CURLE_BAD_FUNCTION_ARGUMENT; /* internal error */ | |
| 1135 goto fail; | |
| 1136 } | |
| 1137 | |
| 1138 /* Method does not contain spaces */ | |
| 1139 end = memchr(hdbuf, ' ', line_end - hdbuf); | |
| 1140 if(!end || end == hdbuf) | |
| 1141 goto fail; | |
| 1142 nva[0].name = (unsigned char *)":method"; | |
| 1143 nva[0].namelen = strlen((char *)nva[0].name); | |
| 1144 nva[0].value = (unsigned char *)hdbuf; | |
| 1145 nva[0].valuelen = (size_t)(end - hdbuf); | |
| 1146 nva[0].flags = NGHTTP3_NV_FLAG_NONE; | |
| 1147 | |
| 1148 hdbuf = end + 1; | |
| 1149 | |
| 1150 /* Path may contain spaces so scan backwards */ | |
| 1151 end = NULL; | |
| 1152 for(i = (size_t)(line_end - hdbuf); i; --i) { | |
| 1153 if(hdbuf[i - 1] == ' ') { | |
| 1154 end = &hdbuf[i - 1]; | |
| 1155 break; | |
| 1156 } | |
| 1157 } | |
| 1158 if(!end || end == hdbuf) | |
| 1159 goto fail; | |
| 1160 nva[1].name = (unsigned char *)":path"; | |
| 1161 nva[1].namelen = strlen((char *)nva[1].name); | |
| 1162 nva[1].value = (unsigned char *)hdbuf; | |
| 1163 nva[1].valuelen = (size_t)(end - hdbuf); | |
| 1164 nva[1].flags = NGHTTP3_NV_FLAG_NONE; | |
| 1165 | |
| 1166 nva[2].name = (unsigned char *)":scheme"; | |
| 1167 nva[2].namelen = strlen((char *)nva[2].name); | |
| 1168 if(conn->handler->flags & PROTOPT_SSL) | |
| 1169 nva[2].value = (unsigned char *)"https"; | |
| 1170 else | |
| 1171 nva[2].value = (unsigned char *)"http"; | |
| 1172 nva[2].valuelen = strlen((char *)nva[2].value); | |
| 1173 nva[2].flags = NGHTTP3_NV_FLAG_NONE; | |
| 1174 | |
| 1175 | |
| 1176 authority_idx = 0; | |
| 1177 i = 3; | |
| 1178 while(i < nheader) { | |
| 1179 size_t hlen; | |
| 1180 | |
| 1181 hdbuf = line_end + 2; | |
| 1182 | |
| 1183 /* check for next CR, but only within the piece of data left in the given | |
| 1184 buffer */ | |
| 1185 line_end = memchr(hdbuf, '\r', len - (hdbuf - (char *)mem)); | |
| 1186 if(!line_end || (line_end == hdbuf)) | |
| 1187 goto fail; | |
| 1188 | |
| 1189 /* header continuation lines are not supported */ | |
| 1190 if(*hdbuf == ' ' || *hdbuf == '\t') | |
| 1191 goto fail; | |
| 1192 | |
| 1193 for(end = hdbuf; end < line_end && *end != ':'; ++end) | |
| 1194 ; | |
| 1195 if(end == hdbuf || end == line_end) | |
| 1196 goto fail; | |
| 1197 hlen = end - hdbuf; | |
| 1198 | |
| 1199 if(hlen == 4 && strncasecompare("host", hdbuf, 4)) { | |
| 1200 authority_idx = i; | |
| 1201 nva[i].name = (unsigned char *)":authority"; | |
| 1202 nva[i].namelen = strlen((char *)nva[i].name); | |
| 1203 } | |
| 1204 else { | |
| 1205 nva[i].name = (unsigned char *)hdbuf; | |
| 1206 nva[i].namelen = (size_t)(end - hdbuf); | |
| 1207 } | |
| 1208 nva[i].flags = NGHTTP3_NV_FLAG_NONE; | |
| 1209 hdbuf = end + 1; | |
| 1210 while(*hdbuf == ' ' || *hdbuf == '\t') | |
| 1211 ++hdbuf; | |
| 1212 end = line_end; | |
| 1213 | |
| 1214 #if 0 /* This should probably go in more or less like this */ | |
| 1215 switch(inspect_header((const char *)nva[i].name, nva[i].namelen, hdbuf, | |
| 1216 end - hdbuf)) { | |
| 1217 case HEADERINST_IGNORE: | |
| 1218 /* skip header fields prohibited by HTTP/2 specification. */ | |
| 1219 --nheader; | |
| 1220 continue; | |
| 1221 case HEADERINST_TE_TRAILERS: | |
| 1222 nva[i].value = (uint8_t*)"trailers"; | |
| 1223 nva[i].value_len = sizeof("trailers") - 1; | |
| 1224 break; | |
| 1225 default: | |
| 1226 nva[i].value = (unsigned char *)hdbuf; | |
| 1227 nva[i].value_len = (size_t)(end - hdbuf); | |
| 1228 } | |
| 1229 #endif | |
| 1230 nva[i].value = (unsigned char *)hdbuf; | |
| 1231 nva[i].valuelen = (size_t)(end - hdbuf); | |
| 1232 nva[i].flags = NGHTTP3_NV_FLAG_NONE; | |
| 1233 | |
| 1234 ++i; | |
| 1235 } | |
| 1236 | |
| 1237 /* :authority must come before non-pseudo header fields */ | |
| 1238 if(authority_idx != 0 && authority_idx != AUTHORITY_DST_IDX) { | |
| 1239 nghttp3_nv authority = nva[authority_idx]; | |
| 1240 for(i = authority_idx; i > AUTHORITY_DST_IDX; --i) { | |
| 1241 nva[i] = nva[i - 1]; | |
| 1242 } | |
| 1243 nva[i] = authority; | |
| 1244 } | |
| 1245 | |
| 1246 /* Warn stream may be rejected if cumulative length of headers is too | |
| 1247 large. */ | |
| 1248 #define MAX_ACC 60000 /* <64KB to account for some overhead */ | |
| 1249 { | |
| 1250 size_t acc = 0; | |
| 1251 for(i = 0; i < nheader; ++i) | |
| 1252 acc += nva[i].namelen + nva[i].valuelen; | |
| 1253 | |
| 1254 if(acc > MAX_ACC) { | |
| 1255 infof(data, "http_request: Warning: The cumulative length of all " | |
| 1256 "headers exceeds %zu bytes and that could cause the " | |
| 1257 "stream to be rejected.\n", MAX_ACC); | |
| 1258 } | |
| 1259 } | |
| 1260 | |
| 1261 switch(data->set.httpreq) { | |
| 1262 case HTTPREQ_POST: | |
| 1263 case HTTPREQ_POST_FORM: | |
| 1264 case HTTPREQ_POST_MIME: | |
| 1265 case HTTPREQ_PUT: { | |
| 1266 nghttp3_data_reader data_reader; | |
| 1267 if(data->state.infilesize != -1) | |
| 1268 stream->upload_left = data->state.infilesize; | |
| 1269 else | |
| 1270 /* data sending without specifying the data amount up front */ | |
| 1271 stream->upload_left = -1; /* unknown, but not zero */ | |
| 1272 | |
| 1273 data_reader.read_data = cb_h3_readfunction; | |
| 1274 | |
| 1275 h3out = calloc(sizeof(struct h3out), 1); | |
| 1276 if(!h3out) { | |
| 1277 result = CURLE_OUT_OF_MEMORY; | |
| 1278 goto fail; | |
| 1279 } | |
| 1280 stream->h3out = h3out; | |
| 1281 | |
| 1282 rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id, | |
| 1283 nva, nheader, &data_reader, | |
| 1284 conn->data); | |
| 1285 if(rc) { | |
| 1286 result = CURLE_SEND_ERROR; | |
| 1287 goto fail; | |
| 1288 } | |
| 1289 break; | |
| 1290 } | |
| 1291 default: | |
| 1292 stream->upload_left = 0; /* nothing left to send */ | |
| 1293 rc = nghttp3_conn_submit_request(qs->h3conn, stream->stream3_id, | |
| 1294 nva, nheader, | |
| 1295 NULL, /* no body! */ | |
| 1296 conn->data); | |
| 1297 if(rc) { | |
| 1298 result = CURLE_SEND_ERROR; | |
| 1299 goto fail; | |
| 1300 } | |
| 1301 break; | |
| 1302 } | |
| 1303 | |
| 1304 Curl_safefree(nva); | |
| 1305 | |
| 1306 infof(data, "Using HTTP/3 Stream ID: %x (easy handle %p)\n", | |
| 1307 stream3_id, (void *)data); | |
| 1308 | |
| 1309 return CURLE_OK; | |
| 1310 | |
| 1311 fail: | |
| 1312 free(nva); | |
| 1313 return result; | |
| 1314 } | |
| 1315 static ssize_t ngh3_stream_send(struct connectdata *conn, | |
| 1316 int sockindex, | |
| 1317 const void *mem, | |
| 1318 size_t len, | |
| 1319 CURLcode *curlcode) | |
| 1320 { | |
| 1321 ssize_t sent; | |
| 1322 struct quicsocket *qs = conn->quic; | |
| 1323 curl_socket_t sockfd = conn->sock[sockindex]; | |
| 1324 struct HTTP *stream = conn->data->req.protop; | |
| 1325 | |
| 1326 if(!stream->h3req) { | |
| 1327 CURLcode result = http_request(conn, mem, len); | |
| 1328 if(result) { | |
| 1329 *curlcode = CURLE_SEND_ERROR; | |
| 1330 return -1; | |
| 1331 } | |
| 1332 sent = len; | |
| 1333 } | |
| 1334 else { | |
| 1335 fprintf(stderr, "ngh3_stream_send() wants to send %zd bytes\n", len); | |
| 1336 if(!stream->upload_len) { | |
| 1337 stream->upload_mem = mem; | |
| 1338 stream->upload_len = len; | |
| 1339 (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id); | |
| 1340 sent = len; | |
| 1341 } | |
| 1342 else { | |
| 1343 *curlcode = CURLE_AGAIN; | |
| 1344 return -1; | |
| 1345 } | |
| 1346 } | |
| 1347 | |
| 1348 if(ng_flush_egress(conn, sockfd, qs)) { | |
| 1349 *curlcode = CURLE_SEND_ERROR; | |
| 1350 return -1; | |
| 1351 } | |
| 1352 | |
| 1353 *curlcode = CURLE_OK; | |
| 1354 return sent; | |
| 1355 } | |
| 1356 | |
| 1357 static void ng_has_connected(struct connectdata *conn, int tempindex) | |
| 1358 { | |
| 1359 conn->recv[FIRSTSOCKET] = ngh3_stream_recv; | |
| 1360 conn->send[FIRSTSOCKET] = ngh3_stream_send; | |
| 1361 conn->handler = &Curl_handler_http3; | |
| 1362 conn->bits.multiplex = TRUE; /* at least potentially multiplexed */ | |
| 1363 conn->httpversion = 30; | |
| 1364 conn->bundle->multiuse = BUNDLE_MULTIPLEX; | |
| 1365 conn->quic = &conn->hequic[tempindex]; | |
| 1366 DEBUGF(infof(conn->data, "ngtcp2 established connection!\n")); | |
| 1367 } | |
| 1368 | |
| 1369 /* | |
| 1370 * There can be multiple connection attempts going on in parallel. | |
| 1371 */ | |
| 1372 CURLcode Curl_quic_is_connected(struct connectdata *conn, | |
| 1373 int sockindex, | |
| 1374 bool *done) | |
| 1375 { | |
| 1376 CURLcode result; | |
| 1377 struct quicsocket *qs = &conn->hequic[sockindex]; | |
| 1378 curl_socket_t sockfd = conn->tempsock[sockindex]; | |
| 1379 | |
| 1380 result = ng_process_ingress(conn, sockfd, qs); | |
| 1381 if(result) | |
| 1382 return result; | |
| 1383 | |
| 1384 result = ng_flush_egress(conn, sockfd, qs); | |
| 1385 if(result) | |
| 1386 return result; | |
| 1387 | |
| 1388 if(ngtcp2_conn_get_handshake_completed(qs->qconn)) { | |
| 1389 *done = TRUE; | |
| 1390 ng_has_connected(conn, sockindex); | |
| 1391 } | |
| 1392 | |
| 1393 return result; | |
| 1394 } | |
| 1395 | |
| 1396 static CURLcode ng_process_ingress(struct connectdata *conn, int sockfd, | |
| 1397 struct quicsocket *qs) | |
| 1398 { | |
| 1399 ssize_t recvd; | |
| 1400 int rv; | |
| 1401 uint8_t buf[65536]; | |
| 1402 size_t bufsize = sizeof(buf); | |
| 1403 struct sockaddr_storage remote_addr; | |
| 1404 socklen_t remote_addrlen; | |
| 1405 ngtcp2_path path; | |
| 1406 ngtcp2_tstamp ts = timestamp(); | |
| 1407 | |
| 1408 for(;;) { | |
| 1409 remote_addrlen = sizeof(remote_addr); | |
| 1410 while((recvd = recvfrom(sockfd, buf, bufsize, MSG_DONTWAIT, | |
| 1411 (struct sockaddr *)&remote_addr, | |
| 1412 &remote_addrlen)) == -1 && | |
| 1413 errno == EINTR) | |
| 1414 ; | |
| 1415 if(recvd == -1) { | |
| 1416 if(errno == EAGAIN || errno == EWOULDBLOCK) | |
| 1417 break; | |
| 1418 | |
| 1419 failf(conn->data, "ngtcp2: recvfrom() unexpectedly returned %d", recvd); | |
| 1420 return CURLE_RECV_ERROR; | |
| 1421 } | |
| 1422 | |
| 1423 ngtcp2_addr_init(&path.local, (uint8_t *)&qs->local_addr, | |
| 1424 qs->local_addrlen, NULL); | |
| 1425 ngtcp2_addr_init(&path.remote, (uint8_t *)&remote_addr, remote_addrlen, | |
| 1426 NULL); | |
| 1427 | |
| 1428 rv = ngtcp2_conn_read_pkt(qs->qconn, &path, buf, recvd, ts); | |
| 1429 if(rv != 0) { | |
| 1430 /* TODO Send CONNECTION_CLOSE if possible */ | |
| 1431 return CURLE_RECV_ERROR; | |
| 1432 } | |
| 1433 } | |
| 1434 | |
| 1435 return CURLE_OK; | |
| 1436 } | |
| 1437 | |
| 1438 static CURLcode ng_flush_egress(struct connectdata *conn, int sockfd, | |
| 1439 struct quicsocket *qs) | |
| 1440 { | |
| 1441 int rv; | |
| 1442 ssize_t sent; | |
| 1443 ssize_t outlen; | |
| 1444 uint8_t out[NGTCP2_MAX_PKTLEN_IPV4]; | |
| 1445 size_t pktlen; | |
| 1446 ngtcp2_path_storage ps; | |
| 1447 ngtcp2_tstamp ts = timestamp(); | |
| 1448 struct sockaddr_storage remote_addr; | |
| 1449 ngtcp2_tstamp expiry; | |
| 1450 ngtcp2_duration timeout; | |
| 1451 int64_t stream_id; | |
| 1452 ssize_t veccnt; | |
| 1453 int fin; | |
| 1454 nghttp3_vec vec[16]; | |
| 1455 ssize_t ndatalen; | |
| 1456 | |
| 1457 switch(qs->local_addr.ss_family) { | |
| 1458 case AF_INET: | |
| 1459 pktlen = NGTCP2_MAX_PKTLEN_IPV4; | |
| 1460 break; | |
| 1461 case AF_INET6: | |
| 1462 pktlen = NGTCP2_MAX_PKTLEN_IPV6; | |
| 1463 break; | |
| 1464 default: | |
| 1465 assert(0); | |
| 1466 } | |
| 1467 | |
| 1468 rv = ngtcp2_conn_handle_expiry(qs->qconn, ts); | |
| 1469 if(rv != 0) { | |
| 1470 failf(conn->data, "ngtcp2_conn_handle_expiry returned error: %s\n", | |
| 1471 ngtcp2_strerror(rv)); | |
| 1472 return CURLE_SEND_ERROR; | |
| 1473 } | |
| 1474 | |
| 1475 ngtcp2_path_storage_zero(&ps); | |
| 1476 | |
| 1477 for(;;) { | |
| 1478 outlen = -1; | |
| 1479 if(qs->h3conn && ngtcp2_conn_get_max_data_left(qs->qconn)) { | |
| 1480 veccnt = nghttp3_conn_writev_stream(qs->h3conn, &stream_id, &fin, vec, | |
| 1481 sizeof(vec) / sizeof(vec[0])); | |
| 1482 if(veccnt < 0) { | |
| 1483 failf(conn->data, "nghttp3_conn_writev_stream returned error: %s\n", | |
| 1484 nghttp3_strerror((int)veccnt)); | |
| 1485 return CURLE_SEND_ERROR; | |
| 1486 } | |
| 1487 else if(veccnt > 0) { | |
| 1488 outlen = | |
| 1489 ngtcp2_conn_writev_stream(qs->qconn, &ps.path, | |
| 1490 out, pktlen, &ndatalen, | |
| 1491 NGTCP2_WRITE_STREAM_FLAG_MORE, | |
| 1492 stream_id, fin, | |
| 1493 (const ngtcp2_vec *)vec, veccnt, ts); | |
| 1494 if(outlen == 0) { | |
| 1495 break; | |
| 1496 } | |
| 1497 if(outlen < 0) { | |
| 1498 if(outlen == NGTCP2_ERR_STREAM_DATA_BLOCKED || | |
| 1499 outlen == NGTCP2_ERR_STREAM_SHUT_WR) { | |
| 1500 rv = nghttp3_conn_block_stream(qs->h3conn, stream_id); | |
| 1501 if(rv != 0) { | |
| 1502 failf(conn->data, | |
| 1503 "nghttp3_conn_block_stream returned error: %s\n", | |
| 1504 nghttp3_strerror(rv)); | |
| 1505 return CURLE_SEND_ERROR; | |
| 1506 } | |
| 1507 continue; | |
| 1508 } | |
| 1509 else if(outlen == NGTCP2_ERR_WRITE_STREAM_MORE) { | |
| 1510 assert(ndatalen > 0); | |
| 1511 rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, | |
| 1512 ndatalen); | |
| 1513 if(rv != 0) { | |
| 1514 failf(conn->data, | |
| 1515 "nghttp3_conn_add_write_offset returned error: %s\n", | |
| 1516 nghttp3_strerror(rv)); | |
| 1517 return CURLE_SEND_ERROR; | |
| 1518 } | |
| 1519 continue; | |
| 1520 } | |
| 1521 else { | |
| 1522 failf(conn->data, "ngtcp2_conn_writev_stream returned error: %s\n", | |
| 1523 ngtcp2_strerror((int)outlen)); | |
| 1524 return CURLE_SEND_ERROR; | |
| 1525 } | |
| 1526 } | |
| 1527 else if(ndatalen > 0) { | |
| 1528 rv = nghttp3_conn_add_write_offset(qs->h3conn, stream_id, ndatalen); | |
| 1529 if(rv != 0) { | |
| 1530 failf(conn->data, | |
| 1531 "nghttp3_conn_add_write_offset returned error: %s\n", | |
| 1532 nghttp3_strerror(rv)); | |
| 1533 return CURLE_SEND_ERROR; | |
| 1534 } | |
| 1535 } | |
| 1536 } | |
| 1537 } | |
| 1538 if(outlen < 0) { | |
| 1539 outlen = ngtcp2_conn_write_pkt(qs->qconn, &ps.path, out, pktlen, ts); | |
| 1540 if(outlen < 0) { | |
| 1541 failf(conn->data, "ngtcp2_conn_write_pkt returned error: %s\n", | |
| 1542 ngtcp2_strerror((int)outlen)); | |
| 1543 return CURLE_SEND_ERROR; | |
| 1544 } | |
| 1545 if(outlen == 0) | |
| 1546 break; | |
| 1547 } | |
| 1548 | |
| 1549 memcpy(&remote_addr, ps.path.remote.addr, ps.path.remote.addrlen); | |
| 1550 while((sent = sendto(sockfd, out, outlen, MSG_DONTWAIT, | |
| 1551 (struct sockaddr *)&remote_addr, | |
| 1552 (socklen_t)ps.path.remote.addrlen)) == -1 && | |
| 1553 errno == EINTR) | |
| 1554 ; | |
| 1555 | |
| 1556 if(sent == -1) { | |
| 1557 if(errno == EAGAIN || errno == EWOULDBLOCK) { | |
| 1558 /* TODO Cache packet */ | |
| 1559 break; | |
| 1560 } | |
| 1561 else { | |
| 1562 failf(conn->data, "sendto() returned %zd (errno %d)\n", sent, | |
| 1563 SOCKERRNO); | |
| 1564 return CURLE_SEND_ERROR; | |
| 1565 } | |
| 1566 } | |
| 1567 } | |
| 1568 | |
| 1569 expiry = ngtcp2_conn_get_expiry(qs->qconn); | |
| 1570 if(expiry != UINT64_MAX) { | |
| 1571 if(expiry <= ts) { | |
| 1572 timeout = NGTCP2_MILLISECONDS; | |
| 1573 } | |
| 1574 else { | |
| 1575 timeout = expiry - ts; | |
| 1576 } | |
| 1577 Curl_expire(conn->data, timeout / NGTCP2_MILLISECONDS, EXPIRE_QUIC); | |
| 1578 } | |
| 1579 | |
| 1580 return CURLE_OK; | |
| 1581 } | |
| 1582 | |
| 1583 /* | |
| 1584 * Called from transfer.c:done_sending when we stop HTTP/3 uploading. | |
| 1585 */ | |
| 1586 CURLcode Curl_quic_done_sending(struct connectdata *conn) | |
| 1587 { | |
| 1588 if(conn->handler == &Curl_handler_http3) { | |
| 1589 /* only for HTTP/3 transfers */ | |
| 1590 struct HTTP *stream = conn->data->req.protop; | |
| 1591 struct quicsocket *qs = conn->quic; | |
| 1592 fprintf(stderr, "!!! Curl_quic_done_sending stream %zu\n", | |
| 1593 stream->stream3_id); | |
| 1594 stream->upload_done = TRUE; | |
| 1595 (void)nghttp3_conn_resume_stream(qs->h3conn, stream->stream3_id); | |
| 1596 } | |
| 1597 | |
| 1598 return CURLE_OK; | |
| 1599 } | |
| 1600 #endif |
