Class: Lich::InternalAPI::ActiveSessions::Server

Inherits:
Object
  • Object
show all
Defined in:
documented/internal_api/active_sessions/server.rb

Overview

Read-only/query plus lifecycle write server for the active sessions API.

The transport is intentionally local-only TCP to keep behavior consistent across Linux, macOS, and Windows. The server delegates all state changes to Registry; it does not own lifecycle policy beyond request routing and thread cleanup.

Constant Summary collapse

READ_TIMEOUT =

Maximum number of seconds to wait for the first request line from a connected client before abandoning the handler.

Returns:

1

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(host:, port:, registry:, auth_token:, server_factory: nil, accept_thread_factory: nil, client_thread_factory: nil) ⇒ void

Parameters:

  • host (String)
  • port (Integer)
  • registry (Lich::InternalAPI::ActiveSessions::Registry)
  • auth_token (String)

    shared secret required by all clients

  • server_factory (#call) (defaults to: nil)

    builds a listening server

  • accept_thread_factory (#call) (defaults to: nil)

    builds the accept-loop thread

  • client_thread_factory (#call) (defaults to: nil)

    builds per-client threads



38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'documented/internal_api/active_sessions/server.rb', line 38

def initialize(host:, port:, registry:, auth_token:, server_factory: nil, accept_thread_factory: nil, client_thread_factory: nil)
  @host = host
  @port = port
  @registry = registry
  @auth_token = auth_token
  @server_factory = server_factory || ->(bind_host, bind_port) { TCPServer.new(bind_host, bind_port) }
  @accept_thread_factory = accept_thread_factory || ->(&block) { Thread.new(&block) }
  @client_thread_factory = client_thread_factory || ->(socket, &block) { Thread.new(socket, &block) }
  @server = nil
  @thread = nil
  @mutex = Mutex.new
  @client_threads = []
  @stopping = false
end

Instance Attribute Details

#auth_tokenObject (readonly)

Returns the value of attribute auth_token.



28
29
30
# File 'documented/internal_api/active_sessions/server.rb', line 28

def auth_token
  @auth_token
end

#hostObject (readonly)

Returns the value of attribute host.



27
28
29
# File 'documented/internal_api/active_sessions/server.rb', line 27

def host
  @host
end

#portObject (readonly)

Returns the value of attribute port.



27
28
29
# File 'documented/internal_api/active_sessions/server.rb', line 27

def port
  @port
end

Instance Method Details

#running?Boolean

Indicates whether the server thread is active.

Returns:

  • (Boolean)


111
112
113
# File 'documented/internal_api/active_sessions/server.rb', line 111

def running?
  @thread&.alive? || false
end

#startBoolean

Starts the TCP server and accept loop.

Returns:

  • (Boolean)

    true when the server is available for requests



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'documented/internal_api/active_sessions/server.rb', line 56

def start
  @mutex.synchronize do
    return true if running?

    @stopping = false
    @server = @server_factory.call(@host, @port)
    @server.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1) rescue nil
    @port = @server.addr[1]
    @thread = @accept_thread_factory.call do
      Lich.log("info: ActiveSessions accept thread started pid=#{Process.pid} port=#{@port}") if defined?(Lich) && Lich.respond_to?(:log)
      accept_loop
    end
  end
  true
rescue StandardError
  stop
  false
end

#stopvoid

This method returns an undefined value.

Stops the server and its accept thread.

Client handler threads are joined with a short timeout so shutdown does not leak long-lived handler threads when the owning process exits.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'documented/internal_api/active_sessions/server.rb', line 81

def stop
  thread = nil
  server = nil
  client_threads = []
  @mutex.synchronize do
    thread = @thread
    server = @server
    client_threads = @client_threads.dup
    @client_threads.clear
    @stopping = true
    @thread = nil
    @server = nil
  end

  server&.close rescue nil
  if thread&.alive?
    thread.join(0.1)
    thread.kill if thread.alive?
  end
  client_threads.each do |client_thread|
    next unless client_thread.respond_to?(:join)

    client_thread.join(0.25)
    client_thread.kill if client_thread.respond_to?(:alive?) && client_thread.alive?
  end
end