1- // core_engine/main.go
2-
31package main
42
53import (
4+ "bytes"
65 "context"
76 "encoding/json"
87 "fmt"
98 "io"
109 "net"
1110 "net/http"
1211 "os"
12+ "os/exec"
1313 "sync"
1414 "time"
15- "os/exec"
15+
1616 "golang.org/x/net/proxy"
1717)
1818
19- // note: This struct defines the input format we expect from Python.
2019type TestConfig struct {
2120 Tag string `json:"tag"`
22- Config json.RawMessage `json:"config"` // * We use RawMessage to keep the outbound config as-is.
21+ Config json.RawMessage `json:"config"`
2322 TestPort int `json:"test_port"`
24- XrayPath string `json:"xray_path"`
23+ XrayPath string `json:"xray_path"`
2524}
2625
27- // note: This struct defines the output format we send back to Python.
2826type TestResult struct {
2927 Tag string `json:"tag"`
30- Ping int64 `json:"ping_ms"` // * Ping in milliseconds
28+ Ping int64 `json:"ping_ms"`
3129 Status string `json:"status"`
3230}
3331
3432func main () {
3533 inputData , err := io .ReadAll (os .Stdin )
3634 if err != nil {
37- fmt .Fprintf (os .Stderr , "Error reading stdin: %v\n " , err )
3835 os .Exit (1 )
3936 }
4037
4138 var configs []TestConfig
4239 if err := json .Unmarshal (inputData , & configs ); err != nil {
43- fmt .Fprintf (os .Stderr , "Error unmarshaling json: %v\n " , err )
4440 os .Exit (1 )
4541 }
4642
47- results := make (chan TestResult , len (configs )) // * Use a buffered channel for collecting results.
43+ results := make (chan TestResult , len (configs ))
4844 var wg sync.WaitGroup
4945
50- // * All tests will run concurrently!
5146 for _ , conf := range configs {
5247 wg .Add (1 )
5348 go func (c TestConfig ) {
@@ -58,48 +53,52 @@ func main() {
5853 results <- TestResult {Tag : c .Tag , Ping : - 1 , Status : "tempfile_error" }
5954 return
6055 }
61- // * Ensure the temp file is cleaned up even if something panics.
6256 defer os .Remove (tmpFile .Name ())
6357
6458 fullConfig := map [string ]interface {}{
59+ "log" : map [string ]string {
60+ "loglevel" : "debug" , // ! Enable debug logging
61+ },
6562 "inbounds" : []map [string ]interface {}{
66- {
67- "protocol" : "socks" ,
68- "port" : c .TestPort ,
69- "listen" : "127.0.0.1" ,
70- "settings" : map [string ]interface {}{
71- "auth" : "noauth" ,
72- "udp" : true ,
73- },
74- },
63+ {"protocol" : "socks" , "port" : c .TestPort , "listen" : "127.0.0.1" , "settings" : map [string ]interface {}{"auth" : "noauth" , "udp" : true }},
7564 },
7665 "outbounds" : []json.RawMessage {c .Config },
7766 }
7867
7968 configBytes , _ := json .Marshal (fullConfig )
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- }
69+ tmpFile .Write (configBytes )
8570 tmpFile .Close ()
8671
87- // * We set a context with a timeout for the entire Xray process.
8872 ctx , cancel := context .WithTimeout (context .Background (), 15 * time .Second )
8973 defer cancel ()
9074
9175 cmd := exec .CommandContext (ctx , c .XrayPath , "-c" , tmpFile .Name ())
9276
77+ // ! Capture Xray's output
78+ var xrayOutput bytes.Buffer
79+ cmd .Stdout = & xrayOutput
80+ cmd .Stderr = & xrayOutput
81+
9382 if err := cmd .Start (); err != nil {
9483 results <- TestResult {Tag : c .Tag , Ping : - 1 , Status : "xray_start_failed" }
9584 return
9685 }
9786
98- time .Sleep (700 * time .Millisecond )
87+ time .Sleep (800 * time .Millisecond ) // Increased sleep time slightly for debug logs
9988
10089 ping , status := testProxy (c .TestPort )
10190
102- // * Killing the process is more reliable than waiting for it to exit.
91+ // ! If test fails, append Xray's log to the status
92+ if status != "success" {
93+ // Sanitize and shorten the log for cleaner output
94+ logStr := string (xrayOutput .Bytes ())
95+ logStr = re .SubexpNames (logStr , - 1 )
96+ if len (logStr ) > 200 {
97+ logStr = logStr [:200 ]
98+ }
99+ status = fmt .Sprintf ("%s | xray_log: %s" , status , logStr )
100+ }
101+
103102 cmd .Process .Kill ()
104103 cmd .Wait ()
105104
@@ -120,7 +119,6 @@ func main() {
120119}
121120
122121func testProxy (port int ) (int64 , string ) {
123- // * This function tests the SOCKS5 proxy that the Xray core creates.
124122 targetURL := "http://cp.cloudflare.com/generate_204"
125123 timeout := 8 * time .Second
126124
@@ -138,14 +136,14 @@ func testProxy(port int) (int64, string) {
138136 start := time .Now ()
139137 resp , err := httpClient .Get (targetURL )
140138 if err != nil {
141- return - 1 , "failed_http"
139+ return - 1 , fmt . Sprintf ( "failed_http: %v" , err )
142140 }
143141 defer resp .Body .Close ()
144142
145- if resp .StatusCode != http .StatusOK {
143+ if resp .StatusCode != http .StatusNoContent {
146144 return - 1 , fmt .Sprintf ("bad_status_%d" , resp .StatusCode )
147145 }
148146
149147 latency := time .Since (start ).Milliseconds ()
150148 return latency , "success"
151- }
149+ }
0 commit comments