Add sing-box API service

This commit is contained in:
世界
2026-06-11 21:11:10 +08:00
parent 1ee3596db9
commit 3a5d654463
36 changed files with 2075 additions and 849 deletions
+50 -18
View File
@@ -28,6 +28,7 @@ type CommandClient struct {
grpcClient daemon.StartedServiceClient
grpcManagedClient daemon.ManagedServiceClient
options CommandClientOptions
remote *remoteConnection
ctx context.Context
cancel context.CancelFunc
clientMutex sync.RWMutex
@@ -147,23 +148,41 @@ func networkConnectionFromFileDescriptor(fileDescriptor int32) (net.Conn, error)
return networkConnection, nil
}
func (c *CommandClient) dialWithRetry(target string, contextDialer func(context.Context, string) (net.Conn, error), retryDial bool) (*grpc.ClientConn, daemon.StartedServiceClient, error) {
func localDialOptions(contextDialer func(context.Context, string) (net.Conn, error)) []grpc.DialOption {
options := []grpc.DialOption{
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithUnaryInterceptor(unaryClientAuthInterceptor),
grpc.WithStreamInterceptor(streamClientAuthInterceptor),
}
if contextDialer != nil {
options = append(options, grpc.WithContextDialer(contextDialer))
}
return options
}
// establishConnection dials the command server the client is bound to: the
// local command server (over socket/XPC) or a remote API service.
func (c *CommandClient) establishConnection() (*grpc.ClientConn, daemon.StartedServiceClient, error) {
if c.remote != nil {
return c.dialRemote()
}
target, contextDialer := dialTarget()
return c.dialWithRetry(target, localDialOptions(contextDialer), true)
}
// dialWithRetry connects to the local command server. The retry loop exists to
// wait out the server starting up: WaitForReady keeps the probe redialing and
// the loop reissues it with a growing delay, so a freshly launched extension is
// picked up without surfacing a transient "unavailable" to the UI.
func (c *CommandClient) dialWithRetry(target string, dialOptions []grpc.DialOption, retryDial bool) (*grpc.ClientConn, daemon.StartedServiceClient, error) {
var connection *grpc.ClientConn
var client daemon.StartedServiceClient
var lastError error
for attempt := range commandClientDialAttempts {
if connection == nil {
options := []grpc.DialOption{
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithUnaryInterceptor(unaryClientAuthInterceptor),
grpc.WithStreamInterceptor(streamClientAuthInterceptor),
}
if contextDialer != nil {
options = append(options, grpc.WithContextDialer(contextDialer))
}
var err error
connection, err = grpc.NewClient(target, options...)
connection, err = grpc.NewClient(target, dialOptions...)
if err != nil {
lastError = err
if !retryDial {
@@ -174,8 +193,7 @@ func (c *CommandClient) dialWithRetry(target string, contextDialer func(context.
}
client = daemon.NewStartedServiceClient(connection)
}
waitDuration := commandClientDialDelay(attempt)
ctx, cancel := context.WithTimeout(context.Background(), waitDuration)
ctx, cancel := context.WithTimeout(context.Background(), commandClientDialDelay(attempt))
_, err := client.GetStartedAt(ctx, &emptypb.Empty{}, grpc.WaitForReady(true))
cancel()
if err == nil {
@@ -190,12 +208,27 @@ func (c *CommandClient) dialWithRetry(target string, contextDialer func(context.
return nil, nil, E.Cause(lastError, "probe command server")
}
func (c *CommandClient) dialRemote() (*grpc.ClientConn, daemon.StartedServiceClient, error) {
connection, err := grpc.NewClient(c.remote.target, c.remote.dialOptions...)
if err != nil {
return nil, nil, E.Cause(err, "create remote command client")
}
client := daemon.NewStartedServiceClient(connection)
ctx, cancel := context.WithTimeout(context.Background(), commandClientRemoteProbeTimeout)
defer cancel()
_, err = client.GetStartedAt(ctx, &emptypb.Empty{})
if err != nil {
connection.Close()
return nil, nil, E.Cause(err, "connect to remote server")
}
return connection, client, nil
}
func (c *CommandClient) Connect() error {
c.clientMutex.Lock()
common.Close(common.PtrOrNil(c.grpcConn))
target, contextDialer := dialTarget()
connection, client, err := c.dialWithRetry(target, contextDialer, true)
connection, client, err := c.establishConnection()
if err != nil {
c.clientMutex.Unlock()
return err
@@ -219,9 +252,9 @@ func (c *CommandClient) ConnectWithFD(fd int32) error {
c.clientMutex.Unlock()
return err
}
connection, client, err := c.dialWithRetry("passthrough:///xpc", func(ctx context.Context, _ string) (net.Conn, error) {
connection, client, err := c.dialWithRetry("passthrough:///xpc", localDialOptions(func(ctx context.Context, _ string) (net.Conn, error) {
return networkConnection, nil
}, false)
}), false)
if err != nil {
networkConnection.Close()
c.clientMutex.Unlock()
@@ -283,8 +316,7 @@ func (c *CommandClient) getClientForCall() (daemon.StartedServiceClient, context
return c.grpcClient, c.ctx, nil
}
target, contextDialer := dialTarget()
connection, client, err := c.dialWithRetry(target, contextDialer, true)
connection, client, err := c.establishConnection()
if err != nil {
return nil, nil, E.Cause(err, "get command client")
}