Class: TunnelRb::Server::ClientRegistry

Inherits:
Object
  • Object
show all
Defined in:
lib/tunnel_rb/server/client_registry.rb

Overview

Thread-safe in-memory registry of connected clients keyed by subdomain, with a reverse index for O(1) lookup by socket.

Instance Method Summary collapse

Constructor Details

#initializeClientRegistry

Returns a new instance of ClientRegistry.



13
14
15
16
17
# File 'lib/tunnel_rb/server/client_registry.rb', line 13

def initialize
  @clients = {}             # subdomain -> Client
  @socket_to_subdomain = {} # control_socket -> subdomain
  @mutex = Mutex.new
end

Instance Method Details

#active_subdomainsObject



67
68
69
# File 'lib/tunnel_rb/server/client_registry.rb', line 67

def active_subdomains
  @mutex.synchronize { @clients.keys.to_set }
end

#each_client(&block) ⇒ Object

Yields each Client under the registry mutex. Block must not block on I/O; collect work and execute it after the iteration.



63
64
65
# File 'lib/tunnel_rb/server/client_registry.rb', line 63

def each_client(&block)
  @mutex.synchronize { @clients.each_value(&block) }
end

#forget(socket) ⇒ Object

Removes a client identified by socket. Returns the subdomain it was registered under, or nil if the socket was already gone (e.g. replaced by a reconnect).



41
42
43
# File 'lib/tunnel_rb/server/client_registry.rb', line 41

def forget(socket)
  @mutex.synchronize { forget_unlocked(socket) }
end

#forget_many(sockets) ⇒ Object

Atomically tags many clients for removal and returns their sockets. Used by the ping loop to evict unresponsive peers without holding the mutex while closing sockets.



78
79
80
81
82
# File 'lib/tunnel_rb/server/client_registry.rb', line 78

def forget_many(sockets)
  @mutex.synchronize do
    sockets.each { |s| forget_unlocked(s) }
  end
end

#lookup(subdomain) ⇒ Object



45
46
47
# File 'lib/tunnel_rb/server/client_registry.rb', line 45

def lookup(subdomain)
  @mutex.synchronize { @clients[subdomain] }
end

#lookup_by_socket(socket) ⇒ Object



49
50
51
52
53
54
# File 'lib/tunnel_rb/server/client_registry.rb', line 49

def lookup_by_socket(socket)
  @mutex.synchronize do
    sub = @socket_to_subdomain[socket]
    sub && @clients[sub]
  end
end

#register(subdomain, socket, token) ⇒ Object

Registers a new client. If a client with the same subdomain is already connected, returns the old socket so the caller can close it outside the registry’s mutex.



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/tunnel_rb/server/client_registry.rb', line 22

def register(subdomain, socket, token)
  old_socket = nil
  client = nil
  @mutex.synchronize do
    old = @clients[subdomain]
    if old && old.socket != socket
      @socket_to_subdomain.delete(old.socket)
      old_socket = old.socket
    end
    client = Client.new(subdomain: subdomain, socket: socket, token: token)
    @clients[subdomain] = client
    @socket_to_subdomain[socket] = subdomain
  end
  [client, old_socket]
end

#sizeObject



71
72
73
# File 'lib/tunnel_rb/server/client_registry.rb', line 71

def size
  @mutex.synchronize { @clients.size }
end

#socketsObject

Snapshot of currently registered control sockets, for IO.select.



57
58
59
# File 'lib/tunnel_rb/server/client_registry.rb', line 57

def sockets
  @mutex.synchronize { @socket_to_subdomain.keys }
end