END_STREAM_REASON_TIMEOUT blurs together two very different error cases
Here's one case where END_STREAM_REASON_TIMEOUT is used (in connection_ap_expire_beginning()):
if (seconds_since_born >= options->SocksTimeout) {
log_fn(severity, LD_APP,
"Tried for %d seconds to get a connection to %s:%d. "
"Giving up. (%s)",
seconds_since_born,
safe_str_client(entry_conn->socks_request->address),
entry_conn->socks_request->port,
conn_state_to_string(CONN_TYPE_AP, base_conn->state));
connection_mark_unattached_ap(entry_conn, END_STREAM_REASON_TIMEOUT);
}
And here's a second case where it is used, in errno_to_stream_end_reason():
S_CASE(ETIMEDOUT):
return END_STREAM_REASON_TIMEOUT;
That first case is a client-side timeout -- e.g. we didn't get a useful circuit. That second case is an exit-side timeout -- we got a circuit, sent the begin cell, and the exit got a tcp timeout failure making the connection to the destination.
We should be helping the controller distinguish between these two cases.
To upload designs, you'll need to enable LFS and have admin enable hashed storage. More information