Commit 46cb05f0 authored by jan.koester's avatar jan.koester
Browse files

test

parent a5845d7a
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -409,6 +409,14 @@ void netplus::tcp::connect(const std::string& addr, int port, bool nonblock)
                    // in errno from the ::connect() call above.
                    errno = err;
                }
            } else {
                // waitWrite() returned false: the attempt neither succeeded nor failed
                // within kConnectAttemptTimeoutMs, it just never finished -- errno is still
                // EINPROGRESS from the ::connect() call above, which strerror()'s to the
                // misleading "Operation now in progress" (sounds like nothing is wrong yet)
                // instead of describing what actually happened: this address didn't respond
                // in time.
                errno = ETIMEDOUT;
            }

            ::close(_Socket);
@@ -523,6 +531,12 @@ void tcp::connectTimeout(const std::string& addr, int port, int timeout_ms) {
                    // in errno from the ::connect() call above.
                    errno = err;
                }
            } else {
                // Timed out (see tcp::connect()'s identical branch above for why this
                // matters): without this, errno is still the stale EINPROGRESS from the
                // ::connect() call above, and the final exception below would claim
                // "Operation now in progress" for what was actually a timeout.
                errno = ETIMEDOUT;
            }
        }

+6 −0
Original line number Diff line number Diff line
@@ -483,6 +483,12 @@ namespace netplus {
		void handshake_after_accept() override { _tls.handshake_after_accept(); }
		void handshake_after_connect() override { _tls.handshake_after_connect(); }
		void connect(const std::string& addr, int port, bool nonblock = false) override;
		// Unlike connect(nonblock=false)'s hardcoded 15s handshake deadline (independent of
		// anything the caller asked for), this bounds the TCP connect AND the TLS handshake
		// together against one caller-supplied budget -- see ssl.cpp for why that matters for
		// a reverse proxy that needs to fail over to a healthy backend quickly instead of
		// waiting out a fixed worst-case on a dead/unresponsive one.
		void connectTimeout(const std::string& addr, int port, int timeout_ms) override;
		void close() override;

		size_t sendData(buffer& data, int flags = 0) override;
+48 −0
Original line number Diff line number Diff line
@@ -170,6 +170,54 @@ void ssl::connect(const std::string& addr, int port, bool nonblock) {
    }
}

// Same idea as connect(nonblock=false), but bounds the TCP connect AND the TLS handshake
// together against one overall timeout_ms budget instead of a fixed worst-case (connect()'s
// underlying tcp::connect() spends up to 10s *per resolved address*, then connect() itself
// gives the handshake a further fixed 15s on top -- up to ~35s total for one dead/unresponsive
// backend, regardless of anything a caller like HttpClient configured). A reverse proxy that
// needs to fail over to a healthy backend quickly instead of hanging a client's request on a
// dead one needs that whole budget to be one number it can actually turn down.
void ssl::connectTimeout(const std::string& addr, int port, int timeout_ms) {
    const auto start = std::chrono::steady_clock::now();

    // Spends only whatever's left of timeout_ms trying every resolved address (see
    // tcp::connectTimeout()'s own doc comment) -- leaves the socket blocking on success, same
    // post-condition connect(nonblock=false) leaves it in before the handshake loop below.
    tcp::connectTimeout(addr, port, timeout_ms);

    _tls.is_client = true;
    _tls.hostname = addr;
    _tls.setSocket(this);

    _tls.queueClientHello();
    _tls.hs_state = tls::HsState::CLI_FLUSH_CH;

    socketwait sw;
    const auto deadline = start + std::chrono::milliseconds(timeout_ms);
    while (!_tls.getHandshakeDone()) {
        if (std::chrono::steady_clock::now() >= deadline) {
            NetException e;
            e[NetException::Error] << "ssl::connectTimeout: TLS handshake timeout";
            throw e;
        }
        while (_tls.hasPendingWrite()) {
            _tls.flush_out();
        }
        try {
            _tls.handshake_after_connect();
        } catch (NetException &e) {
            if (e.getErrorType() == NetException::Note) {
                // Need more data from peer -- wait for socket readability, but never past
                // this call's own deadline (waitRead's own timeout_ms is a per-iteration
                // poll, not the overall budget -- see connect()'s identical 100ms poll).
                sw.waitRead(*this, 100);
                continue;
            }
            throw;
        }
    }
}

// ============================================================================
// Close - Reset TLS state and close TCP connection
// ============================================================================