Add sing-box API service
This commit is contained in:
@@ -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")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user