Retry on a new circuit for more reasons.
In git commit 388ac4126 I added some code that will never be reached:
if (rh->length > 0 && edge_reason_is_retriable(reason) &&
[...]
case END_STREAM_REASON_CONNECTREFUSED:
if (!conn->chosen_exit_optional)
break; /* break means it'll close, below */
/* Else fall through: expire this circuit, clear the
* chosen_exit_name field, and try again. */
[...]
case END_STREAM_REASON_TIMEOUT:
since those two reasons aren't included in edge_reason_is_retriable().
One fix would be just to remove those dead code lines.
But another fix would be to include both of those reasons in the is_retriable() list, to better tolerate broken exits -- for example, an exit that sets -j REJECT in an iptable rule, rather than putting everything in its exit policy; or an exit for which the routing to the destination has a loop, causing the ttl to expire and some internet router to send back an icmp unreachable.
The downsides are a) if will take many seconds longer before your connection fails, if it fails, and b) we throw away circuits even more often, since we'll now chew through several circuits every time you attempt a destination that is down or unreachable.
The upside is that users will see fewer false failures, in proportion to how many broken or misconfigured exits there are.
I'm not entirely sure which side of the fence I'm on here. I think I lean slightly towards retrying for these two cases.