Add config-driven transport randomization for mux, ping timing, and HTTP reuse

This commit is contained in:
Amin.MasterkinG
2026-04-21 15:51:20 +03:30
parent 74ff27ac78
commit ab7365e35d
5 changed files with 414 additions and 98 deletions
+2 -2
View File
@@ -196,11 +196,11 @@ func (c *Client) idleIntervalForStreak(streak int64) time.Duration {
if interval > c.cfg.PingMaxIntervalMS {
interval = c.cfg.PingMaxIntervalMS
}
return time.Duration(interval) * time.Millisecond
return c.pingIntervalWithJitter(time.Duration(interval) * time.Millisecond)
}
func (c *Client) scheduleAggressivePing(now time.Time) {
c.nextPingDueUnixMS.Store(now.Add(time.Duration(c.cfg.IdlePollIntervalMS) * time.Millisecond).UnixMilli())
c.nextPingDueUnixMS.Store(now.Add(c.pingIntervalWithJitter(time.Duration(c.cfg.IdlePollIntervalMS) * time.Millisecond)).UnixMilli())
}
func (c *Client) setPingState(state int32) {
+105 -9
View File
@@ -18,12 +18,16 @@ import (
"sync"
"time"
"masterhttprelayvpn/internal/config"
"masterhttprelayvpn/internal/protocol"
)
type sendWorker struct {
id int
httpClient *http.Client
id int
httpClient *http.Client
httpTransport *http.Transport
transportUseCount int
transportReuseLimit int
}
type dequeuedPacket struct {
@@ -35,10 +39,8 @@ func (c *Client) startSendWorkers(ctx context.Context, wg *sync.WaitGroup) {
for i := 0; i < c.cfg.WorkerCount; i++ {
worker := &sendWorker{
id: i + 1,
httpClient: &http.Client{
Timeout: time.Duration(c.cfg.HTTPRequestTimeoutMS) * time.Millisecond,
},
}
worker.resetHTTPClient(c.cfg)
wg.Add(1)
go func(w *sendWorker) {
@@ -137,12 +139,15 @@ func (c *Client) buildNextBatch(connections []*SOCKSConnection, totalQueuedBytes
})
}
if len(connections) > 1 {
rotationEvery := c.cfg.MuxRotateEveryBatches
rotationEvery := c.effectiveMuxRotateEveryBatches()
if rotationEvery < 1 {
rotationEvery = 1
}
turn := c.batchCursor.Add(1) - 1
start = int((turn / uint64(rotationEvery)) % uint64(len(connections)))
if offset := c.randomMuxStartOffset(len(connections)); offset > 0 {
start = (start + offset) % len(connections)
}
}
maxPackets, maxBatchBytes := c.effectiveBatchLimits(totalQueuedBytes)
maxPerSOCKS := c.cfg.MaxPacketsPerSOCKSPerBatch
@@ -245,7 +250,7 @@ func (c *Client) shouldSendPing(connections []*SOCKSConnection, totalQueuedBytes
func (c *Client) effectiveBatchLimits(totalQueuedBytes int) (int, int) {
maxPackets := c.cfg.MaxPacketsPerBatch
maxBatchBytes := c.cfg.MaxBatchBytes
if totalQueuedBytes < c.cfg.MuxBurstThresholdBytes {
if totalQueuedBytes < c.effectiveBurstThresholdBytes() {
if reducedPackets := maxPackets / 2; reducedPackets >= 1 {
maxPackets = reducedPackets
}
@@ -276,7 +281,7 @@ func (c *Client) effectiveBatchLimits(totalQueuedBytes int) (int, int) {
func (c *Client) effectiveWaitInterval(totalQueuedBytes int) time.Duration {
interval := time.Duration(c.cfg.WorkerPollIntervalMS) * time.Millisecond
if totalQueuedBytes >= c.cfg.MuxBurstThresholdBytes {
if totalQueuedBytes >= c.effectiveBurstThresholdBytes() {
if burst := interval / 2; burst >= 25*time.Millisecond {
return burst
}
@@ -286,7 +291,7 @@ func (c *Client) effectiveWaitInterval(totalQueuedBytes int) time.Duration {
}
func (c *Client) effectiveConcurrentBatches(totalQueuedBytes int) int {
if totalQueuedBytes >= c.cfg.MuxBurstThresholdBytes {
if totalQueuedBytes >= c.effectiveBurstThresholdBytes() {
return c.cfg.MaxConcurrentBatches
}
return 1
@@ -362,6 +367,46 @@ func (c *Client) jitterDuration(base time.Duration) time.Duration {
return base + jitter
}
func (c *Client) pingIntervalWithJitter(base time.Duration) time.Duration {
if base <= 0 || !c.cfg.HTTPRandomizeTransport || c.cfg.PingIntervalJitterMS <= 0 {
return base
}
jitter := time.Duration(randomIndex(c.cfg.PingIntervalJitterMS+1)) * time.Millisecond
return base + jitter
}
func (c *Client) effectiveBurstThresholdBytes() int {
threshold := c.cfg.MuxBurstThresholdBytes
if !c.cfg.HTTPRandomizeTransport || c.cfg.MuxBurstThresholdJitterBytes <= 0 {
return threshold
}
delta := randomIndex(c.cfg.MuxBurstThresholdJitterBytes + 1)
if randomIndex(2) == 0 {
if adjusted := threshold - delta; adjusted >= c.cfg.MaxChunkSize {
return adjusted
}
return c.cfg.MaxChunkSize
}
return threshold + delta
}
func (c *Client) effectiveMuxRotateEveryBatches() int {
rotationEvery := c.cfg.MuxRotateEveryBatches
if !c.cfg.HTTPRandomizeTransport || c.cfg.MuxRotateJitterBatches <= 0 {
return rotationEvery
}
return rotationEvery + randomIndex(c.cfg.MuxRotateJitterBatches+1)
}
func (c *Client) randomMuxStartOffset(connectionCount int) int {
if !c.cfg.HTTPRandomizeTransport || connectionCount <= 1 {
return 0
}
return randomIndex(connectionCount)
}
func (c *Client) requeueSelected(selected []dequeuedPacket) {
grouped := make(map[*SOCKSConnection][]string)
for _, entry := range selected {
@@ -450,6 +495,7 @@ func (w *sendWorker) postBatch(ctx context.Context, c *Client, batch protocol.Ba
}
resp, err := w.httpClient.Do(req)
w.recordTransportUse(c.cfg)
if err != nil {
if pingOnly {
c.failPing()
@@ -512,6 +558,56 @@ func (w *sendWorker) postBatch(ctx context.Context, c *Client, batch protocol.Ba
return nil
}
func (w *sendWorker) resetHTTPClient(cfg config.Config) {
transport := &http.Transport{
Proxy: http.ProxyFromEnvironment,
ForceAttemptHTTP2: true,
MaxIdleConns: 32,
MaxIdleConnsPerHost: 8,
IdleConnTimeout: w.randomizedIdleConnTimeout(cfg),
}
w.httpTransport = transport
w.httpClient = &http.Client{
Timeout: time.Duration(cfg.HTTPRequestTimeoutMS) * time.Millisecond,
Transport: transport,
}
w.transportUseCount = 0
w.transportReuseLimit = w.nextTransportReuseLimit(cfg)
}
func (w *sendWorker) recordTransportUse(cfg config.Config) {
if !cfg.HTTPRandomizeTransport {
return
}
w.transportUseCount++
if w.transportReuseLimit > 0 && w.transportUseCount >= w.transportReuseLimit {
if w.httpTransport != nil {
w.httpTransport.CloseIdleConnections()
}
w.resetHTTPClient(cfg)
}
}
func (w *sendWorker) randomizedIdleConnTimeout(cfg config.Config) time.Duration {
minTimeout := cfg.HTTPIdleConnTimeoutMinMS
maxTimeout := cfg.HTTPIdleConnTimeoutMaxMS
if !cfg.HTTPRandomizeTransport || maxTimeout <= minTimeout {
return time.Duration(minTimeout) * time.Millisecond
}
return time.Duration(minTimeout+randomIndex(maxTimeout-minTimeout+1)) * time.Millisecond
}
func (w *sendWorker) nextTransportReuseLimit(cfg config.Config) int {
if !cfg.HTTPRandomizeTransport || cfg.HTTPTransportReuseMax <= cfg.HTTPTransportReuseMin {
return cfg.HTTPTransportReuseMin
}
return cfg.HTTPTransportReuseMin + randomIndex(cfg.HTTPTransportReuseMax-cfg.HTTPTransportReuseMin+1)
}
func (c *Client) applyResponseBatch(batch protocol.Batch) error {
for _, packet := range batch.Packets {
if packet.Type == protocol.PacketTypePong {
+54
View File
@@ -27,6 +27,10 @@ func testClientConfig() config.Config {
PingMaxIntervalMS: 60000,
MaxQueueBytesPerSOCKS: 4096,
HTTPBatchRandomize: false,
HTTPIdleConnTimeoutMinMS: 15000,
HTTPIdleConnTimeoutMaxMS: 45000,
HTTPTransportReuseMin: 8,
HTTPTransportReuseMax: 24,
}
}
@@ -254,6 +258,56 @@ func TestEffectiveConcurrentBatchesUsesBurstThreshold(t *testing.T) {
}
}
func TestEffectiveBurstThresholdBytesStaysWithinConfiguredJitterRange(t *testing.T) {
cfg := testClientConfig()
cfg.HTTPRandomizeTransport = true
cfg.MuxBurstThresholdBytes = 4096
cfg.MuxBurstThresholdJitterBytes = 512
client := New(cfg, nil)
for i := 0; i < 50; i++ {
got := client.effectiveBurstThresholdBytes()
if got < cfg.MaxChunkSize {
t.Fatalf("expected threshold >= max chunk size, got %d", got)
}
if got < cfg.MuxBurstThresholdBytes-cfg.MuxBurstThresholdJitterBytes || got > cfg.MuxBurstThresholdBytes+cfg.MuxBurstThresholdJitterBytes {
t.Fatalf("threshold %d outside jitter range", got)
}
}
}
func TestPingIntervalWithJitterStaysWithinConfiguredRange(t *testing.T) {
cfg := testClientConfig()
cfg.HTTPRandomizeTransport = true
cfg.PingIntervalJitterMS = 250
client := New(cfg, nil)
base := 2 * time.Second
for i := 0; i < 50; i++ {
got := client.pingIntervalWithJitter(base)
if got < base || got > base+250*time.Millisecond {
t.Fatalf("ping interval %v outside expected jitter range", got)
}
}
}
func TestSendWorkerTransportReuseLimitStaysWithinConfiguredRange(t *testing.T) {
cfg := testClientConfig()
cfg.HTTPRandomizeTransport = true
cfg.HTTPTransportReuseMin = 3
cfg.HTTPTransportReuseMax = 7
cfg.HTTPIdleConnTimeoutMinMS = 1000
cfg.HTTPIdleConnTimeoutMaxMS = 2000
worker := &sendWorker{id: 1}
for i := 0; i < 50; i++ {
limit := worker.nextTransportReuseLimit(cfg)
if limit < cfg.HTTPTransportReuseMin || limit > cfg.HTTPTransportReuseMax {
t.Fatalf("reuse limit %d outside expected range", limit)
}
}
}
func TestBuildPollBatchSkipsWhenTransportBusy(t *testing.T) {
cfg := testClientConfig()
client := New(cfg, nil)
+203 -87
View File
@@ -16,97 +16,113 @@ import (
)
type Config struct {
AESEncryptionKey string
RelayURL string
HTTPUserAgentsFile string
HTTPHeaderProfile string
HTTPRandomizeHeaders bool
HTTPPaddingHeader string
HTTPPaddingMinBytes int
HTTPPaddingMaxBytes int
HTTPReferer string
HTTPAcceptLanguage string
HTTPRandomizeQuerySuffix bool
HTTPTimingJitterMS int
HTTPBatchRandomize bool
HTTPBatchPacketsJitter int
HTTPBatchBytesJitter int
ServerHost string
ServerPort int
SOCKSHost string
SOCKSPort int
SOCKSAuth bool
SOCKSUsername string
SOCKSPassword string
LogLevel string
MaxChunkSize int
MaxPacketsPerBatch int
MaxBatchBytes int
WorkerCount int
MaxConcurrentBatches int
MaxPacketsPerSOCKSPerBatch int
MuxRotateEveryBatches int
MuxBurstThresholdBytes int
HTTPRequestTimeoutMS int
WorkerPollIntervalMS int
IdlePollIntervalMS int
PingWarmThresholdMS int
PingBackoffBaseMS int
PingBackoffStepMS int
PingMaxIntervalMS int
MaxQueueBytesPerSOCKS int
AckTimeoutMS int
MaxRetryCount int
ReorderTimeoutMS int
MaxReorderBufferPackets int
SessionIdleTimeoutMS int
SOCKSIdleTimeoutMS int
ReadBodyLimitBytes int
MaxServerQueueBytes int
AESEncryptionKey string
RelayURL string
HTTPUserAgentsFile string
HTTPHeaderProfile string
HTTPRandomizeHeaders bool
HTTPRandomizeTransport bool
HTTPPaddingHeader string
HTTPPaddingMinBytes int
HTTPPaddingMaxBytes int
HTTPReferer string
HTTPAcceptLanguage string
HTTPRandomizeQuerySuffix bool
HTTPTimingJitterMS int
HTTPIdleConnTimeoutMinMS int
HTTPIdleConnTimeoutMaxMS int
HTTPTransportReuseMin int
HTTPTransportReuseMax int
HTTPBatchRandomize bool
HTTPBatchPacketsJitter int
HTTPBatchBytesJitter int
ServerHost string
ServerPort int
SOCKSHost string
SOCKSPort int
SOCKSAuth bool
SOCKSUsername string
SOCKSPassword string
LogLevel string
MaxChunkSize int
MaxPacketsPerBatch int
MaxBatchBytes int
WorkerCount int
MaxConcurrentBatches int
MaxPacketsPerSOCKSPerBatch int
MuxRotateEveryBatches int
MuxRotateJitterBatches int
MuxBurstThresholdBytes int
MuxBurstThresholdJitterBytes int
HTTPRequestTimeoutMS int
WorkerPollIntervalMS int
IdlePollIntervalMS int
PingIntervalJitterMS int
PingWarmThresholdMS int
PingBackoffBaseMS int
PingBackoffStepMS int
PingMaxIntervalMS int
MaxQueueBytesPerSOCKS int
AckTimeoutMS int
MaxRetryCount int
ReorderTimeoutMS int
MaxReorderBufferPackets int
SessionIdleTimeoutMS int
SOCKSIdleTimeoutMS int
ReadBodyLimitBytes int
MaxServerQueueBytes int
}
func Load(path string) (Config, error) {
cfg := Config{
SOCKSHost: "127.0.0.1",
SOCKSPort: 1080,
HTTPUserAgentsFile: "user-agents.txt",
HTTPHeaderProfile: "browser",
HTTPRandomizeHeaders: true,
HTTPPaddingHeader: "X-Padding",
HTTPPaddingMinBytes: 16,
HTTPPaddingMaxBytes: 48,
HTTPRandomizeQuerySuffix: false,
HTTPTimingJitterMS: 50,
HTTPBatchRandomize: true,
HTTPBatchPacketsJitter: 4,
HTTPBatchBytesJitter: 32768,
ServerHost: "127.0.0.1",
ServerPort: 28080,
LogLevel: "INFO",
MaxChunkSize: 16 * 1024,
MaxPacketsPerBatch: 32,
MaxBatchBytes: 256 * 1024,
WorkerCount: 4,
MaxConcurrentBatches: 4,
MaxPacketsPerSOCKSPerBatch: 2,
MuxRotateEveryBatches: 1,
MuxBurstThresholdBytes: 128 * 1024,
HTTPRequestTimeoutMS: 15000,
WorkerPollIntervalMS: 200,
IdlePollIntervalMS: 1000,
PingWarmThresholdMS: 5000,
PingBackoffBaseMS: 5000,
PingBackoffStepMS: 5000,
PingMaxIntervalMS: 60000,
MaxQueueBytesPerSOCKS: 1024 * 1024,
AckTimeoutMS: 5000,
MaxRetryCount: 5,
ReorderTimeoutMS: 5000,
MaxReorderBufferPackets: 128,
SessionIdleTimeoutMS: 5 * 60 * 1000,
SOCKSIdleTimeoutMS: 2 * 60 * 1000,
ReadBodyLimitBytes: 2 * 1024 * 1024,
MaxServerQueueBytes: 2 * 1024 * 1024,
SOCKSHost: "127.0.0.1",
SOCKSPort: 1080,
HTTPUserAgentsFile: "user-agents.txt",
HTTPHeaderProfile: "browser",
HTTPRandomizeHeaders: true,
HTTPRandomizeTransport: false,
HTTPPaddingHeader: "X-Padding",
HTTPPaddingMinBytes: 16,
HTTPPaddingMaxBytes: 48,
HTTPRandomizeQuerySuffix: false,
HTTPTimingJitterMS: 50,
HTTPIdleConnTimeoutMinMS: 15000,
HTTPIdleConnTimeoutMaxMS: 45000,
HTTPTransportReuseMin: 8,
HTTPTransportReuseMax: 24,
HTTPBatchRandomize: true,
HTTPBatchPacketsJitter: 4,
HTTPBatchBytesJitter: 32768,
ServerHost: "127.0.0.1",
ServerPort: 28080,
LogLevel: "INFO",
MaxChunkSize: 16 * 1024,
MaxPacketsPerBatch: 32,
MaxBatchBytes: 256 * 1024,
WorkerCount: 4,
MaxConcurrentBatches: 4,
MaxPacketsPerSOCKSPerBatch: 2,
MuxRotateEveryBatches: 1,
MuxRotateJitterBatches: 0,
MuxBurstThresholdBytes: 128 * 1024,
MuxBurstThresholdJitterBytes: 0,
HTTPRequestTimeoutMS: 15000,
WorkerPollIntervalMS: 200,
IdlePollIntervalMS: 1000,
PingIntervalJitterMS: 0,
PingWarmThresholdMS: 5000,
PingBackoffBaseMS: 5000,
PingBackoffStepMS: 5000,
PingMaxIntervalMS: 60000,
MaxQueueBytesPerSOCKS: 1024 * 1024,
AckTimeoutMS: 5000,
MaxRetryCount: 5,
ReorderTimeoutMS: 5000,
MaxReorderBufferPackets: 128,
SessionIdleTimeoutMS: 5 * 60 * 1000,
SOCKSIdleTimeoutMS: 2 * 60 * 1000,
ReadBodyLimitBytes: 2 * 1024 * 1024,
MaxServerQueueBytes: 2 * 1024 * 1024,
}
file, err := os.Open(path)
@@ -146,6 +162,13 @@ func Load(path string) (Config, error) {
}
cfg.HTTPRandomizeHeaders = randomize
case "HTTP_RANDOMIZE_TRANSPORT":
randomize, err := strconv.ParseBool(value)
if err != nil {
return Config{}, fmt.Errorf("parse HTTP_RANDOMIZE_TRANSPORT: %w", err)
}
cfg.HTTPRandomizeTransport = randomize
case "HTTP_PADDING_HEADER":
cfg.HTTPPaddingHeader = trimString(value)
case "HTTP_PADDING_MIN_BYTES":
@@ -180,6 +203,34 @@ func Load(path string) (Config, error) {
}
cfg.HTTPTimingJitterMS = valueInt
case "HTTP_IDLE_CONN_TIMEOUT_MIN_MS":
valueInt, err := strconv.Atoi(value)
if err != nil {
return Config{}, fmt.Errorf("parse HTTP_IDLE_CONN_TIMEOUT_MIN_MS: %w", err)
}
cfg.HTTPIdleConnTimeoutMinMS = valueInt
case "HTTP_IDLE_CONN_TIMEOUT_MAX_MS":
valueInt, err := strconv.Atoi(value)
if err != nil {
return Config{}, fmt.Errorf("parse HTTP_IDLE_CONN_TIMEOUT_MAX_MS: %w", err)
}
cfg.HTTPIdleConnTimeoutMaxMS = valueInt
case "HTTP_TRANSPORT_REUSE_MIN":
valueInt, err := strconv.Atoi(value)
if err != nil {
return Config{}, fmt.Errorf("parse HTTP_TRANSPORT_REUSE_MIN: %w", err)
}
cfg.HTTPTransportReuseMin = valueInt
case "HTTP_TRANSPORT_REUSE_MAX":
valueInt, err := strconv.Atoi(value)
if err != nil {
return Config{}, fmt.Errorf("parse HTTP_TRANSPORT_REUSE_MAX: %w", err)
}
cfg.HTTPTransportReuseMax = valueInt
case "HTTP_BATCH_RANDOMIZE":
randomize, err := strconv.ParseBool(value)
if err != nil {
@@ -281,6 +332,13 @@ func Load(path string) (Config, error) {
}
cfg.MuxRotateEveryBatches = count
case "MUX_ROTATE_JITTER_BATCHES":
count, err := strconv.Atoi(value)
if err != nil {
return Config{}, fmt.Errorf("parse MUX_ROTATE_JITTER_BATCHES: %w", err)
}
cfg.MuxRotateJitterBatches = count
case "MUX_BURST_THRESHOLD_BYTES":
size, err := strconv.Atoi(value)
if err != nil {
@@ -288,6 +346,13 @@ func Load(path string) (Config, error) {
}
cfg.MuxBurstThresholdBytes = size
case "MUX_BURST_THRESHOLD_JITTER_BYTES":
size, err := strconv.Atoi(value)
if err != nil {
return Config{}, fmt.Errorf("parse MUX_BURST_THRESHOLD_JITTER_BYTES: %w", err)
}
cfg.MuxBurstThresholdJitterBytes = size
case "HTTP_REQUEST_TIMEOUT_MS":
timeout, err := strconv.Atoi(value)
if err != nil {
@@ -309,6 +374,13 @@ func Load(path string) (Config, error) {
}
cfg.IdlePollIntervalMS = interval
case "PING_INTERVAL_JITTER_MS":
interval, err := strconv.Atoi(value)
if err != nil {
return Config{}, fmt.Errorf("parse PING_INTERVAL_JITTER_MS: %w", err)
}
cfg.PingIntervalJitterMS = interval
case "PING_WARM_THRESHOLD_MS":
threshold, err := strconv.Atoi(value)
if err != nil {
@@ -429,22 +501,35 @@ func (c Config) ValidateClient() error {
if c.HTTPRequestTimeoutMS < 1 {
return fmt.Errorf("invalid HTTP_REQUEST_TIMEOUT_MS: %d", c.HTTPRequestTimeoutMS)
}
if c.MaxConcurrentBatches < 1 {
return fmt.Errorf("invalid MAX_CONCURRENT_BATCHES: %d", c.MaxConcurrentBatches)
}
if c.MaxConcurrentBatches > c.WorkerCount {
return fmt.Errorf("MAX_CONCURRENT_BATCHES must be <= WORKER_COUNT")
}
if c.MaxPacketsPerSOCKSPerBatch < 1 {
return fmt.Errorf("invalid MAX_PACKETS_PER_SOCKS_PER_BATCH: %d", c.MaxPacketsPerSOCKSPerBatch)
}
if c.MuxRotateEveryBatches < 1 {
return fmt.Errorf("invalid MUX_ROTATE_EVERY_BATCHES: %d", c.MuxRotateEveryBatches)
}
if c.MuxRotateJitterBatches < 0 {
return fmt.Errorf("invalid MUX_ROTATE_JITTER_BATCHES: %d", c.MuxRotateJitterBatches)
}
if c.MuxBurstThresholdBytes < c.MaxChunkSize {
return fmt.Errorf("MUX_BURST_THRESHOLD_BYTES must be >= MAX_CHUNK_SIZE")
}
if c.MuxBurstThresholdJitterBytes < 0 {
return fmt.Errorf("invalid MUX_BURST_THRESHOLD_JITTER_BYTES: %d", c.MuxBurstThresholdJitterBytes)
}
if c.WorkerPollIntervalMS < 1 {
return fmt.Errorf("invalid WORKER_POLL_INTERVAL_MS: %d", c.WorkerPollIntervalMS)
}
@@ -452,15 +537,23 @@ func (c Config) ValidateClient() error {
if c.IdlePollIntervalMS < c.WorkerPollIntervalMS {
return fmt.Errorf("IDLE_POLL_INTERVAL_MS must be >= WORKER_POLL_INTERVAL_MS")
}
if c.PingIntervalJitterMS < 0 {
return fmt.Errorf("invalid PING_INTERVAL_JITTER_MS: %d", c.PingIntervalJitterMS)
}
if c.PingWarmThresholdMS < 1 {
return fmt.Errorf("invalid PING_WARM_THRESHOLD_MS: %d", c.PingWarmThresholdMS)
}
if c.PingBackoffBaseMS < c.IdlePollIntervalMS {
return fmt.Errorf("PING_BACKOFF_BASE_MS must be >= IDLE_POLL_INTERVAL_MS")
}
if c.PingBackoffStepMS < 1 {
return fmt.Errorf("invalid PING_BACKOFF_STEP_MS: %d", c.PingBackoffStepMS)
}
if c.PingMaxIntervalMS < c.PingBackoffBaseMS {
return fmt.Errorf("PING_MAX_INTERVAL_MS must be >= PING_BACKOFF_BASE_MS")
}
@@ -472,9 +565,11 @@ func (c Config) ValidateClient() error {
if c.MaxRetryCount < 0 {
return fmt.Errorf("invalid MAX_RETRY_COUNT: %d", c.MaxRetryCount)
}
if c.ReorderTimeoutMS < 1 {
return fmt.Errorf("invalid REORDER_TIMEOUT_MS: %d", c.ReorderTimeoutMS)
}
if c.MaxReorderBufferPackets < 1 {
return fmt.Errorf("invalid MAX_REORDER_BUFFER_PACKETS: %d", c.MaxReorderBufferPackets)
}
@@ -490,12 +585,31 @@ func (c Config) ValidateClient() error {
if c.HTTPPaddingMaxBytes < c.HTTPPaddingMinBytes {
return fmt.Errorf("HTTP_PADDING_MAX_BYTES must be >= HTTP_PADDING_MIN_BYTES")
}
if c.HTTPTimingJitterMS < 0 {
return fmt.Errorf("invalid HTTP_TIMING_JITTER_MS: %d", c.HTTPTimingJitterMS)
}
if c.HTTPIdleConnTimeoutMinMS < 1 {
return fmt.Errorf("invalid HTTP_IDLE_CONN_TIMEOUT_MIN_MS: %d", c.HTTPIdleConnTimeoutMinMS)
}
if c.HTTPIdleConnTimeoutMaxMS < c.HTTPIdleConnTimeoutMinMS {
return fmt.Errorf("HTTP_IDLE_CONN_TIMEOUT_MAX_MS must be >= HTTP_IDLE_CONN_TIMEOUT_MIN_MS")
}
if c.HTTPTransportReuseMin < 1 {
return fmt.Errorf("invalid HTTP_TRANSPORT_REUSE_MIN: %d", c.HTTPTransportReuseMin)
}
if c.HTTPTransportReuseMax < c.HTTPTransportReuseMin {
return fmt.Errorf("HTTP_TRANSPORT_REUSE_MAX must be >= HTTP_TRANSPORT_REUSE_MIN")
}
if c.HTTPBatchPacketsJitter < 0 {
return fmt.Errorf("invalid HTTP_BATCH_PACKETS_JITTER: %d", c.HTTPBatchPacketsJitter)
}
if c.HTTPBatchBytesJitter < 0 {
return fmt.Errorf("invalid HTTP_BATCH_BYTES_JITTER: %d", c.HTTPBatchBytesJitter)
}
@@ -523,9 +637,11 @@ func (c Config) ValidateServer() error {
if c.SOCKSIdleTimeoutMS < 1 {
return fmt.Errorf("invalid SOCKS_IDLE_TIMEOUT_MS: %d", c.SOCKSIdleTimeoutMS)
}
if c.ReorderTimeoutMS < 1 {
return fmt.Errorf("invalid REORDER_TIMEOUT_MS: %d", c.ReorderTimeoutMS)
}
if c.MaxReorderBufferPackets < 1 {
return fmt.Errorf("invalid MAX_REORDER_BUFFER_PACKETS: %d", c.MaxReorderBufferPackets)
}