mirror of
https://github.com/curl/curl.git
synced 2026-08-26 22:34:45 +03:00
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:
parent
1c18f8da51
commit
71b7e01610
48 changed files with 6908 additions and 4675 deletions
166
lib/url.c
166
lib/url.c
|
|
@ -452,7 +452,7 @@ CURLcode Curl_close(struct Curl_easy **datap)
|
|||
Curl_resolver_cancel(data);
|
||||
Curl_resolver_cleanup(data->state.async.resolver);
|
||||
|
||||
Curl_http2_cleanup_dependencies(data);
|
||||
Curl_data_priority_cleanup(data);
|
||||
|
||||
/* No longer a dirty share, if it exists */
|
||||
if(data->share) {
|
||||
|
|
@ -640,14 +640,15 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data)
|
|||
set->maxage_conn = 118;
|
||||
set->maxlifetime_conn = 0;
|
||||
set->http09_allowed = FALSE;
|
||||
set->httpwant =
|
||||
#ifdef USE_HTTP2
|
||||
CURL_HTTP_VERSION_2TLS
|
||||
set->httpwant = CURL_HTTP_VERSION_2TLS
|
||||
#else
|
||||
CURL_HTTP_VERSION_1_1
|
||||
set->httpwant = CURL_HTTP_VERSION_1_1
|
||||
#endif
|
||||
;
|
||||
Curl_http2_init_userset(set);
|
||||
#if defined(USE_HTTP2) || defined(USE_HTTP3)
|
||||
memset(&set->priority, 0, sizeof(set->priority));
|
||||
#endif
|
||||
set->quick_exit = 0L;
|
||||
return result;
|
||||
}
|
||||
|
|
@ -877,24 +878,6 @@ void Curl_disconnect(struct Curl_easy *data,
|
|||
conn_free(data, conn);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function should return TRUE if the socket is to be assumed to
|
||||
* be dead. Most commonly this happens when the server has closed the
|
||||
* connection due to inactivity.
|
||||
*/
|
||||
static bool SocketIsDead(curl_socket_t sock)
|
||||
{
|
||||
int sval;
|
||||
bool ret_val = TRUE;
|
||||
|
||||
sval = SOCKET_READABLE(sock, 0);
|
||||
if(sval == 0)
|
||||
/* timeout */
|
||||
ret_val = FALSE;
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
/*
|
||||
* IsMultiplexingPossible()
|
||||
*
|
||||
|
|
@ -1025,8 +1008,7 @@ static bool extract_if_dead(struct connectdata *conn,
|
|||
|
||||
}
|
||||
else {
|
||||
/* Use the general method for determining the death of a connection */
|
||||
dead = SocketIsDead(conn->sock[FIRSTSOCKET]);
|
||||
dead = !Curl_conn_is_alive(data, conn);
|
||||
}
|
||||
|
||||
if(dead) {
|
||||
|
|
@ -1355,8 +1337,10 @@ ConnectionExists(struct Curl_easy *data,
|
|||
/* If multiplexing isn't enabled on the h2 connection and h1 is
|
||||
explicitly requested, handle it: */
|
||||
if((needle->handler->protocol & PROTO_FAMILY_HTTP) &&
|
||||
(check->httpversion >= 20) &&
|
||||
(data->state.httpwant < CURL_HTTP_VERSION_2_0))
|
||||
(((check->httpversion >= 20) &&
|
||||
(data->state.httpwant < CURL_HTTP_VERSION_2_0))
|
||||
|| ((check->httpversion >= 30) &&
|
||||
(data->state.httpwant < CURL_HTTP_VERSION_3))))
|
||||
continue;
|
||||
|
||||
if(get_protocol_family(needle->handler) == PROTO_FAMILY_SSH) {
|
||||
|
|
@ -1478,9 +1462,8 @@ ConnectionExists(struct Curl_easy *data,
|
|||
#ifdef USE_NGHTTP2
|
||||
/* If multiplexed, make sure we don't go over concurrency limit */
|
||||
if(check->bits.multiplex) {
|
||||
/* Multiplexed connections can only be HTTP/2 for now */
|
||||
struct http_conn *httpc = &check->proto.httpc;
|
||||
if(multiplexed >= httpc->settings.max_concurrent_streams) {
|
||||
if(multiplexed >= Curl_conn_get_max_concurrent(data, check,
|
||||
FIRSTSOCKET)) {
|
||||
infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)",
|
||||
multiplexed);
|
||||
continue;
|
||||
|
|
@ -1562,8 +1545,6 @@ static struct connectdata *allocate_conn(struct Curl_easy *data)
|
|||
|
||||
conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
|
||||
conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
|
||||
conn->tempsock[0] = CURL_SOCKET_BAD; /* no file descriptor */
|
||||
conn->tempsock[1] = CURL_SOCKET_BAD; /* no file descriptor */
|
||||
conn->connection_id = -1; /* no ID */
|
||||
conn->port = -1; /* unknown at this point */
|
||||
conn->remote_port = -1; /* unknown at this point */
|
||||
|
|
@ -3356,12 +3337,6 @@ static void reuse_conn(struct Curl_easy *data,
|
|||
struct connectdata *temp,
|
||||
struct connectdata *existing)
|
||||
{
|
||||
/* 'local_ip' and 'local_port' get filled with local's numerical
|
||||
ip address and port number whenever an outgoing connection is
|
||||
**established** from the primary socket to a remote address. */
|
||||
char local_ip[MAX_IPADR_LEN] = "";
|
||||
int local_port = -1;
|
||||
|
||||
/* get the user+password information from the temp struct since it may
|
||||
* be new for this request even when we re-use an existing connection */
|
||||
if(temp->user) {
|
||||
|
|
@ -3409,12 +3384,8 @@ static void reuse_conn(struct Curl_easy *data,
|
|||
existing->hostname_resolve = temp->hostname_resolve;
|
||||
temp->hostname_resolve = NULL;
|
||||
|
||||
/* persist connection info in session handle */
|
||||
if(existing->transport == TRNSPRT_TCP) {
|
||||
Curl_conninfo_local(data, existing->sock[FIRSTSOCKET],
|
||||
local_ip, &local_port);
|
||||
}
|
||||
Curl_persistconninfo(data, existing, local_ip, local_port);
|
||||
/* persist existing connection info in data */
|
||||
Curl_conn_ev_update_info(data, existing);
|
||||
|
||||
conn_reset_all_postponed_data(temp); /* free buffers */
|
||||
|
||||
|
|
@ -3891,6 +3862,13 @@ static CURLcode create_conn(struct Curl_easy *data,
|
|||
* Resolve the address of the server or proxy
|
||||
*************************************************************/
|
||||
result = resolve_server(data, conn, async);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
/* Everything general done, inform filters that they need
|
||||
* to prepare for a data transfer.
|
||||
*/
|
||||
result = Curl_conn_ev_data_setup(data);
|
||||
|
||||
out:
|
||||
return result;
|
||||
|
|
@ -4029,3 +4007,103 @@ CURLcode Curl_init_do(struct Curl_easy *data, struct connectdata *conn)
|
|||
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
#if defined(USE_HTTP2) || defined(USE_HTTP3)
|
||||
|
||||
#ifdef USE_NGHTTP2
|
||||
|
||||
static void priority_remove_child(struct Curl_easy *parent,
|
||||
struct Curl_easy *child)
|
||||
{
|
||||
struct Curl_data_prio_node **pnext = &parent->set.priority.children;
|
||||
struct Curl_data_prio_node *pnode = parent->set.priority.children;
|
||||
|
||||
DEBUGASSERT(child->set.priority.parent == parent);
|
||||
while(pnode && pnode->data != child) {
|
||||
pnext = &pnode->next;
|
||||
pnode = pnode->next;
|
||||
}
|
||||
|
||||
DEBUGASSERT(pnode);
|
||||
if(pnode) {
|
||||
*pnext = pnode->next;
|
||||
free(pnode);
|
||||
}
|
||||
|
||||
child->set.priority.parent = 0;
|
||||
child->set.priority.exclusive = FALSE;
|
||||
}
|
||||
|
||||
CURLcode Curl_data_priority_add_child(struct Curl_easy *parent,
|
||||
struct Curl_easy *child,
|
||||
bool exclusive)
|
||||
{
|
||||
if(child->set.priority.parent) {
|
||||
priority_remove_child(child->set.priority.parent, child);
|
||||
}
|
||||
|
||||
if(parent) {
|
||||
struct Curl_data_prio_node **tail;
|
||||
struct Curl_data_prio_node *pnode;
|
||||
|
||||
pnode = calloc(1, sizeof(*pnode));
|
||||
if(!pnode)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
pnode->data = child;
|
||||
|
||||
if(parent->set.priority.children && exclusive) {
|
||||
/* exclusive: move all existing children underneath the new child */
|
||||
struct Curl_data_prio_node *node = parent->set.priority.children;
|
||||
while(node) {
|
||||
node->data->set.priority.parent = child;
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
tail = &child->set.priority.children;
|
||||
while(*tail)
|
||||
tail = &(*tail)->next;
|
||||
|
||||
DEBUGASSERT(!*tail);
|
||||
*tail = parent->set.priority.children;
|
||||
parent->set.priority.children = 0;
|
||||
}
|
||||
|
||||
tail = &parent->set.priority.children;
|
||||
while(*tail) {
|
||||
(*tail)->data->set.priority.exclusive = FALSE;
|
||||
tail = &(*tail)->next;
|
||||
}
|
||||
|
||||
DEBUGASSERT(!*tail);
|
||||
*tail = pnode;
|
||||
}
|
||||
|
||||
child->set.priority.parent = parent;
|
||||
child->set.priority.exclusive = exclusive;
|
||||
return CURLE_OK;
|
||||
}
|
||||
|
||||
#endif /* USE_NGHTTP2 */
|
||||
|
||||
void Curl_data_priority_cleanup(struct Curl_easy *data)
|
||||
{
|
||||
#ifdef USE_NGHTTP2
|
||||
while(data->set.priority.children) {
|
||||
struct Curl_easy *tmp = data->set.priority.children->data;
|
||||
priority_remove_child(data, tmp);
|
||||
if(data->set.priority.parent)
|
||||
Curl_data_priority_add_child(data->set.priority.parent, tmp, FALSE);
|
||||
}
|
||||
|
||||
if(data->set.priority.parent)
|
||||
priority_remove_child(data->set.priority.parent, data);
|
||||
#endif
|
||||
(void)data;
|
||||
}
|
||||
|
||||
void Curl_data_priority_clear_state(struct Curl_easy *data)
|
||||
{
|
||||
memset(&data->state.priority, 0, sizeof(data->state.priority));
|
||||
}
|
||||
|
||||
#endif /* defined(USE_HTTP2) || defined(USE_HTTP3) */
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue