wasm: one IPN per runtime with an explicit readiness handshake #20
@@ -44,7 +44,6 @@ import (
|
||||
"tailscale.com/ipn"
|
||||
"tailscale.com/ipn/ipnauth"
|
||||
"tailscale.com/ipn/ipnlocal"
|
||||
"tailscale.com/ipn/ipnserver"
|
||||
"tailscale.com/ipn/localapi"
|
||||
"tailscale.com/ipn/store/mem"
|
||||
"tailscale.com/logpolicy"
|
||||
@@ -53,7 +52,6 @@ import (
|
||||
"tailscale.com/net/netns"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/net/tsdial"
|
||||
"tailscale.com/safesocket"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/tsd"
|
||||
"tailscale.com/types/logid"
|
||||
@@ -205,7 +203,6 @@ func newIPN(jsConfig js.Value, shutdownCh chan struct{}) (map[string]any, error)
|
||||
// initDriveForRemote must be called before NewLocalBackend (SubSystem is set-once).
|
||||
driveFS := initDriveForRemote(sys)
|
||||
|
||||
srv := ipnserver.New(logf, logid, sys.Bus.Get(), sys.NetMon.Get())
|
||||
lb, err := ipnlocal.NewLocalBackend(logf, logid, sys, controlclient.LoginEphemeral)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ipnlocal.NewLocalBackend: %w", err)
|
||||
@@ -214,11 +211,9 @@ func newIPN(jsConfig js.Value, shutdownCh chan struct{}) (map[string]any, error)
|
||||
return nil, fmt.Errorf("starting netstack: %w", err)
|
||||
}
|
||||
wireTaildropFileOps(lb, jsConfig.Get("fileOps"))
|
||||
srv.SetLocalBackend(lb)
|
||||
|
||||
jsIPN := &jsIPN{
|
||||
dialer: dialer,
|
||||
srv: srv,
|
||||
lb: lb,
|
||||
ns: ns,
|
||||
controlURL: controlURL,
|
||||
@@ -411,7 +406,6 @@ func newIPN(jsConfig js.Value, shutdownCh chan struct{}) (map[string]any, error)
|
||||
|
||||
type jsIPN struct {
|
||||
dialer *tsdial.Dialer
|
||||
srv *ipnserver.Server
|
||||
lb *ipnlocal.LocalBackend
|
||||
ns *netstack.Impl
|
||||
controlURL string
|
||||
@@ -422,9 +416,6 @@ type jsIPN struct {
|
||||
funnelMu sync.Mutex
|
||||
funnelPorts map[uint16]*funnelListenerEntry
|
||||
|
||||
// ln is the safesocket listener created by run(); stored here so shutdown
|
||||
// can close it and unblock srv.Run.
|
||||
ln net.Listener
|
||||
shutdownCh chan struct{} // closed by shutdown() to unblock main()
|
||||
shutdownOnce sync.Once
|
||||
}
|
||||
@@ -617,18 +608,6 @@ func (i *jsIPN) run(jsCallbacks js.Value) {
|
||||
}
|
||||
}()
|
||||
|
||||
ln, err := safesocket.Listen("")
|
||||
if err != nil {
|
||||
log.Fatalf("safesocket.Listen: %v", err)
|
||||
}
|
||||
i.ln = ln
|
||||
|
||||
go func() {
|
||||
err := i.srv.Run(context.Background(), ln)
|
||||
if err != nil && !errors.Is(err, net.ErrClosed) {
|
||||
log.Fatalf("ipnserver.Run exited: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (i *jsIPN) login() {
|
||||
@@ -652,9 +631,6 @@ func (i *jsIPN) shutdown() js.Value {
|
||||
if i.lb != nil {
|
||||
i.lb.Shutdown()
|
||||
}
|
||||
if i.ln != nil {
|
||||
i.ln.Close()
|
||||
}
|
||||
close(i.shutdownCh)
|
||||
})
|
||||
return nil, nil
|
||||
|
||||
@@ -5,22 +5,15 @@ package safesocket
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/akutz/memconn"
|
||||
)
|
||||
|
||||
const memName = "Tailscale-IPN"
|
||||
|
||||
// memSeq ensures each IPN instance in the same WASM process gets a distinct
|
||||
// memconn address, so concurrent instances do not conflict on the registry.
|
||||
var memSeq atomic.Int64
|
||||
|
||||
func listen(path string) (net.Listener, error) {
|
||||
name := fmt.Sprintf("%s-%d", memName, memSeq.Add(1))
|
||||
return memconn.Listen("memu", name)
|
||||
return memconn.Listen("memu", memName)
|
||||
}
|
||||
|
||||
func connect(ctx context.Context, _ string) (net.Conn, error) {
|
||||
|
||||
Reference in New Issue
Block a user