Files
sing-box/experimental/boxdd/application_service.go
2026-07-25 22:58:38 +08:00

141 lines
4.5 KiB
Go

package main
import (
"context"
"reflect"
"time"
"github.com/sagernet/sing-box/common/networkquality"
"github.com/sagernet/sing-box/common/stun"
"github.com/sagernet/sing-box/daemon"
"github.com/sagernet/sing-box/experimental/libbox"
"github.com/sagernet/sing-box/include"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/schema"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
)
var _ ApplicationServiceServer = (*applicationService)(nil)
type applicationService struct {
UnimplementedApplicationServiceServer
startedService *daemon.StartedService
}
func (s *applicationService) CheckConfig(ctx context.Context, request *ConfigContent) (*emptypb.Empty, error) {
err := s.startedService.CheckConfig(ctx, request.Content)
if err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
return &emptypb.Empty{}, nil
}
func (s *applicationService) FormatConfig(ctx context.Context, request *ConfigContent) (*ConfigContent, error) {
content, err := s.startedService.FormatConfig(ctx, request.Content)
if err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
return &ConfigContent{Content: content}, nil
}
func (s *applicationService) GenerateConfigSchema(ctx context.Context, request *emptypb.Empty) (*ConfigContent, error) {
content, err := schema.Generate(include.Context(context.Background()), reflect.TypeFor[option.Options]())
if err != nil {
return nil, status.Error(codes.Internal, err.Error())
}
return &ConfigContent{Content: string(content)}, nil
}
func (s *applicationService) EncodeProfile(ctx context.Context, request *ProfileContent) (*ProfileData, error) {
content := libbox.ProfileContent{
Name: request.Name,
Type: int32(request.Type),
Config: request.Config,
RemotePath: request.RemotePath,
AutoUpdate: request.AutoUpdate,
AutoUpdateInterval: request.AutoUpdateInterval,
LastUpdated: request.LastUpdated,
}
return &ProfileData{Data: content.Encode()}, nil
}
func (s *applicationService) DecodeProfile(ctx context.Context, request *ProfileData) (*ProfileContent, error) {
content, err := libbox.DecodeProfileContent(request.Data)
if err != nil {
return nil, status.Error(codes.InvalidArgument, err.Error())
}
return &ProfileContent{
Type: ProfileContent_Type(content.Type),
Name: content.Name,
Config: content.Config,
RemotePath: content.RemotePath,
AutoUpdate: content.AutoUpdate,
AutoUpdateInterval: content.AutoUpdateInterval,
LastUpdated: content.LastUpdated,
}, nil
}
func (s *applicationService) ArchiveReport(ctx context.Context, request *ArchiveReportRequest) (*emptypb.Empty, error) {
err := libbox.CreateZipArchive(request.SourcePath, request.DestinationPath, request.Encrypt)
if err != nil {
return nil, err
}
return &emptypb.Empty{}, nil
}
func (s *applicationService) StartStandaloneNetworkQualityTest(
request *StandaloneNetworkQualityTestRequest,
server grpc.ServerStreamingServer[daemon.NetworkQualityTestProgress],
) error {
httpClient := networkquality.NewHTTPClient(nil)
defer httpClient.CloseIdleConnections()
measurementClientFactory, err := networkquality.NewOptionalHTTP3Factory(nil, request.Http3)
if err != nil {
return err
}
result, err := networkquality.Run(networkquality.Options{
ConfigURL: request.ConfigUrl,
HTTPClient: httpClient,
NewMeasurementClient: measurementClientFactory,
Serial: request.Serial,
MaxRuntime: time.Duration(request.MaxRuntimeSeconds) * time.Second,
Context: server.Context(),
OnProgress: func(progress networkquality.Progress) {
_ = server.Send(daemon.NewNetworkQualityTestProgress(progress))
},
})
if err != nil {
return server.Send(&daemon.NetworkQualityTestProgress{
IsFinal: true,
Error: err.Error(),
})
}
return server.Send(daemon.NewNetworkQualityTestResult(result))
}
func (s *applicationService) StartStandaloneSTUNTest(
request *StandaloneSTUNTestRequest,
server grpc.ServerStreamingServer[daemon.STUNTestProgress],
) error {
result, err := stun.Run(stun.Options{
Server: request.Server,
Context: server.Context(),
OnProgress: func(progress stun.Progress) {
_ = server.Send(daemon.NewSTUNTestProgress(progress))
},
})
if err != nil {
return server.Send(&daemon.STUNTestProgress{
IsFinal: true,
Error: err.Error(),
})
}
return server.Send(daemon.NewSTUNTestResult(result))
}