Add sing-box API service
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
package daemon
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/url"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
"google.golang.org/grpc/metadata"
|
||||
)
|
||||
|
||||
type RemoteClientOptions struct {
|
||||
ServerURL string
|
||||
Secret string
|
||||
}
|
||||
|
||||
func (o RemoteClientOptions) ServerTarget() (string, credentials.TransportCredentials, error) {
|
||||
if o.ServerURL == "" {
|
||||
return "", nil, E.New("missing server URL")
|
||||
}
|
||||
serverURL, err := url.Parse(o.ServerURL)
|
||||
if err != nil {
|
||||
return "", nil, E.Cause(err, "invalid server URL: ", o.ServerURL)
|
||||
}
|
||||
var enableTLS bool
|
||||
switch serverURL.Scheme {
|
||||
case "http":
|
||||
case "https":
|
||||
enableTLS = true
|
||||
default:
|
||||
return "", nil, E.New("invalid server URL scheme: ", serverURL.Scheme, ", expected http or https")
|
||||
}
|
||||
host := serverURL.Hostname()
|
||||
if host == "" {
|
||||
return "", nil, E.New("missing host in server URL: ", o.ServerURL)
|
||||
}
|
||||
port := serverURL.Port()
|
||||
if port == "" {
|
||||
if enableTLS {
|
||||
port = "443"
|
||||
} else {
|
||||
port = "80"
|
||||
}
|
||||
}
|
||||
transportCredentials := insecure.NewCredentials()
|
||||
if enableTLS {
|
||||
transportCredentials = credentials.NewTLS(&tls.Config{ServerName: host})
|
||||
}
|
||||
return net.JoinHostPort(host, port), transportCredentials, nil
|
||||
}
|
||||
|
||||
func NewRemoteClient(options RemoteClientOptions) (*grpc.ClientConn, error) {
|
||||
target, transportCredentials, err := options.ServerTarget()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return grpc.NewClient(target,
|
||||
grpc.WithTransportCredentials(transportCredentials),
|
||||
grpc.WithUnaryInterceptor(NewClientAuthUnaryInterceptor(options.Secret)),
|
||||
grpc.WithStreamInterceptor(NewClientAuthStreamInterceptor(options.Secret)),
|
||||
)
|
||||
}
|
||||
|
||||
func NewClientAuthUnaryInterceptor(secret string) grpc.UnaryClientInterceptor {
|
||||
return func(ctx context.Context, method string, request, reply any, clientConn *grpc.ClientConn, invoker grpc.UnaryInvoker, options ...grpc.CallOption) error {
|
||||
if secret != "" {
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, "authorization", "Bearer "+secret)
|
||||
}
|
||||
return invoker(ctx, method, request, reply, clientConn, options...)
|
||||
}
|
||||
}
|
||||
|
||||
func NewClientAuthStreamInterceptor(secret string) grpc.StreamClientInterceptor {
|
||||
return func(ctx context.Context, desc *grpc.StreamDesc, clientConn *grpc.ClientConn, method string, streamer grpc.Streamer, options ...grpc.CallOption) (grpc.ClientStream, error) {
|
||||
if secret != "" {
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, "authorization", "Bearer "+secret)
|
||||
}
|
||||
return streamer(ctx, desc, clientConn, method, options...)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user