lib: connect/h2/h3 refactor

Refactoring of connection setup and happy eyeballing. Move
nghttp2. ngtcp2, quiche and msh3 into connection filters.

 - eyeballing cfilter that uses sub-filters for performing parallel connects
 - socket cfilter for all transport types, including QUIC
 - QUIC implementations in cfilter, can now participate in eyeballing
 - connection setup is more dynamic in order to adapt to what filter did
   really connect.  Relevant to see if a SSL filter needs to be added or
   if SSL has already been provided
 - HTTP/3 test cases similar to HTTP/2
 - multiuse of parallel transfers for HTTP/3, tested for ngtcp2 and quiche

 - Fix for data attach/detach in VTLS filters that could lead to crashes
   during parallel transfers.
 - Eliminating setup() methods in cfilters, no longer needed.
 - Improving Curl_conn_is_alive() to replace Curl_connalive() and
   integrated ssl alive checks into cfilter.
 - Adding CF_CNTRL_CONN_INFO_UPDATE to tell filters to update
   connection into and persist it at the easy handle.

 - Several more cfilter related cleanups and moves:
   - stream_weigth and dependency info is now wrapped in struct
     Curl_data_priority
   - Curl_data_priority members depend is available in HTTP2|HTTP3
   - Curl_data_priority members depend on NGHTTP2 support
   - handling init/reset/cleanup of priority part of url.c
   - data->state.priority same struct, but shallow copy for compares only

 - PROTOPT_STREAM has been removed
   - Curl_conn_is_mulitplex() now available to check on capability

 - Adding query method to connection filters.
   - ngtcp2+quiche: implementing query for max concurrent transfers.

 - Adding is_alive and keep_alive cfilter methods. Adding DATA_SETUP event.
   - setting keepalive timestamp on connect
   - DATA_SETUP is called after the connection has been completely
     setup (but may not connected yet) to allow filters to initialize
     data members they use.

 - there is no socket to be had with msh3, it is unclear how select
   shall work

 - manual test via "curl --http3 https://curl.se" fail with "empty
   reply from server".

 - Various socket/conn related cleanups:
   - Curl_socket is now Curl_socket_open and in cf-socket.c
   - Curl_closesocket is now Curl_socket_close and in cf-socket.c
   - Curl_ssl_use has been replaced with Cur_conn_is_ssl
   - Curl_conn_tcp_accepted_set has been split into
     Curl_conn_tcp_listen_set and Curl_conn_tcp_accepted_set
     with a clearer purpose

Closes #10141
This commit is contained in:
Stefan Eissing 2022-12-30 09:14:55 +01:00 committed by Daniel Stenberg
parent 1c18f8da51
commit 71b7e01610
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
48 changed files with 6908 additions and 4675 deletions

View file

@ -62,6 +62,7 @@
#include "cookie.h"
#include "vauth/vauth.h"
#include "vtls/vtls.h"
#include "vquic/vquic.h"
#include "http_digest.h"
#include "http_ntlm.h"
#include "curl_ntlm_wb.h"
@ -241,10 +242,7 @@ static CURLcode h3_setup_conn(struct Curl_easy *data,
}
#endif
DEBUGF(infof(data, "HTTP/3 direct conn setup(conn #%ld, index=%d)",
conn->connection_id, FIRSTSOCKET));
return Curl_conn_socket_set(data, conn, FIRSTSOCKET);
return CURLE_OK;
#else /* ENABLE_QUIC */
(void)conn;
(void)data;
@ -275,12 +273,6 @@ static CURLcode http_setup_conn(struct Curl_easy *data,
if(conn->transport == TRNSPRT_QUIC) {
return h3_setup_conn(data, conn);
}
else {
if(!CONN_INUSE(conn))
/* if not already multi-using, setup connection details */
Curl_http2_setup_conn(conn);
Curl_http2_setup_req(data);
}
return CURLE_OK;
}
@ -1610,8 +1602,6 @@ CURLcode Curl_http_done(struct Curl_easy *data,
return CURLE_OK;
Curl_dyn_free(&http->send_buffer);
Curl_http2_done(data, premature);
Curl_quic_done(data, premature);
Curl_mime_cleanpart(&http->form);
Curl_dyn_reset(&data->state.headerb);
Curl_hyper_done(data);
@ -1664,17 +1654,10 @@ bool Curl_use_http_1_1plus(const struct Curl_easy *data,
static const char *get_http_string(const struct Curl_easy *data,
const struct connectdata *conn)
{
#ifdef ENABLE_QUIC
if((data->state.httpwant == CURL_HTTP_VERSION_3) ||
(conn->httpversion == 30))
if(Curl_conn_is_http3(data, conn, FIRSTSOCKET))
return "3";
#endif
#ifdef USE_NGHTTP2
if(conn->proto.httpc.h2)
if(Curl_conn_is_http2(data, conn, FIRSTSOCKET))
return "2";
#endif
if(Curl_use_http_1_1plus(data, conn))
return "1.1";
@ -2561,7 +2544,7 @@ CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
/* In HTTP2, we send request body in DATA frame regardless of
its size. */
if(conn->httpversion != 20 &&
if(conn->httpversion < 20 &&
!data->state.expect100header &&
(http->postsize < MAX_INITIAL_POST_SIZE)) {
/* if we don't use expect: 100 AND
@ -3021,50 +3004,35 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
the rest of the request in the PERFORM phase. */
*done = TRUE;
if(conn->transport != TRNSPRT_QUIC) {
if(conn->httpversion < 20) { /* unless the connection is re-used and
already http2 */
switch(conn->alpn) {
case CURL_HTTP_VERSION_2:
conn->httpversion = 20; /* we know we're on HTTP/2 now */
result = Curl_http2_switched(data, NULL, 0);
if(result)
return result;
break;
case CURL_HTTP_VERSION_1_1:
/* continue with HTTP/1.1 when explicitly requested */
break;
default:
/* Check if user wants to use HTTP/2 with clear TCP */
#ifdef USE_NGHTTP2
if(data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) {
#ifndef CURL_DISABLE_PROXY
if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
/* We don't support HTTP/2 proxies yet. Also it's debatable
whether or not this setting should apply to HTTP/2 proxies. */
infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
break;
}
#endif
DEBUGF(infof(data, "HTTP/2 over clean TCP"));
conn->httpversion = 20;
result = Curl_http2_switched(data, NULL, 0);
if(result)
return result;
}
#endif
break;
}
}
else {
/* prepare for an http2 request */
result = Curl_http2_setup(data, conn);
if(Curl_conn_is_http3(data, conn, FIRSTSOCKET)
|| Curl_conn_is_http2(data, conn, FIRSTSOCKET)
|| conn->httpversion == 20 /* like to get rid of this */) {
/* all fine, we are set */
}
else { /* undecided */
switch(conn->alpn) {
case CURL_HTTP_VERSION_2:
result = Curl_http2_switch(data, conn, FIRSTSOCKET, NULL, 0);
if(result)
return result;
break;
case CURL_HTTP_VERSION_1_1:
/* continue with HTTP/1.1 when explicitly requested */
break;
default:
/* Check if user wants to use HTTP/2 with clear TCP */
if(Curl_http2_may_switch(data, conn, FIRSTSOCKET)) {
DEBUGF(infof(data, "HTTP/2 over clean TCP"));
result = Curl_http2_switch(data, conn, FIRSTSOCKET, NULL, 0);
if(result)
return result;
}
break;
}
}
http = data->req.p.http;
DEBUGASSERT(http);
@ -3224,7 +3192,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
}
if(!(conn->handler->flags&PROTOPT_SSL) &&
conn->httpversion != 20 &&
conn->httpversion < 20 &&
(data->state.httpwant == CURL_HTTP_VERSION_2)) {
/* append HTTP2 upgrade magic stuff to the HTTP request if it isn't done
over SSL */
@ -3282,7 +3250,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
}
}
if((conn->httpversion == 20) && data->req.upload_chunky)
if((conn->httpversion >= 20) && data->req.upload_chunky)
/* upload_chunky was set above to set up the request in a chunky fashion,
but is disabled here again to avoid that the chunked encoded version is
actually used when sending the request body over h2 */
@ -3669,7 +3637,8 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
#endif
)) {
/* the ALPN of the current request */
enum alpnid id = (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1;
enum alpnid id = (conn->httpversion == 30)? ALPN_h3 :
(conn->httpversion == 20) ? ALPN_h2 : ALPN_h1;
result = Curl_altsvc_parse(data, data->asi,
headp + strlen("Alt-Svc:"),
id, conn->host.name,
@ -3963,7 +3932,8 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
/* switch to http2 now. The bytes after response headers
are also processed here, otherwise they are lost. */
result = Curl_http2_switched(data, k->str, *nread);
result = Curl_http2_switch(data, conn, FIRSTSOCKET,
k->str, *nread);
if(result)
return result;
*nread = 0;
@ -4191,11 +4161,8 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
stream. In order to do this, we keep reading until we
close the stream. */
if(0 == k->maxdownload
#if defined(USE_NGHTTP2)
&& !((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
conn->httpversion == 20)
#endif
)
&& !Curl_conn_is_http2(data, conn, FIRSTSOCKET)
&& !Curl_conn_is_http3(data, conn, FIRSTSOCKET))
*stop_reading = TRUE;
if(*stop_reading) {