Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions backend/xray/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,21 @@ func (c *Core) GenerateConfigFile(config []byte) error {
return err
}

var versionProbeTimeout = 30 * time.Second

func (c *Core) refreshVersion() (string, error) {
cmd := exec.Command(c.executablePath, "version")
ctx, cancel := context.WithTimeout(context.Background(), versionProbeTimeout)
defer cancel()

cmd := exec.CommandContext(ctx, c.executablePath, "version")
cmd.WaitDelay = time.Second
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
if ctx.Err() != nil {
return "", fmt.Errorf("xray version probe did not finish within %s: %w", versionProbeTimeout, ctx.Err())
}
if err != nil && !errors.Is(err, exec.ErrWaitDelay) {
return "", err
}

Expand Down
72 changes: 72 additions & 0 deletions backend/xray/core_version_probe_unix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//go:build !windows

package xray

import (
"os"
"path/filepath"
"testing"
"time"
)

func writeVersionStub(t *testing.T, body string) string {
t.Helper()
path := filepath.Join(t.TempDir(), "xray")
if err := os.WriteFile(path, []byte("#!/bin/sh\n"+body+"\n"), 0o755); err != nil {
t.Fatal(err)
}
return path
}

func newCoreWithin(t *testing.T, path string, limit time.Duration) error {
t.Helper()
done := make(chan error, 1)
go func() {
_, err := NewXRayCore(path, t.TempDir(), t.TempDir(), 16, 16)
done <- err
}()
select {
case err := <-done:
return err
case <-time.After(limit):
t.Fatalf("NewXRayCore did not return within %s while the version probe hung", limit)
return nil
}
}

func TestVersionProbeOfAHungBinaryIsBounded(t *testing.T) {
previous := versionProbeTimeout
versionProbeTimeout = 500 * time.Millisecond
t.Cleanup(func() { versionProbeTimeout = previous })

err := newCoreWithin(t, writeVersionStub(t, "exec sleep 30"), 5*time.Second)
if err == nil {
t.Fatal("expected an error from a version probe that never answered")
}
}

func TestVersionProbeSurvivesABinaryThatForksAndHoldsStdout(t *testing.T) {
err := newCoreWithin(t, writeVersionStub(t, "echo 'Xray 26.10.31 (Xray, Penetrates Everything.)'\nsleep 30 &\nexit 0"), 15*time.Second)
if err != nil {
t.Fatalf("expected the printed version to be accepted, got %v", err)
}
}

func TestVersionProbeOfAWorkingBinaryStillParses(t *testing.T) {
path := writeVersionStub(t, "echo 'Xray 26.10.31 (Xray, Penetrates Everything.)'")
core, err := NewXRayCore(path, t.TempDir(), t.TempDir(), 16, 16)
if err != nil {
t.Fatal(err)
}
if core.Version() != "26.10.31" {
t.Fatalf("version = %q", core.Version())
}
}

func TestVersionProbeWaitsForASlowButHealthyBinary(t *testing.T) {
t.Parallel()
err := newCoreWithin(t, writeVersionStub(t, "sleep 11\necho 'Xray 26.10.31 (Xray, Penetrates Everything.)'"), 40*time.Second)
if err != nil {
t.Fatalf("a version probe that answers after 11 s must still be accepted, got %v", err)
}
}
Loading