diff options
Diffstat (limited to 'libs/libcurl/src/url.c')
-rw-r--r-- | libs/libcurl/src/url.c | 757 |
1 files changed, 464 insertions, 293 deletions
diff --git a/libs/libcurl/src/url.c b/libs/libcurl/src/url.c index dcd4b25d38..0e462be922 100644 --- a/libs/libcurl/src/url.c +++ b/libs/libcurl/src/url.c @@ -83,8 +83,9 @@ #include "multiif.h"
#include "easyif.h"
#include "speedcheck.h"
-#include "warnless.h"
+#include "curlx/warnless.h"
#include "getinfo.h"
+#include "pop3.h"
#include "urlapi-int.h"
#include "system_win32.h"
#include "hsts.h"
@@ -117,9 +118,9 @@ #include "strdup.h"
#include "setopt.h"
#include "altsvc.h"
-#include "dynbuf.h"
+#include "curlx/dynbuf.h"
#include "headers.h"
-#include "strparse.h"
+#include "curlx/strparse.h"
/* The last 3 #include files should be in this order */
#include "curl_printf.h"
#include "curl_memory.h"
@@ -271,7 +272,7 @@ CURLcode Curl_close(struct Curl_easy **datap) data->state.referer = NULL;
up_free(data);
- Curl_dyn_free(&data->state.headerb);
+ curlx_dyn_free(&data->state.headerb);
Curl_flush_cookies(data, TRUE);
#ifndef CURL_DISABLE_ALTSVC
Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]);
@@ -290,9 +291,10 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_safefree(data->info.contenttype);
Curl_safefree(data->info.wouldredirect);
- /* this destroys the channel and we cannot use it anymore after this */
- Curl_resolver_cancel(data);
- Curl_resolver_cleanup(data->state.async.resolver);
+ /* release any resolve information this transfer kept */
+ Curl_async_destroy(data);
+ Curl_resolv_unlink(data, &data->state.dns[0]); /* done with this */
+ Curl_resolv_unlink(data, &data->state.dns[1]);
data_priority_cleanup(data);
@@ -303,6 +305,7 @@ CURLcode Curl_close(struct Curl_easy **datap) Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
}
+ Curl_hash_destroy(&data->meta_hash);
#ifndef CURL_DISABLE_PROXY
Curl_safefree(data->state.aptr.proxyuserpwd);
#endif
@@ -392,9 +395,6 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) set->socks5auth = CURLAUTH_BASIC | CURLAUTH_GSSAPI;
#endif
- /* make libcurl quiet by default: */
- set->hide_progress = TRUE; /* CURLOPT_NOPROGRESS changes these */
-
Curl_mime_initpart(&set->mimepost);
Curl_ssl_easy_config_init(data);
@@ -481,9 +481,25 @@ CURLcode Curl_init_userdefined(struct Curl_easy *data) memset(&set->priority, 0, sizeof(set->priority));
#endif
set->quick_exit = 0L;
+#ifndef CURL_DISABLE_WEBSOCKETS
+ set->ws_raw_mode = FALSE;
+ set->ws_no_auto_pong = FALSE;
+#endif
+
return result;
}
+/* easy->meta_hash destructor. Should never be called as elements
+ * MUST be added with their own destructor */
+static void easy_meta_freeentry(void *p)
+{
+ (void)p;
+ /* Will always be FALSE. Cannot use a 0 assert here since compilers
+ * are not in agreement if they then want a NORETURN attribute or
+ * not. *sigh* */
+ DEBUGASSERT(p == NULL);
+}
+
/**
* Curl_open()
*
@@ -506,8 +522,19 @@ CURLcode Curl_open(struct Curl_easy **curl) }
data->magic = CURLEASY_MAGIC_NUMBER;
+ /* most recent connection is not yet defined */
+ data->state.lastconnect_id = -1;
+ data->state.recent_conn_id = -1;
+ /* and not assigned an id yet */
+ data->id = -1;
+ data->mid = UINT_MAX;
+ data->master_mid = UINT_MAX;
+ data->progress.hide = TRUE;
+ data->state.current_speed = -1; /* init to negative == impossible */
- Curl_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER);
+ Curl_hash_init(&data->meta_hash, 23,
+ Curl_hash_str, curlx_str_key_compare, easy_meta_freeentry);
+ curlx_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER);
Curl_req_init(&data->req);
Curl_initinfo(data);
#ifndef CURL_DISABLE_HTTP
@@ -515,35 +542,14 @@ CURLcode Curl_open(struct Curl_easy **curl) #endif
Curl_netrc_init(&data->state.netrc);
- result = Curl_resolver_init(data, &data->state.async.resolver);
- if(result) {
- DEBUGF(fprintf(stderr, "Error: resolver_init failed\n"));
- goto out;
- }
-
result = Curl_init_userdefined(data);
- if(result)
- goto out;
-
- /* most recent connection is not yet defined */
- data->state.lastconnect_id = -1;
- data->state.recent_conn_id = -1;
- /* and not assigned an id yet */
- data->id = -1;
- data->mid = -1;
-#ifndef CURL_DISABLE_DOH
- data->set.dohfor_mid = -1;
-#endif
-
- data->progress.flags |= PGRS_HIDE;
- data->state.current_speed = -1; /* init to negative == impossible */
-out:
if(result) {
- Curl_resolver_cleanup(data->state.async.resolver);
- Curl_dyn_free(&data->state.headerb);
+ curlx_dyn_free(&data->state.headerb);
Curl_freeset(data);
Curl_req_free(&data->req, data);
+ Curl_hash_destroy(&data->meta_hash);
+ data->magic = 0;
free(data);
data = NULL;
}
@@ -595,6 +601,8 @@ void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn) Curl_safefree(conn->unix_domain_socket);
#endif
Curl_safefree(conn->destination);
+ Curl_uint_spbset_destroy(&conn->xfers_attached);
+ Curl_hash_destroy(&conn->meta_hash);
free(conn); /* free all the connection oriented data */
}
@@ -672,7 +680,7 @@ static bool conn_maxage(struct Curl_easy *data, {
timediff_t idletime, lifetime;
- idletime = Curl_timediff(now, conn->lastused);
+ idletime = curlx_timediff(now, conn->lastused);
idletime /= 1000; /* integer seconds is fine */
if(idletime > data->set.maxage_conn) {
@@ -681,7 +689,7 @@ static bool conn_maxage(struct Curl_easy *data, return TRUE;
}
- lifetime = Curl_timediff(now, conn->created);
+ lifetime = curlx_timediff(now, conn->created);
lifetime /= 1000; /* integer seconds is fine */
if(data->set.maxlifetime_conn && lifetime > data->set.maxlifetime_conn) {
@@ -709,7 +717,7 @@ bool Curl_conn_seems_dead(struct connectdata *conn, bool dead;
struct curltime now;
if(!pnow) {
- now = Curl_now();
+ now = curlx_now();
pnow = &now;
}
@@ -767,7 +775,7 @@ CURLcode Curl_conn_upkeep(struct Curl_easy *data, struct curltime *now)
{
CURLcode result = CURLE_OK;
- if(Curl_timediff(*now, conn->keepalive) <= data->set.upkeep_interval_ms)
+ if(curlx_timediff(*now, conn->keepalive) <= data->set.upkeep_interval_ms)
return result;
/* briefly attach for action */
@@ -793,8 +801,12 @@ CURLcode Curl_conn_upkeep(struct Curl_easy *data, static bool ssh_config_matches(struct connectdata *one,
struct connectdata *two)
{
- return Curl_safecmp(one->proto.sshc.rsa, two->proto.sshc.rsa) &&
- Curl_safecmp(one->proto.sshc.rsa_pub, two->proto.sshc.rsa_pub);
+ struct ssh_conn *sshc1, *sshc2;
+
+ sshc1 = Curl_conn_meta_get(one, CURL_META_SSH_CONN);
+ sshc2 = Curl_conn_meta_get(two, CURL_META_SSH_CONN);
+ return (sshc1 && sshc2 && Curl_safecmp(sshc1->rsa, sshc2->rsa) &&
+ Curl_safecmp(sshc1->rsa_pub, sshc2->rsa_pub));
}
#endif
@@ -813,25 +825,19 @@ struct url_conn_match { BIT(seen_multiplex_conn);
};
-static bool url_match_conn(struct connectdata *conn, void *userdata)
+static bool url_match_connect_config(struct connectdata *conn,
+ struct url_conn_match *m)
{
- struct url_conn_match *match = userdata;
- struct Curl_easy *data = match->data;
- struct connectdata *needle = match->needle;
-
- /* Check if `conn` can be used for transfer `data` */
-
+ /* connect-only or to-be-closed connections will not be reused */
if(conn->connect_only || conn->bits.close)
- /* connect-only or to-be-closed connections will not be reused */
return FALSE;
- if(data->set.ipver != CURL_IPRESOLVE_WHATEVER
- && data->set.ipver != conn->ip_version) {
- /* skip because the connection is not via the requested IP version */
+ /* ip_version must match */
+ if(m->data->set.ipver != CURL_IPRESOLVE_WHATEVER
+ && m->data->set.ipver != conn->ip_version)
return FALSE;
- }
- if(needle->localdev || needle->localport) {
+ if(m->needle->localdev || m->needle->localport) {
/* If we are bound to a specific local end (IP+port), we must not reuse a
random other one, although if we did not ask for a particular one we
can reuse one that was bound.
@@ -843,74 +849,115 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) likely also reuse the exact same binding parameters and missing out a
few edge cases should not hurt anyone much.
*/
- if((conn->localport != needle->localport) ||
- (conn->localportrange != needle->localportrange) ||
- (needle->localdev &&
- (!conn->localdev || strcmp(conn->localdev, needle->localdev))))
+ if((conn->localport != m->needle->localport) ||
+ (conn->localportrange != m->needle->localportrange) ||
+ (m->needle->localdev &&
+ (!conn->localdev || strcmp(conn->localdev, m->needle->localdev))))
return FALSE;
}
- if(needle->bits.conn_to_host != conn->bits.conn_to_host)
+ if(m->needle->bits.conn_to_host != conn->bits.conn_to_host)
/* do not mix connections that use the "connect to host" feature and
* connections that do not use this feature */
return FALSE;
- if(needle->bits.conn_to_port != conn->bits.conn_to_port)
+ if(m->needle->bits.conn_to_port != conn->bits.conn_to_port)
/* do not mix connections that use the "connect to port" feature and
* connections that do not use this feature */
return FALSE;
+ /* Does `conn` use the correct protocol? */
+#ifdef USE_UNIX_SOCKETS
+ if(m->needle->unix_domain_socket) {
+ if(!conn->unix_domain_socket)
+ return FALSE;
+ if(strcmp(m->needle->unix_domain_socket, conn->unix_domain_socket))
+ return FALSE;
+ if(m->needle->bits.abstract_unix_socket != conn->bits.abstract_unix_socket)
+ return FALSE;
+ }
+ else if(conn->unix_domain_socket)
+ return FALSE;
+#endif
+
+ return TRUE;
+}
+
+static bool url_match_fully_connected(struct connectdata *conn,
+ struct url_conn_match *m)
+{
if(!Curl_conn_is_connected(conn, FIRSTSOCKET) ||
conn->bits.asks_multiplex) {
/* Not yet connected, or not yet decided if it multiplexes. The later
* happens for HTTP/2 Upgrade: requests that need a response. */
- if(match->may_multiplex) {
- match->seen_pending_conn = TRUE;
+ if(m->may_multiplex) {
+ m->seen_pending_conn = TRUE;
/* Do not pick a connection that has not connected yet */
- infof(data, "Connection #%" FMT_OFF_T
+ infof(m->data, "Connection #%" FMT_OFF_T
" is not open enough, cannot reuse", conn->connection_id);
}
/* Do not pick a connection that has not connected yet */
return FALSE;
}
- /* `conn` is connected. If it has transfers, can we add ours to it? */
+ return TRUE;
+}
+static bool url_match_multi(struct connectdata *conn,
+ struct url_conn_match *m)
+{
+ if(CONN_INUSE(conn)) {
+ DEBUGASSERT(conn->attached_multi);
+ if(conn->attached_multi != m->data->multi)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static bool url_match_multiplex_needs(struct connectdata *conn,
+ struct url_conn_match *m)
+{
if(CONN_INUSE(conn)) {
if(!conn->bits.multiplex) {
/* conn busy and conn cannot take more transfers */
- match->seen_single_use_conn = TRUE;
+ m->seen_single_use_conn = TRUE;
return FALSE;
}
- match->seen_multiplex_conn = TRUE;
- if(!match->may_multiplex)
+ m->seen_multiplex_conn = TRUE;
+ if(!m->may_multiplex || !url_match_multi(conn, m))
/* conn busy and transfer cannot be multiplexed */
return FALSE;
- else {
- /* transfer and conn multiplex. Are they on the same multi? */
- struct Curl_llist_node *e = Curl_llist_head(&conn->easyq);
- struct Curl_easy *entry = Curl_node_elem(e);
- if(entry->multi != data->multi)
- return FALSE;
- }
}
- /* `conn` is connected and we could add the transfer to it, if
- * all the other criteria do match. */
+ return TRUE;
+}
- /* Does `conn` use the correct protocol? */
-#ifdef USE_UNIX_SOCKETS
- if(needle->unix_domain_socket) {
- if(!conn->unix_domain_socket)
- return FALSE;
- if(strcmp(needle->unix_domain_socket, conn->unix_domain_socket))
+static bool url_match_multiplex_limits(struct connectdata *conn,
+ struct url_conn_match *m)
+{
+ if(CONN_INUSE(conn) && m->may_multiplex) {
+ DEBUGASSERT(conn->bits.multiplex);
+ /* If multiplexed, make sure we do not go over concurrency limit */
+ if(CONN_ATTACHED(conn) >=
+ Curl_multi_max_concurrent_streams(m->data->multi)) {
+ infof(m->data, "client side MAX_CONCURRENT_STREAMS reached"
+ ", skip (%u)", CONN_ATTACHED(conn));
return FALSE;
- if(needle->bits.abstract_unix_socket != conn->bits.abstract_unix_socket)
+ }
+ if(CONN_ATTACHED(conn) >=
+ Curl_conn_get_max_concurrent(m->data, conn, FIRSTSOCKET)) {
+ infof(m->data, "MAX_CONCURRENT_STREAMS reached, skip (%u)",
+ CONN_ATTACHED(conn));
return FALSE;
+ }
+ /* When not multiplexed, we have a match here! */
+ infof(m->data, "Multiplexed connection found");
}
- else if(conn->unix_domain_socket)
- return FALSE;
-#endif
+ return TRUE;
+}
- if(needle->handler->flags&PROTOPT_SSL) {
+static bool url_match_ssl_use(struct connectdata *conn,
+ struct url_conn_match *m)
+{
+ if(m->needle->handler->flags&PROTOPT_SSL) {
/* We are looking for SSL, if `conn` does not do it, not a match. */
if(!Curl_conn_is_ssl(conn, FIRSTSOCKET))
return FALSE;
@@ -918,34 +965,39 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) else if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
/* We are not *requiring* SSL, however `conn` has it. If the
* protocol *family* is not the same, not a match. */
- if(get_protocol_family(conn->handler) != needle->handler->protocol)
+ if(get_protocol_family(conn->handler) != m->needle->handler->protocol)
return FALSE;
}
+ return TRUE;
+}
#ifndef CURL_DISABLE_PROXY
- if(needle->bits.httpproxy != conn->bits.httpproxy ||
- needle->bits.socksproxy != conn->bits.socksproxy)
+static bool url_match_proxy_use(struct connectdata *conn,
+ struct url_conn_match *m)
+{
+ if(m->needle->bits.httpproxy != conn->bits.httpproxy ||
+ m->needle->bits.socksproxy != conn->bits.socksproxy)
return FALSE;
- if(needle->bits.socksproxy &&
- !socks_proxy_info_matches(&needle->socks_proxy,
+ if(m->needle->bits.socksproxy &&
+ !socks_proxy_info_matches(&m->needle->socks_proxy,
&conn->socks_proxy))
return FALSE;
- if(needle->bits.httpproxy) {
- if(needle->bits.tunnel_proxy != conn->bits.tunnel_proxy)
+ if(m->needle->bits.httpproxy) {
+ if(m->needle->bits.tunnel_proxy != conn->bits.tunnel_proxy)
return FALSE;
- if(!proxy_info_matches(&needle->http_proxy, &conn->http_proxy))
+ if(!proxy_info_matches(&m->needle->http_proxy, &conn->http_proxy))
return FALSE;
- if(IS_HTTPS_PROXY(needle->http_proxy.proxytype)) {
+ if(IS_HTTPS_PROXY(m->needle->http_proxy.proxytype)) {
/* https proxies come in different types, http/1.1, h2, ... */
- if(needle->http_proxy.proxytype != conn->http_proxy.proxytype)
+ if(m->needle->http_proxy.proxytype != conn->http_proxy.proxytype)
return FALSE;
/* match SSL config to proxy */
- if(!Curl_ssl_conn_config_match(data, conn, TRUE)) {
- DEBUGF(infof(data,
+ if(!Curl_ssl_conn_config_match(m->data, conn, TRUE)) {
+ DEBUGF(infof(m->data,
"Connection #%" FMT_OFF_T
" has different SSL proxy parameters, cannot reuse",
conn->connection_id));
@@ -955,109 +1007,134 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) * further below */
}
}
+ return TRUE;
+}
+#else
+#define url_match_proxy_use(c,m) ((void)c, (void)m, TRUE)
#endif
#ifndef CURL_DISABLE_HTTP
- if(match->may_multiplex &&
- (data->state.http_neg.allowed & (CURL_HTTP_V2x|CURL_HTTP_V3x)) &&
- (needle->handler->protocol & CURLPROTO_HTTP) &&
+static bool url_match_http_multiplex(struct connectdata *conn,
+ struct url_conn_match *m)
+{
+ if(m->may_multiplex &&
+ (m->data->state.http_neg.allowed & (CURL_HTTP_V2x|CURL_HTTP_V3x)) &&
+ (m->needle->handler->protocol & CURLPROTO_HTTP) &&
!conn->httpversion_seen) {
- if(data->set.pipewait) {
- infof(data, "Server upgrade does not support multiplex yet, wait");
- match->found = NULL;
- match->wait_pipe = TRUE;
+ if(m->data->set.pipewait) {
+ infof(m->data, "Server upgrade does not support multiplex yet, wait");
+ m->found = NULL;
+ m->wait_pipe = TRUE;
return TRUE; /* stop searching, we want to wait */
}
- infof(data, "Server upgrade cannot be used");
+ infof(m->data, "Server upgrade cannot be used");
return FALSE;
}
-#endif
-
- if(!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) {
- /* This protocol requires credentials per connection,
- so verify that we are using the same name and password as well */
- if(Curl_timestrcmp(needle->user, conn->user) ||
- Curl_timestrcmp(needle->passwd, conn->passwd) ||
- Curl_timestrcmp(needle->sasl_authzid, conn->sasl_authzid) ||
- Curl_timestrcmp(needle->oauth_bearer, conn->oauth_bearer)) {
- /* one of them was different */
- return FALSE;
- }
- }
-
-#ifdef HAVE_GSSAPI
- /* GSS delegation differences do not actually affect every connection
- and auth method, but this check takes precaution before efficiency */
- if(needle->gssapi_delegation != conn->gssapi_delegation)
- return FALSE;
-#endif
+ return TRUE;
+}
-#ifndef CURL_DISABLE_HTTP
+static bool url_match_http_version(struct connectdata *conn,
+ struct url_conn_match *m)
+{
/* If looking for HTTP and the HTTP versions allowed do not include
* the HTTP version of conn, continue looking. */
- if((needle->handler->protocol & PROTO_FAMILY_HTTP)) {
- switch(Curl_conn_http_version(data, conn)) {
+ if((m->needle->handler->protocol & PROTO_FAMILY_HTTP)) {
+ switch(Curl_conn_http_version(m->data, conn)) {
case 30:
- if(!(data->state.http_neg.allowed & CURL_HTTP_V3x)) {
- DEBUGF(infof(data, "not reusing conn #%" CURL_FORMAT_CURL_OFF_T
+ if(!(m->data->state.http_neg.allowed & CURL_HTTP_V3x)) {
+ DEBUGF(infof(m->data, "not reusing conn #%" CURL_FORMAT_CURL_OFF_T
", we do not want h3", conn->connection_id));
return FALSE;
}
break;
case 20:
- if(!(data->state.http_neg.allowed & CURL_HTTP_V2x)) {
- DEBUGF(infof(data, "not reusing conn #%" CURL_FORMAT_CURL_OFF_T
+ if(!(m->data->state.http_neg.allowed & CURL_HTTP_V2x)) {
+ DEBUGF(infof(m->data, "not reusing conn #%" CURL_FORMAT_CURL_OFF_T
", we do not want h2", conn->connection_id));
return FALSE;
}
break;
default:
- if(!(data->state.http_neg.allowed & CURL_HTTP_V1x)) {
- DEBUGF(infof(data, "not reusing conn #%" CURL_FORMAT_CURL_OFF_T
+ if(!(m->data->state.http_neg.allowed & CURL_HTTP_V1x)) {
+ DEBUGF(infof(m->data, "not reusing conn #%" CURL_FORMAT_CURL_OFF_T
", we do not want h1", conn->connection_id));
return FALSE;
}
break;
}
}
+ return TRUE;
+}
+#else
+#define url_match_http_multiplex(c,m) ((void)c, (void)m, TRUE)
+#define url_match_http_version(c,m) ((void)c, (void)m, TRUE)
#endif
+static bool url_match_proto_config(struct connectdata *conn,
+ struct url_conn_match *m)
+{
+ if(!url_match_http_version(conn, m))
+ return FALSE;
+
#ifdef USE_SSH
- else if(get_protocol_family(needle->handler) & PROTO_FAMILY_SSH) {
- if(!ssh_config_matches(needle, conn))
+ if(get_protocol_family(m->needle->handler) & PROTO_FAMILY_SSH) {
+ if(!ssh_config_matches(m->needle, conn))
return FALSE;
}
#endif
#ifndef CURL_DISABLE_FTP
- else if(get_protocol_family(needle->handler) & PROTO_FAMILY_FTP) {
- /* Also match ACCOUNT, ALTERNATIVE-TO-USER, USE_SSL and CCC options */
- if(Curl_timestrcmp(needle->proto.ftpc.account,
- conn->proto.ftpc.account) ||
- Curl_timestrcmp(needle->proto.ftpc.alternative_to_user,
- conn->proto.ftpc.alternative_to_user) ||
- (needle->proto.ftpc.use_ssl != conn->proto.ftpc.use_ssl) ||
- (needle->proto.ftpc.ccc != conn->proto.ftpc.ccc))
+ else if(get_protocol_family(m->needle->handler) & PROTO_FAMILY_FTP) {
+ if(!ftp_conns_match(m->needle, conn))
return FALSE;
}
#endif
+ return TRUE;
+}
+static bool url_match_auth(struct connectdata *conn,
+ struct url_conn_match *m)
+{
+ if(!(m->needle->handler->flags & PROTOPT_CREDSPERREQUEST)) {
+ /* This protocol requires credentials per connection,
+ so verify that we are using the same name and password as well */
+ if(Curl_timestrcmp(m->needle->user, conn->user) ||
+ Curl_timestrcmp(m->needle->passwd, conn->passwd) ||
+ Curl_timestrcmp(m->needle->sasl_authzid, conn->sasl_authzid) ||
+ Curl_timestrcmp(m->needle->oauth_bearer, conn->oauth_bearer)) {
+ /* one of them was different */
+ return FALSE;
+ }
+ }
+#ifdef HAVE_GSSAPI
+ /* GSS delegation differences do not actually affect every connection
+ and auth method, but this check takes precaution before efficiency */
+ if(m->needle->gssapi_delegation != conn->gssapi_delegation)
+ return FALSE;
+#endif
+
+ return TRUE;
+}
+
+static bool url_match_destination(struct connectdata *conn,
+ struct url_conn_match *m)
+{
/* Additional match requirements if talking TLS OR
* not talking to an HTTP proxy OR using a tunnel through a proxy */
- if((needle->handler->flags&PROTOPT_SSL)
+ if((m->needle->handler->flags&PROTOPT_SSL)
#ifndef CURL_DISABLE_PROXY
- || !needle->bits.httpproxy || needle->bits.tunnel_proxy
+ || !m->needle->bits.httpproxy || m->needle->bits.tunnel_proxy
#endif
) {
- if(!strcasecompare(needle->handler->scheme, conn->handler->scheme)) {
+ if(!strcasecompare(m->needle->handler->scheme, conn->handler->scheme)) {
/* `needle` and `conn` do not have the same scheme... */
- if(get_protocol_family(conn->handler) != needle->handler->protocol) {
+ if(get_protocol_family(conn->handler) != m->needle->handler->protocol) {
/* and `conn`s protocol family is not the protocol `needle` wants.
* IMAPS would work for IMAP, but no vice versa. */
return FALSE;
}
/* We are in an IMAPS vs IMAP like case. We expect `conn` to have SSL */
if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
- DEBUGF(infof(data,
+ DEBUGF(infof(m->data,
"Connection #%" FMT_OFF_T " has compatible protocol famiy, "
"but no SSL, no match", conn->connection_id));
return FALSE;
@@ -1065,42 +1142,52 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) }
/* If needle has "conn_to_*" set, conn must match this */
- if((needle->bits.conn_to_host && !strcasecompare(
- needle->conn_to_host.name, conn->conn_to_host.name)) ||
- (needle->bits.conn_to_port &&
- needle->conn_to_port != conn->conn_to_port))
+ if((m->needle->bits.conn_to_host && !strcasecompare(
+ m->needle->conn_to_host.name, conn->conn_to_host.name)) ||
+ (m->needle->bits.conn_to_port &&
+ m->needle->conn_to_port != conn->conn_to_port))
return FALSE;
/* hostname and port must match */
- if(!strcasecompare(needle->host.name, conn->host.name) ||
- needle->remote_port != conn->remote_port)
+ if(!strcasecompare(m->needle->host.name, conn->host.name) ||
+ m->needle->remote_port != conn->remote_port)
return FALSE;
+ }
+ return TRUE;
+}
- /* If talking TLS, conn needs to use the same SSL options. */
- if((needle->handler->flags & PROTOPT_SSL) &&
- !Curl_ssl_conn_config_match(data, conn, FALSE)) {
- DEBUGF(infof(data,
- "Connection #%" FMT_OFF_T
- " has different SSL parameters, cannot reuse",
- conn->connection_id));
- return FALSE;
- }
+static bool url_match_ssl_config(struct connectdata *conn,
+ struct url_conn_match *m)
+{
+ /* If talking TLS, conn needs to use the same SSL options. */
+ if((m->needle->handler->flags & PROTOPT_SSL) &&
+ !Curl_ssl_conn_config_match(m->data, conn, FALSE)) {
+ DEBUGF(infof(m->data,
+ "Connection #%" FMT_OFF_T
+ " has different SSL parameters, cannot reuse",
+ conn->connection_id));
+ return FALSE;
}
+ return TRUE;
+}
#ifdef USE_NTLM
+static bool url_match_auth_ntlm(struct connectdata *conn,
+ struct url_conn_match *m)
+{
/* If we are looking for an HTTP+NTLM connection, check if this is
already authenticating with the right credentials. If not, keep
looking so that we can reuse NTLM connections if
possible. (Especially we must not reuse the same connection if
partway through a handshake!) */
- if(match->want_ntlm_http) {
- if(Curl_timestrcmp(needle->user, conn->user) ||
- Curl_timestrcmp(needle->passwd, conn->passwd)) {
+ if(m->want_ntlm_http) {
+ if(Curl_timestrcmp(m->needle->user, conn->user) ||
+ Curl_timestrcmp(m->needle->passwd, conn->passwd)) {
/* we prefer a credential match, but this is at least a connection
that can be reused and "upgraded" to NTLM */
if(conn->http_ntlm_state == NTLMSTATE_NONE)
- match->found = conn;
+ m->found = conn;
return FALSE;
}
}
@@ -1111,15 +1198,15 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) #ifndef CURL_DISABLE_PROXY
/* Same for Proxy NTLM authentication */
- if(match->want_proxy_ntlm_http) {
+ if(m->want_proxy_ntlm_http) {
/* Both conn->http_proxy.user and conn->http_proxy.passwd can be
* NULL */
if(!conn->http_proxy.user || !conn->http_proxy.passwd)
return FALSE;
- if(Curl_timestrcmp(needle->http_proxy.user,
+ if(Curl_timestrcmp(m->needle->http_proxy.user,
conn->http_proxy.user) ||
- Curl_timestrcmp(needle->http_proxy.passwd,
+ Curl_timestrcmp(m->needle->http_proxy.passwd,
conn->http_proxy.passwd))
return FALSE;
}
@@ -1128,53 +1215,83 @@ static bool url_match_conn(struct connectdata *conn, void *userdata) return FALSE;
}
#endif
- if(match->want_ntlm_http || match->want_proxy_ntlm_http) {
+ if(m->want_ntlm_http || m->want_proxy_ntlm_http) {
/* Credentials are already checked, we may use this connection.
* With NTLM being weird as it is, we MUST use a
* connection where it has already been fully negotiated.
* If it has not, we keep on looking for a better one. */
- match->found = conn;
+ m->found = conn;
- if((match->want_ntlm_http &&
+ if((m->want_ntlm_http &&
(conn->http_ntlm_state != NTLMSTATE_NONE)) ||
- (match->want_proxy_ntlm_http &&
+ (m->want_proxy_ntlm_http &&
(conn->proxy_ntlm_state != NTLMSTATE_NONE))) {
/* We must use this connection, no other */
- match->force_reuse = TRUE;
+ m->force_reuse = TRUE;
return TRUE;
}
/* Continue look up for a better connection */
return FALSE;
}
+ return TRUE;
+}
+#else
+#define url_match_auth_ntlm(c,m) ((void)c, (void)m, TRUE)
#endif
- if(CONN_INUSE(conn)) {
- DEBUGASSERT(match->may_multiplex);
- DEBUGASSERT(conn->bits.multiplex);
- /* If multiplexed, make sure we do not go over concurrency limit */
- if(CONN_INUSE(conn) >=
- Curl_multi_max_concurrent_streams(data->multi)) {
- infof(data, "client side MAX_CONCURRENT_STREAMS reached"
- ", skip (%zu)", CONN_INUSE(conn));
- return FALSE;
- }
- if(CONN_INUSE(conn) >=
- Curl_conn_get_max_concurrent(data, conn, FIRSTSOCKET)) {
- infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)",
- CONN_INUSE(conn));
- return FALSE;
- }
- /* When not multiplexed, we have a match here! */
- infof(data, "Multiplexed connection found");
- }
- else if(Curl_conn_seems_dead(conn, data, NULL)) {
- /* removed and disconnect. Do not treat as aborted. */
- Curl_conn_terminate(data, conn, FALSE);
+static bool url_match_conn(struct connectdata *conn, void *userdata)
+{
+ struct url_conn_match *m = userdata;
+ /* Check if `conn` can be used for transfer `m->data` */
+
+ /* general connect config setting match? */
+ if(!url_match_connect_config(conn, m))
+ return FALSE;
+
+ if(!url_match_destination(conn, m))
+ return FALSE;
+
+ if(!url_match_fully_connected(conn, m))
+ return FALSE;
+
+ if(!url_match_multiplex_needs(conn, m))
+ return FALSE;
+
+ if(!url_match_ssl_use(conn, m))
+ return FALSE;
+ if(!url_match_proxy_use(conn, m))
+ return FALSE;
+ if(!url_match_ssl_config(conn, m))
+ return FALSE;
+
+ if(!url_match_http_multiplex(conn, m))
+ return FALSE;
+ else if(m->wait_pipe)
+ /* we decided to wait on PIPELINING */
+ return TRUE;
+
+ if(!url_match_auth(conn, m))
+ return FALSE;
+
+ if(!url_match_proto_config(conn, m))
+ return FALSE;
+
+ if(!url_match_auth_ntlm(conn, m))
+ return FALSE;
+ else if(m->force_reuse)
+ return TRUE;
+
+ if(!url_match_multiplex_limits(conn, m))
+ return FALSE;
+
+ if(!CONN_INUSE(conn) && Curl_conn_seems_dead(conn, m->data, NULL)) {
+ /* remove and disconnect. */
+ Curl_conn_terminate(m->data, conn, FALSE);
return FALSE;
}
- /* We have found a connection. Let's stop searching. */
- match->found = conn;
+ /* conn matches our needs. */
+ m->found = conn;
return TRUE;
}
@@ -1310,7 +1427,7 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) connclose(conn, "Default to force-close");
/* Store creation time to help future close decision making */
- conn->created = Curl_now();
+ conn->created = curlx_now();
/* Store current time to give a baseline to keepalive connection times. */
conn->keepalive = conn->created;
@@ -1346,8 +1463,8 @@ static struct connectdata *allocate_conn(struct Curl_easy *data) conn->connect_only = data->set.connect_only;
conn->transport = TRNSPRT_TCP; /* most of them are TCP streams */
- /* Initialize the easy handle list */
- Curl_llist_init(&conn->easyq, NULL);
+ /* Initialize the attached xfers bitset */
+ Curl_uint_spbset_init(&conn->xfers_attached);
#ifdef HAVE_GSSAPI
conn->data_prot = PROT_CLEAR;
@@ -1666,7 +1783,7 @@ static void zonefrom_url(CURLU *uh, struct Curl_easy *data, if(!uc && zoneid) {
const char *p = zoneid;
curl_off_t scope;
- if(!Curl_str_number(&p, &scope, UINT_MAX))
+ if(!curlx_str_number(&p, &scope, UINT_MAX))
/* A plain number, use it directly as a scope id. */
conn->scope_id = (unsigned int)scope;
#ifdef HAVE_IF_NAMETOINDEX
@@ -1909,7 +2026,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, port = data->set.use_port;
else {
const char *p = data->state.up.port;
- if(Curl_str_number(&p, &port, 0xffff))
+ if(curlx_str_number(&p, &port, 0xffff))
valid = FALSE;
}
if(valid)
@@ -2243,7 +2360,7 @@ static CURLcode parse_proxy(struct Curl_easy *data, if(portptr) {
curl_off_t num;
const char *p = portptr;
- if(!Curl_str_number(&p, &num, 0xffff))
+ if(!curlx_str_number(&p, &num, 0xffff))
port = (int)num;
free(portptr);
}
@@ -2887,7 +3004,7 @@ static CURLcode parse_connect_to_host_port(struct Curl_easy *data, if(*host_portno) {
curl_off_t portparse;
const char *p = host_portno;
- if(Curl_str_number(&p, &portparse, 0xffff)) {
+ if(curlx_str_number(&p, &portparse, 0xffff)) {
failf(data, "No valid port number in connect to host string (%s)",
host_portno);
result = CURLE_SETOPT_OPTION_SYNTAX;
@@ -2965,7 +3082,7 @@ static CURLcode parse_connect_to_string(struct Curl_easy *data, char *ptr_next = strchr(ptr, ':');
if(ptr_next) {
curl_off_t port_to_match;
- if(!Curl_str_number(&ptr, &port_to_match, 0xffff) &&
+ if(!curlx_str_number(&ptr, &port_to_match, 0xffff) &&
(port_to_match == (curl_off_t)conn->remote_port))
port_match = TRUE;
ptr = ptr_next + 1;
@@ -3045,10 +3162,14 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, DEBUGF(infof(data, "Alt-svc check wanted=%x, allowed=%x",
neg->wanted, neg->allowed));
+#ifdef USE_HTTP3
if(neg->allowed & CURL_HTTP_V3x)
allowed_alpns |= ALPN_h3;
+#endif
+#ifdef USE_HTTP2
if(neg->allowed & CURL_HTTP_V2x)
allowed_alpns |= ALPN_h2;
+#endif
if(neg->allowed & CURL_HTTP_V1x)
allowed_alpns |= ALPN_h1;
allowed_alpns &= (int)data->asi->flags;
@@ -3124,13 +3245,14 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data, #ifdef USE_UNIX_SOCKETS
static CURLcode resolve_unix(struct Curl_easy *data,
struct connectdata *conn,
- char *unix_path)
+ char *unix_path,
+ struct Curl_dns_entry **pdns)
{
- struct Curl_dns_entry *hostaddr = NULL;
+ struct Curl_dns_entry *hostaddr;
bool longpath = FALSE;
DEBUGASSERT(unix_path);
- DEBUGASSERT(conn->dns_entry == NULL);
+ *pdns = NULL;
/* Unix domain sockets are local. The host gets ignored, just use the
* specified domain socket address. Do not cache "DNS entries". There is
@@ -3150,7 +3272,7 @@ static CURLcode resolve_unix(struct Curl_easy *data, }
hostaddr->refcount = 1; /* connection is the only one holding this */
- conn->dns_entry = hostaddr;
+ *pdns = hostaddr;
return CURLE_OK;
}
#endif
@@ -3160,31 +3282,35 @@ static CURLcode resolve_unix(struct Curl_easy *data, *************************************************************/
static CURLcode resolve_server(struct Curl_easy *data,
struct connectdata *conn,
- bool *async)
+ bool *async,
+ struct Curl_dns_entry **pdns)
{
struct hostname *ehost;
timediff_t timeout_ms = Curl_timeleft(data, NULL, TRUE);
const char *peertype = "host";
- int rc;
+ CURLcode result;
+
+ *pdns = NULL;
+
#ifdef USE_UNIX_SOCKETS
- char *unix_path = conn->unix_domain_socket;
+ {
+ char *unix_path = conn->unix_domain_socket;
#ifndef CURL_DISABLE_PROXY
- if(!unix_path && CONN_IS_PROXIED(conn) && conn->socks_proxy.host.name &&
- !strncmp(UNIX_SOCKET_PREFIX"/",
- conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX)))
- unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1;
+ if(!unix_path && CONN_IS_PROXIED(conn) && conn->socks_proxy.host.name &&
+ !strncmp(UNIX_SOCKET_PREFIX"/",
+ conn->socks_proxy.host.name, sizeof(UNIX_SOCKET_PREFIX)))
+ unix_path = conn->socks_proxy.host.name + sizeof(UNIX_SOCKET_PREFIX) - 1;
#endif
- if(unix_path) {
- /* This only works if previous transport is TRNSPRT_TCP. Check it? */
- conn->transport = TRNSPRT_UNIX;
- return resolve_unix(data, conn, unix_path);
+ if(unix_path) {
+ /* This only works if previous transport is TRNSPRT_TCP. Check it? */
+ conn->transport = TRNSPRT_UNIX;
+ return resolve_unix(data, conn, unix_path, pdns);
+ }
}
#endif
- DEBUGASSERT(conn->dns_entry == NULL);
-
#ifndef CURL_DISABLE_PROXY
if(CONN_IS_PROXIED(conn)) {
ehost = conn->bits.socksproxy ? &conn->socks_proxy.host :
@@ -3206,22 +3332,25 @@ static CURLcode resolve_server(struct Curl_easy *data, if(!conn->hostname_resolve)
return CURLE_OUT_OF_MEMORY;
- rc = Curl_resolv_timeout(data, conn->hostname_resolve,
- conn->primary.remote_port,
- &conn->dns_entry, timeout_ms);
- if(rc == CURLRESOLV_PENDING)
+ result = Curl_resolv_timeout(data, conn->hostname_resolve,
+ conn->primary.remote_port, conn->ip_version,
+ pdns, timeout_ms);
+ DEBUGASSERT(!result || !*pdns);
+ if(result == CURLE_AGAIN) {
*async = TRUE;
- else if(rc == CURLRESOLV_TIMEDOUT) {
+ return CURLE_OK;
+ }
+ else if(result == CURLE_OPERATION_TIMEDOUT) {
failf(data, "Failed to resolve %s '%s' with timeout after %"
FMT_TIMEDIFF_T " ms", peertype, ehost->dispname,
- Curl_timediff(Curl_now(), data->progress.t_startsingle));
+ curlx_timediff(curlx_now(), data->progress.t_startsingle));
return CURLE_OPERATION_TIMEDOUT;
}
- else if(!conn->dns_entry) {
+ else if(result) {
failf(data, "Could not resolve %s: %s", peertype, ehost->dispname);
- return CURLE_COULDNT_RESOLVE_HOST;
+ return result;
}
-
+ DEBUGASSERT(*pdns);
return CURLE_OK;
}
@@ -3300,6 +3429,15 @@ static void reuse_conn(struct Curl_easy *data, Curl_conn_free(data, temp);
}
+static void conn_meta_freeentry(void *p)
+{
+ (void)p;
+ /* Will always be FALSE. Cannot use a 0 assert here since compilers
+ * are not in agreement if they then want a NORETURN attribute or
+ * not. *sigh* */
+ DEBUGASSERT(p == NULL);
+}
+
/**
* create_conn() sets up a new connectdata struct, or reuses an already
* existing one, and resolves hostname.
@@ -3310,14 +3448,14 @@ static void reuse_conn(struct Curl_easy *data, *
* @param data The sessionhandle pointer
* @param in_connect is set to the next connection data pointer
- * @param async is set TRUE when an async DNS resolution is pending
+ * @param reusedp is set to to TRUE if connection was reused
* @see Curl_setup_conn()
*
*/
static CURLcode create_conn(struct Curl_easy *data,
struct connectdata **in_connect,
- bool *async)
+ bool *reusedp)
{
CURLcode result = CURLE_OK;
struct connectdata *conn;
@@ -3327,7 +3465,7 @@ static CURLcode create_conn(struct Curl_easy *data, bool force_reuse = FALSE;
bool waitpipe = FALSE;
- *async = FALSE;
+ *reusedp = FALSE;
*in_connect = NULL;
/*************************************************************
@@ -3355,6 +3493,9 @@ static CURLcode create_conn(struct Curl_easy *data, *in_connect = conn;
/* Do the unfailable inits first, before checks that may early return */
+ Curl_hash_init(&conn->meta_hash, 23,
+ Curl_hash_str, curlx_str_key_compare, conn_meta_freeentry);
+
/* GSSAPI related inits */
Curl_sec_conn_init(conn);
@@ -3498,28 +3639,26 @@ static CURLcode create_conn(struct Curl_easy *data, /* conn_protocol can only provide "old" protocols */
data->info.conn_protocol = (conn->handler->protocol) & CURLPROTO_MASK;
result = conn->handler->connect_it(data, &done);
+ if(result)
+ goto out;
/* Setup a "faked" transfer that will do nothing */
+ Curl_attach_connection(data, conn);
+ result = Curl_cpool_add(data, conn);
if(!result) {
- Curl_attach_connection(data, conn);
- result = Curl_cpool_add(data, conn);
+ /* Setup whatever necessary for a resumed transfer */
+ result = setup_range(data);
if(!result) {
- /* Setup whatever necessary for a resumed transfer */
- result = setup_range(data);
+ Curl_xfer_setup_nop(data);
+ result = Curl_init_do(data, conn);
}
-
- if(result) {
- DEBUGASSERT(conn->handler->done);
- /* we ignore the return code for the protocol-specific DONE */
- (void)conn->handler->done(data, result, FALSE);
- goto out;
- }
- Curl_xfer_setup_nop(data);
}
- /* since we skip do_init() */
- Curl_init_do(data, conn);
-
+ if(result) {
+ DEBUGASSERT(conn->handler->done);
+ /* we ignore the return code for the protocol-specific DONE */
+ (void)conn->handler->done(data, result, FALSE);
+ }
goto out;
}
#endif
@@ -3597,10 +3736,12 @@ static CURLcode create_conn(struct Curl_easy *data, conn->bits.tls_enable_alpn = TRUE;
}
- if(waitpipe)
+ if(waitpipe) {
/* There is a connection that *might* become usable for multiplexing
"soon", and we wait for that */
+ infof(data, "Waiting on connection to negotiate possible multiplexing.");
connections_available = FALSE;
+ }
else {
switch(Curl_cpool_check_limits(data, conn)) {
case CPOOL_LIMIT_DEST:
@@ -3608,13 +3749,12 @@ static CURLcode create_conn(struct Curl_easy *data, connections_available = FALSE;
break;
case CPOOL_LIMIT_TOTAL:
-#ifndef CURL_DISABLE_DOH
- if(data->set.dohfor_mid >= 0)
- infof(data, "Allowing DoH to override max connection limit");
- else
-#endif
- {
- infof(data, "No connections available in cache");
+ if(data->master_mid != UINT_MAX)
+ CURL_TRC_M(data, "Allowing sub-requests (like DoH) to override "
+ "max connection limit");
+ else {
+ infof(data, "No connections available, total of %ld reached.",
+ data->multi->max_total_connections);
connections_available = FALSE;
}
break;
@@ -3624,8 +3764,6 @@ static CURLcode create_conn(struct Curl_easy *data, }
if(!connections_available) {
- infof(data, "No connections available.");
-
Curl_conn_free(data, conn);
*in_connect = NULL;
@@ -3670,7 +3808,9 @@ static CURLcode create_conn(struct Curl_easy *data, }
/* Setup and init stuff before DO starts, in preparing for the transfer. */
- Curl_init_do(data, conn);
+ result = Curl_init_do(data, conn);
+ if(result)
+ goto out;
/*
* Setup whatever necessary for a resumed transfer
@@ -3685,15 +3825,7 @@ static CURLcode create_conn(struct Curl_easy *data, /* We are reusing the connection - no need to resolve anything, and
idnconvert_hostname() was called already in create_conn() for the reuse
case. */
- *async = FALSE;
- }
- else {
- /*************************************************************
- * Resolve the address of the server or proxy
- *************************************************************/
- result = resolve_server(data, conn, async);
- if(result)
- goto out;
+ *reusedp = TRUE;
}
/* persist the scheme and handler the transfer is using */
@@ -3722,21 +3854,17 @@ out: * Curl_setup_conn() also handles reused connections
*/
CURLcode Curl_setup_conn(struct Curl_easy *data,
+ struct Curl_dns_entry *dns,
bool *protocol_done)
{
CURLcode result = CURLE_OK;
struct connectdata *conn = data->conn;
+ DEBUGASSERT(dns);
Curl_pgrsTime(data, TIMER_NAMELOOKUP);
- if(conn->handler->flags & PROTOPT_NONETWORK) {
- /* nothing to setup when not using a network */
- *protocol_done = TRUE;
- return result;
- }
-
if(!conn->bits.reuse)
- result = Curl_conn_setup(data, conn, FIRSTSOCKET, conn->dns_entry,
+ result = Curl_conn_setup(data, conn, FIRSTSOCKET, dns,
CURL_CF_SSL_DEFAULT);
if(!result)
result = Curl_headers_init(data);
@@ -3752,31 +3880,52 @@ CURLcode Curl_connect(struct Curl_easy *data, {
CURLcode result;
struct connectdata *conn;
+ bool reused = FALSE;
*asyncp = FALSE; /* assume synchronous resolves by default */
+ *protocol_done = FALSE;
/* Set the request to virgin state based on transfer settings */
Curl_req_hard_reset(&data->req, data);
/* call the stuff that needs to be called */
- result = create_conn(data, &conn, asyncp);
+ result = create_conn(data, &conn, &reused);
+
+ if(result == CURLE_NO_CONNECTION_AVAILABLE) {
+ DEBUGASSERT(!conn);
+ return result;
+ }
if(!result) {
- if(CONN_INUSE(conn) > 1)
- /* multiplexed */
+ DEBUGASSERT(conn);
+ if(reused) {
+ if(CONN_ATTACHED(conn) > 1)
+ /* multiplexed */
+ *protocol_done = TRUE;
+ }
+ else if(conn->handler->flags & PROTOPT_NONETWORK) {
+ *asyncp = FALSE;
+ Curl_pgrsTime(data, TIMER_NAMELOOKUP);
*protocol_done = TRUE;
- else if(!*asyncp) {
- /* DNS resolution is done: that is either because this is a reused
- connection, in which case DNS was unnecessary, or because DNS
- really did finish already (synch resolver/fast async resolve) */
- result = Curl_setup_conn(data, protocol_done);
+ }
+ else {
+ /*************************************************************
+ * Resolve the address of the server or proxy
+ *************************************************************/
+ struct Curl_dns_entry *dns;
+ result = resolve_server(data, conn, asyncp, &dns);
+ if(!result) {
+ *asyncp = !dns;
+ if(dns)
+ /* DNS resolution is done: that is either because this is a reused
+ connection, in which case DNS was unnecessary, or because DNS
+ really did finish already (synch resolver/fast async resolve) */
+ result = Curl_setup_conn(data, dns, protocol_done);
+ }
}
}
- if(result == CURLE_NO_CONNECTION_AVAILABLE) {
- return result;
- }
- else if(result && conn) {
+ if(result && conn) {
/* We are not allowed to return failure with memory left allocated in the
connectdata struct, free those here */
Curl_detach_connection(data);
@@ -3923,3 +4072,25 @@ void Curl_data_priority_clear_state(struct Curl_easy *data) }
#endif /* defined(USE_HTTP2) || defined(USE_HTTP3) */
+
+
+CURLcode Curl_conn_meta_set(struct connectdata *conn, const char *key,
+ void *meta_data, Curl_meta_dtor *meta_dtor)
+{
+ if(!Curl_hash_add2(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1,
+ meta_data, meta_dtor)) {
+ meta_dtor(CURL_UNCONST(key), strlen(key) + 1, meta_data);
+ return CURLE_OUT_OF_MEMORY;
+ }
+ return CURLE_OK;
+}
+
+void Curl_conn_meta_remove(struct connectdata *conn, const char *key)
+{
+ Curl_hash_delete(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1);
+}
+
+void *Curl_conn_meta_get(struct connectdata *conn, const char *key)
+{
+ return Curl_hash_pick(&conn->meta_hash, CURL_UNCONST(key), strlen(key) + 1);
+}
|