mirror of
https://github.com/shtorm-7/sing-box-extended.git
synced 2026-09-15 21:00:27 +00:00
132 lines
4.4 KiB
Go
132 lines
4.4 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/sagernet/sing-box/common/networkquality"
|
|
"github.com/sagernet/sing-box/daemon"
|
|
E "github.com/sagernet/sing/common/exceptions"
|
|
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var (
|
|
commandAPINetworkQualityFlagConfigURL string
|
|
commandAPINetworkQualityFlagSerial bool
|
|
commandAPINetworkQualityFlagMaxRuntime int
|
|
commandAPINetworkQualityFlagHTTP3 bool
|
|
commandAPINetworkQualityFlagOutbound string
|
|
)
|
|
|
|
var commandAPINetworkQuality = &cobra.Command{
|
|
Use: "networkquality",
|
|
Short: "Run a network quality test",
|
|
Args: cobra.NoArgs,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
return runAPINetworkQuality()
|
|
},
|
|
}
|
|
|
|
func init() {
|
|
commandAPINetworkQuality.Flags().StringVar(
|
|
&commandAPINetworkQualityFlagConfigURL,
|
|
"config-url", "",
|
|
"Network quality test config URL (default: Apple mensura)",
|
|
)
|
|
commandAPINetworkQuality.Flags().BoolVar(
|
|
&commandAPINetworkQualityFlagSerial,
|
|
"serial", false,
|
|
"Run download and upload tests sequentially instead of in parallel",
|
|
)
|
|
commandAPINetworkQuality.Flags().IntVar(
|
|
&commandAPINetworkQualityFlagMaxRuntime,
|
|
"max-runtime", int(networkquality.DefaultMaxRuntime/time.Second),
|
|
"Network quality maximum runtime in seconds",
|
|
)
|
|
commandAPINetworkQuality.Flags().BoolVar(
|
|
&commandAPINetworkQualityFlagHTTP3,
|
|
"http3", false,
|
|
"Use HTTP/3 (QUIC) for measurement traffic",
|
|
)
|
|
commandAPINetworkQuality.Flags().StringVarP(
|
|
&commandAPINetworkQualityFlagOutbound,
|
|
"outbound", "o", "",
|
|
"Use specified tag instead of default outbound",
|
|
)
|
|
commandAPIRoot.AddCommand(commandAPINetworkQuality)
|
|
}
|
|
|
|
func runAPINetworkQuality() error {
|
|
clientConn, client, err := createAPIClient()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer clientConn.Close()
|
|
stream, err := client.StartNetworkQualityTest(globalCtx, &daemon.NetworkQualityTestRequest{
|
|
ConfigURL: commandAPINetworkQualityFlagConfigURL,
|
|
OutboundTag: commandAPINetworkQualityFlagOutbound,
|
|
Serial: commandAPINetworkQualityFlagSerial,
|
|
MaxRuntimeSeconds: int32(commandAPINetworkQualityFlagMaxRuntime),
|
|
Http3: commandAPINetworkQualityFlagHTTP3,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
writeStderrLine("==== NETWORK QUALITY TEST ====")
|
|
for {
|
|
progress, recvErr := stream.Recv()
|
|
if recvErr != nil {
|
|
return recvErr
|
|
}
|
|
if !progress.GetIsFinal() {
|
|
writeNetworkQualityProgress(progress)
|
|
continue
|
|
}
|
|
writeStderrLine("")
|
|
if progress.GetError() != "" {
|
|
return E.New(progress.GetError())
|
|
}
|
|
writeStderrLine(strings.Repeat("-", 40))
|
|
fmt.Fprintf(os.Stdout, "Idle Latency: %d ms\n", progress.GetIdleLatencyMs())
|
|
fmt.Fprintf(os.Stdout, "Download Capacity: %-20s Accuracy: %s\n",
|
|
networkquality.FormatBitrate(progress.GetDownloadCapacity()),
|
|
networkquality.Accuracy(progress.GetDownloadCapacityAccuracy()))
|
|
fmt.Fprintf(os.Stdout, "Upload Capacity: %-20s Accuracy: %s\n",
|
|
networkquality.FormatBitrate(progress.GetUploadCapacity()),
|
|
networkquality.Accuracy(progress.GetUploadCapacityAccuracy()))
|
|
fmt.Fprintf(os.Stdout, "Download Responsiveness: %-20s Accuracy: %s\n",
|
|
fmt.Sprintf("%d RPM", progress.GetDownloadRPM()),
|
|
networkquality.Accuracy(progress.GetDownloadRPMAccuracy()))
|
|
fmt.Fprintf(os.Stdout, "Upload Responsiveness: %-20s Accuracy: %s\n",
|
|
fmt.Sprintf("%d RPM", progress.GetUploadRPM()),
|
|
networkquality.Accuracy(progress.GetUploadRPMAccuracy()))
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func writeNetworkQualityProgress(progress *daemon.NetworkQualityTestProgress) {
|
|
if !commandAPINetworkQualityFlagSerial && networkquality.Phase(progress.GetPhase()) != networkquality.PhaseIdle {
|
|
writeProgress(fmt.Sprintf("Download: %s RPM: %d Upload: %s RPM: %d",
|
|
networkquality.FormatBitrate(progress.GetDownloadCapacity()), progress.GetDownloadRPM(),
|
|
networkquality.FormatBitrate(progress.GetUploadCapacity()), progress.GetUploadRPM()))
|
|
return
|
|
}
|
|
switch networkquality.Phase(progress.GetPhase()) {
|
|
case networkquality.PhaseIdle:
|
|
if progress.GetIdleLatencyMs() > 0 {
|
|
writeProgress(fmt.Sprintf("Idle Latency: %d ms", progress.GetIdleLatencyMs()))
|
|
} else {
|
|
writeProgress("Measuring idle latency...")
|
|
}
|
|
case networkquality.PhaseDownload:
|
|
writeProgress(fmt.Sprintf("Download: %s RPM: %d",
|
|
networkquality.FormatBitrate(progress.GetDownloadCapacity()), progress.GetDownloadRPM()))
|
|
case networkquality.PhaseUpload:
|
|
writeProgress(fmt.Sprintf("Upload: %s RPM: %d",
|
|
networkquality.FormatBitrate(progress.GetUploadCapacity()), progress.GetUploadRPM()))
|
|
}
|
|
}
|