diff options
author | Brad King <brad.king@kitware.com> | 2023-05-30 13:38:38 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2023-05-30 13:39:43 (GMT) |
commit | a6c9b53273eb3ef8b4d9a0a2a0fc6a4822211b9d (patch) | |
tree | 23bfdb5c7a2254a82571a4acb78b5a25c1641898 /Utilities/cmcurl/lib/vquic/vquic.c | |
parent | 7f1abf62e12a261a2fc91a2d527b5c4a30e25d41 (diff) | |
parent | 80cb6a512119ea6f8f8cf480c78e1e32d494e6ca (diff) | |
download | CMake-a6c9b53273eb3ef8b4d9a0a2a0fc6a4822211b9d.zip CMake-a6c9b53273eb3ef8b4d9a0a2a0fc6a4822211b9d.tar.gz CMake-a6c9b53273eb3ef8b4d9a0a2a0fc6a4822211b9d.tar.bz2 |
Merge branch 'upstream-curl' into update-curl
* upstream-curl:
curl 2023-05-30 (7ab9d437)
Diffstat (limited to 'Utilities/cmcurl/lib/vquic/vquic.c')
-rw-r--r-- | Utilities/cmcurl/lib/vquic/vquic.c | 328 |
1 files changed, 277 insertions, 51 deletions
diff --git a/Utilities/cmcurl/lib/vquic/vquic.c b/Utilities/cmcurl/lib/vquic/vquic.c index bbdeabd..f850029 100644 --- a/Utilities/cmcurl/lib/vquic/vquic.c +++ b/Utilities/cmcurl/lib/vquic/vquic.c @@ -22,12 +22,25 @@ * ***************************************************************************/ +/* WIP, experimental: use recvmmsg() on linux + * we have no configure check, yet + * and also it is only available for _GNU_SOURCE, which + * we do not use otherwise. +#define HAVE_SENDMMSG + */ +#if defined(HAVE_SENDMMSG) +#define _GNU_SOURCE +#include <sys/socket.h> +#undef _GNU_SOURCE +#endif + #include "curl_setup.h" #ifdef HAVE_FCNTL_H #include <fcntl.h> #endif #include "urldata.h" +#include "bufq.h" #include "dynbuf.h" #include "cfilters.h" #include "curl_log.h" @@ -51,9 +64,13 @@ #define QLOGMODE O_WRONLY|O_CREAT #endif +#define NW_CHUNK_SIZE (64 * 1024) +#define NW_SEND_CHUNKS 2 + + void Curl_quic_ver(char *p, size_t len) { -#ifdef USE_NGTCP2 +#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) Curl_ngtcp2_ver(p, len); #elif defined(USE_QUICHE) Curl_quiche_ver(p, len); @@ -62,17 +79,10 @@ void Curl_quic_ver(char *p, size_t len) #endif } -CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx, size_t pktbuflen) +CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx) { - qctx->num_blocked_pkt = 0; - qctx->num_blocked_pkt_sent = 0; - memset(&qctx->blocked_pkt, 0, sizeof(qctx->blocked_pkt)); - - qctx->pktbuflen = pktbuflen; - qctx->pktbuf = malloc(qctx->pktbuflen); - if(!qctx->pktbuf) - return CURLE_OUT_OF_MEMORY; - + Curl_bufq_init2(&qctx->sendbuf, NW_CHUNK_SIZE, NW_SEND_CHUNKS, + BUFQ_OPT_SOFT_LIMIT); #if defined(__linux__) && defined(UDP_SEGMENT) && defined(HAVE_SENDMSG) qctx->no_gso = FALSE; #else @@ -84,8 +94,7 @@ CURLcode vquic_ctx_init(struct cf_quic_ctx *qctx, size_t pktbuflen) void vquic_ctx_free(struct cf_quic_ctx *qctx) { - free(qctx->pktbuf); - qctx->pktbuf = NULL; + Curl_bufq_free(&qctx->sendbuf); } static CURLcode send_packet_no_gso(struct Curl_cfilter *cf, @@ -215,11 +224,11 @@ static CURLcode send_packet_no_gso(struct Curl_cfilter *cf, return CURLE_OK; } -CURLcode vquic_send_packet(struct Curl_cfilter *cf, - struct Curl_easy *data, - struct cf_quic_ctx *qctx, - const uint8_t *pkt, size_t pktlen, size_t gsolen, - size_t *psent) +static CURLcode vquic_send_packets(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + const uint8_t *pkt, size_t pktlen, + size_t gsolen, size_t *psent) { if(qctx->no_gso && pktlen > gsolen) { return send_packet_no_gso(cf, data, qctx, pkt, pktlen, gsolen, psent); @@ -228,53 +237,270 @@ CURLcode vquic_send_packet(struct Curl_cfilter *cf, return do_sendmsg(cf, data, qctx, pkt, pktlen, gsolen, psent); } +CURLcode vquic_flush(struct Curl_cfilter *cf, struct Curl_easy *data, + struct cf_quic_ctx *qctx) +{ + const unsigned char *buf; + size_t blen, sent; + CURLcode result; + size_t gsolen; + + while(Curl_bufq_peek(&qctx->sendbuf, &buf, &blen)) { + gsolen = qctx->gsolen; + if(qctx->split_len) { + gsolen = qctx->split_gsolen; + if(blen > qctx->split_len) + blen = qctx->split_len; + } + + DEBUGF(LOG_CF(data, cf, "vquic_send(len=%zu, gso=%zu)", + blen, gsolen)); + result = vquic_send_packets(cf, data, qctx, buf, blen, gsolen, &sent); + DEBUGF(LOG_CF(data, cf, "vquic_send(len=%zu, gso=%zu) -> %d, sent=%zu", + blen, gsolen, result, sent)); + if(result) { + if(result == CURLE_AGAIN) { + Curl_bufq_skip(&qctx->sendbuf, sent); + if(qctx->split_len) + qctx->split_len -= sent; + } + return result; + } + Curl_bufq_skip(&qctx->sendbuf, sent); + if(qctx->split_len) + qctx->split_len -= sent; + } + return CURLE_OK; +} + +CURLcode vquic_send(struct Curl_cfilter *cf, struct Curl_easy *data, + struct cf_quic_ctx *qctx, size_t gsolen) +{ + qctx->gsolen = gsolen; + return vquic_flush(cf, data, qctx); +} +CURLcode vquic_send_tail_split(struct Curl_cfilter *cf, struct Curl_easy *data, + struct cf_quic_ctx *qctx, size_t gsolen, + size_t tail_len, size_t tail_gsolen) +{ + DEBUGASSERT(Curl_bufq_len(&qctx->sendbuf) > tail_len); + qctx->split_len = Curl_bufq_len(&qctx->sendbuf) - tail_len; + qctx->split_gsolen = gsolen; + qctx->gsolen = tail_gsolen; + DEBUGF(LOG_CF(data, cf, "vquic_send_tail_split: [%zu gso=%zu][%zu gso=%zu]", + qctx->split_len, qctx->split_gsolen, + tail_len, qctx->gsolen)); + return vquic_flush(cf, data, qctx); +} -void vquic_push_blocked_pkt(struct Curl_cfilter *cf, - struct cf_quic_ctx *qctx, - const uint8_t *pkt, size_t pktlen, size_t gsolen) +#ifdef HAVE_SENDMMSG +static CURLcode recvmmsg_packets(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + size_t max_pkts, + vquic_recv_pkt_cb *recv_cb, void *userp) { - struct vquic_blocked_pkt *blkpkt; +#define MMSG_NUM 64 + struct iovec msg_iov[MMSG_NUM]; + struct mmsghdr mmsg[MMSG_NUM]; + uint8_t bufs[MMSG_NUM][2*1024]; + struct sockaddr_storage remote_addr[MMSG_NUM]; + size_t total_nread, pkts; + int mcount, i, n; + CURLcode result = CURLE_OK; + + DEBUGASSERT(max_pkts > 0); + pkts = 0; + total_nread = 0; + while(pkts < max_pkts) { + n = (int)CURLMIN(MMSG_NUM, max_pkts); + memset(&mmsg, 0, sizeof(mmsg)); + for(i = 0; i < n; ++i) { + msg_iov[i].iov_base = bufs[i]; + msg_iov[i].iov_len = (int)sizeof(bufs[i]); + mmsg[i].msg_hdr.msg_iov = &msg_iov[i]; + mmsg[i].msg_hdr.msg_iovlen = 1; + mmsg[i].msg_hdr.msg_name = &remote_addr[i]; + mmsg[i].msg_hdr.msg_namelen = sizeof(remote_addr[i]); + } - (void)cf; - assert(qctx->num_blocked_pkt < - sizeof(qctx->blocked_pkt) / sizeof(qctx->blocked_pkt[0])); + while((mcount = recvmmsg(qctx->sockfd, mmsg, n, 0, NULL)) == -1 && + SOCKERRNO == EINTR) + ; + if(mcount == -1) { + if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { + DEBUGF(LOG_CF(data, cf, "ingress, recvmmsg -> EAGAIN")); + goto out; + } + if(!cf->connected && SOCKERRNO == ECONNREFUSED) { + const char *r_ip; + int r_port; + Curl_cf_socket_peek(cf->next, data, NULL, NULL, + &r_ip, &r_port, NULL, NULL); + failf(data, "QUIC: connection to %s port %u refused", + r_ip, r_port); + result = CURLE_COULDNT_CONNECT; + goto out; + } + failf(data, "QUIC: recvmsg() unexpectedly returned %d (errno=%d)", + mcount, SOCKERRNO); + result = CURLE_RECV_ERROR; + goto out; + } - blkpkt = &qctx->blocked_pkt[qctx->num_blocked_pkt++]; + DEBUGF(LOG_CF(data, cf, "recvmmsg() -> %d packets", mcount)); + pkts += mcount; + for(i = 0; i < mcount; ++i) { + total_nread += mmsg[i].msg_len; + result = recv_cb(bufs[i], mmsg[i].msg_len, + mmsg[i].msg_hdr.msg_name, mmsg[i].msg_hdr.msg_namelen, + 0, userp); + if(result) + goto out; + } + } - blkpkt->pkt = pkt; - blkpkt->pktlen = pktlen; - blkpkt->gsolen = gsolen; +out: + DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zd bytes -> %d", + pkts, total_nread, result)); + return result; } -CURLcode vquic_send_blocked_pkt(struct Curl_cfilter *cf, +#elif defined(HAVE_SENDMSG) +static CURLcode recvmsg_packets(struct Curl_cfilter *cf, struct Curl_easy *data, - struct cf_quic_ctx *qctx) + struct cf_quic_ctx *qctx, + size_t max_pkts, + vquic_recv_pkt_cb *recv_cb, void *userp) { - size_t sent; - CURLcode curlcode; - struct vquic_blocked_pkt *blkpkt; + struct iovec msg_iov; + struct msghdr msg; + uint8_t buf[64*1024]; + struct sockaddr_storage remote_addr; + size_t total_nread, pkts; + ssize_t nread; + CURLcode result = CURLE_OK; - (void)cf; - for(; qctx->num_blocked_pkt_sent < qctx->num_blocked_pkt; - ++qctx->num_blocked_pkt_sent) { - blkpkt = &qctx->blocked_pkt[qctx->num_blocked_pkt_sent]; - curlcode = vquic_send_packet(cf, data, qctx, blkpkt->pkt, - blkpkt->pktlen, blkpkt->gsolen, &sent); - - if(curlcode) { - if(curlcode == CURLE_AGAIN) { - blkpkt->pkt += sent; - blkpkt->pktlen -= sent; + msg_iov.iov_base = buf; + msg_iov.iov_len = (int)sizeof(buf); + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = &msg_iov; + msg.msg_iovlen = 1; + + DEBUGASSERT(max_pkts > 0); + for(pkts = 0, total_nread = 0; pkts < max_pkts;) { + msg.msg_name = &remote_addr; + msg.msg_namelen = sizeof(remote_addr); + while((nread = recvmsg(qctx->sockfd, &msg, 0)) == -1 && + SOCKERRNO == EINTR) + ; + if(nread == -1) { + if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { + goto out; } - return curlcode; + if(!cf->connected && SOCKERRNO == ECONNREFUSED) { + const char *r_ip; + int r_port; + Curl_cf_socket_peek(cf->next, data, NULL, NULL, + &r_ip, &r_port, NULL, NULL); + failf(data, "QUIC: connection to %s port %u refused", + r_ip, r_port); + result = CURLE_COULDNT_CONNECT; + goto out; + } + failf(data, "QUIC: recvmsg() unexpectedly returned %zd (errno=%d)", + nread, SOCKERRNO); + result = CURLE_RECV_ERROR; + goto out; + } + + ++pkts; + total_nread += (size_t)nread; + result = recv_cb(buf, (size_t)nread, msg.msg_name, msg.msg_namelen, + 0, userp); + if(result) + goto out; + } + +out: + DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zd bytes -> %d", + pkts, total_nread, result)); + return result; +} + +#else /* HAVE_SENDMMSG || HAVE_SENDMSG */ +static CURLcode recvfrom_packets(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + size_t max_pkts, + vquic_recv_pkt_cb *recv_cb, void *userp) +{ + uint8_t buf[64*1024]; + int bufsize = (int)sizeof(buf); + struct sockaddr_storage remote_addr; + socklen_t remote_addrlen = sizeof(remote_addr); + size_t total_nread, pkts; + ssize_t nread; + CURLcode result = CURLE_OK; + + DEBUGASSERT(max_pkts > 0); + for(pkts = 0, total_nread = 0; pkts < max_pkts;) { + while((nread = recvfrom(qctx->sockfd, (char *)buf, bufsize, 0, + (struct sockaddr *)&remote_addr, + &remote_addrlen)) == -1 && + SOCKERRNO == EINTR) + ; + if(nread == -1) { + if(SOCKERRNO == EAGAIN || SOCKERRNO == EWOULDBLOCK) { + DEBUGF(LOG_CF(data, cf, "ingress, recvfrom -> EAGAIN")); + goto out; + } + if(!cf->connected && SOCKERRNO == ECONNREFUSED) { + const char *r_ip; + int r_port; + Curl_cf_socket_peek(cf->next, data, NULL, NULL, + &r_ip, &r_port, NULL, NULL); + failf(data, "QUIC: connection to %s port %u refused", + r_ip, r_port); + result = CURLE_COULDNT_CONNECT; + goto out; + } + failf(data, "QUIC: recvfrom() unexpectedly returned %zd (errno=%d)", + nread, SOCKERRNO); + result = CURLE_RECV_ERROR; + goto out; } + + ++pkts; + total_nread += (size_t)nread; + result = recv_cb(buf, (size_t)nread, &remote_addr, remote_addrlen, + 0, userp); + if(result) + goto out; } - qctx->num_blocked_pkt = 0; - qctx->num_blocked_pkt_sent = 0; +out: + DEBUGF(LOG_CF(data, cf, "recvd %zu packets with %zd bytes -> %d", + pkts, total_nread, result)); + return result; +} +#endif /* !HAVE_SENDMMSG && !HAVE_SENDMSG */ - return CURLE_OK; +CURLcode vquic_recv_packets(struct Curl_cfilter *cf, + struct Curl_easy *data, + struct cf_quic_ctx *qctx, + size_t max_pkts, + vquic_recv_pkt_cb *recv_cb, void *userp) +{ +#if defined(HAVE_SENDMMSG) + return recvmmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); +#elif defined(HAVE_SENDMSG) + return recvmsg_packets(cf, data, qctx, max_pkts, recv_cb, userp); +#else + return recvfrom_packets(cf, data, qctx, max_pkts, recv_cb, userp); +#endif } /* @@ -330,7 +556,7 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf, { (void)transport; DEBUGASSERT(transport == TRNSPRT_QUIC); -#ifdef USE_NGTCP2 +#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) return Curl_cf_ngtcp2_create(pcf, data, conn, ai); #elif defined(USE_QUICHE) return Curl_cf_quiche_create(pcf, data, conn, ai); @@ -349,7 +575,7 @@ bool Curl_conn_is_http3(const struct Curl_easy *data, const struct connectdata *conn, int sockindex) { -#ifdef USE_NGTCP2 +#if defined(USE_NGTCP2) && defined(USE_NGHTTP3) return Curl_conn_is_ngtcp2(data, conn, sockindex); #elif defined(USE_QUICHE) return Curl_conn_is_quiche(data, conn, sockindex); |