Time client-side TCP connections when idle, not only while connecting

ss-local, ss-tunnel and ss-redir only timed the upstream connect (as the
C relays did), so a client that never finished its SOCKS handshake, or a
stream whose peer vanished without a FIN, held a pool slot forever. With
the pool now a hard cap that pauses accepting when full, enough of them
stopped the listener for good. Every connection now starts with the idle
deadline, connected() re-arms it, and traffic refreshes it for all kinds
(the idle timeout keeps its 24 h floor, MIN_TCP_IDLE_TIMEOUT).
This commit is contained in:
Max Lv
2026-10-15 00:34:32 +08:00
parent 4eb6a7415b
commit 5e4f6a6fa3
+6 -3
View File
@@ -412,6 +412,10 @@ Flow Connection::start(ss_socket_t fd) noexcept
return close();
}
const TcpConfig &cfg = relay_->config_;
// Every connection is timed from the start, so a client that never
// finishes its handshake cannot hold a pool slot forever. Later stages
// replace this with their own deadline.
relay_->set_deadline(*this, Timed::idle);
switch (cfg.kind) {
case Kind::local:
if (verbose) {
@@ -443,7 +447,6 @@ Flow Connection::start(ss_socket_t fd) noexcept
return close();
}
stage_ = Stage::header;
relay_->set_deadline(*this, Timed::idle);
update_interest();
return Flow::proceed;
}
@@ -974,7 +977,7 @@ Flow Connection::server_connect(const sockaddr *target) noexcept
Flow Connection::connected() noexcept
{
if (client_kind()) {
relay_->clear_deadline(*this); // only the connect was timed
relay_->set_deadline(*this, Timed::idle); // replaces the connect deadline
}
stage_ = Stage::stream;
if (pump_to_upstream() == Flow::closed) {
@@ -1044,7 +1047,7 @@ Connection::Pull Connection::pull(ss_socket_t fd, bool decode, FixedBuffer<kRela
cfg.on_traffic();
}
}
if (cfg.kind == Kind::server && stage_ == Stage::stream) {
if (stage_ == Stage::stream) {
relay_->set_deadline(*this, Timed::idle);
}
std::span<const std::uint8_t> in(relay_->scratch_.data(), *r);