Files
GoTunnel/mobile/gotunnelmobile/gotunnelmobile.go

145 lines
2.8 KiB
Go

package gotunnelmobile
import (
"context"
"strings"
"sync"
"github.com/gotunnel/internal/client/tunnel"
"github.com/gotunnel/pkg/crypto"
)
// Service exposes a gomobile-friendly wrapper around the Go tunnel client.
type Service struct {
mu sync.Mutex
server string
token string
dataDir string
clientName string
clientID string
disableTLS bool
client *tunnel.Client
cancel context.CancelFunc
running bool
status string
lastError string
}
// NewService creates a mobile client service wrapper.
func NewService() *Service {
return &Service{status: "stopped"}
}
// Configure stores the parameters used by Start.
func (s *Service) Configure(server, token, dataDir, clientName, clientID string, disableTLS bool) {
s.mu.Lock()
defer s.mu.Unlock()
s.server = strings.TrimSpace(server)
s.token = strings.TrimSpace(token)
s.dataDir = strings.TrimSpace(dataDir)
s.clientName = strings.TrimSpace(clientName)
s.clientID = strings.TrimSpace(clientID)
s.disableTLS = disableTLS
}
// Start launches the tunnel loop in the background.
func (s *Service) Start() string {
s.mu.Lock()
if s.running {
s.mu.Unlock()
return ""
}
if s.server == "" || s.token == "" {
s.mu.Unlock()
return "server and token are required"
}
features := tunnel.MobilePlatformFeatures()
client := tunnel.NewClientWithOptions(s.server, s.token, tunnel.ClientOptions{
DataDir: s.dataDir,
ClientID: s.clientID,
ClientName: s.clientName,
Features: &features,
})
if !s.disableTLS {
client.TLSEnabled = true
client.TLSConfig = crypto.ClientTLSConfig()
}
ctx, cancel := context.WithCancel(context.Background())
s.client = client
s.cancel = cancel
s.running = true
s.status = "running"
s.lastError = ""
s.mu.Unlock()
go func() {
err := client.RunContext(ctx)
s.mu.Lock()
defer s.mu.Unlock()
s.running = false
s.cancel = nil
s.client = nil
if err != nil {
s.status = "error"
s.lastError = err.Error()
return
}
if s.status != "stopped" {
s.status = "stopped"
}
}()
return ""
}
// Stop cancels the running tunnel loop.
func (s *Service) Stop() string {
s.mu.Lock()
cancel := s.cancel
s.cancel = nil
s.running = false
s.status = "stopped"
s.mu.Unlock()
if cancel != nil {
cancel()
}
return ""
}
// Restart restarts the service with the stored configuration.
func (s *Service) Restart() string {
s.Stop()
return s.Start()
}
// IsRunning reports whether the tunnel loop is active.
func (s *Service) IsRunning() bool {
s.mu.Lock()
defer s.mu.Unlock()
return s.running
}
// Status returns a coarse-grained runtime status.
func (s *Service) Status() string {
s.mu.Lock()
defer s.mu.Unlock()
return s.status
}
// LastError returns the last background error string, if any.
func (s *Service) LastError() string {
s.mu.Lock()
defer s.mu.Unlock()
return s.lastError
}