@@ -10,22 +10,21 @@ import (
1010 "net/http"
1111 "os"
1212 "os/exec"
13- "runtime"
1413 "strings"
1514 "sync"
1615 "time"
16+
1717 "golang.org/x/net/proxy"
1818)
1919
2020type TestJob struct {
21- Tag string `json:"tag"`
22- Protocol string `json:"protocol"`
23- Config json.RawMessage `json:"config,omitempty"`
24- ConfigURI string `json:"config_uri,omitempty"`
25- ListenIP string `json:"listen_ip"`
26- TestPort int `json:"test_port"`
27- ClientPath string `json:"client_path,omitempty"`
28- FragmentConfig json.RawMessage `json:"fragment_config,omitempty"`
21+ Tag string `json:"tag"`
22+ Protocol string `json:"protocol"`
23+ Config json.RawMessage `json:"config,omitempty"`
24+ ConfigURI string `json:"config_uri,omitempty"`
25+ ListenIP string `json:"listen_ip"`
26+ TestPort int `json:"test_port"`
27+ ClientPath string `json:"client_path,omitempty"`
2928}
3029
3130type TestResult struct {
@@ -47,13 +46,7 @@ func main() {
4746 wg .Add (1 )
4847 go func (j TestJob ) {
4948 defer wg .Done ()
50-
51- if j .ClientPath != "" {
52- runIndividualTest (j , results )
53- } else {
54- ping , status := testProxy (j .ListenIP , j .TestPort )
55- results <- TestResult {Tag : j .Tag , Ping : ping , Status : status }
56- }
49+ runTest (j , results )
5750 }(job )
5851 }
5952
@@ -66,14 +59,20 @@ func main() {
6659 fmt .Println (string (outputData ))
6760}
6861
69- func runIndividualTest (j TestJob , results chan <- TestResult ) {
62+ func runTest (j TestJob , results chan <- TestResult ) {
7063 var cmd * exec.Cmd
71- configFile , err := os .CreateTemp ("" , "client-test-*.json" )
72- if err != nil { results <- TestResult {Tag : j .Tag , Ping : - 1 , Status : "tempfile_error" }; return }
73- defer os .Remove (configFile .Name ())
64+ var configFile * os.File
65+ var err error
66+
67+ // Create a context with a timeout for the entire test
68+ ctx , cancel := context .WithTimeout (context .Background (), 15 * time .Second )
69+ defer cancel ()
7470
7571 if j .Protocol == "hysteria2" {
76- // Simplified Hysteria config creation
72+ configFile , err = os .CreateTemp ("" , "hysteria-*.json" )
73+ if err != nil { results <- TestResult {Tag : j .Tag , Ping : - 1 , Status : "tempfile_error" }; return }
74+ defer os .Remove (configFile .Name ())
75+
7776 uriParts := strings .Split (strings .Split (j .ConfigURI , "://" )[1 ], "@" )
7877 serverParts := strings .Split (uriParts [1 ], "?" )[0 ]
7978 sni := strings .Split (strings .Split (j .ConfigURI , "sni=" )[1 ], "#" )[0 ]
@@ -85,30 +84,46 @@ func runIndividualTest(j TestJob, results chan<- TestResult) {
8584 configBytes , _ := json .Marshal (config )
8685 configFile .Write (configBytes )
8786 configFile .Close ()
88- cmd = exec .Command ( j .ClientPath , "client" , "-c" , configFile .Name ())
87+ cmd = exec .CommandContext ( ctx , j .ClientPath , "client" , "-c" , configFile .Name ()) // ! Use CommandContext
8988 } else {
90- // Logic for other individual clients can be added here
91- results <- TestResult {Tag : j .Tag , Ping : - 1 , Status : "unsupported_individual_client" }; return
89+ // Default is Xray
90+ configFile , err = os .CreateTemp ("" , "xray-*.json" )
91+ if err != nil { results <- TestResult {Tag : j .Tag , Ping : - 1 , Status : "tempfile_error" }; return }
92+ defer os .Remove (configFile .Name ())
93+
94+ fullConfig := map [string ]interface {}{
95+ "log" : map [string ]string {"loglevel" : "warning" },
96+ "inbounds" : []map [string ]interface {}{{"protocol" : "socks" , "port" : j .TestPort , "listen" : j .ListenIP , "settings" : map [string ]interface {}{"auth" : "noauth" , "udp" : true }}},
97+ "outbounds" : []json.RawMessage {j .Config },
98+ }
99+ configBytes , _ := json .Marshal (fullConfig )
100+ configFile .Write (configBytes )
101+ configFile .Close ()
102+ cmd = exec .CommandContext (ctx , j .ClientPath , "-c" , configFile .Name ()) // ! Use CommandContext
92103 }
93104
94- ctx , cancel := context .WithTimeout (context .Background (), 15 * time .Second )
95- defer cancel ()
96- if runtime .GOOS == "windows" { setHideWindow (cmd ) }
105+ setHideWindow (cmd ) // This function is platform-specific
97106
98107 var clientOutput bytes.Buffer
99108 cmd .Stdout , cmd .Stderr = & clientOutput , & clientOutput
100109
101- if err := cmd .Start (); err != nil { results <- TestResult {Tag : j .Tag , Ping : - 1 , Status : "client_start_failed" }; return }
102- time .Sleep (1200 * time .Millisecond ) // Hysteria might take longer to start
110+ if err := cmd .Start (); err != nil {
111+ results <- TestResult {Tag : j .Tag , Ping : - 1 , Status : "client_start_failed" }
112+ return
113+ }
103114
115+ time .Sleep (900 * time .Millisecond )
104116 ping , status := testProxy (j .ListenIP , j .TestPort )
117+
105118 if status != "success" {
106119 logStr := strings .ReplaceAll (string (clientOutput .Bytes ()), "\n " , " " )
107- status = fmt .Sprintf ("%s | log: %s" , status , logStr [:200 ])
120+ if len (logStr ) > 200 { logStr = logStr [:200 ] }
121+ status = fmt .Sprintf ("%s | log: %s" , status , logStr )
108122 }
109123
110124 cmd .Process .Kill ()
111125 cmd .Wait ()
126+
112127 results <- TestResult {Tag : j .Tag , Ping : ping , Status : status }
113128}
114129
@@ -117,26 +132,11 @@ func testProxy(listenIP string, port int) (int64, string) {
117132 timeout := 8 * time .Second
118133 dialer , err := proxy .SOCKS5 ("tcp" , fmt .Sprintf ("%s:%d" , listenIP , port ), nil , proxy .Direct )
119134 if err != nil { return - 1 , fmt .Sprintf ("failed_dialer: %v" , err ) }
120-
121- httpClient := & http.Client {
122- Transport : & http.Transport {
123- DialContext : func (ctx context.Context , network , addr string ) (net.Conn , error ) {
124- return dialer .Dial (network , addr )
125- },
126- },
127- Timeout : timeout ,
128- }
129-
135+ httpClient := & http.Client { Transport : & http.Transport {DialContext : func (ctx context.Context , network , addr string ) (net.Conn , error ) { return dialer .Dial (network , addr ) }}, Timeout : timeout }
130136 start := time .Now ()
131137 resp , err := httpClient .Get (targetURL )
132- if err != nil {
133- return - 1 , fmt .Sprintf ("failed_http: %v" , err )
134- }
138+ if err != nil { return - 1 , fmt .Sprintf ("failed_http: %v" , err ) }
135139 defer resp .Body .Close ()
136-
137- if resp .StatusCode != http .StatusNoContent {
138- return - 1 , fmt .Sprintf ("bad_status_%d" , resp .StatusCode )
139- }
140-
140+ if resp .StatusCode != http .StatusNoContent { return - 1 , fmt .Sprintf ("bad_status_%d" , resp .StatusCode ) }
141141 return time .Since (start ).Milliseconds (), "success"
142142}
0 commit comments