tools: Fix mising cleanup

This commit is contained in:
世界
2026-05-26 11:24:39 +08:00
parent 824ccb8b3a
commit 01edf1a64b
6 changed files with 287 additions and 138 deletions
+254 -138
View File
@@ -265,11 +265,11 @@ func (c *CommandClient) Disconnect() error {
return common.Close(common.PtrOrNil(c.grpcConn))
}
func (c *CommandClient) getClientForCall() (daemon.StartedServiceClient, error) {
func (c *CommandClient) getClientForCall() (daemon.StartedServiceClient, context.Context, error) {
c.clientMutex.RLock()
if c.grpcClient != nil {
defer c.clientMutex.RUnlock()
return c.grpcClient, nil
return c.grpcClient, c.ctx, nil
}
c.clientMutex.RUnlock()
@@ -277,20 +277,20 @@ func (c *CommandClient) getClientForCall() (daemon.StartedServiceClient, error)
defer c.clientMutex.Unlock()
if c.grpcClient != nil {
return c.grpcClient, nil
return c.grpcClient, c.ctx, nil
}
target, contextDialer := dialTarget()
connection, client, err := c.dialWithRetry(target, contextDialer, true)
if err != nil {
return nil, E.Cause(err, "get command client")
return nil, nil, E.Cause(err, "get command client")
}
c.grpcConn = connection
c.grpcClient = client
if c.ctx == nil {
c.ctx, c.cancel = context.WithCancel(context.Background())
}
return c.grpcClient, nil
return c.grpcClient, c.ctx, nil
}
func (c *CommandClient) closeConnection() {
@@ -303,8 +303,8 @@ func (c *CommandClient) closeConnection() {
}
}
func callWithResult[T any](c *CommandClient, call func(client daemon.StartedServiceClient) (T, error)) (T, error) {
client, err := c.getClientForCall()
func callWithResult[T any](c *CommandClient, call func(ctx context.Context, client daemon.StartedServiceClient) (T, error)) (T, error) {
client, ctx, err := c.getClientForCall()
if err != nil {
var zero T
return zero, err
@@ -312,7 +312,7 @@ func callWithResult[T any](c *CommandClient, call func(client daemon.StartedServ
if c.standalone {
defer c.closeConnection()
}
return call(client)
return call(ctx, client)
}
func (c *CommandClient) getStreamContext() (daemon.StartedServiceClient, context.Context) {
@@ -482,8 +482,8 @@ func (c *CommandClient) handleOutboundsStream() {
}
func (c *CommandClient) SelectOutbound(groupTag string, outboundTag string) error {
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.SelectOutbound(context.Background(), &daemon.SelectOutboundRequest{
_, err := callWithResult(c, func(ctx context.Context, client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.SelectOutbound(ctx, &daemon.SelectOutboundRequest{
GroupTag: groupTag,
OutboundTag: outboundTag,
})
@@ -495,8 +495,8 @@ func (c *CommandClient) SelectOutbound(groupTag string, outboundTag string) erro
}
func (c *CommandClient) URLTest(groupTag string) error {
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.URLTest(context.Background(), &daemon.URLTestRequest{
_, err := callWithResult(c, func(ctx context.Context, client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.URLTest(ctx, &daemon.URLTestRequest{
OutboundTag: groupTag,
})
})
@@ -507,8 +507,8 @@ func (c *CommandClient) URLTest(groupTag string) error {
}
func (c *CommandClient) SetClashMode(newMode string) error {
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.SetClashMode(context.Background(), &daemon.ClashMode{
_, err := callWithResult(c, func(ctx context.Context, client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.SetClashMode(ctx, &daemon.ClashMode{
Mode: newMode,
})
})
@@ -519,8 +519,8 @@ func (c *CommandClient) SetClashMode(newMode string) error {
}
func (c *CommandClient) CloseConnection(connId string) error {
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.CloseConnection(context.Background(), &daemon.CloseConnectionRequest{
_, err := callWithResult(c, func(ctx context.Context, client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.CloseConnection(ctx, &daemon.CloseConnectionRequest{
Id: connId,
})
})
@@ -531,8 +531,8 @@ func (c *CommandClient) CloseConnection(connId string) error {
}
func (c *CommandClient) CloseConnections() error {
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.CloseAllConnections(context.Background(), &emptypb.Empty{})
_, err := callWithResult(c, func(ctx context.Context, client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.CloseAllConnections(ctx, &emptypb.Empty{})
})
if err != nil {
return E.Cause(err, "close all connections")
@@ -541,8 +541,8 @@ func (c *CommandClient) CloseConnections() error {
}
func (c *CommandClient) ServiceReload() error {
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.ReloadService(context.Background(), &emptypb.Empty{})
_, err := callWithResult(c, func(ctx context.Context, client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.ReloadService(ctx, &emptypb.Empty{})
})
if err != nil {
return E.Cause(err, "reload service")
@@ -551,8 +551,8 @@ func (c *CommandClient) ServiceReload() error {
}
func (c *CommandClient) ServiceClose() error {
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.StopService(context.Background(), &emptypb.Empty{})
_, err := callWithResult(c, func(ctx context.Context, client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.StopService(ctx, &emptypb.Empty{})
})
if err != nil {
return E.Cause(err, "stop service")
@@ -561,8 +561,8 @@ func (c *CommandClient) ServiceClose() error {
}
func (c *CommandClient) ClearLogs() error {
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.ClearLogs(context.Background(), &emptypb.Empty{})
_, err := callWithResult(c, func(ctx context.Context, client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.ClearLogs(ctx, &emptypb.Empty{})
})
if err != nil {
return E.Cause(err, "clear logs")
@@ -571,8 +571,8 @@ func (c *CommandClient) ClearLogs() error {
}
func (c *CommandClient) GetSystemProxyStatus() (*SystemProxyStatus, error) {
return callWithResult(c, func(client daemon.StartedServiceClient) (*SystemProxyStatus, error) {
status, err := client.GetSystemProxyStatus(context.Background(), &emptypb.Empty{})
return callWithResult(c, func(ctx context.Context, client daemon.StartedServiceClient) (*SystemProxyStatus, error) {
status, err := client.GetSystemProxyStatus(ctx, &emptypb.Empty{})
if err != nil {
return nil, E.Cause(err, "get system proxy status")
}
@@ -581,8 +581,8 @@ func (c *CommandClient) GetSystemProxyStatus() (*SystemProxyStatus, error) {
}
func (c *CommandClient) SetSystemProxyEnabled(isEnabled bool) error {
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.SetSystemProxyEnabled(context.Background(), &daemon.SetSystemProxyEnabledRequest{
_, err := callWithResult(c, func(ctx context.Context, client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.SetSystemProxyEnabled(ctx, &daemon.SetSystemProxyEnabledRequest{
Enabled: isEnabled,
})
})
@@ -593,8 +593,8 @@ func (c *CommandClient) SetSystemProxyEnabled(isEnabled bool) error {
}
func (c *CommandClient) TriggerGoCrash() error {
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.TriggerDebugCrash(context.Background(), &daemon.DebugCrashRequest{
_, err := callWithResult(c, func(ctx context.Context, client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.TriggerDebugCrash(ctx, &daemon.DebugCrashRequest{
Type: daemon.DebugCrashRequest_GO,
})
})
@@ -605,8 +605,8 @@ func (c *CommandClient) TriggerGoCrash() error {
}
func (c *CommandClient) TriggerNativeCrash() error {
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.TriggerDebugCrash(context.Background(), &daemon.DebugCrashRequest{
_, err := callWithResult(c, func(ctx context.Context, client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.TriggerDebugCrash(ctx, &daemon.DebugCrashRequest{
Type: daemon.DebugCrashRequest_NATIVE,
})
})
@@ -617,8 +617,8 @@ func (c *CommandClient) TriggerNativeCrash() error {
}
func (c *CommandClient) TriggerOOMReport() error {
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.TriggerOOMReport(context.Background(), &emptypb.Empty{})
_, err := callWithResult(c, func(ctx context.Context, client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.TriggerOOMReport(ctx, &emptypb.Empty{})
})
if err != nil {
return E.Cause(err, "trigger oom report")
@@ -627,8 +627,8 @@ func (c *CommandClient) TriggerOOMReport() error {
}
func (c *CommandClient) GetDeprecatedNotes() (DeprecatedNoteIterator, error) {
return callWithResult(c, func(client daemon.StartedServiceClient) (DeprecatedNoteIterator, error) {
warnings, err := client.GetDeprecatedWarnings(context.Background(), &emptypb.Empty{})
return callWithResult(c, func(ctx context.Context, client daemon.StartedServiceClient) (DeprecatedNoteIterator, error) {
warnings, err := client.GetDeprecatedWarnings(ctx, &emptypb.Empty{})
if err != nil {
return nil, E.Cause(err, "get deprecated warnings")
}
@@ -646,8 +646,8 @@ func (c *CommandClient) GetDeprecatedNotes() (DeprecatedNoteIterator, error) {
}
func (c *CommandClient) GetStartedAt() (int64, error) {
return callWithResult(c, func(client daemon.StartedServiceClient) (int64, error) {
startedAt, err := client.GetStartedAt(context.Background(), &emptypb.Empty{})
return callWithResult(c, func(ctx context.Context, client daemon.StartedServiceClient) (int64, error) {
startedAt, err := client.GetStartedAt(ctx, &emptypb.Empty{})
if err != nil {
return 0, E.Cause(err, "get started at")
}
@@ -656,8 +656,8 @@ func (c *CommandClient) GetStartedAt() (int64, error) {
}
func (c *CommandClient) SetGroupExpand(groupTag string, isExpand bool) error {
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.SetGroupExpand(context.Background(), &daemon.SetGroupExpandRequest{
_, err := callWithResult(c, func(ctx context.Context, client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.SetGroupExpand(ctx, &daemon.SetGroupExpandRequest{
GroupTag: groupTag,
IsExpand: isExpand,
})
@@ -668,15 +668,30 @@ func (c *CommandClient) SetGroupExpand(groupTag string, isExpand bool) error {
return nil
}
func (c *CommandClient) StartNetworkQualityTest(configURL string, outboundTag string, serial bool, maxRuntimeSeconds int32, http3 bool, handler NetworkQualityTestHandler) error {
client, err := c.getClientForCall()
func (c *CommandClient) StartNetworkQualityTest(configURL string, outboundTag string, serial bool, maxRuntimeSeconds int32, http3 bool, handler NetworkQualityTestHandler) (*NetworkQualityTestSession, error) {
client, parentCtx, err := c.getClientForCall()
if err != nil {
return E.Cause(err, "start network quality test")
return nil, E.Cause(err, "start network quality test")
}
if c.standalone {
defer c.closeConnection()
streamCtx, cancel := context.WithCancel(parentCtx)
session := &NetworkQualityTestSession{
streamSession: streamSession{
ctx: streamCtx,
cancel: cancel,
closeDone: make(chan struct{}),
},
}
stream, err := client.StartNetworkQualityTest(context.Background(), &daemon.NetworkQualityTestRequest{
failStart := func(cause error, message string) (*NetworkQualityTestSession, error) {
cancel()
if c.standalone {
c.closeConnection()
}
return nil, E.Cause(cause, message)
}
stream, err := client.StartNetworkQualityTest(streamCtx, &daemon.NetworkQualityTestRequest{
ConfigURL: configURL,
OutboundTag: outboundTag,
Serial: serial,
@@ -684,106 +699,178 @@ func (c *CommandClient) StartNetworkQualityTest(configURL string, outboundTag st
Http3: http3,
})
if err != nil {
return E.Cause(err, "start network quality test")
return failStart(err, "start network quality test")
}
for {
event, recvErr := stream.Recv()
if recvErr != nil {
recvErr = E.Cause(recvErr, "network quality test recv")
handler.OnError(recvErr.Error())
return recvErr
}
if event.IsFinal {
if event.Error != "" {
handler.OnError(event.Error)
} else {
handler.OnResult(&NetworkQualityResult{
DownloadCapacity: event.DownloadCapacity,
UploadCapacity: event.UploadCapacity,
DownloadRPM: event.DownloadRPM,
UploadRPM: event.UploadRPM,
IdleLatencyMs: event.IdleLatencyMs,
DownloadCapacityAccuracy: event.DownloadCapacityAccuracy,
UploadCapacityAccuracy: event.UploadCapacityAccuracy,
DownloadRPMAccuracy: event.DownloadRPMAccuracy,
UploadRPMAccuracy: event.UploadRPMAccuracy,
})
standalone := c.standalone
go func() {
defer func() {
close(session.closeDone)
if standalone {
c.closeConnection()
}
return nil
}()
for {
event, recvErr := stream.Recv()
if recvErr != nil {
if session.ctx.Err() != nil {
return
}
handler.OnError(E.Cause(recvErr, "network quality test recv").Error())
return
}
if event.IsFinal {
if event.Error != "" {
handler.OnError(event.Error)
} else {
handler.OnResult(&NetworkQualityResult{
DownloadCapacity: event.DownloadCapacity,
UploadCapacity: event.UploadCapacity,
DownloadRPM: event.DownloadRPM,
UploadRPM: event.UploadRPM,
IdleLatencyMs: event.IdleLatencyMs,
DownloadCapacityAccuracy: event.DownloadCapacityAccuracy,
UploadCapacityAccuracy: event.UploadCapacityAccuracy,
DownloadRPMAccuracy: event.DownloadRPMAccuracy,
UploadRPMAccuracy: event.UploadRPMAccuracy,
})
}
return
}
handler.OnProgress(networkQualityProgressFromGRPC(event))
}
handler.OnProgress(networkQualityProgressFromGRPC(event))
}
}()
return session, nil
}
func (c *CommandClient) StartSTUNTest(server string, outboundTag string, handler STUNTestHandler) error {
client, err := c.getClientForCall()
func (c *CommandClient) StartSTUNTest(server string, outboundTag string, handler STUNTestHandler) (*STUNTestSession, error) {
client, parentCtx, err := c.getClientForCall()
if err != nil {
return E.Cause(err, "start stun test")
return nil, E.Cause(err, "start stun test")
}
if c.standalone {
defer c.closeConnection()
streamCtx, cancel := context.WithCancel(parentCtx)
session := &STUNTestSession{
streamSession: streamSession{
ctx: streamCtx,
cancel: cancel,
closeDone: make(chan struct{}),
},
}
stream, err := client.StartSTUNTest(context.Background(), &daemon.STUNTestRequest{
failStart := func(cause error, message string) (*STUNTestSession, error) {
cancel()
if c.standalone {
c.closeConnection()
}
return nil, E.Cause(cause, message)
}
stream, err := client.StartSTUNTest(streamCtx, &daemon.STUNTestRequest{
Server: server,
OutboundTag: outboundTag,
})
if err != nil {
return E.Cause(err, "start stun test")
return failStart(err, "start stun test")
}
for {
event, recvErr := stream.Recv()
if recvErr != nil {
recvErr = E.Cause(recvErr, "stun test recv")
handler.OnError(recvErr.Error())
return recvErr
}
if event.IsFinal {
if event.Error != "" {
handler.OnError(event.Error)
} else {
handler.OnResult(&STUNTestResult{
ExternalAddr: event.ExternalAddr,
LatencyMs: event.LatencyMs,
NATMapping: event.NatMapping,
NATFiltering: event.NatFiltering,
NATTypeSupported: event.NatTypeSupported,
})
standalone := c.standalone
go func() {
defer func() {
close(session.closeDone)
if standalone {
c.closeConnection()
}
return nil
}()
for {
event, recvErr := stream.Recv()
if recvErr != nil {
if session.ctx.Err() != nil {
return
}
handler.OnError(E.Cause(recvErr, "stun test recv").Error())
return
}
if event.IsFinal {
if event.Error != "" {
handler.OnError(event.Error)
} else {
handler.OnResult(&STUNTestResult{
ExternalAddr: event.ExternalAddr,
LatencyMs: event.LatencyMs,
NATMapping: event.NatMapping,
NATFiltering: event.NatFiltering,
NATTypeSupported: event.NatTypeSupported,
})
}
return
}
handler.OnProgress(stunTestProgressFromGRPC(event))
}
handler.OnProgress(stunTestProgressFromGRPC(event))
}
}()
return session, nil
}
func (c *CommandClient) SubscribeTailscaleStatus(handler TailscaleStatusHandler) error {
client, err := c.getClientForCall()
func (c *CommandClient) SubscribeTailscaleStatus(handler TailscaleStatusHandler) (*TailscaleStatusSubscription, error) {
client, parentCtx, err := c.getClientForCall()
if err != nil {
return E.Cause(err, "subscribe tailscale status")
return nil, E.Cause(err, "subscribe tailscale status")
}
if c.standalone {
defer c.closeConnection()
streamCtx, cancel := context.WithCancel(parentCtx)
session := &TailscaleStatusSubscription{
streamSession: streamSession{
ctx: streamCtx,
cancel: cancel,
closeDone: make(chan struct{}),
},
}
stream, err := client.SubscribeTailscaleStatus(context.Background(), &emptypb.Empty{})
if err != nil {
return E.Cause(err, "subscribe tailscale status")
}
for {
event, recvErr := stream.Recv()
if recvErr != nil {
if status.Code(recvErr) == codes.NotFound || status.Code(recvErr) == codes.Unavailable {
return nil
}
recvErr = E.Cause(recvErr, "tailscale status recv")
handler.OnError(recvErr.Error())
return recvErr
failStart := func(cause error, message string) (*TailscaleStatusSubscription, error) {
cancel()
if c.standalone {
c.closeConnection()
}
handler.OnStatusUpdate(tailscaleStatusUpdateFromGRPC(event))
return nil, E.Cause(cause, message)
}
stream, err := client.SubscribeTailscaleStatus(streamCtx, &emptypb.Empty{})
if err != nil {
return failStart(err, "subscribe tailscale status")
}
standalone := c.standalone
go func() {
defer func() {
close(session.closeDone)
if standalone {
c.closeConnection()
}
}()
for {
event, recvErr := stream.Recv()
if recvErr != nil {
if session.ctx.Err() != nil {
return
}
if status.Code(recvErr) == codes.NotFound || status.Code(recvErr) == codes.Unavailable {
return
}
handler.OnError(E.Cause(recvErr, "tailscale status recv").Error())
return
}
handler.OnStatusUpdate(tailscaleStatusUpdateFromGRPC(event))
}
}()
return session, nil
}
func (c *CommandClient) SetTailscaleExitNode(endpointTag string, stableID string) error {
_, err := callWithResult(c, func(client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.SetTailscaleExitNode(context.Background(), &daemon.SetTailscaleExitNodeRequest{
_, err := callWithResult(c, func(ctx context.Context, client daemon.StartedServiceClient) (*emptypb.Empty, error) {
return client.SetTailscaleExitNode(ctx, &daemon.SetTailscaleExitNodeRequest{
EndpointTag: endpointTag,
StableID: stableID,
})
@@ -794,39 +881,68 @@ func (c *CommandClient) SetTailscaleExitNode(endpointTag string, stableID string
return nil
}
func (c *CommandClient) StartTailscalePing(endpointTag string, peerIP string, handler TailscalePingHandler) error {
client, err := c.getClientForCall()
func (c *CommandClient) StartTailscalePing(endpointTag string, peerIP string, handler TailscalePingHandler) (*TailscalePingSession, error) {
client, parentCtx, err := c.getClientForCall()
if err != nil {
return E.Cause(err, "start tailscale ping")
return nil, E.Cause(err, "start tailscale ping")
}
if c.standalone {
defer c.closeConnection()
streamCtx, cancel := context.WithCancel(parentCtx)
session := &TailscalePingSession{
streamSession: streamSession{
ctx: streamCtx,
cancel: cancel,
closeDone: make(chan struct{}),
},
}
stream, err := client.StartTailscalePing(context.Background(), &daemon.TailscalePingRequest{
failStart := func(cause error, message string) (*TailscalePingSession, error) {
cancel()
if c.standalone {
c.closeConnection()
}
return nil, E.Cause(cause, message)
}
stream, err := client.StartTailscalePing(streamCtx, &daemon.TailscalePingRequest{
EndpointTag: endpointTag,
PeerIP: peerIP,
})
if err != nil {
return E.Cause(err, "start tailscale ping")
return failStart(err, "start tailscale ping")
}
for {
event, recvErr := stream.Recv()
if recvErr != nil {
recvErr = E.Cause(recvErr, "tailscale ping recv")
handler.OnError(recvErr.Error())
return recvErr
standalone := c.standalone
go func() {
defer func() {
close(session.closeDone)
if standalone {
c.closeConnection()
}
}()
for {
event, recvErr := stream.Recv()
if recvErr != nil {
if session.ctx.Err() != nil {
return
}
handler.OnError(E.Cause(recvErr, "tailscale ping recv").Error())
return
}
handler.OnPingResult(tailscalePingResultFromGRPC(event))
}
handler.OnPingResult(tailscalePingResultFromGRPC(event))
}
}()
return session, nil
}
func (c *CommandClient) StartTailscaleSSHSession(opts *TailscaleSSHOptions, handler TailscaleSSHHandler) (*TailscaleSSHSession, error) {
client, err := c.getClientForCall()
client, parentCtx, err := c.getClientForCall()
if err != nil {
return nil, E.Cause(err, "start tailscale ssh session")
}
streamCtx, cancel := context.WithCancel(context.Background())
streamCtx, cancel := context.WithCancel(parentCtx)
failStart := func(cause error, message string) (*TailscaleSSHSession, error) {
cancel()
if c.standalone {
+17
View File
@@ -1,14 +1,31 @@
package libbox
import (
"context"
"slices"
"strings"
"sync"
"time"
"github.com/sagernet/sing-box/daemon"
M "github.com/sagernet/sing/common/metadata"
)
type streamSession struct {
ctx context.Context
cancel context.CancelFunc
closeOnce sync.Once
closeDone chan struct{}
}
func (s *streamSession) Close() error {
s.closeOnce.Do(func() {
s.cancel()
})
<-s.closeDone
return nil
}
type StatusMessage struct {
Memory int64
Goroutines int32
+4
View File
@@ -34,6 +34,10 @@ type NetworkQualityTestHandler interface {
OnError(message string)
}
type NetworkQualityTestSession struct {
streamSession
}
func networkQualityProgressFromGRPC(event *daemon.NetworkQualityTestProgress) *NetworkQualityProgress {
return &NetworkQualityProgress{
Phase: event.Phase,
@@ -24,6 +24,10 @@ type STUNTestHandler interface {
OnError(message string)
}
type STUNTestSession struct {
streamSession
}
func stunTestProgressFromGRPC(event *daemon.STUNTestProgress) *STUNTestProgress {
return &STUNTestProgress{
Phase: event.Phase,
@@ -84,6 +84,10 @@ type TailscaleStatusHandler interface {
OnError(message string)
}
type TailscaleStatusSubscription struct {
streamSession
}
func tailscaleStatusUpdateFromGRPC(update *daemon.TailscaleStatusUpdate) *TailscaleStatusUpdate {
endpoints := make([]*TailscaleEndpointStatus, len(update.Endpoints))
for i, endpoint := range update.Endpoints {
@@ -16,6 +16,10 @@ type TailscalePingHandler interface {
OnError(message string)
}
type TailscalePingSession struct {
streamSession
}
func tailscalePingResultFromGRPC(response *daemon.TailscalePingResponse) *TailscalePingResult {
return &TailscalePingResult{
LatencyMs: response.LatencyMs,