return result;
}
-static void async_ares_destroy(struct Curl_easy *data);
+static void async_ares_cleanup(struct Curl_easy *data);
-/*
- * For asyn-ares, this is the same as abort.
- */
-void Curl_async_shutdown(struct Curl_easy *data)
+void Curl_async_ares_shutdown(struct Curl_easy *data)
{
struct async_ares_ctx *ares = &data->state.async.ares;
- if(ares->channel) {
+ if(ares->channel)
ares_cancel(ares->channel);
+ async_ares_cleanup(data);
+}
+
+void Curl_async_ares_destroy(struct Curl_easy *data)
+{
+ struct async_ares_ctx *ares = &data->state.async.ares;
+ Curl_async_ares_shutdown(data);
+ if(ares->channel) {
+ ares_destroy(ares->channel);
ares->channel = NULL;
}
- async_ares_destroy(data);
}
/*
- * async_ares_destroy() cleans up async resolver data.
+ * async_ares_cleanup() cleans up async resolver data.
*/
-static void async_ares_destroy(struct Curl_easy *data)
+static void async_ares_cleanup(struct Curl_easy *data)
{
struct async_ares_ctx *ares = &data->state.async.ares;
if(ares->temp_ai) {
Curl_freeaddrinfo(ares->temp_ai);
ares->temp_ai = NULL;
}
- Curl_safefree(data->state.async.hostname);
+#ifdef USE_HTTPSRR
+ Curl_httpsrr_cleanup(&ares->hinfo);
+#endif
}
/*
*dns = data->state.async.dns;
CURL_TRC_DNS(data, "is_resolved() result=%d, dns=%sfound",
result, *dns ? "" : "not ");
- async_ares_destroy(data);
+ async_ares_cleanup(data);
}
return result;
}
* default.
*/
if(!servers) {
- Curl_async_shutdown(data);
+ Curl_async_destroy(data);
result = async_ares_init_lazy(data);
if(!result) {
/* this now needs to restore the other options set to c-ares */
#endif
#endif /* CURLRES_ASYNCH */
+
+#ifdef USE_CURL_ASYNC
+
+#include "doh.h"
+
+void Curl_async_shutdown(struct Curl_easy *data)
+{
+#ifdef CURLRES_ARES
+ Curl_async_ares_shutdown(data);
+#endif
+#ifdef CURLRES_THREADED
+ Curl_async_thrdd_shutdown(data);
+#endif
+#ifndef CURL_DISABLE_DOH
+ Curl_doh_cleanup(data);
+#endif
+ Curl_safefree(data->state.async.hostname);
+}
+
+void Curl_async_destroy(struct Curl_easy *data)
+{
+#ifdef CURLRES_ARES
+ Curl_async_ares_destroy(data);
+#endif
+#ifdef CURLRES_THREADED
+ Curl_async_thrdd_destroy(data);
+#endif
+#ifndef CURL_DISABLE_DOH
+ Curl_doh_cleanup(data);
+#endif
+ Curl_safefree(data->state.async.hostname);
+}
+
+#endif /* USE_CURL_ASYNC */
wakeup_close(sock_rd);
#endif
}
-
- Curl_safefree(data->state.async.hostname);
}
#ifdef USE_HTTPSRR_ARES
* Until we gain a way to signal the resolver threads to stop early, we must
* simply wait for them and ignore their results.
*/
-void Curl_async_shutdown(struct Curl_easy *data)
+void Curl_async_thrdd_shutdown(struct Curl_easy *data)
{
struct async_thrdd_ctx *thrdd = &data->state.async.thrdd;
async_thrdd_destroy(data);
}
+void Curl_async_thrdd_destroy(struct Curl_easy *data)
+{
+ Curl_async_thrdd_shutdown(data);
+}
+
/*
* Curl_async_await()
*
*/
CURLcode Curl_async_get_impl(struct Curl_easy *easy, void **impl);
-/*
- * Curl_async_shutdown().
- *
- * This frees the resources of any async resolve operation.
- */
-void Curl_async_shutdown(struct Curl_easy *data);
-
/* Curl_async_getsock()
*
* This function is called from the Curl_multi_getsock() function. 'sock' is a
#endif
};
+void Curl_async_ares_shutdown(struct Curl_easy *data);
+void Curl_async_ares_destroy(struct Curl_easy *data);
+
/*
* Function provided by the resolver backend to set DNS servers to use.
*/
#endif
};
+void Curl_async_thrdd_shutdown(struct Curl_easy *data);
+void Curl_async_thrdd_destroy(struct Curl_easy *data);
+
#endif /* CURLRES_THREADED */
#ifndef CURL_DISABLE_DOH
/* convert these functions if an asynch resolver is not used */
#define Curl_async_get_impl(x,y) (*(y) = NULL, CURLE_OK)
-#define Curl_async_shutdown(x) Curl_nop_stmt
#define Curl_async_is_resolved(x,y) CURLE_COULDNT_RESOLVE_HOST
#define Curl_async_await(x,y) CURLE_COULDNT_RESOLVE_HOST
#define Curl_async_global_init() CURLE_OK
#if defined(CURLRES_ASYNCH) || !defined(CURL_DISABLE_DOH)
#define USE_CURL_ASYNC
+#endif
+#ifdef USE_CURL_ASYNC
struct Curl_async {
#ifdef CURLRES_ARES /* */
struct async_ares_ctx ares;
BIT(done);
};
-#endif
+/*
+ * Curl_async_shutdown().
+ *
+ * This shuts down all ongoing operations.
+ */
+void Curl_async_shutdown(struct Curl_easy *data);
+
+/*
+ * Curl_async_destroy().
+ *
+ * This frees the resources of any async resolve.
+ */
+void Curl_async_destroy(struct Curl_easy *data);
+#else /* !USE_CURL_ASYNC */
+#define Curl_async_shutdown(x) Curl_nop_stmt
+#endif /* USE_CURL_ASYNC */
+
/********** end of generic resolver interface functions *****************/
#endif /* HEADER_CURL_ASYN_H */
}
static size_t
-doh_write_cb(char *contents, size_t size, size_t nmemb, void *userp)
+doh_probe_write_cb(char *contents, size_t size, size_t nmemb, void *userp)
{
size_t realsize = size * nmemb;
- struct dynbuf *mem = (struct dynbuf *)userp;
+ struct Curl_easy *data = userp;
+ struct doh_request *doh_req = Curl_meta_get(data, CURL_EZM_DOH_PROBE);
+ if(!doh_req)
+ return CURL_WRITEFUNC_ERROR;
- if(Curl_dyn_addn(mem, contents, realsize))
+ if(Curl_dyn_addn(&doh_req->resp_body, contents, realsize))
return 0;
return realsize;
}
#endif
-/* called from multi.c when this DoH transfer is complete */
-static int doh_done(struct Curl_easy *doh, CURLcode result)
+/* called from multi when a sub transfer, e.g. doh probe, is done.
+ * This looks up the the probe response at its meta CURL_EZM_DOH_PROBE
+ * and copies the response body over to the struct at the master's
+ * meta at CURL_EZM_DOH_MASTER. */
+static void doh_probe_done(struct Curl_easy *data,
+ struct Curl_easy *doh, CURLcode result)
{
- struct Curl_easy *data; /* the transfer that asked for the DoH probe */
+ struct doh_probes *dohp = data->state.async.doh;
+ DEBUGASSERT(dohp);
+ if(dohp) {
+ struct doh_request *doh_req = Curl_meta_get(doh, CURL_EZM_DOH_PROBE);
+ int i;
+
+ for(i = 0; i < DOH_SLOT_COUNT; ++i) {
+ if(dohp->probe_resp[i].probe_mid == doh->mid)
+ break;
+ }
+ if(i >= DOH_SLOT_COUNT) {
+ failf(data, "unknown sub request done");
+ return;
+ }
- data = Curl_multi_get_handle(doh->multi, doh->set.dohfor_mid);
- if(!data) {
- DEBUGF(infof(doh, "doh_done: xfer for mid=%" FMT_OFF_T
- " not found", doh->set.dohfor_mid));
- DEBUGASSERT(0);
- }
- else {
- struct doh_probes *dohp = data->state.async.doh;
- /* one of the DoH request done for the 'data' transfer is now complete! */
dohp->pending--;
infof(doh, "a DoH request is completed, %u to go", dohp->pending);
+ dohp->probe_resp[i].result = result;
+ /* We expect either the meta data still to exist or the sub request
+ * to have already failed. */
+ DEBUGASSERT(doh_req || result);
+ if(doh_req) {
+ if(!result) {
+ dohp->probe_resp[i].dnstype = doh_req->dnstype;
+ result = Curl_dyn_addn(&dohp->probe_resp[i].body,
+ Curl_dyn_ptr(&doh_req->resp_body),
+ Curl_dyn_len(&doh_req->resp_body));
+ Curl_dyn_free(&doh_req->resp_body);
+ }
+ Curl_meta_clear(doh, CURL_EZM_DOH_PROBE);
+ }
+
if(result)
infof(doh, "DoH request %s", curl_easy_strerror(result));
Curl_expire(data, 0, EXPIRE_RUN_NOW);
}
}
- return 0;
+}
+
+static void doh_probe_dtor(void *key, size_t klen, void *e)
+{
+ (void)key;
+ (void)klen;
+ if(e) {
+ struct doh_request *doh_req = e;
+ curl_slist_free_all(doh_req->req_hds);
+ Curl_dyn_free(&doh_req->resp_body);
+ free(e);
+ }
}
#define ERROR_CHECK_SETOPT(x,y) \
goto error; \
} while(0)
-static CURLcode doh_run_probe(struct Curl_easy *data,
- struct doh_probe *p, DNStype dnstype,
+static CURLcode doh_probe_run(struct Curl_easy *data,
+ DNStype dnstype,
const char *host,
const char *url, CURLM *multi,
- struct curl_slist *headers)
+ curl_off_t *pmid)
{
struct Curl_easy *doh = NULL;
CURLcode result = CURLE_OK;
timediff_t timeout_ms;
- DOHcode d = doh_req_encode(host, dnstype, p->req_body, sizeof(p->req_body),
- &p->req_body_len);
+ struct doh_request *doh_req;
+ DOHcode d;
+
+ *pmid = -1;
+
+ doh_req = calloc(1, sizeof(*doh_req));
+ if(!doh_req)
+ return CURLE_OUT_OF_MEMORY;
+ doh_req->dnstype = dnstype;
+ Curl_dyn_init(&doh_req->resp_body, DYN_DOH_RESPONSE);
+
+ d = doh_req_encode(host, dnstype, doh_req->req_body,
+ sizeof(doh_req->req_body),
+ &doh_req->req_body_len);
if(d) {
failf(data, "Failed to encode DoH packet [%d]", d);
- return CURLE_OUT_OF_MEMORY;
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
}
- p->dnstype = dnstype;
- Curl_dyn_init(&p->resp_body, DYN_DOH_RESPONSE);
-
timeout_ms = Curl_timeleft(data, NULL, TRUE);
if(timeout_ms <= 0) {
result = CURLE_OPERATION_TIMEDOUT;
goto error;
}
+
+ doh_req->req_hds =
+ curl_slist_append(NULL, "Content-Type: application/dns-message");
+ if(!doh_req->req_hds) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+
/* Curl_open() is the internal version of curl_easy_init() */
result = Curl_open(&doh);
if(result)
/* pass in the struct pointer via a local variable to please coverity and
the gcc typecheck helpers */
- doh->state.internal = TRUE;
#ifndef CURL_DISABLE_VERBOSE_STRINGS
doh->state.feat = &Curl_trc_feat_dns;
#endif
ERROR_CHECK_SETOPT(CURLOPT_URL, url);
ERROR_CHECK_SETOPT(CURLOPT_DEFAULT_PROTOCOL, "https");
- ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_write_cb);
- ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, &p->resp_body);
- ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, p->req_body);
- ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)p->req_body_len);
- ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, headers);
+ ERROR_CHECK_SETOPT(CURLOPT_WRITEFUNCTION, doh_probe_write_cb);
+ ERROR_CHECK_SETOPT(CURLOPT_WRITEDATA, doh);
+ ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDS, doh_req->req_body);
+ ERROR_CHECK_SETOPT(CURLOPT_POSTFIELDSIZE, (long)doh_req->req_body_len);
+ ERROR_CHECK_SETOPT(CURLOPT_HTTPHEADER, doh_req->req_hds);
#ifdef USE_HTTP2
ERROR_CHECK_SETOPT(CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_2TLS);
ERROR_CHECK_SETOPT(CURLOPT_PIPEWAIT, 1L);
(void)curl_easy_setopt(doh, CURLOPT_SSL_OPTIONS, mask);
}
- doh->set.fmultidone = doh_done;
- doh->set.dohfor_mid = data->mid; /* for which transfer this is done */
+ doh->state.internal = TRUE;
+ doh->master_mid = data->mid; /* master transfer of this one */
+
+ if(Curl_meta_set(doh, CURL_EZM_DOH_PROBE, doh_req, doh_probe_dtor)) {
+ result = CURLE_OUT_OF_MEMORY;
+ goto error;
+ }
+ doh_req = NULL;
/* DoH handles must not inherit private_data. The handles may be passed to
the user via callbacks and the user will be able to identify them as
if(curl_multi_add_handle(multi, doh))
goto error;
- p->easy_mid = doh->mid;
+ *pmid = doh->mid;
return CURLE_OK;
error:
Curl_close(&doh);
- p->easy_mid = -1;
+ if(doh_req)
+ doh_probe_dtor(NULL, 0, doh_req);
return result;
}
int *waitp)
{
CURLcode result = CURLE_OK;
- struct doh_probes *dohp;
+ struct doh_probes *dohp = NULL;
struct connectdata *conn = data->conn;
size_t i;
- *waitp = FALSE;
-
- DEBUGASSERT(!data->state.async.doh);
DEBUGASSERT(conn);
+ DEBUGASSERT(!data->state.async.doh);
+ if(data->state.async.doh)
+ Curl_doh_cleanup(data);
data->state.async.done = FALSE;
data->state.async.port = port;
return NULL;
/* start clean, consider allocating this struct on demand */
- dohp = data->state.async.doh = calloc(1, sizeof(struct doh_probes));
+ data->state.async.doh = dohp = calloc(1, sizeof(struct doh_probes));
if(!dohp)
return NULL;
for(i = 0; i < DOH_SLOT_COUNT; ++i) {
- dohp->probe[i].easy_mid = -1;
+ dohp->probe_resp[i].probe_mid = -1;
+ Curl_dyn_init(&dohp->probe_resp[i].body, DYN_DOH_RESPONSE);
}
conn->bits.doh = TRUE;
dohp->host = data->state.async.hostname;
dohp->port = data->state.async.port;
- dohp->req_hds =
- curl_slist_append(NULL,
- "Content-Type: application/dns-message");
- if(!dohp->req_hds)
- goto error;
+ /* We are making sub easy handles and want to be called back when
+ * one is done. */
+ data->sub_xfer_done = doh_probe_done;
/* create IPv4 DoH request */
- (void)ip_version; /* WHY not select on this for ipv4? */
- result = doh_run_probe(data, &dohp->probe[DOH_SLOT_IPV4],
- DNS_TYPE_A, hostname, data->set.str[STRING_DOH],
- data->multi, dohp->req_hds);
+ result = doh_probe_run(data, DNS_TYPE_A,
+ hostname, data->set.str[STRING_DOH],
+ data->multi,
+ &dohp->probe_resp[DOH_SLOT_IPV4].probe_mid);
if(result)
goto error;
dohp->pending++;
#ifdef USE_IPV6
if((ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
/* create IPv6 DoH request */
- result = doh_run_probe(data, &dohp->probe[DOH_SLOT_IPV6],
- DNS_TYPE_AAAA, hostname, data->set.str[STRING_DOH],
- data->multi, dohp->req_hds);
+ result = doh_probe_run(data, DNS_TYPE_AAAA,
+ hostname, data->set.str[STRING_DOH],
+ data->multi,
+ &dohp->probe_resp[DOH_SLOT_IPV6].probe_mid);
if(result)
goto error;
dohp->pending++;
if(!qname)
goto error;
}
- result = doh_run_probe(data, &dohp->probe[DOH_SLOT_HTTPS_RR],
- DNS_TYPE_HTTPS,
+ result = doh_probe_run(data, DNS_TYPE_HTTPS,
qname ? qname : hostname, data->set.str[STRING_DOH],
- data->multi, dohp->req_hds);
+ data->multi,
+ &dohp->probe_resp[DOH_SLOT_HTTPS_RR].probe_mid);
free(qname);
if(result)
goto error;
if(!dohp)
return CURLE_OUT_OF_MEMORY;
- if(dohp->probe[DOH_SLOT_IPV4].easy_mid < 0 &&
- dohp->probe[DOH_SLOT_IPV6].easy_mid < 0) {
+ if(dohp->probe_resp[DOH_SLOT_IPV4].probe_mid < 0 &&
+ dohp->probe_resp[DOH_SLOT_IPV6].probe_mid < 0) {
failf(data, "Could not DoH-resolve: %s", dohp->host);
return CONN_IS_PROXIED(data->conn) ? CURLE_COULDNT_RESOLVE_PROXY :
CURLE_COULDNT_RESOLVE_HOST;
/* parse the responses, create the struct and return it! */
de_init(&de);
for(slot = 0; slot < DOH_SLOT_COUNT; slot++) {
- struct doh_probe *p = &dohp->probe[slot];
+ struct doh_response *p = &dohp->probe_resp[slot];
if(!p->dnstype)
continue;
- rc[slot] = doh_resp_decode(Curl_dyn_uptr(&p->resp_body),
- Curl_dyn_len(&p->resp_body),
+ rc[slot] = doh_resp_decode(Curl_dyn_uptr(&p->body),
+ Curl_dyn_len(&p->body),
p->dnstype, &de);
- Curl_dyn_free(&p->resp_body);
#ifndef CURL_DISABLE_VERBOSE_STRINGS
if(rc[slot]) {
infof(data, "DoH: %s type %s for %s", doh_strerror(rc[slot]),
curl_off_t mid;
size_t slot;
for(slot = 0; slot < DOH_SLOT_COUNT; slot++) {
- mid = doh->probe[slot].easy_mid;
+ mid = doh->probe_resp[slot].probe_mid;
if(mid < 0)
continue;
- doh->probe[slot].easy_mid = -1;
- /* should have been called before data is removed from multi handle */
+ doh->probe_resp[slot].probe_mid = -1;
DEBUGASSERT(data->multi);
probe_data = data->multi ? Curl_multi_get_handle(data->multi, mid) :
NULL;
if(!probe_data) {
DEBUGF(infof(data, "Curl_doh_close: xfer for mid=%"
FMT_OFF_T " not found!",
- doh->probe[slot].easy_mid));
+ doh->probe_resp[slot].probe_mid));
continue;
}
/* data->multi might already be reset at this time */
curl_multi_remove_handle(data->multi, probe_data);
Curl_close(&probe_data);
}
+ data->sub_xfer_done = NULL;
}
}
void Curl_doh_cleanup(struct Curl_easy *data)
{
- struct doh_probes *doh = data->state.async.doh;
- if(doh) {
+ struct doh_probes *dohp = data->state.async.doh;
+ if(dohp) {
+ int i;
Curl_doh_close(data);
- curl_slist_free_all(doh->req_hds);
- data->state.async.doh->req_hds = NULL;
+ for(i = 0; i < DOH_SLOT_COUNT; ++i) {
+ Curl_dyn_free(&dohp->probe_resp[i].body);
+ }
Curl_safefree(data->state.async.doh);
}
- Curl_safefree(data->state.async.hostname);
}
#endif /* CURL_DISABLE_DOH */
DNS_TYPE_HTTPS = 65
} DNStype;
-/* one of these for each DoH request */
-struct doh_probe {
- curl_off_t easy_mid; /* multi id of easy handle doing the lookup */
- DNStype dnstype;
- unsigned char req_body[512];
- size_t req_body_len;
- struct dynbuf resp_body;
-};
-
enum doh_slot_num {
/* Explicit values for first two symbols so as to match hard-coded
* constants in existing code
DOH_SLOT_COUNT
};
-struct doh_probes {
+#define CURL_EZM_DOH_PROBE "ezm:doh-p"
+
+/* each DoH probe request has this
+ * as easy meta for CURL_EZM_DOH_PROBE */
+struct doh_request {
+ DNStype dnstype;
+ unsigned char req_body[512];
+ size_t req_body_len;
struct curl_slist *req_hds;
- struct doh_probe probe[DOH_SLOT_COUNT];
+ struct dynbuf resp_body;
+};
+
+struct doh_response {
+ curl_off_t probe_mid;
+ struct dynbuf body;
+ DNStype dnstype;
+ CURLcode result;
+};
+
+/* each transfer firing off DoH requests has this
+ * as easy meta for CURL_EZM_DOH_MASTER */
+struct doh_probes {
+ struct doh_response probe_resp[DOH_SLOT_COUNT];
unsigned int pending; /* still outstanding probes */
int port;
const char *host;
return result;
}
+static void dupeasy_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_easy_duphandle() is an external interface to allow duplication of a
* given input easy handle. The returned handle will be a new working handle
*/
outcurl->set.buffer_size = data->set.buffer_size;
+ Curl_hash_init(&outcurl->meta_hash, 23,
+ Curl_hash_str, Curl_str_key_compare, dupeasy_meta_freeentry);
Curl_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER);
Curl_netrc_init(&outcurl->state.netrc);
outcurl->state.recent_conn_id = -1;
outcurl->id = -1;
outcurl->mid = -1;
+ outcurl->master_mid = -1;
#ifndef CURL_DISABLE_HTTP
Curl_llist_init(&outcurl->state.httphdrs, NULL);
{
struct Curl_easy *data = d;
Curl_req_hard_reset(&data->req, data);
+ Curl_hash_clean(&data->meta_hash);
+ /* clear all meta data */
+ Curl_meta_reset(data);
+ /* clear any async resolve data */
+ Curl_async_shutdown(data);
/* zero out UserDefined data: */
Curl_freeset(data);
memset(&data->set, 0, sizeof(struct UserDefined));
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH)
Curl_http_auth_cleanup_digest(data);
#endif
+ data->master_mid = -1;
}
/*
return CURLE_NOT_BUILT_IN;
#endif
}
+
+CURLcode Curl_meta_set(struct Curl_easy *data, const char *key,
+ void *meta_data, Curl_meta_dtor *meta_dtor)
+{
+ if(!Curl_hash_add2(&data->meta_hash, CURL_UNCONST(key), strlen(key) + 1,
+ meta_data, meta_dtor)) {
+ meta_dtor(CURL_UNCONST(key), strlen(key) + 1, meta_data);
+ }
+ return CURLE_OK;
+}
+
+void Curl_meta_clear(struct Curl_easy *data, const char *key)
+{
+ Curl_hash_delete(&data->meta_hash, CURL_UNCONST(key), strlen(key) + 1);
+}
+
+void *Curl_meta_get(struct Curl_easy *data, const char *key)
+{
+ return Curl_hash_pick(&data->meta_hash, CURL_UNCONST(key), strlen(key) + 1);
+}
+
+void Curl_meta_reset(struct Curl_easy *data)
+{
+ Curl_hash_clean(&data->meta_hash);
+}
/* No luck, we need to resolve hostname. Notify user callback. */
if(data->set.resolver_start) {
- void *resolver;
+ void *resolver = NULL;
int st;
+#ifdef CURLRES_ASYNCH
if(Curl_async_get_impl(data, &resolver))
goto error;
+#endif
Curl_set_in_callback(data, TRUE);
st = data->set.resolver_start(resolver, NULL,
data->set.resolver_start_client);
if(dns)
Curl_resolv_unlink(data, &dns);
*entry = NULL;
+ Curl_async_shutdown(data);
return CURLE_COULDNT_RESOLVE_HOST;
}
data->state.async.ip_version);
if(*dns) {
/* Tell a possibly async resolver we no longer need the results. */
+ infof(data, "Hostname '%s' was found in DNS cache",
+ data->state.async.hostname);
Curl_async_shutdown(data);
data->state.async.dns = *dns;
data->state.async.done = TRUE;
- infof(data, "Hostname '%s' was found in DNS cache",
- data->state.async.hostname);
return CURLE_OK;
}
data->multi = NULL; /* clear the association to this multi handle */
data->mid = -1;
+ data->master_mid = -1;
/* NOTE NOTE NOTE
We do not touch the easy handle here! */
}
if(MSTATE_COMPLETED == data->mstate) {
- if(data->set.fmultidone) {
- /* signal via callback instead */
- data->set.fmultidone(data, result);
+ if(data->master_mid >= 0) {
+ /* A sub transfer, not for msgsent to application */
+ struct Curl_easy *mdata;
+
+ CURL_TRC_M(data, "sub xfer done for master %" FMT_OFF_T,
+ data->master_mid);
+ mdata = Curl_multi_get_handle(multi, data->master_mid);
+ if(mdata) {
+ if(mdata->sub_xfer_done)
+ mdata->sub_xfer_done(mdata, data, result);
+ else
+ CURL_TRC_M(data, "master easy %" FMT_OFF_T
+ " without sub_xfer_done.", data->master_mid);
+ }
+ else {
+ CURL_TRC_M(data, "master easy %" FMT_OFF_T " already gone.",
+ data->master_mid);
+ }
}
else {
/* now fill in the Curl_message with this info */
if(req->sendbuf_init)
Curl_bufq_free(&req->sendbuf);
Curl_client_cleanup(data);
-
-#ifndef CURL_DISABLE_DOH
- Curl_doh_cleanup(data);
-#endif
}
static CURLcode xfer_send(struct Curl_easy *data,
Curl_safefree(data->info.contenttype);
Curl_safefree(data->info.wouldredirect);
- Curl_async_shutdown(data);
+ Curl_async_destroy(data);
data_priority_cleanup(data);
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
set->postfieldsize = -1; /* unknown size */
set->maxredirs = 30; /* sensible default */
-#ifndef CURL_DISABLE_DOH
- set->dohfor_mid = -1;
-#endif
-
set->method = HTTPREQ_GET; /* Default HTTP request */
#ifndef CURL_DISABLE_RTSP
set->rtspreq = RTSPREQ_OPTIONS; /* Default RTSP request */
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()
*
data->magic = CURLEASY_MAGIC_NUMBER;
+ Curl_hash_init(&data->meta_hash, 23,
+ Curl_hash_str, Curl_str_key_compare, easy_meta_freeentry);
Curl_dyn_init(&data->state.headerb, CURL_MAX_HTTP_HEADER);
Curl_req_init(&data->req);
Curl_initinfo(data);
/* and not assigned an id yet */
data->id = -1;
data->mid = -1;
-#ifndef CURL_DISABLE_DOH
- data->set.dohfor_mid = -1;
-#endif
+ data->master_mid = -1;
data->progress.flags |= PGRS_HIDE;
data->state.current_speed = -1; /* init to negative == impossible */
Curl_dyn_free(&data->state.headerb);
Curl_freeset(data);
Curl_req_free(&data->req, data);
+ Curl_hash_destroy(&data->meta_hash);
free(data);
data = NULL;
}
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
- {
+ if(data->master_mid >= 0)
+ 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;
char **userptr, char **passwdptr,
char **optionsptr);
+/* Attach/Clear/Get meta data for an easy handle. Needs to provide
+ * a destructor, will be automatically called when the easy handle
+ * is reset or closed. */
+typedef void Curl_meta_dtor(void *key, size_t key_len, void *meta_data);
+
+/* Set the transfer meta data for the key. Any existing entry for that
+ * key will be destroyed.
+ * Takes ownership of `meta_data` and destroys it when the call fails. */
+CURLcode Curl_meta_set(struct Curl_easy *data, const char *key,
+ void *meta_data, Curl_meta_dtor *meta_dtor);
+void Curl_meta_clear(struct Curl_easy *data, const char *key);
+void *Curl_meta_get(struct Curl_easy *data, const char *key);
+void Curl_meta_reset(struct Curl_easy *data);
+
/* Get protocol handler for a URI scheme
* @param scheme URI scheme, case-insensitive
* @return NULL of handler not found
BLOB_LAST
};
-/* callback that gets called when this easy handle is completed within a multi
- handle. Only used for internally created transfers, like for example
- DoH. */
-typedef int (*multidone_func)(struct Curl_easy *easy, CURLcode result);
struct UserDefined {
FILE *err; /* the stderr user data goes here */
before resolver start */
void *resolver_start_client; /* pointer to pass to resolver start callback */
long upkeep_interval_ms; /* Time between calls for connection upkeep. */
- multidone_func fmultidone;
-#ifndef CURL_DISABLE_DOH
- curl_off_t dohfor_mid; /* this is a DoH request for that transfer */
-#endif
CURLU *uh; /* URL handle for the current parsed URL */
#ifndef CURL_DISABLE_HTTP
void *trailer_data; /* pointer to pass to trailer data callback */
#define IS_MIME_POST(a) FALSE
#endif
+/* callback that gets called when a sub easy (data->master_mid set) is
+ DONE. Called on the master easy. */
+typedef void multi_sub_xfer_done_cb(struct Curl_easy *master_easy,
+ struct Curl_easy *sub_easy,
+ CURLcode result);
+
/*
* The 'connectdata' struct MUST have all the connection oriented stuff as we
* may have several simultaneous connections and connection structs in memory.
* libcurl application or implicitly during `curl_easy_perform()`,
* a unique identifier inside this one multi instance. */
curl_off_t mid;
+ curl_off_t master_mid; /* if set, this transfer belongs to a master */
+ multi_sub_xfer_done_cb *sub_xfer_done;
struct connectdata *conn;
struct Curl_llist_node multi_queue; /* for multihandle list management */
struct to which this "belongs" when used
by the easy interface */
struct Curl_share *share; /* Share, handles global variable mutexing */
+
+ /* `meta_hash` is a general key-value store for implementations
+ * with the lifetime of the easy handle.
+ * Elements need to be added with their own destructor to be invoked when
+ * the easy handle is cleaned up (see Curl_hash_add2()).*/
+ struct Curl_hash meta_hash;
+
#ifdef USE_LIBPSL
struct PslCache *psl; /* The associated PSL cache. */
#endif
'Curl_creader_def_close' => 'internal api',
'Curl_creader_def_read' => 'internal api',
'Curl_creader_def_total_length' => 'internal api',
+ 'Curl_meta_reset' => 'internal api',
'Curl_trc_dns' => 'internal api',
);