Move MTProxy policy engine from tgnet/ to self-contained jni/mtproxy/ module. Consolidate reconnect hold computation into single MtProxyRetryAuthority owner (was split across Connection, ConnectionSocket, endpoint policy, and probe coordinator, causing hold misalignment bugs). Add MtProxyTerminalDiagnostic module with host tests for pre-I/O verdict preservation (fixes 02.07/30.06 clobber livelocks). Auto-generate phase classification (isPreIoTerminalVerdict, needsReconnectBackoff, isObservationFacadePhase, isLocalSchedulerTimeout) from single Python contract into both C++ and Java to eliminate parallel maintenance. Pass native hold (coordinator terminal hold + endpoint cooldown) via JNI to Java so reconnect timer and health store use THE clock, not re-derived ones. Optimize log flushing: batch debug lines, flush errors immediately. Add module boundary guard, host build system (MSVC), and unit tests for retry logic and terminal diagnostic derivation.
92 lines
2.8 KiB
C++
92 lines
2.8 KiB
C++
/*
|
|
* This is the source code of tgnet library v. 1.1
|
|
* It is licensed under GNU GPL v. 2 or later.
|
|
*/
|
|
|
|
#include "MtProxyRetryAuthority.h"
|
|
|
|
#include <algorithm>
|
|
#include <openssl/rand.h>
|
|
|
|
#include "MtProxyHandshakeScheduler.h"
|
|
#include "MtProxyPhaseContract.h"
|
|
|
|
namespace MtProxyRetry {
|
|
|
|
uint32_t backoffBaseMs(TrafficClass trafficClass) {
|
|
switch (trafficClass) {
|
|
case TrafficClass::Generic:
|
|
return 1800;
|
|
case TrafficClass::GenericMedia:
|
|
case TrafficClass::Push:
|
|
return 2500;
|
|
case TrafficClass::Download:
|
|
case TrafficClass::Upload:
|
|
return 3500;
|
|
default:
|
|
return 2500;
|
|
}
|
|
}
|
|
|
|
uint32_t backoffMaxMs(TrafficClass trafficClass) {
|
|
switch (trafficClass) {
|
|
case TrafficClass::Generic:
|
|
return 8000;
|
|
case TrafficClass::GenericMedia:
|
|
case TrafficClass::Push:
|
|
return 12000;
|
|
case TrafficClass::Download:
|
|
case TrafficClass::Upload:
|
|
return 16000;
|
|
default:
|
|
return 12000;
|
|
}
|
|
}
|
|
|
|
static uint32_t reconnectJitterMs(uint32_t limit) {
|
|
if (limit == 0) {
|
|
return 0;
|
|
}
|
|
uint32_t value = 0;
|
|
RAND_bytes(reinterpret_cast<uint8_t *>(&value), sizeof(value));
|
|
return value % (limit + 1);
|
|
}
|
|
|
|
ReconnectHoldDecision nextReconnectHold(const ReconnectHoldInput &input) {
|
|
ReconnectHoldDecision decision;
|
|
if (!MtProxyPhase::needsReconnectBackoff(input.diagnostic)) {
|
|
decision.nextBackoffMs = input.previousBackoffMs;
|
|
return decision;
|
|
}
|
|
uint32_t base = backoffBaseMs(input.trafficClass);
|
|
uint32_t maxDelay = backoffMaxMs(input.trafficClass);
|
|
uint32_t delay = input.previousBackoffMs == 0
|
|
? base
|
|
: std::min(input.previousBackoffMs * 2, maxDelay);
|
|
delay = std::max(delay, base);
|
|
delay = std::min(delay, maxDelay);
|
|
delay += reconnectJitterMs(std::min(delay / 4, 2000U));
|
|
decision.shouldHold = true;
|
|
decision.nextBackoffMs = delay;
|
|
decision.delayMs = delay;
|
|
decision.source = "exp_backoff";
|
|
// The probe coordinator's terminal hold (budget backoff / profiles
|
|
// exhausted) is the authoritative clock: retrying earlier is guaranteed
|
|
// to be denied pre-TCP anyway, so wait out whichever is longer.
|
|
if (input.coordinatorHoldMs > decision.delayMs) {
|
|
decision.delayMs = input.coordinatorHoldMs;
|
|
decision.source = "coordinator_hold";
|
|
}
|
|
return decision;
|
|
}
|
|
|
|
uint32_t endpointCooldownWaitMs(const EndpointCooldownWaitInput &input) {
|
|
uint32_t delay = mtProxyHandshakeSchedulerRetryDelay(
|
|
input.now, input.cooldownUntil, input.priority, input.connectionPatternMode);
|
|
if (input.cooldownRemainingMs > 0 && (int64_t) delay < input.cooldownRemainingMs) {
|
|
delay = (uint32_t) input.cooldownRemainingMs;
|
|
}
|
|
return delay;
|
|
}
|
|
|
|
}
|