/* called from multi.c when this DoH transfer is complete */
static int doh_done(struct Curl_easy *doh, CURLcode result)
{
- struct Curl_easy *data = doh->set.dohfor;
- struct dohdata *dohp = data->req.doh;
- /* so 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);
- if(result)
- infof(doh, "DoH request %s", curl_easy_strerror(result));
+ struct Curl_easy *data;
- if(!dohp->pending) {
- /* DoH completed */
- curl_slist_free_all(dohp->headers);
- dohp->headers = NULL;
- Curl_expire(data, 0, EXPIRE_RUN_NOW);
+ data = Curl_multi_get_handle(doh->multi, doh->set.dohfor_mid);
+ if(!data) {
+ DEBUGF(infof(doh, "doh_done: xfer for mid=%" CURL_FORMAT_CURL_OFF_T
+ " not found", doh->set.dohfor_mid));
+ DEBUGASSERT(0);
+ }
+ else {
+ struct dohdata *dohp = data->req.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);
+ if(result)
+ infof(doh, "DoH request %s", curl_easy_strerror(result));
+
+ if(!dohp->pending) {
+ /* DoH completed */
+ curl_slist_free_all(dohp->headers);
+ dohp->headers = NULL;
+ Curl_expire(data, 0, EXPIRE_RUN_NOW);
+ }
}
return 0;
}
}
doh->set.fmultidone = doh_done;
- doh->set.dohfor = data; /* identify for which transfer this is done */
- p->easy = doh;
+ doh->set.dohfor_mid = data->mid; /* for which transfer this is done */
/* 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;
}
else
goto error;
CURLcode result = CURLE_OK;
struct dohdata *dohp;
struct connectdata *conn = data->conn;
+ size_t i;
#ifdef USE_HTTPSRR
/* for now, this is only used when ECH is enabled */
# ifdef USE_ECH
if(!dohp)
return NULL;
+ for(i = 0; i < DOH_PROBE_SLOTS; ++i) {
+ dohp->probe[i].easy_mid = -1;
+ }
+
conn->bits.doh = TRUE;
dohp->host = hostname;
dohp->port = port;
if(!dohp)
return CURLE_OUT_OF_MEMORY;
- if(!dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy &&
- !dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy) {
+ if(dohp->probe[DOH_PROBE_SLOT_IPADDR_V4].easy_mid < 0 &&
+ dohp->probe[DOH_PROBE_SLOT_IPADDR_V6].easy_mid < 0) {
failf(data, "Could not DoH-resolve: %s", data->state.async.hostname);
return CONN_IS_PROXIED(data->conn)?CURLE_COULDNT_RESOLVE_PROXY:
CURLE_COULDNT_RESOLVE_HOST;
void Curl_doh_close(struct Curl_easy *data)
{
struct dohdata *doh = data->req.doh;
- if(doh) {
+ if(doh && data->multi) {
+ struct Curl_easy *probe_data;
+ curl_off_t mid;
size_t slot;
for(slot = 0; slot < DOH_PROBE_SLOTS; slot++) {
- if(!doh->probe[slot].easy)
+ mid = doh->probe[slot].easy_mid;
+ if(mid < 0)
+ continue;
+ doh->probe[slot].easy_mid = -1;
+ /* should have been called before data is removed from multi handle */
+ 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=%"
+ CURL_FORMAT_CURL_OFF_T " not found!",
+ doh->probe[slot].easy_mid));
continue;
+ }
/* data->multi might already be reset at this time */
- if(doh->probe[slot].easy->multi)
- curl_multi_remove_handle(doh->probe[slot].easy->multi,
- doh->probe[slot].easy);
- Curl_close(&doh->probe[slot].easy);
+ curl_multi_remove_handle(data->multi, probe_data);
+ Curl_close(&probe_data);
}
}
}
/* one of these for each DoH request */
struct dnsprobe {
- CURL *easy;
+ curl_off_t easy_mid; /* multi id of easy handle doing the lookup */
DNStype dnstype;
unsigned char dohbuffer[512];
size_t dohlen;
struct bufc_pool stream_bufcp; /* spares for stream buffers */
struct dynbuf scratch; /* scratch buffer for temp use */
- struct Curl_hash streams; /* hash of `data->id` to `h2_stream_ctx` */
+ struct Curl_hash streams; /* hash of `data->mid` to `h2_stream_ctx` */
size_t drain_total; /* sum of all stream's UrlState drain */
uint32_t max_concurrent_streams;
uint32_t goaway_error; /* goaway error code from server */
};
#define H2_STREAM_CTX(ctx,data) ((struct h2_stream_ctx *)(\
- data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL))
+ data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
static struct h2_stream_ctx *h2_stream_ctx_create(struct cf_h2_ctx *ctx)
{
if(!stream)
return CURLE_OUT_OF_MEMORY;
- if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) {
+ if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
h2_stream_ctx_free(stream);
return CURLE_OUT_OF_MEMORY;
}
nghttp2_session_send(ctx->h2);
}
- Curl_hash_offt_remove(&ctx->streams, data->id);
+ Curl_hash_offt_remove(&ctx->streams, data->mid);
}
static int h2_client_new(struct Curl_cfilter *cf,
* (unlikely) or the transfer has been done, cleaned up its resources, but
* a read() is called anyway. It is not clear what the calling sequence
* is for such a case. */
- failf(data, "[%zd-%zd], http/2 recv on a transfer never opened "
- "or already cleared", (ssize_t)data->id,
- (ssize_t)cf->conn->connection_id);
+ failf(data, "http/2 recv on a transfer never opened "
+ "or already cleared, mid=%" CURL_FORMAT_CURL_OFF_T, data->mid);
*err = CURLE_HTTP2;
return -1;
}
data->set.server_response_timeout;
data->state.conn_cache->closure_handle->set.no_signal =
data->set.no_signal;
+
+ /* the identifier inside the connection cache */
data->id = data->state.conn_cache->next_easy_id++;
if(data->state.conn_cache->next_easy_id <= 0)
data->state.conn_cache->next_easy_id = 0;
+ /* the identifier inside the multi instance */
+ data->mid = multi->next_easy_mid++;
+ if(multi->next_easy_mid <= 0)
+ multi->next_easy_mid = 0;
+
CONNCACHE_UNLOCK(data);
multi_warn_debug(multi, data);
since we are not part of that multi handle anymore */
data->state.conn_cache = NULL;
- data->multi = NULL; /* clear the association to this multi handle */
-
/* make sure there is no pending message in the queue sent from this easy
handle */
for(e = Curl_llist_head(&multi->msglist); e; e = Curl_node_next(e)) {
}
}
+ data->multi = NULL; /* clear the association to this multi handle */
+ data->mid = -1;
+
/* NOTE NOTE NOTE
We do not touch the easy handle here! */
multi->num_easy--; /* one less to care about now */
multi->xfer_ulbuf_len = 0;
multi->xfer_ulbuf_borrowed = FALSE;
}
+
+struct Curl_easy *Curl_multi_get_handle(struct Curl_multi *multi,
+ curl_off_t mid)
+{
+
+ if(mid >= 0) {
+ struct Curl_easy *data;
+ struct Curl_llist_node *e;
+
+ for(e = Curl_llist_head(&multi->process); e; e = Curl_node_next(e)) {
+ data = Curl_node_elem(e);
+ if(data->mid == mid)
+ return data;
+ }
+ /* may be in msgsent queue */
+ for(e = Curl_llist_head(&multi->msgsent); e; e = Curl_node_next(e)) {
+ data = Curl_node_elem(e);
+ if(data->mid == mid)
+ return data;
+ }
+ /* may be in pending queue */
+ for(e = Curl_llist_head(&multi->pending); e; e = Curl_node_next(e)) {
+ data = Curl_node_elem(e);
+ if(data->mid == mid)
+ return data;
+ }
+ }
+ return NULL;
+}
struct Curl_llist process; /* not in PENDING or MSGSENT */
struct Curl_llist pending; /* in PENDING */
struct Curl_llist msgsent; /* in MSGSENT */
+ curl_off_t next_easy_mid; /* next multi-id for easy handle added */
/* callback function and user data pointer for the *socket() API */
curl_socket_callback socket_cb;
*/
void Curl_multi_xfer_ulbuf_release(struct Curl_easy *data, char *buf);
+/**
+ * Get the transfer handle for the given id. Returns NULL if not found.
+ */
+struct Curl_easy *Curl_multi_get_handle(struct Curl_multi *multi,
+ curl_off_t id);
+
#endif /* HEADER_CURL_MULTIIF_H */
if(!aborted)
(void)req_flush(data);
Curl_client_reset(data);
+#ifndef CURL_DISABLE_DOH
+ Curl_doh_close(data);
+#endif
return CURLE_OK;
}
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 */
Curl_disconnect(data, conn_candidate, FALSE);
else
#ifndef CURL_DISABLE_DOH
- if(data->set.dohfor)
+ if(data->set.dohfor_mid >= 0)
infof(data, "Allowing DoH to override max connection limit");
else
#endif
long upkeep_interval_ms; /* Time between calls for connection upkeep. */
multidone_func fmultidone;
#ifndef CURL_DISABLE_DOH
- struct Curl_easy *dohfor; /* this is a DoH request for that transfer */
+ 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
other using the same cache. For easier tracking
in log output.
This may wrap around after LONG_MAX to 0 again, so it
- has no uniqueness guarantee for very large processings. */
+ has no uniqueness guarantee for very large processings.
+ Note: it has no uniqueness either IFF more than one connection cache
+ is used by the libcurl application. */
curl_off_t id;
+ /* once an easy handle is added to a multi, either explicitly by the
+ * libcurl application or implicitly during `curl_easy_perform()`,
+ * a unique identifier inside this one multi instance. */
+ curl_off_t mid;
struct connectdata *conn;
struct Curl_llist_node multi_queue; /* for multihandle list management */
struct cf_call_data call_data;
struct curltime connect_started; /* time the current attempt started */
struct curltime handshake_at; /* time connect handshake finished */
- struct Curl_hash streams; /* hash `data->id` to `stream_ctx` */
+ struct Curl_hash streams; /* hash `data->mid` to `stream_ctx` */
/* Flags written by msh3/msquic thread */
bool handshake_complete;
bool handshake_succeeded;
};
#define H3_STREAM_CTX(ctx,data) ((struct stream_ctx *)((data && ctx)? \
- Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL))
+ Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
static void h3_stream_ctx_free(struct stream_ctx *stream)
{
H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
CURL_TRC_CF(data, cf, "data setup");
- if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) {
+ if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
h3_stream_ctx_free(stream);
return CURLE_OUT_OF_MEMORY;
}
(void)cf;
if(stream) {
CURL_TRC_CF(data, cf, "easy handle is done");
- Curl_hash_offt_remove(&ctx->streams, data->id);
+ Curl_hash_offt_remove(&ctx->streams, data->mid);
}
}
struct curltime reconnect_at; /* time the next attempt should start */
struct bufc_pool stream_bufcp; /* chunk pool for streams */
struct dynbuf scratch; /* temp buffer for header construction */
- struct Curl_hash streams; /* hash `data->id` to `h3_stream_ctx` */
+ struct Curl_hash streams; /* hash `data->mid` to `h3_stream_ctx` */
size_t max_stream_window; /* max flow window for one stream */
uint64_t max_idle_ms; /* max idle time for QUIC connection */
uint64_t used_bidi_streams; /* bidi streams we have opened */
};
#define H3_STREAM_CTX(ctx,data) ((struct h3_stream_ctx *)(\
- data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL))
+ data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
#define H3_STREAM_CTX_ID(ctx,id) ((struct h3_stream_ctx *)(\
Curl_hash_offt_get(&(ctx)->streams, (id))))
stream->sendbuf_len_in_flight = 0;
Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
- if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) {
+ if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
h3_stream_ctx_free(stream);
return CURLE_OUT_OF_MEMORY;
}
CURL_TRC_CF(data, cf, "[%" CURL_PRId64 "] easy handle is done",
stream->id);
cf_ngtcp2_stream_close(cf, data, stream);
- Curl_hash_offt_remove(&ctx->streams, data->id);
+ Curl_hash_offt_remove(&ctx->streams, data->mid);
}
}
struct curltime first_byte_at; /* when first byte was recvd */
struct curltime reconnect_at; /* time the next attempt should start */
struct bufc_pool stream_bufcp; /* chunk pool for streams */
- struct Curl_hash streams; /* hash `data->id` to `h3_stream_ctx` */
+ struct Curl_hash streams; /* hash `data->mid` to `h3_stream_ctx` */
size_t max_stream_window; /* max flow window for one stream */
uint64_t max_idle_ms; /* max idle time for QUIC connection */
BIT(initialized);
};
#define H3_STREAM_CTX(ctx,data) ((struct h3_stream_ctx *)(\
- data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL))
+ data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
static void h3_stream_ctx_free(struct h3_stream_ctx *stream)
{
stream->recv_buf_nonflow = 0;
Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
- if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) {
+ if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
h3_stream_ctx_free(stream);
return CURLE_OUT_OF_MEMORY;
}
stream->closed = TRUE;
}
- Curl_hash_offt_remove(&ctx->streams, data->id);
+ Curl_hash_offt_remove(&ctx->streams, data->mid);
}
}
struct curltime handshake_at; /* time connect handshake finished */
struct curltime reconnect_at; /* time the next attempt should start */
struct bufc_pool stream_bufcp; /* chunk pool for streams */
- struct Curl_hash streams; /* hash `data->id` to `stream_ctx` */
+ struct Curl_hash streams; /* hash `data->mid` to `stream_ctx` */
curl_off_t data_recvd;
BIT(initialized);
BIT(goaway); /* got GOAWAY from server */
};
#define H3_STREAM_CTX(ctx,data) ((struct stream_ctx *)(\
- data? Curl_hash_offt_get(&(ctx)->streams, (data)->id) : NULL))
+ data? Curl_hash_offt_get(&(ctx)->streams, (data)->mid) : NULL))
static void h3_stream_ctx_free(struct stream_ctx *stream)
{
H3_STREAM_RECV_CHUNKS, BUFQ_OPT_SOFT_LIMIT);
Curl_h1_req_parse_init(&stream->h1, H1_PARSE_DEFAULT_MAX_LINE_LEN);
- if(!Curl_hash_offt_set(&ctx->streams, data->id, stream)) {
+ if(!Curl_hash_offt_set(&ctx->streams, data->mid, stream)) {
h3_stream_ctx_free(stream);
return CURLE_OUT_OF_MEMORY;
}
if(result)
CURL_TRC_CF(data, cf, "data_done, flush egress -> %d", result);
}
- Curl_hash_offt_remove(&ctx->streams, data->id);
+ Curl_hash_offt_remove(&ctx->streams, data->mid);
}
}