mirror of
https://github.com/masterking32/MasterHttpRelayVPN.git
synced 2026-05-19 08:04:38 +03:00
Add relay header profiles and randomized request timing/batching
This commit is contained in:
@@ -42,23 +42,13 @@ func (b *relayHeaderBuilder) Apply(req *http.Request) {
|
||||
req.Header.Set("User-Agent", ua)
|
||||
}
|
||||
|
||||
if b.cfg.HTTPHeaderProfile == "browser" {
|
||||
req.Header.Set("Accept", pickRandomString(
|
||||
"*/*",
|
||||
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
||||
"application/json,text/plain,*/*",
|
||||
))
|
||||
|
||||
req.Header.Set("Accept-Language", b.pickAcceptLanguage())
|
||||
req.Header.Set("Cache-Control", pickRandomString("no-cache", "max-age=0", "no-store"))
|
||||
req.Header.Set("Pragma", "no-cache")
|
||||
req.Header.Set("Sec-Fetch-Dest", "empty")
|
||||
req.Header.Set("Sec-Fetch-Mode", "cors")
|
||||
req.Header.Set("Sec-Fetch-Site", pickRandomString("same-origin", "same-site", "cross-site"))
|
||||
req.Header.Set("Priority", pickRandomString("u=0, i", "u=1, i"))
|
||||
if referer := b.pickReferer(); referer != "" {
|
||||
req.Header.Set("Referer", referer)
|
||||
}
|
||||
switch b.cfg.HTTPHeaderProfile {
|
||||
case "browser":
|
||||
b.applyBrowserProfile(req)
|
||||
case "cdn":
|
||||
b.applyCDNProfile(req)
|
||||
case "api":
|
||||
b.applyAPIProfile(req)
|
||||
}
|
||||
|
||||
if b.cfg.HTTPRandomizeHeaders {
|
||||
@@ -75,6 +65,57 @@ func (b *relayHeaderBuilder) Apply(req *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
func (b *relayHeaderBuilder) applyBrowserProfile(req *http.Request) {
|
||||
req.Header.Set("Accept", pickRandomString(
|
||||
"*/*",
|
||||
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
||||
"application/json,text/plain,*/*",
|
||||
))
|
||||
req.Header.Set("Accept-Language", b.pickAcceptLanguage())
|
||||
req.Header.Set("Cache-Control", pickRandomString("no-cache", "max-age=0", "no-store"))
|
||||
req.Header.Set("Pragma", "no-cache")
|
||||
req.Header.Set("Sec-Fetch-Dest", "empty")
|
||||
req.Header.Set("Sec-Fetch-Mode", "cors")
|
||||
req.Header.Set("Sec-Fetch-Site", pickRandomString("same-origin", "same-site", "cross-site"))
|
||||
req.Header.Set("Priority", pickRandomString("u=0, i", "u=1, i"))
|
||||
if maybeTrue() {
|
||||
req.Header.Set("DNT", "1")
|
||||
}
|
||||
if referer := b.pickReferer(); referer != "" {
|
||||
req.Header.Set("Referer", referer)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *relayHeaderBuilder) applyCDNProfile(req *http.Request) {
|
||||
req.Header.Set("Accept", pickRandomString("*/*", "application/octet-stream,*/*", "application/json,*/*"))
|
||||
req.Header.Set("Accept-Language", b.pickAcceptLanguage())
|
||||
req.Header.Set("Cache-Control", pickRandomString("no-store", "no-cache", "max-age=0"))
|
||||
req.Header.Set("Pragma", "no-cache")
|
||||
req.Header.Set("X-Requested-With", pickRandomString("XMLHttpRequest", "Fetch"))
|
||||
req.Header.Set("Sec-Fetch-Dest", "empty")
|
||||
req.Header.Set("Sec-Fetch-Mode", pickRandomString("cors", "same-origin"))
|
||||
req.Header.Set("Sec-Fetch-Site", pickRandomString("same-origin", "same-site"))
|
||||
if referer := b.pickReferer(); referer != "" {
|
||||
req.Header.Set("Referer", referer)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *relayHeaderBuilder) applyAPIProfile(req *http.Request) {
|
||||
req.Header.Set("Accept", pickRandomString("application/json", "application/octet-stream", "application/json,text/plain,*/*"))
|
||||
req.Header.Set("Cache-Control", pickRandomString("no-store", "no-cache"))
|
||||
req.Header.Set("Pragma", "no-cache")
|
||||
req.Header.Set("X-Requested-With", pickRandomString("XMLHttpRequest", "APIClient"))
|
||||
if maybeTrue() {
|
||||
req.Header.Set("X-Requested-At", randomHex(6))
|
||||
}
|
||||
if b.cfg.HTTPAcceptLanguage != "" {
|
||||
req.Header.Set("Accept-Language", b.pickAcceptLanguage())
|
||||
}
|
||||
if referer := b.pickReferer(); referer != "" && maybeTrue() {
|
||||
req.Header.Set("Referer", referer)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *relayHeaderBuilder) pickUserAgent() string {
|
||||
if len(b.userAgents) == 0 {
|
||||
return ""
|
||||
@@ -225,6 +266,10 @@ func randomPadding(minBytes int, maxBytes int) string {
|
||||
return padding
|
||||
}
|
||||
|
||||
func maybeTrue() bool {
|
||||
return randomIndex(2) == 0
|
||||
}
|
||||
|
||||
func randomHex(byteCount int) string {
|
||||
if byteCount <= 0 {
|
||||
return ""
|
||||
|
||||
@@ -58,14 +58,14 @@ func (w *sendWorker) run(ctx context.Context, c *Client) {
|
||||
c.reclaimExpiredInFlight()
|
||||
batch, selected := c.buildNextBatch()
|
||||
if len(batch.Packets) == 0 {
|
||||
c.waitForSendWork(ctx, pollInterval)
|
||||
c.waitForSendWork(ctx, c.jitterDuration(pollInterval))
|
||||
continue
|
||||
}
|
||||
|
||||
if err := batch.Validate(); err != nil {
|
||||
c.log.Errorf("<red>worker=<cyan>%d</cyan> invalid batch: <cyan>%v</cyan></red>", w.id, err)
|
||||
c.requeueSelected(selected)
|
||||
c.waitForSendWork(ctx, pollInterval)
|
||||
c.waitForSendWork(ctx, c.jitterDuration(pollInterval))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -75,14 +75,14 @@ func (w *sendWorker) run(ctx context.Context, c *Client) {
|
||||
if err != nil {
|
||||
c.log.Errorf("<red>worker=<cyan>%d</cyan> encrypt batch failed: <cyan>%v</cyan></red>", w.id, err)
|
||||
c.requeueSelected(selected)
|
||||
c.waitForSendWork(ctx, pollInterval)
|
||||
c.waitForSendWork(ctx, c.jitterDuration(pollInterval))
|
||||
continue
|
||||
}
|
||||
|
||||
if err := w.postBatch(ctx, c, batch, body); err != nil {
|
||||
c.log.Warnf("<yellow>worker=<cyan>%d</cyan> send failed for batch=<cyan>%s</cyan>: <cyan>%v</cyan></yellow>", w.id, batch.BatchID, err)
|
||||
c.requeueSelected(selected)
|
||||
c.waitForSendWork(ctx, pollInterval)
|
||||
c.waitForSendWork(ctx, c.jitterDuration(pollInterval))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -114,16 +114,17 @@ func (c *Client) buildNextBatch() (protocol.Batch, []dequeuedPacket) {
|
||||
if len(connections) > 1 {
|
||||
start = int(c.batchCursor.Add(1)-1) % len(connections)
|
||||
}
|
||||
maxPackets, maxBatchBytes := c.effectiveBatchLimits()
|
||||
|
||||
selected := make([]dequeuedPacket, 0, c.cfg.MaxPacketsPerBatch)
|
||||
packets := make([]protocol.Packet, 0, c.cfg.MaxPacketsPerBatch)
|
||||
selected := make([]dequeuedPacket, 0, maxPackets)
|
||||
packets := make([]protocol.Packet, 0, maxPackets)
|
||||
totalBytes := 0
|
||||
|
||||
for len(selected) < c.cfg.MaxPacketsPerBatch {
|
||||
for len(selected) < maxPackets {
|
||||
progress := false
|
||||
|
||||
for offset := range connections {
|
||||
if len(selected) >= c.cfg.MaxPacketsPerBatch {
|
||||
if len(selected) >= maxPackets {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -135,7 +136,7 @@ func (c *Client) buildNextBatch() (protocol.Batch, []dequeuedPacket) {
|
||||
}
|
||||
|
||||
packetBytes := len(item.Packet.Payload)
|
||||
if len(selected) > 0 && totalBytes+packetBytes > c.cfg.MaxBatchBytes {
|
||||
if len(selected) > 0 && totalBytes+packetBytes > maxBatchBytes {
|
||||
socksConn.RequeueFront([]*SOCKSOutboundQueueItem{item})
|
||||
continue
|
||||
}
|
||||
@@ -173,7 +174,7 @@ func (c *Client) buildPollBatch(connections []*SOCKSConnection) (protocol.Batch,
|
||||
now := time.Now()
|
||||
nowUnixMS := now.UnixMilli()
|
||||
lastUnixMS := c.lastPollUnixMS.Load()
|
||||
minInterval := time.Duration(c.cfg.IdlePollIntervalMS) * time.Millisecond
|
||||
minInterval := c.jitterDuration(time.Duration(c.cfg.IdlePollIntervalMS) * time.Millisecond)
|
||||
if lastUnixMS > 0 && nowUnixMS-lastUnixMS < minInterval.Milliseconds() {
|
||||
return protocol.Batch{}, false
|
||||
}
|
||||
@@ -188,6 +189,39 @@ func (c *Client) buildPollBatch(connections []*SOCKSConnection) (protocol.Batch,
|
||||
return batch, true
|
||||
}
|
||||
|
||||
func (c *Client) effectiveBatchLimits() (int, int) {
|
||||
maxPackets := c.cfg.MaxPacketsPerBatch
|
||||
maxBatchBytes := c.cfg.MaxBatchBytes
|
||||
if !c.cfg.HTTPBatchRandomize {
|
||||
return maxPackets, maxBatchBytes
|
||||
}
|
||||
|
||||
if c.cfg.HTTPBatchPacketsJitter > 0 && maxPackets > 1 {
|
||||
delta := randomIndex(c.cfg.HTTPBatchPacketsJitter + 1)
|
||||
if adjusted := maxPackets - delta; adjusted >= 1 {
|
||||
maxPackets = adjusted
|
||||
}
|
||||
}
|
||||
|
||||
if c.cfg.HTTPBatchBytesJitter > 0 && maxBatchBytes > c.cfg.MaxChunkSize {
|
||||
delta := randomIndex(c.cfg.HTTPBatchBytesJitter + 1)
|
||||
if adjusted := maxBatchBytes - delta; adjusted >= c.cfg.MaxChunkSize {
|
||||
maxBatchBytes = adjusted
|
||||
}
|
||||
}
|
||||
|
||||
return maxPackets, maxBatchBytes
|
||||
}
|
||||
|
||||
func (c *Client) jitterDuration(base time.Duration) time.Duration {
|
||||
if base <= 0 || c.cfg.HTTPTimingJitterMS <= 0 {
|
||||
return base
|
||||
}
|
||||
|
||||
jitter := time.Duration(randomIndex(c.cfg.HTTPTimingJitterMS+1)) * time.Millisecond
|
||||
return base + jitter
|
||||
}
|
||||
|
||||
func (c *Client) requeueSelected(selected []dequeuedPacket) {
|
||||
grouped := make(map[*SOCKSConnection][]string)
|
||||
for _, entry := range selected {
|
||||
@@ -343,15 +377,18 @@ func (c *Client) applyResponsePacket(packet protocol.Packet) error {
|
||||
protocol.PacketTypeSOCKSAuthFailed,
|
||||
protocol.PacketTypeSOCKSUpstreamUnavailable:
|
||||
message := packet.Type.String()
|
||||
|
||||
if len(packet.Payload) > 0 {
|
||||
message = string(packet.Payload)
|
||||
}
|
||||
|
||||
_ = socksConn.AckPacket(packet)
|
||||
socksConn.ConnectFailure = message
|
||||
c.log.Warnf(
|
||||
"<yellow>connect failure applied socks_id=<cyan>%d</cyan> reason=<cyan>%s</cyan></yellow>",
|
||||
socksConn.ID, message,
|
||||
)
|
||||
|
||||
socksConn.CompleteConnect(fmt.Errorf("%s", message))
|
||||
_ = socksConn.CloseLocal()
|
||||
return nil
|
||||
|
||||
@@ -73,6 +73,7 @@ func TestBuildNextBatchRotatesAcrossConnections(t *testing.T) {
|
||||
MaxBatchBytes: 4096,
|
||||
WorkerCount: 1,
|
||||
MaxQueueBytesPerSOCKS: 4096,
|
||||
HTTPBatchRandomize: false,
|
||||
}
|
||||
|
||||
client := New(cfg, nil)
|
||||
|
||||
+122
-58
@@ -16,67 +16,75 @@ import (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
AESEncryptionKey string
|
||||
RelayURL string
|
||||
HTTPUserAgentsFile string
|
||||
HTTPHeaderProfile string
|
||||
HTTPRandomizeHeaders bool
|
||||
HTTPPaddingHeader string
|
||||
HTTPPaddingMinBytes int
|
||||
HTTPPaddingMaxBytes int
|
||||
HTTPReferer string
|
||||
HTTPAcceptLanguage string
|
||||
ServerHost string
|
||||
ServerPort int
|
||||
SOCKSHost string
|
||||
SOCKSPort int
|
||||
SOCKSAuth bool
|
||||
SOCKSUsername string
|
||||
SOCKSPassword string
|
||||
LogLevel string
|
||||
MaxChunkSize int
|
||||
MaxPacketsPerBatch int
|
||||
MaxBatchBytes int
|
||||
WorkerCount int
|
||||
HTTPRequestTimeoutMS int
|
||||
WorkerPollIntervalMS int
|
||||
IdlePollIntervalMS int
|
||||
MaxQueueBytesPerSOCKS int
|
||||
AckTimeoutMS int
|
||||
MaxRetryCount int
|
||||
SessionIdleTimeoutMS int
|
||||
SOCKSIdleTimeoutMS int
|
||||
ReadBodyLimitBytes int
|
||||
MaxServerQueueBytes int
|
||||
AESEncryptionKey string
|
||||
RelayURL string
|
||||
HTTPUserAgentsFile string
|
||||
HTTPHeaderProfile string
|
||||
HTTPRandomizeHeaders bool
|
||||
HTTPPaddingHeader string
|
||||
HTTPPaddingMinBytes int
|
||||
HTTPPaddingMaxBytes int
|
||||
HTTPReferer string
|
||||
HTTPAcceptLanguage string
|
||||
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
|
||||
HTTPRequestTimeoutMS int
|
||||
WorkerPollIntervalMS int
|
||||
IdlePollIntervalMS int
|
||||
MaxQueueBytesPerSOCKS int
|
||||
AckTimeoutMS int
|
||||
MaxRetryCount 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,
|
||||
ServerHost: "127.0.0.1",
|
||||
ServerPort: 28080,
|
||||
LogLevel: "INFO",
|
||||
MaxChunkSize: 16 * 1024,
|
||||
MaxPacketsPerBatch: 32,
|
||||
MaxBatchBytes: 256 * 1024,
|
||||
WorkerCount: 4,
|
||||
HTTPRequestTimeoutMS: 15000,
|
||||
WorkerPollIntervalMS: 200,
|
||||
IdlePollIntervalMS: 1000,
|
||||
MaxQueueBytesPerSOCKS: 1024 * 1024,
|
||||
AckTimeoutMS: 5000,
|
||||
MaxRetryCount: 5,
|
||||
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,
|
||||
HTTPPaddingHeader: "X-Padding",
|
||||
HTTPPaddingMinBytes: 16,
|
||||
HTTPPaddingMaxBytes: 48,
|
||||
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,
|
||||
HTTPRequestTimeoutMS: 15000,
|
||||
WorkerPollIntervalMS: 200,
|
||||
IdlePollIntervalMS: 1000,
|
||||
MaxQueueBytesPerSOCKS: 1024 * 1024,
|
||||
AckTimeoutMS: 5000,
|
||||
MaxRetryCount: 5,
|
||||
SessionIdleTimeoutMS: 5 * 60 * 1000,
|
||||
SOCKSIdleTimeoutMS: 2 * 60 * 1000,
|
||||
ReadBodyLimitBytes: 2 * 1024 * 1024,
|
||||
MaxServerQueueBytes: 2 * 1024 * 1024,
|
||||
}
|
||||
|
||||
file, err := os.Open(path)
|
||||
@@ -114,6 +122,7 @@ func Load(path string) (Config, error) {
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse HTTP_RANDOMIZE_HEADERS: %w", err)
|
||||
}
|
||||
|
||||
cfg.HTTPRandomizeHeaders = randomize
|
||||
case "HTTP_PADDING_HEADER":
|
||||
cfg.HTTPPaddingHeader = trimString(value)
|
||||
@@ -122,17 +131,47 @@ func Load(path string) (Config, error) {
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse HTTP_PADDING_MIN_BYTES: %w", err)
|
||||
}
|
||||
|
||||
cfg.HTTPPaddingMinBytes = size
|
||||
case "HTTP_PADDING_MAX_BYTES":
|
||||
size, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse HTTP_PADDING_MAX_BYTES: %w", err)
|
||||
}
|
||||
|
||||
cfg.HTTPPaddingMaxBytes = size
|
||||
case "HTTP_REFERER":
|
||||
cfg.HTTPReferer = trimString(value)
|
||||
case "HTTP_ACCEPT_LANGUAGE":
|
||||
cfg.HTTPAcceptLanguage = trimString(value)
|
||||
case "HTTP_TIMING_JITTER_MS":
|
||||
valueInt, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse HTTP_TIMING_JITTER_MS: %w", err)
|
||||
}
|
||||
|
||||
cfg.HTTPTimingJitterMS = valueInt
|
||||
case "HTTP_BATCH_RANDOMIZE":
|
||||
randomize, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse HTTP_BATCH_RANDOMIZE: %w", err)
|
||||
}
|
||||
|
||||
cfg.HTTPBatchRandomize = randomize
|
||||
case "HTTP_BATCH_PACKETS_JITTER":
|
||||
valueInt, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse HTTP_BATCH_PACKETS_JITTER: %w", err)
|
||||
}
|
||||
|
||||
cfg.HTTPBatchPacketsJitter = valueInt
|
||||
case "HTTP_BATCH_BYTES_JITTER":
|
||||
valueInt, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse HTTP_BATCH_BYTES_JITTER: %w", err)
|
||||
}
|
||||
|
||||
cfg.HTTPBatchBytesJitter = valueInt
|
||||
case "SERVER_HOST":
|
||||
cfg.ServerHost = trimString(value)
|
||||
case "SERVER_PORT":
|
||||
@@ -140,6 +179,7 @@ func Load(path string) (Config, error) {
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse SERVER_PORT: %w", err)
|
||||
}
|
||||
|
||||
cfg.ServerPort = port
|
||||
case "SOCKS_HOST":
|
||||
cfg.SOCKSHost = trimString(value)
|
||||
@@ -148,12 +188,14 @@ func Load(path string) (Config, error) {
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse SOCKS_PORT: %w", err)
|
||||
}
|
||||
|
||||
cfg.SOCKSPort = port
|
||||
case "SOCKS_AUTH":
|
||||
auth, err := strconv.ParseBool(value)
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse SOCKS_AUTH: %w", err)
|
||||
}
|
||||
|
||||
cfg.SOCKSAuth = auth
|
||||
case "SOCKS_USERNAME":
|
||||
cfg.SOCKSUsername = trimString(value)
|
||||
@@ -166,60 +208,70 @@ func Load(path string) (Config, error) {
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse MAX_CHUNK_SIZE: %w", err)
|
||||
}
|
||||
|
||||
cfg.MaxChunkSize = size
|
||||
case "MAX_PACKETS_PER_BATCH":
|
||||
count, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse MAX_PACKETS_PER_BATCH: %w", err)
|
||||
}
|
||||
|
||||
cfg.MaxPacketsPerBatch = count
|
||||
case "MAX_BATCH_BYTES":
|
||||
size, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse MAX_BATCH_BYTES: %w", err)
|
||||
}
|
||||
|
||||
cfg.MaxBatchBytes = size
|
||||
case "WORKER_COUNT":
|
||||
count, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse WORKER_COUNT: %w", err)
|
||||
}
|
||||
|
||||
cfg.WorkerCount = count
|
||||
case "HTTP_REQUEST_TIMEOUT_MS":
|
||||
timeout, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse HTTP_REQUEST_TIMEOUT_MS: %w", err)
|
||||
}
|
||||
|
||||
cfg.HTTPRequestTimeoutMS = timeout
|
||||
case "WORKER_POLL_INTERVAL_MS":
|
||||
interval, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse WORKER_POLL_INTERVAL_MS: %w", err)
|
||||
}
|
||||
|
||||
cfg.WorkerPollIntervalMS = interval
|
||||
case "IDLE_POLL_INTERVAL_MS":
|
||||
interval, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse IDLE_POLL_INTERVAL_MS: %w", err)
|
||||
}
|
||||
|
||||
cfg.IdlePollIntervalMS = interval
|
||||
case "MAX_QUEUE_BYTES_PER_SOCKS":
|
||||
size, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse MAX_QUEUE_BYTES_PER_SOCKS: %w", err)
|
||||
}
|
||||
|
||||
cfg.MaxQueueBytesPerSOCKS = size
|
||||
case "ACK_TIMEOUT_MS":
|
||||
timeout, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse ACK_TIMEOUT_MS: %w", err)
|
||||
}
|
||||
|
||||
cfg.AckTimeoutMS = timeout
|
||||
case "MAX_RETRY_COUNT":
|
||||
count, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse MAX_RETRY_COUNT: %w", err)
|
||||
}
|
||||
|
||||
cfg.MaxRetryCount = count
|
||||
case "SESSION_IDLE_TIMEOUT_MS":
|
||||
timeout, err := strconv.Atoi(value)
|
||||
@@ -232,18 +284,21 @@ func Load(path string) (Config, error) {
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse SOCKS_IDLE_TIMEOUT_MS: %w", err)
|
||||
}
|
||||
|
||||
cfg.SOCKSIdleTimeoutMS = timeout
|
||||
case "READ_BODY_LIMIT_BYTES":
|
||||
size, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse READ_BODY_LIMIT_BYTES: %w", err)
|
||||
}
|
||||
|
||||
cfg.ReadBodyLimitBytes = size
|
||||
case "MAX_SERVER_QUEUE_BYTES":
|
||||
size, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return Config{}, fmt.Errorf("parse MAX_SERVER_QUEUE_BYTES: %w", err)
|
||||
}
|
||||
|
||||
cfg.MaxServerQueueBytes = size
|
||||
}
|
||||
}
|
||||
@@ -292,7 +347,7 @@ func (c Config) ValidateClient() error {
|
||||
return fmt.Errorf("invalid MAX_RETRY_COUNT: %d", c.MaxRetryCount)
|
||||
}
|
||||
|
||||
if c.HTTPHeaderProfile != "browser" && c.HTTPHeaderProfile != "minimal" {
|
||||
if c.HTTPHeaderProfile != "browser" && c.HTTPHeaderProfile != "cdn" && c.HTTPHeaderProfile != "api" && c.HTTPHeaderProfile != "minimal" {
|
||||
return fmt.Errorf("invalid HTTP_HEADER_PROFILE: %s", c.HTTPHeaderProfile)
|
||||
}
|
||||
|
||||
@@ -303,6 +358,15 @@ 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.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)
|
||||
}
|
||||
|
||||
if c.MaxQueueBytesPerSOCKS < c.MaxChunkSize {
|
||||
return fmt.Errorf("MAX_QUEUE_BYTES_PER_SOCKS must be >= MAX_CHUNK_SIZE")
|
||||
|
||||
Reference in New Issue
Block a user