Skip to content

Commit

Permalink
Merge pull request #1088 from apernet/wip-shutdown
Browse files Browse the repository at this point in the history
feat: graceful client shutdown
  • Loading branch information
tobyxdd authored May 18, 2024
2 parents 4c0bd74 + b216c4f commit 15e5846
Showing 1 changed file with 39 additions and 4 deletions.
43 changes: 39 additions & 4 deletions app/cmd/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import (
"net"
"net/netip"
"os"
"os/signal"
"runtime"
"slices"
"strconv"
"strings"
"syscall"
"time"

"github.com/spf13/cobra"
Expand Down Expand Up @@ -512,23 +514,52 @@ func runClient(cmd *cobra.Command, args []string) {
})
}

runner.Run()
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM)
defer signal.Stop(signalChan)

runnerChan := make(chan clientModeRunnerResult, 1)
go func() {
runnerChan <- runner.Run()
}()

select {
case <-signalChan:
logger.Info("received signal, shutting down gracefully")
case r := <-runnerChan:
if r.OK {
logger.Info(r.Msg)
} else {
_ = c.Close() // Close the client here as Fatal will exit the program without running defer
if r.Err != nil {
logger.Fatal(r.Msg, zap.Error(r.Err))
} else {
logger.Fatal(r.Msg)
}
}
}
}

type clientModeRunner struct {
ModeMap map[string]func() error
}

type clientModeRunnerResult struct {
OK bool
Msg string
Err error
}

func (r *clientModeRunner) Add(name string, f func() error) {
if r.ModeMap == nil {
r.ModeMap = make(map[string]func() error)
}
r.ModeMap[name] = f
}

func (r *clientModeRunner) Run() {
func (r *clientModeRunner) Run() clientModeRunnerResult {
if len(r.ModeMap) == 0 {
logger.Fatal("no mode specified")
return clientModeRunnerResult{OK: false, Msg: "no mode specified"}
}

type modeError struct {
Expand All @@ -546,9 +577,13 @@ func (r *clientModeRunner) Run() {
for i := 0; i < len(r.ModeMap); i++ {
e := <-errChan
if e.Err != nil {
logger.Fatal("failed to run "+e.Name, zap.Error(e.Err))
return clientModeRunnerResult{OK: false, Msg: "failed to run " + e.Name, Err: e.Err}
}
}

// We don't really have any such cases, as currently none of our modes would stop on themselves without error.
// But we leave the possibility here for future expansion.
return clientModeRunnerResult{OK: true, Msg: "finished without error"}
}

func clientSOCKS5(config socks5Config, c client.Client) error {
Expand Down

0 comments on commit 15e5846

Please sign in to comment.