mirror of
https://github.com/v2rayA/v2rayA.git
synced 2026-10-29 13:38:50 +08:00
The core hooks ran under exec.Command with no deadline while Start held the manager's lock, so a hook that never returned left the proxy stopped and every status read blocked. They now run with the startup timeout as their deadline in their own process group. The custom-inbound POST and DELETE handlers did not take the mutation turn; two concurrent additions read the same list and the last write won. They take it like every other state-changing handler. A failed HTTP bind ended in log.Fatal before the deferred cleanup, so the core and the transparent-proxy rules stayed behind; run() now returns the error through the same cleanup the shutdown path uses. The resolv.conf backup was written in place; a crash mid-write left a truncated file that the next start restored as the original. It is written to a temporary file, synced and renamed, and an empty backup is ignored on restore.
68 lines
2.0 KiB
Go
68 lines
2.0 KiB
Go
package v2ray
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/v2rayA/v2rayA/conf"
|
|
"github.com/v2rayA/v2rayA/db/configure"
|
|
)
|
|
|
|
func TestRunPreStartHookTimesOut(t *testing.T) {
|
|
dir := t.TempDir()
|
|
hook := filepath.Join(dir, "slow-hook")
|
|
if err := os.WriteFile(hook, []byte("#!/bin/sh\nsleep 3\n"), 0700); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
previous := *conf.GetEnvironmentConfig()
|
|
config := previous
|
|
config.CoreHook = hook
|
|
config.Config = dir
|
|
config.CoreStartupTimeout = 1
|
|
conf.SetConfig(config)
|
|
t.Cleanup(func() { conf.SetConfig(previous) })
|
|
|
|
started := time.Now()
|
|
err := (&CoreProcessManager{}).runPreStartHook()
|
|
elapsed := time.Since(started)
|
|
if elapsed >= 2*time.Second {
|
|
t.Fatalf("hook returned after %v, want less than 2s", elapsed)
|
|
}
|
|
if err == nil || !strings.Contains(err.Error(), hook) || !strings.Contains(err.Error(), "1s") {
|
|
t.Fatalf("error %q must name hook %q and deadline 1s", err, hook)
|
|
}
|
|
}
|
|
|
|
// Shutdown asks for the transparent proxy teardown twice: pre_run calls it and
|
|
// Stop calls it again through beforeStop. Only the first call may run the
|
|
// user's hooks and delete the rules.
|
|
func TestCheckAndStopTransparentProxyRunsOnce(t *testing.T) {
|
|
var m CoreProcessManager
|
|
if m.transparentOn.Load() {
|
|
t.Fatal("a fresh manager must not claim installed rules")
|
|
}
|
|
// Without a setup, the teardown is a no-op.
|
|
m.CheckAndStopTransparentProxy(&configure.Setting{Transparent: configure.TransparentClose})
|
|
if m.transparentOn.Load() {
|
|
t.Error("the mark must stay off")
|
|
}
|
|
|
|
m.transparentOn.Store(true)
|
|
m.CheckAndStopTransparentProxy(&configure.Setting{Transparent: configure.TransparentClose})
|
|
if m.transparentOn.Load() {
|
|
t.Error("the first teardown must clear the mark")
|
|
}
|
|
|
|
// A teardown with no setting and no running template cannot remove
|
|
// anything, so the mark has to survive for the next attempt.
|
|
m.transparentOn.Store(true)
|
|
m.CheckAndStopTransparentProxy(nil)
|
|
if !m.transparentOn.Load() {
|
|
t.Error("a teardown that did nothing must keep the mark")
|
|
}
|
|
}
|