*/
struct eyeballer {
const char *name;
+ const struct Curl_addrinfo *first; /* complete address list, not owned */
const struct Curl_addrinfo *addr; /* List of addresses to try, not owned */
int ai_family; /* matching address family only */
cf_ip_connect_create *cf_create; /* for creating cf */
expire_id timeout_id; /* ID for Curl_expire() */
CURLcode result;
int error;
+ BIT(rewinded); /* if we rewinded the addr list */
BIT(has_started); /* attempts have started */
BIT(is_done); /* out of addresses/time */
BIT(connected); /* cf has connected */
+ BIT(inconclusive); /* connect was not a hard failure, we
+ * might talk to a restarting server */
};
#endif
"ip"));
baller->cf_create = cf_create;
- baller->addr = addr;
+ baller->first = baller->addr = addr;
baller->ai_family = ai_family;
baller->primary = primary;
baller->delay_ms = delay_ms;
}
}
+static void baller_rewind(struct eyeballer *baller)
+{
+ baller->rewinded = TRUE;
+ baller->addr = baller->first;
+ baller->inconclusive = FALSE;
+}
+
static void baller_next_addr(struct eyeballer *baller)
{
baller->addr = addr_next_match(baller->addr, baller->ai_family);
{
if(cf->sockindex == FIRSTSOCKET) {
baller_next_addr(baller);
+ /* If we get inconclusive answers from the server(s), we make
+ * a second iteration over the address list */
+ if(!baller->addr && baller->inconclusive && !baller->rewinded)
+ baller_rewind(baller);
baller_start(cf, data, baller, timeoutms);
}
else {
baller->result = CURLE_OPERATION_TIMEDOUT;
}
}
+ else if(baller->result == CURLE_WEIRD_SERVER_REPLY)
+ baller->inconclusive = TRUE;
}
return baller->result;
}
ngtcp2_conn_in_draining_period(ctx->qconn)) {
/* When a QUIC server instance is shutting down, it may send us a
* CONNECTION_CLOSE right away. Our connection then enters the DRAINING
- * state.
- * This may be a stopping of the service or it may be that the server
- * is reloading and a new instance will start serving soon.
- * In any case, we tear down our socket and start over with a new one.
- * We re-open the underlying UDP cf right now, but do not start
- * connecting until called again.
- */
- int reconn_delay_ms = 200;
-
- CURL_TRC_CF(data, cf, "connect, remote closed, reconnect after %dms",
- reconn_delay_ms);
- Curl_conn_cf_close(cf->next, data);
- cf_ngtcp2_ctx_clear(ctx);
- result = Curl_conn_cf_connect(cf->next, data, FALSE, done);
- if(!result && *done) {
- *done = FALSE;
- ctx->reconnect_at = now;
- ctx->reconnect_at.tv_usec += reconn_delay_ms * 1000;
- Curl_expire(data, reconn_delay_ms, EXPIRE_QUIC);
- result = CURLE_OK;
- }
+ * state. The CONNECT may work in the near future again. Indicate
+ * that as a "weird" reply. */
+ result = CURLE_WEIRD_SERVER_REPLY;
}
#ifndef CURL_DISABLE_VERBOSE_STRINGS
else if(quiche_conn_is_draining(ctx->qconn)) {
/* When a QUIC server instance is shutting down, it may send us a
* CONNECTION_CLOSE right away. Our connection then enters the DRAINING
- * state.
- * This may be a stopping of the service or it may be that the server
- * is reloading and a new instance will start serving soon.
- * In any case, we tear down our socket and start over with a new one.
- * We re-open the underlying UDP cf right now, but do not start
- * connecting until called again.
- */
- int reconn_delay_ms = 200;
-
- CURL_TRC_CF(data, cf, "connect, remote closed, reconnect after %dms",
- reconn_delay_ms);
- Curl_conn_cf_close(cf->next, data);
- cf_quiche_ctx_clear(ctx);
- result = Curl_conn_cf_connect(cf->next, data, FALSE, done);
- if(!result && *done) {
- *done = FALSE;
- ctx->reconnect_at = Curl_now();
- ctx->reconnect_at.tv_usec += reconn_delay_ms * 1000;
- Curl_expire(data, reconn_delay_ms, EXPIRE_QUIC);
- result = CURLE_OK;
- }
+ * state. The CONNECT may work in the near future again. Indicate
+ * that as a "weird" reply. */
+ result = CURLE_WEIRD_SERVER_REPLY;
}
out: