message: Port dial outcome logic fix from
diff: @@ -500,6 +500,15 @@ where
  })
  }
  + /// Checks if an address is blocked.
  + #[cfg(test)]
  + pub(crate) fn is_blocked(&self, addr: SocketAddr) -> bool {
  + match self.outgoing.get(&addr) {
  + Some(outgoing) => matches!(outgoing.state, OutgoingState::Blocked { .. }),
  + None => false,
  + }
  + }
  +
  /// Removes an address from the block list.
  ///
  /// Does nothing if the address was not blocked.
  @@ -690,14 +699,17 @@ where
  DialOutcome::Failed { addr, error, when } => {
  info!(err = display_error(&error), "outgoing connection failed");
  - let failures_so_far = if let Some(outgoing) = self.outgoing.get(&addr) {
  + let failures_so_far: Option<_> = if let Some(outgoing) = self.outgoing.get(&addr) {
  match outgoing.state {
  OutgoingState::Connecting { failures_so_far,.. } => {
  - failures_so_far + 1
  + Some(failures_so_far + 1)
  }
  OutgoingState::Blocked { .. } => {
  debug!("failed dial outcome after block ignored");
  - 1
  +
  + // We do not set the connection to "waiting" if an out-of-order failed
  + // connection arrives, but continue to honor the blocking.
  + None
  }
  OutgoingState::Waiting { .. } |
  OutgoingState::Connected { .. } |
  @@ -705,14 +717,20 @@ where
  warn!(
  "processing dial outcome on a connection that was not marked as connecting or blocked"
  );
  - 1
  +
  + // Ensure we do not override the existing state, return early.
  + None
  }
  }
  } else {
  warn!("processing dial outcome non-existent connection");
  - 1
  +
  + // If the connection does not exist, do not introduce it!
  + None
  };
  + // If we had actual failure we are going to honor, set the waiting state.
  + if let Some(failures_so_far) = failures_so_far {
  self.change_outgoing_state(
  addr,
  OutgoingState::Waiting {
  @@ -721,6 +739,8 @@ where
  last_failure: when,
  },
  );
  + }
  +
  None
  }
  DialOutcome::Loopback { addr } => {
  @@ -1282,4 +1302,42 @@ mod tests {
  // We now expect to be connected through the first connection (see documentation).
  assert_eq!(manager.get_route(id_a), Some(&1));
  }
  +
  + #[test]
  + fn blocking_not_overridden_by_racing_failed_connections() {
  + init_logging();
  +
  + let mut clock = TestClock::new();
  +
  + let addr_a: SocketAddr = "1.2.3.4:1234".parse().unwrap();
  +
  + let mut manager = OutgoingManager::<u32, TestDialerError>::new(test_config());
  +
  + assert!(!manager.is_blocked(addr_a));
  +
  + // Block `addr_a` from the start.
  + assert!(manager.block_addr(addr_a, clock.now()).is_none());
  + assert!(manager.is_blocked(addr_a));
  +
  + clock.advance_time(60);
  +
  + // Receive an "illegal" dial outcome, even though we did not dial.
  + assert!(manager
  + .handle_dial_outcome(DialOutcome::Failed {
  + addr: addr_a,
  + error: TestDialerError { id: 12345 },
  +
  + /// The moment the connection attempt failed.
  + when: clock.now(),
  + })
  + .is_none());
  +
  + // The failed connection should _not_ have reset the block!
  + assert!(manager.is_blocked(addr_a));
  + clock.advance_time(60);
  + assert!(manager.is_blocked(addr_a));
  +
  + assert!(manager.perform_housekeeping(clock.now()).is_empty());
  + assert!(manager.is_blocked(addr_a));
  + }
  }
  