Enhance MTProxy checker by introducing a new -probe mode deep-strict for stricter response validation. Update README and Docker documentation to clarify probing modes, exit codes, and the new MTPROXY_TDLIB_HELPER and MTPROXY_TDLIB_TIMEOUT environment variables for external helper integration. Refactor connection handling and error reporting to improve robustness and clarity in response verification.
This commit is contained in:
@@ -39,6 +39,8 @@ type config struct {
|
||||
dcIDs []int16
|
||||
probe checker.ProbeMode
|
||||
allowedPrefixes []netip.Prefix
|
||||
tdlibHelper string
|
||||
tdlibTimeout time.Duration
|
||||
}
|
||||
|
||||
func loadConfig() (*config, error) {
|
||||
@@ -90,6 +92,15 @@ func loadConfig() (*config, error) {
|
||||
return nil, err
|
||||
}
|
||||
probe := checker.ParseProbe(os.Getenv("MTPROXY_PROBE"))
|
||||
tdlibHelper := strings.TrimSpace(os.Getenv("MTPROXY_TDLIB_HELPER"))
|
||||
tdlibTimeoutStr := strings.TrimSpace(os.Getenv("MTPROXY_TDLIB_TIMEOUT"))
|
||||
if tdlibTimeoutStr == "" {
|
||||
tdlibTimeoutStr = "45s"
|
||||
}
|
||||
tdlibTimeout, err := time.ParseDuration(tdlibTimeoutStr)
|
||||
if err != nil || tdlibTimeout <= 0 {
|
||||
return nil, fmt.Errorf("MTPROXY_TDLIB_TIMEOUT: invalid duration %q", tdlibTimeoutStr)
|
||||
}
|
||||
return &config{
|
||||
listFile: listFile,
|
||||
checkInterval: interval,
|
||||
@@ -98,6 +109,8 @@ func loadConfig() (*config, error) {
|
||||
dcIDs: dcIDs,
|
||||
probe: probe,
|
||||
allowedPrefixes: prefixes,
|
||||
tdlibHelper: tdlibHelper,
|
||||
tdlibTimeout: tdlibTimeout,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -108,14 +121,32 @@ type dcProbeResult struct {
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type standardProbe struct {
|
||||
OK bool `json:"ok"`
|
||||
ExitCode int `json:"exit_code"`
|
||||
Error string `json:"error,omitempty"`
|
||||
ParseError string `json:"parse_error,omitempty"`
|
||||
}
|
||||
|
||||
type tdlibProbe struct {
|
||||
SkippedReason string `json:"skipped_reason,omitempty"`
|
||||
Ran bool `json:"ran"`
|
||||
OK bool `json:"ok"`
|
||||
ExitCode int `json:"exit_code,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
DurationMs int64 `json:"duration_ms,omitempty"`
|
||||
}
|
||||
|
||||
type proxyEntry struct {
|
||||
RawLine string `json:"raw_line"`
|
||||
URL string `json:"url,omitempty"`
|
||||
CheckedAt string `json:"checked_at,omitempty"`
|
||||
Standard standardProbe `json:"standard"`
|
||||
TDLib tdlibProbe `json:"tdlib"`
|
||||
OK bool `json:"ok"`
|
||||
ExitCode int `json:"exit_code"`
|
||||
Error string `json:"error,omitempty"`
|
||||
ParseError string `json:"parse_error,omitempty"`
|
||||
CheckedAt string `json:"checked_at,omitempty"`
|
||||
DCs []dcProbeResult `json:"dcs,omitempty"`
|
||||
}
|
||||
|
||||
@@ -185,9 +216,13 @@ func aggregateExitFromDCs(dcs []dcProbeResult) int {
|
||||
return 1
|
||||
}
|
||||
|
||||
func checkOneLine(ctx context.Context, line string, dcIDs []int16, perDCTimeout time.Duration, probe checker.ProbeMode) proxyEntry {
|
||||
func checkOneLine(ctx context.Context, line string, dcIDs []int16, perDCTimeout time.Duration, probe checker.ProbeMode, tdlibHelper string, tdlibTimeout time.Duration) (ent proxyEntry) {
|
||||
now := time.Now().UTC().Format(time.RFC3339)
|
||||
ent := proxyEntry{RawLine: line, CheckedAt: now}
|
||||
ent = proxyEntry{RawLine: line, CheckedAt: now}
|
||||
defer func() {
|
||||
ent.Standard = standardProbe{OK: ent.OK, ExitCode: ent.ExitCode, Error: ent.Error, ParseError: ent.ParseError}
|
||||
ent.TDLib = runTDLibBlock(line, tdlibHelper, tdlibTimeout, probe, ent)
|
||||
}()
|
||||
if len(dcIDs) == 0 {
|
||||
ent.ExitCode = 2
|
||||
ent.Error = "no DC ids configured"
|
||||
@@ -210,8 +245,8 @@ func checkOneLine(ctx context.Context, line string, dcIDs []int16, perDCTimeout
|
||||
}
|
||||
if len(dcIDs) == 1 {
|
||||
dcCtx, cancel := context.WithTimeout(ctx, perDCTimeout)
|
||||
defer cancel()
|
||||
err = checker.Check(dcCtx, t.Host, t.Port, parsed, dcIDs[0], &checker.Options{Probe: probe})
|
||||
cancel()
|
||||
code, msg := checkresult.Classify(err)
|
||||
ent.ExitCode = code
|
||||
ent.OK = err == nil
|
||||
@@ -262,6 +297,8 @@ func runCycle(cfg *config, st *store) {
|
||||
ParseError: err.Error(),
|
||||
Error: err.Error(),
|
||||
CheckedAt: finished.Format(time.RFC3339),
|
||||
Standard: standardProbe{OK: false, ExitCode: 2, Error: err.Error(), ParseError: err.Error()},
|
||||
TDLib: tdlibProbe{SkippedReason: "cycle aborted (list file error)", Ran: false},
|
||||
}}, finished, time.Now().Add(cfg.checkInterval))
|
||||
log.Printf("read list file: %v", err)
|
||||
return
|
||||
@@ -272,8 +309,11 @@ func runCycle(cfg *config, st *store) {
|
||||
if len(cfg.dcIDs) > 1 {
|
||||
total = cfg.checkTimeout * time.Duration(len(cfg.dcIDs))
|
||||
}
|
||||
if cfg.probe == checker.ProbeFast && strings.TrimSpace(cfg.tdlibHelper) != "" {
|
||||
total += cfg.tdlibTimeout
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), total)
|
||||
ent := checkOneLine(ctx, line, cfg.dcIDs, cfg.checkTimeout, cfg.probe)
|
||||
ent := checkOneLine(ctx, line, cfg.dcIDs, cfg.checkTimeout, cfg.probe, cfg.tdlibHelper, cfg.tdlibTimeout)
|
||||
cancel()
|
||||
entries = append(entries, ent)
|
||||
}
|
||||
@@ -353,7 +393,11 @@ func run() error {
|
||||
|
||||
errCh := make(chan error, 1)
|
||||
go func() {
|
||||
log.Printf("listening on %s, list=%s interval=%s dcs=%v", cfg.httpAddr, cfg.listFile, cfg.checkInterval, cfg.dcIDs)
|
||||
if cfg.tdlibHelper != "" {
|
||||
log.Printf("listening on %s, list=%s interval=%s dcs=%v tdlib_helper=%s tdlib_timeout=%s", cfg.httpAddr, cfg.listFile, cfg.checkInterval, cfg.dcIDs, cfg.tdlibHelper, cfg.tdlibTimeout)
|
||||
} else {
|
||||
log.Printf("listening on %s, list=%s interval=%s dcs=%v", cfg.httpAddr, cfg.listFile, cfg.checkInterval, cfg.dcIDs)
|
||||
}
|
||||
errCh <- srv.ListenAndServe()
|
||||
}()
|
||||
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"mtproxy_checker/internal/checker"
|
||||
"mtproxy_checker/internal/tdlibexec"
|
||||
)
|
||||
|
||||
func runTDLibBlock(proxyLine, helper string, timeout time.Duration, probe checker.ProbeMode, ent proxyEntry) tdlibProbe {
|
||||
if probe != checker.ProbeFast {
|
||||
return tdlibProbe{SkippedReason: "tdlib runs only with probe fast (or empty MTPROXY_PROBE)", Ran: false}
|
||||
}
|
||||
if ent.ParseError != "" {
|
||||
return tdlibProbe{SkippedReason: "skipped: standard parse error", Ran: false}
|
||||
}
|
||||
if strings.TrimSpace(helper) == "" {
|
||||
return tdlibProbe{SkippedReason: "MTPROXY_TDLIB_HELPER not set", Ran: false}
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), timeout)
|
||||
defer cancel()
|
||||
res, err := tdlibexec.Run(ctx, helper, proxyLine)
|
||||
if err != nil {
|
||||
if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {
|
||||
return tdlibProbe{Ran: true, OK: false, ExitCode: 4, Error: err.Error(), DurationMs: res.DurationMs}
|
||||
}
|
||||
return tdlibProbe{Ran: true, OK: false, ExitCode: 1, Error: err.Error(), DurationMs: res.DurationMs}
|
||||
}
|
||||
return tdlibProbe{Ran: true, OK: res.OK, ExitCode: res.ExitCode, Error: res.Error, DurationMs: res.DurationMs}
|
||||
}
|
||||
Reference in New Issue
Block a user