|
@@ -425,14 +425,17 @@ directory_pick_generic_dirserver(dirinfo_type_t type, int pds_flags,
|
|
|
* Use <b>pds_flags</b> as arguments to router_pick_directory_server()
|
|
|
* or router_pick_trusteddirserver().
|
|
|
*/
|
|
|
-MOCK_IMPL(void, directory_get_from_dirserver, (uint8_t dir_purpose,
|
|
|
- uint8_t router_purpose,
|
|
|
- const char *resource,
|
|
|
- int pds_flags))
|
|
|
+MOCK_IMPL(void, directory_get_from_dirserver, (
|
|
|
+ uint8_t dir_purpose,
|
|
|
+ uint8_t router_purpose,
|
|
|
+ const char *resource,
|
|
|
+ int pds_flags,
|
|
|
+ download_want_authority_t want_authority))
|
|
|
{
|
|
|
const routerstatus_t *rs = NULL;
|
|
|
const or_options_t *options = get_options();
|
|
|
- int prefer_authority = directory_fetches_from_authorities(options);
|
|
|
+ int prefer_authority = (directory_fetches_from_authorities(options)
|
|
|
+ || want_authority == DL_WANT_AUTHORITY);
|
|
|
int require_authority = 0;
|
|
|
int get_via_tor = purpose_needs_anonymity(dir_purpose, router_purpose);
|
|
|
dirinfo_type_t type = dir_fetch_type(dir_purpose, router_purpose, resource);
|
|
@@ -958,6 +961,12 @@ directory_initiate_command_rend(const tor_addr_t *_addr,
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ * a consensus during bootstrap */
|
|
|
+ if (connection_dir_avoid_extra_connection_for_purpose(dir_purpose)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
conn = dir_connection_new(tor_addr_family(&addr));
|
|
|
|
|
|
|
|
@@ -998,6 +1007,9 @@ directory_initiate_command_rend(const tor_addr_t *_addr,
|
|
|
conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING;
|
|
|
|
|
|
case 0:
|
|
|
+ if (connection_dir_close_consensus_conn_if_extra(conn)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
directory_send_command(conn, dir_purpose, 1, resource,
|
|
|
payload, payload_len,
|
|
@@ -1041,6 +1053,9 @@ directory_initiate_command_rend(const tor_addr_t *_addr,
|
|
|
connection_mark_for_close(TO_CONN(conn));
|
|
|
return;
|
|
|
}
|
|
|
+ if (connection_dir_close_consensus_conn_if_extra(conn)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING;
|
|
|
|
|
|
directory_send_command(conn, dir_purpose, 0, resource,
|
|
@@ -3423,8 +3438,205 @@ connection_dir_finished_flushing(dir_connection_t *conn)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ * and connection_dir_close_extra_consensus_conns() that returns 0 if
|
|
|
+ * we can't have, or don't want to close, excess consensus connections. */
|
|
|
+int
|
|
|
+connection_dir_would_close_consensus_conn_helper(void)
|
|
|
+{
|
|
|
+ const or_options_t *options = get_options();
|
|
|
+
|
|
|
+
|
|
|
+ * have created any in the first place */
|
|
|
+ if (!networkstatus_consensus_can_use_multiple_directories(options)) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * If there aren't any excess, we don't have anything to close. */
|
|
|
+ if (!networkstatus_consensus_has_excess_connections()) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * consensus, and we are still bootstrapping (that is, we have no usable
|
|
|
+ * consensus), we don't want to close any until one starts downloading. */
|
|
|
+ if (!networkstatus_consensus_is_downloading_usable_flavor()
|
|
|
+ && networkstatus_consensus_is_boostrapping(time(NULL))) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * we might still have some excess connections hanging around. So we still
|
|
|
+ * have to check if we want to close any, even if we've stopped
|
|
|
+ * bootstrapping. */
|
|
|
+ return 1;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * new consensus connection would become excess immediately, so return 1.
|
|
|
+ * Otherwise, return 0. */
|
|
|
+int
|
|
|
+connection_dir_avoid_extra_connection_for_purpose(unsigned int purpose)
|
|
|
+{
|
|
|
+ const or_options_t *options = get_options();
|
|
|
+
|
|
|
+
|
|
|
+ if (purpose != DIR_PURPOSE_FETCH_CONSENSUS) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * have created any in the first place */
|
|
|
+ if (!networkstatus_consensus_can_use_multiple_directories(options)) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ * bootstrapping (that is, we have no usable consensus), we can be sure that
|
|
|
+ * any further connections would be excess. */
|
|
|
+ if (networkstatus_consensus_is_downloading_usable_flavor()
|
|
|
+ && networkstatus_consensus_is_boostrapping(time(NULL))) {
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * conn:
|
|
|
+ * - if we don't have a consensus, and we're downloading a consensus, and conn
|
|
|
+ * is not downloading a consensus yet, close it;
|
|
|
+ * - if we do have a consensus, conn is excess, close it. */
|
|
|
+int
|
|
|
+connection_dir_close_consensus_conn_if_extra(dir_connection_t *conn)
|
|
|
+{
|
|
|
+ tor_assert(conn);
|
|
|
+ tor_assert(conn->base_.type == CONN_TYPE_DIR);
|
|
|
+
|
|
|
+
|
|
|
+ if (conn->base_.purpose != DIR_PURPOSE_FETCH_CONSENSUS) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if (conn->base_.marked_for_close) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!connection_dir_would_close_consensus_conn_helper()) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ const int we_are_bootstrapping = networkstatus_consensus_is_boostrapping(
|
|
|
+ time(NULL));
|
|
|
+
|
|
|
+
|
|
|
+ * as this is prone to race-conditions. So leave it for
|
|
|
+ * connection_dir_consider_close_extra_consensus_conns() to clean up.
|
|
|
+ *
|
|
|
+ * But if conn has just started connecting, or we have a consensus already,
|
|
|
+ * we can be sure it's not needed any more. */
|
|
|
+ if (!we_are_bootstrapping
|
|
|
+ || conn->base_.state == DIR_CONN_STATE_CONNECTING) {
|
|
|
+ connection_close_immediate(&conn->base_);
|
|
|
+ connection_mark_for_close(&conn->base_);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * them:
|
|
|
+ * - if we don't have a consensus, and we're downloading a consensus, keep an
|
|
|
+ * earlier connection, or a connection to a fallback directory, and close
|
|
|
+ * all other connections;
|
|
|
+ * - if we do have a consensus, close all connections: they are all excess. */
|
|
|
+void
|
|
|
+connection_dir_close_extra_consensus_conns(void)
|
|
|
+{
|
|
|
+ if (!connection_dir_would_close_consensus_conn_helper()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ int we_are_bootstrapping = networkstatus_consensus_is_boostrapping(
|
|
|
+ time(NULL));
|
|
|
+
|
|
|
+ const char *usable_resource = networkstatus_get_flavor_name(
|
|
|
+ usable_consensus_flavor());
|
|
|
+ smartlist_t *consens_usable_conns =
|
|
|
+ connection_dir_list_by_purpose_and_resource(
|
|
|
+ DIR_PURPOSE_FETCH_CONSENSUS,
|
|
|
+ usable_resource);
|
|
|
+
|
|
|
+
|
|
|
+ * keep, favouring:
|
|
|
+ * - connections opened earlier (they are likely to have progressed further)
|
|
|
+ * - connections to fallbacks (to reduce the load on authorities) */
|
|
|
+ dir_connection_t *kept_download_conn = NULL;
|
|
|
+ int kept_is_authority = 0;
|
|
|
+ if (we_are_bootstrapping) {
|
|
|
+ SMARTLIST_FOREACH_BEGIN(consens_usable_conns,
|
|
|
+ dir_connection_t *, d) {
|
|
|
+ tor_assert(d);
|
|
|
+ int d_is_authority = router_digest_is_trusted_dir(d->identity_digest);
|
|
|
+
|
|
|
+ * prefer fallbacks. */
|
|
|
+ if (d->base_.state != DIR_CONN_STATE_CONNECTING) {
|
|
|
+ if (!kept_download_conn || (kept_is_authority && !d_is_authority)) {
|
|
|
+ kept_download_conn = d;
|
|
|
+ kept_is_authority = d_is_authority;
|
|
|
+
|
|
|
+ * of any other connections */
|
|
|
+ if (!kept_is_authority)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } SMARTLIST_FOREACH_END(d);
|
|
|
+ }
|
|
|
+
|
|
|
+ SMARTLIST_FOREACH_BEGIN(consens_usable_conns,
|
|
|
+ dir_connection_t *, d) {
|
|
|
+ tor_assert(d);
|
|
|
+
|
|
|
+ if (kept_download_conn && d == kept_download_conn)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ if (!d->base_.marked_for_close) {
|
|
|
+ connection_close_immediate(&d->base_);
|
|
|
+ connection_mark_for_close(&d->base_);
|
|
|
+ }
|
|
|
+ } SMARTLIST_FOREACH_END(d);
|
|
|
+
|
|
|
+ smartlist_free(consens_usable_conns);
|
|
|
+ consens_usable_conns = NULL;
|
|
|
+
|
|
|
+
|
|
|
+ const int final_connecting_conn_count =
|
|
|
+ connection_dir_count_by_purpose_resource_and_state(
|
|
|
+ DIR_PURPOSE_FETCH_CONSENSUS,
|
|
|
+ usable_resource,
|
|
|
+ DIR_CONN_STATE_CONNECTING);
|
|
|
+ if (final_connecting_conn_count > 0) {
|
|
|
+ log_warn(LD_BUG, "Expected 0 consensus connections connecting after "
|
|
|
+ "cleanup, got %d.", final_connecting_conn_count);
|
|
|
+ }
|
|
|
+ const int expected_final_conn_count = (we_are_bootstrapping ? 1 : 0);
|
|
|
+ const int final_conn_count =
|
|
|
+ connection_dir_count_by_purpose_and_resource(
|
|
|
+ DIR_PURPOSE_FETCH_CONSENSUS,
|
|
|
+ usable_resource);
|
|
|
+ if (final_conn_count > expected_final_conn_count) {
|
|
|
+ log_warn(LD_BUG, "Expected %d consensus connections after cleanup, got "
|
|
|
+ "%d.", expected_final_conn_count, final_connecting_conn_count);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
- * server */
|
|
|
+ * server, and return 0, or, if the connection is an excess bootstrap
|
|
|
+ * connection, close all excess bootstrap connections.
|
|
|
+ * Only used when connections don't immediately connect. */
|
|
|
int
|
|
|
connection_dir_finished_connecting(dir_connection_t *conn)
|
|
|
{
|
|
@@ -3435,31 +3647,64 @@ connection_dir_finished_connecting(dir_connection_t *conn)
|
|
|
log_debug(LD_HTTP,"Dir connection to router %s:%u established.",
|
|
|
conn->base_.address,conn->base_.port);
|
|
|
|
|
|
- conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING;
|
|
|
+ if (connection_dir_close_consensus_conn_if_extra(conn)) {
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ conn->base_.state = DIR_CONN_STATE_CLIENT_SENDING;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
|
|
|
- * in <b>dls</b> and whether we are acting as directory <b>server</b>, and
|
|
|
- * then return a list of int pointers defining download delays in seconds.
|
|
|
- * Helper function for download_status_increment_failure() and
|
|
|
- * download_status_reset(). */
|
|
|
+ * in <b>dls</b> and <b>options</b>.
|
|
|
+ * Then return a list of int pointers defining download delays in seconds.
|
|
|
+ * Helper function for download_status_increment_failure(),
|
|
|
+ * download_status_reset(), and download_status_increment_attempt(). */
|
|
|
static const smartlist_t *
|
|
|
-find_dl_schedule_and_len(download_status_t *dls, int server)
|
|
|
+find_dl_schedule(download_status_t *dls, const or_options_t *options)
|
|
|
{
|
|
|
+
|
|
|
+ const int dir_server = options->DirPort_set;
|
|
|
+ const int multi_d = networkstatus_consensus_can_use_multiple_directories(
|
|
|
+ options);
|
|
|
+ const int we_are_bootstrapping = networkstatus_consensus_is_boostrapping(
|
|
|
+ time(NULL));
|
|
|
+ const int use_fallbacks = networkstatus_consensus_can_use_extra_fallbacks(
|
|
|
+ options);
|
|
|
switch (dls->schedule) {
|
|
|
case DL_SCHED_GENERIC:
|
|
|
- if (server)
|
|
|
- return get_options()->TestingServerDownloadSchedule;
|
|
|
- else
|
|
|
- return get_options()->TestingClientDownloadSchedule;
|
|
|
+ if (dir_server) {
|
|
|
+ return options->TestingServerDownloadSchedule;
|
|
|
+ } else {
|
|
|
+ return options->TestingClientDownloadSchedule;
|
|
|
+ }
|
|
|
case DL_SCHED_CONSENSUS:
|
|
|
- if (server)
|
|
|
- return get_options()->TestingServerConsensusDownloadSchedule;
|
|
|
- else
|
|
|
- return get_options()->TestingClientConsensusDownloadSchedule;
|
|
|
+ if (!multi_d) {
|
|
|
+ return options->TestingServerConsensusDownloadSchedule;
|
|
|
+ } else {
|
|
|
+ if (we_are_bootstrapping) {
|
|
|
+ if (!use_fallbacks) {
|
|
|
+
|
|
|
+ return
|
|
|
+ options->TestingClientBootstrapConsensusAuthorityOnlyDownloadSchedule;
|
|
|
+ } else if (dls->want_authority) {
|
|
|
+
|
|
|
+ * connecting to an authority */
|
|
|
+ return
|
|
|
+ options->TestingClientBootstrapConsensusAuthorityDownloadSchedule;
|
|
|
+ } else {
|
|
|
+
|
|
|
+ */
|
|
|
+ return
|
|
|
+ options->TestingClientBootstrapConsensusFallbackDownloadSchedule;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ return options->TestingClientConsensusDownloadSchedule;
|
|
|
+ }
|
|
|
+ }
|
|
|
case DL_SCHED_BRIDGE:
|
|
|
- return get_options()->TestingBridgeDownloadSchedule;
|
|
|
+ return options->TestingBridgeDownloadSchedule;
|
|
|
default:
|
|
|
tor_assert(0);
|
|
|
}
|
|
@@ -3468,54 +3713,168 @@ find_dl_schedule_and_len(download_status_t *dls, int server)
|
|
|
return NULL;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
+
|
|
|
+ * Set dls->next_attempt_at based on now, and return the delay.
|
|
|
+ * Helper for download_status_increment_failure and
|
|
|
+ * download_status_increment_attempt. */
|
|
|
+STATIC int
|
|
|
+download_status_schedule_get_delay(download_status_t *dls,
|
|
|
+ const smartlist_t *schedule,
|
|
|
+ time_t now)
|
|
|
+{
|
|
|
+ tor_assert(dls);
|
|
|
+ tor_assert(schedule);
|
|
|
+
|
|
|
+ int delay = INT_MAX;
|
|
|
+ uint8_t dls_schedule_position = (dls->increment_on
|
|
|
+ == DL_SCHED_INCREMENT_ATTEMPT
|
|
|
+ ? dls->n_download_attempts
|
|
|
+ : dls->n_download_failures);
|
|
|
+
|
|
|
+ if (dls_schedule_position < smartlist_len(schedule))
|
|
|
+ delay = *(int *)smartlist_get(schedule, dls_schedule_position);
|
|
|
+ else if (dls_schedule_position == IMPOSSIBLE_TO_DOWNLOAD)
|
|
|
+ delay = INT_MAX;
|
|
|
+ else
|
|
|
+ delay = *(int *)smartlist_get(schedule, smartlist_len(schedule) - 1);
|
|
|
+
|
|
|
+
|
|
|
+ * non-negative allows us to safely do the wrapping check below. */
|
|
|
+ tor_assert(delay >= 0);
|
|
|
+
|
|
|
+
|
|
|
+ * that won't overflow (since delay is non-negative). */
|
|
|
+ if (delay < INT_MAX && now <= INT_MAX - delay) {
|
|
|
+ dls->next_attempt_at = now+delay;
|
|
|
+ } else {
|
|
|
+ dls->next_attempt_at = TIME_MAX;
|
|
|
+ }
|
|
|
+
|
|
|
+ return delay;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * incremented dls_n_download_increments times. The message varies based on
|
|
|
+ * was_schedule_incremented (if not, not_incremented_response is logged), and
|
|
|
+ * the values of increment, dls_next_attempt_at, and now.
|
|
|
+ * Helper for download_status_increment_failure and
|
|
|
+ * download_status_increment_attempt. */
|
|
|
+static void
|
|
|
+download_status_log_helper(const char *item, int was_schedule_incremented,
|
|
|
+ const char *increment_action,
|
|
|
+ const char *not_incremented_response,
|
|
|
+ uint8_t dls_n_download_increments, int increment,
|
|
|
+ time_t dls_next_attempt_at, time_t now)
|
|
|
+{
|
|
|
+ if (item) {
|
|
|
+ if (!was_schedule_incremented)
|
|
|
+ log_debug(LD_DIR, "%s %s %d time(s); I'll try again %s.",
|
|
|
+ item, increment_action, (int)dls_n_download_increments,
|
|
|
+ not_incremented_response);
|
|
|
+ else if (increment == 0)
|
|
|
+ log_debug(LD_DIR, "%s %s %d time(s); I'll try again immediately.",
|
|
|
+ item, increment_action, (int)dls_n_download_increments);
|
|
|
+ else if (dls_next_attempt_at < TIME_MAX)
|
|
|
+ log_debug(LD_DIR, "%s %s %d time(s); I'll try again in %d seconds.",
|
|
|
+ item, increment_action, (int)dls_n_download_increments,
|
|
|
+ (int)(dls_next_attempt_at-now));
|
|
|
+ else
|
|
|
+ log_debug(LD_DIR, "%s %s %d time(s); Giving up for a while.",
|
|
|
+ item, increment_action, (int)dls_n_download_increments);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * Called when an attempt to download <b>dls</b> has failed with HTTP status
|
|
|
* <b>status_code</b>. Increment the failure count (if the code indicates a
|
|
|
- * real failure) and set <b>dls</b>-\>next_attempt_at to an appropriate time
|
|
|
- * in the future. */
|
|
|
+ * real failure, or if we're a server) and set <b>dls</b>-\>next_attempt_at to
|
|
|
+ * an appropriate time in the future and return it.
|
|
|
+ * If <b>dls->increment_on</b> is DL_SCHED_INCREMENT_ATTEMPT, increment the
|
|
|
+ * failure count, and return a time in the far future for the next attempt (to
|
|
|
+ * avoid an immediate retry). */
|
|
|
time_t
|
|
|
download_status_increment_failure(download_status_t *dls, int status_code,
|
|
|
const char *item, int server, time_t now)
|
|
|
{
|
|
|
- const smartlist_t *schedule;
|
|
|
- int increment;
|
|
|
+ int increment = -1;
|
|
|
tor_assert(dls);
|
|
|
+
|
|
|
+
|
|
|
if (status_code != 503 || server) {
|
|
|
if (dls->n_download_failures < IMPOSSIBLE_TO_DOWNLOAD-1)
|
|
|
++dls->n_download_failures;
|
|
|
}
|
|
|
|
|
|
- schedule = find_dl_schedule_and_len(dls, server);
|
|
|
+ if (dls->increment_on == DL_SCHED_INCREMENT_FAILURE) {
|
|
|
+
|
|
|
+ * connection until that connection fails.
|
|
|
+ * We'll never find out about successful connections, but this doesn't
|
|
|
+ * matter, because schedules are reset after a successful download.
|
|
|
+ */
|
|
|
+ if (dls->n_download_attempts < IMPOSSIBLE_TO_DOWNLOAD-1)
|
|
|
+ ++dls->n_download_attempts;
|
|
|
|
|
|
- if (dls->n_download_failures < smartlist_len(schedule))
|
|
|
- increment = *(int *)smartlist_get(schedule, dls->n_download_failures);
|
|
|
- else if (dls->n_download_failures == IMPOSSIBLE_TO_DOWNLOAD)
|
|
|
- increment = INT_MAX;
|
|
|
- else
|
|
|
- increment = *(int *)smartlist_get(schedule, smartlist_len(schedule) - 1);
|
|
|
+
|
|
|
+ */
|
|
|
+ const smartlist_t *schedule = find_dl_schedule(dls, get_options());
|
|
|
+ increment = download_status_schedule_get_delay(dls, schedule, now);
|
|
|
+ }
|
|
|
|
|
|
- if (increment < INT_MAX)
|
|
|
- dls->next_attempt_at = now+increment;
|
|
|
- else
|
|
|
- dls->next_attempt_at = TIME_MAX;
|
|
|
+ download_status_log_helper(item, !dls->increment_on, "failed",
|
|
|
+ "concurrently", dls->n_download_failures,
|
|
|
+ increment, dls->next_attempt_at, now);
|
|
|
|
|
|
- if (item) {
|
|
|
- if (increment == 0)
|
|
|
- log_debug(LD_DIR, "%s failed %d time(s); I'll try again immediately.",
|
|
|
- item, (int)dls->n_download_failures);
|
|
|
- else if (dls->next_attempt_at < TIME_MAX)
|
|
|
- log_debug(LD_DIR, "%s failed %d time(s); I'll try again in %d seconds.",
|
|
|
- item, (int)dls->n_download_failures,
|
|
|
- (int)(dls->next_attempt_at-now));
|
|
|
- else
|
|
|
- log_debug(LD_DIR, "%s failed %d time(s); Giving up for a while.",
|
|
|
- item, (int)dls->n_download_failures);
|
|
|
+ if (dls->increment_on == DL_SCHED_INCREMENT_ATTEMPT) {
|
|
|
+
|
|
|
+ * connections instead */
|
|
|
+ return TIME_MAX;
|
|
|
+ } else {
|
|
|
+ return dls->next_attempt_at;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+ * attempt-based (potentially concurrent) download schedule.
|
|
|
+ * Called when an attempt to download <b>dls</b> is being initiated.
|
|
|
+ * Increment the attempt count and set <b>dls</b>-\>next_attempt_at to an
|
|
|
+ * appropriate time in the future and return it.
|
|
|
+ * If <b>dls->increment_on</b> is DL_SCHED_INCREMENT_FAILURE, don't increment
|
|
|
+ * the attempts, and return a time in the far future (to avoid launching a
|
|
|
+ * concurrent attempt). */
|
|
|
+time_t
|
|
|
+download_status_increment_attempt(download_status_t *dls, const char *item,
|
|
|
+ time_t now)
|
|
|
+{
|
|
|
+ int delay = -1;
|
|
|
+ tor_assert(dls);
|
|
|
+
|
|
|
+ if (dls->increment_on == DL_SCHED_INCREMENT_FAILURE) {
|
|
|
+
|
|
|
+ attempts */
|
|
|
+ log_info(LD_BUG, "Tried to launch an attempt-based connection on a "
|
|
|
+ "failure-based schedule.");
|
|
|
+ return TIME_MAX;
|
|
|
}
|
|
|
+
|
|
|
+ if (dls->n_download_attempts < IMPOSSIBLE_TO_DOWNLOAD-1)
|
|
|
+ ++dls->n_download_attempts;
|
|
|
+
|
|
|
+ const smartlist_t *schedule = find_dl_schedule(dls, get_options());
|
|
|
+ delay = download_status_schedule_get_delay(dls, schedule, now);
|
|
|
+
|
|
|
+ download_status_log_helper(item, dls->increment_on, "attempted",
|
|
|
+ "on failure", dls->n_download_attempts,
|
|
|
+ delay, dls->next_attempt_at, now);
|
|
|
+
|
|
|
return dls->next_attempt_at;
|
|
|
}
|
|
|
|
|
|
|
|
|
* immediately, and/or to show that we don't need it anymore.
|
|
|
*
|
|
|
+ * Must be called to initialise a download schedule, otherwise the zeroth item
|
|
|
+ * in the schedule will never be used.
|
|
|
+ *
|
|
|
* (We find the zeroth element of the download schedule, and set
|
|
|
* next_attempt_at to be the appropriate offset from 'now'. In most
|
|
|
* cases this means setting it to 'now', so the item will be immediately
|
|
@@ -3524,14 +3883,16 @@ download_status_increment_failure(download_status_t *dls, int status_code,
|
|
|
void
|
|
|
download_status_reset(download_status_t *dls)
|
|
|
{
|
|
|
- if (dls->n_download_failures == IMPOSSIBLE_TO_DOWNLOAD)
|
|
|
+ if (dls->n_download_failures == IMPOSSIBLE_TO_DOWNLOAD
|
|
|
+ || dls->n_download_attempts == IMPOSSIBLE_TO_DOWNLOAD)
|
|
|
return;
|
|
|
|
|
|
- const smartlist_t *schedule = find_dl_schedule_and_len(
|
|
|
- dls, get_options()->DirPort_set);
|
|
|
+ const smartlist_t *schedule = find_dl_schedule(dls, get_options());
|
|
|
|
|
|
dls->n_download_failures = 0;
|
|
|
+ dls->n_download_attempts = 0;
|
|
|
dls->next_attempt_at = time(NULL) + *(int *)smartlist_get(schedule, 0);
|
|
|
+
|
|
|
}
|
|
|
|
|
|
|
|
@@ -3542,6 +3903,22 @@ download_status_get_n_failures(const download_status_t *dls)
|
|
|
return dls->n_download_failures;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+ * (if any). This can differ from download_status_get_n_failures() due to
|
|
|
+ * outstanding concurrent attempts. */
|
|
|
+int
|
|
|
+download_status_get_n_attempts(const download_status_t *dls)
|
|
|
+{
|
|
|
+ return dls->n_download_attempts;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+time_t
|
|
|
+download_status_get_next_attempt_at(const download_status_t *dls)
|
|
|
+{
|
|
|
+ return dls->next_attempt_at;
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
* fetches have failed (with uppercase fingerprints listed in <b>failed</b>,
|
|
|
* either as descriptor digests or as identity digests based on
|