@@ -12,7 +12,7 @@ import (
1212 "os"
1313 "sync"
1414 "time"
15-
15+ "os/exec"
1616 "golang.org/x/net/proxy"
1717)
1818
@@ -21,6 +21,7 @@ type TestConfig struct {
2121 Tag string `json:"tag"`
2222 Config json.RawMessage `json:"config"` // * We use RawMessage to keep the outbound config as-is.
2323 TestPort int `json:"test_port"`
24+ XrayPath string `json:"xray_path"`
2425}
2526
2627// note: This struct defines the output format we send back to Python.
@@ -31,7 +32,6 @@ type TestResult struct {
3132}
3233
3334func main () {
34- // ! Read the list of configs from Standard Input (stdin).
3535 inputData , err := io .ReadAll (os .Stdin )
3636 if err != nil {
3737 fmt .Fprintf (os .Stderr , "Error reading stdin: %v\n " , err )
@@ -44,21 +44,21 @@ func main() {
4444 os .Exit (1 )
4545 }
4646
47- results := make ([]TestResult , 0 )
48- var wg sync.WaitGroup // ? A WaitGroup waits for a collection of goroutines to finish.
49- var mu sync.Mutex // ? A Mutex prevents race conditions when writing to the results slice.
47+ results := make (chan TestResult , len (configs )) // * Use a buffered channel for collecting results.
48+ var wg sync.WaitGroup
5049
5150 // * All tests will run concurrently!
5251 for _ , conf := range configs {
5352 wg .Add (1 )
54- go func (c TestConfig ) {
53+ go func (c Test - Config ) {
5554 defer wg .Done ()
5655
57- // * Create a temporary config file for the Xray core to use.
5856 tmpFile , err := os .CreateTemp ("" , "xray-test-*.json" )
5957 if err != nil {
58+ results <- TestResult {Tag : c .Tag , Ping : - 1 , Status : "tempfile_error" }
6059 return
6160 }
61+ // * Ensure the temp file is cleaned up even if something panics.
6262 defer os .Remove (tmpFile .Name ())
6363
6464 fullConfig := map [string ]interface {}{
@@ -67,32 +67,55 @@ func main() {
6767 "protocol" : "socks" ,
6868 "port" : c .TestPort ,
6969 "listen" : "127.0.0.1" ,
70- "settings" : map [string ]interface {}{"udp" : true },
70+ "settings" : map [string ]interface {}{
71+ "auth" : "noauth" ,
72+ "udp" : true ,
73+ },
7174 },
7275 },
7376 "outbounds" : []json.RawMessage {c .Config },
7477 }
7578
7679 configBytes , _ := json .Marshal (fullConfig )
77- tmpFile .Write (configBytes )
80+ if _ , err := tmpFile .Write (configBytes ); err != nil {
81+ results <- TestResult {Tag : c .Tag , Ping : - 1 , Status : "tempfile_write_error" }
82+ tmpFile .Close ()
83+ return
84+ }
7885 tmpFile .Close ()
7986
80- // todo: We need a way to run the xray executable here.
81- // For now, let's assume it's running and we can test the SOCKS port.
82- // This part will be completed in the next steps.
87+ // * We set a context with a timeout for the entire Xray process.
88+ ctx , cancel := context .WithTimeout (context .Background (), 15 * time .Second )
89+ defer cancel ()
90+
91+ cmd := exec .CommandContext (ctx , c .XrayPath , "-c" , tmpFile .Name ())
92+
93+ if err := cmd .Start (); err != nil {
94+ results <- TestResult {Tag : c .Tag , Ping : - 1 , Status : "xray_start_failed" }
95+ return
96+ }
97+
98+ time .Sleep (700 * time .Millisecond )
8399
84100 ping , status := testProxy (c .TestPort )
85101
86- mu .Lock ()
87- results = append (results , TestResult {Tag : c .Tag , Ping : ping , Status : status })
88- mu .Unlock ()
102+ // * Killing the process is more reliable than waiting for it to exit.
103+ cmd .Process .Kill ()
104+ cmd .Wait () /
105+
106+ results <- TestResult {Tag : c .Tag , Ping : ping , Status : status }
89107 }(conf )
90108 }
91109
92- wg .Wait () // * Wait for all tests to complete.
110+ wg .Wait ()
111+ close (results )
112+
113+ finalResults := make ([]TestResult , 0 , len (configs ))
114+ for res := range results {
115+ finalResults = append (finalResults , res )
116+ }
93117
94- // ! Print the final results as a JSON array to Standard Output (stdout).
95- outputData , _ := json .Marshal (results )
118+ outputData , _ := json .Marshal (finalResults )
96119 fmt .Println (string (outputData ))
97120}
98121
0 commit comments