Class: AgentsControl::Hooks::Server
- Inherits:
-
Object
- Object
- AgentsControl::Hooks::Server
- Defined in:
- lib/agents_control/hooks/server.rb
Overview
Receiver for events from agents.
HTTP over bare sockets, not webrick: it left the stdlib in Ruby 3, and pulling in a gem just to accept local POSTs isn't worth it. The subset of the protocol needed — one request line, headers, a body — fits in about fifty lines.
Listens strictly on 127.0.0.1. The port must never be exposed externally under any circumstances: anyone who could reach it could grant the agent permission to run commands.
The handler holds the connection open until a human answers — this is exactly how a hook blocks the agent. So every connection gets its own thread, and there can be as many as there are sessions waiting at once.
Defined Under Namespace
Classes: PortBusy
Constant Summary collapse
- HOST =
"127.0.0.1"
Instance Attribute Summary collapse
-
#port ⇒ Object
readonly
Returns the value of attribute port.
-
#secret ⇒ Object
readonly
Returns the value of attribute secret.
Instance Method Summary collapse
-
#initialize(port: 0, secret: nil, logger: nil) ⇒ Server
constructor
A new instance of Server.
-
#occupant_hint ⇒ Object
"Something's already running somewhere" with no indication of where is a useless message: finding the process becomes a manual hunt.
- #start(&handler) ⇒ Object
- #stop ⇒ Object
- #url ⇒ Object
- #wait ⇒ Object
Constructor Details
#initialize(port: 0, secret: nil, logger: nil) ⇒ Server
Returns a new instance of Server.
30 31 32 33 34 35 |
# File 'lib/agents_control/hooks/server.rb', line 30 def initialize(port: 0, secret: nil, logger: nil) @requested_port = port @secret = secret || SecureRandom.hex(16) @logger = logger @running = false end |
Instance Attribute Details
#port ⇒ Object (readonly)
Returns the value of attribute port.
28 29 30 |
# File 'lib/agents_control/hooks/server.rb', line 28 def port @port end |
#secret ⇒ Object (readonly)
Returns the value of attribute secret.
28 29 30 |
# File 'lib/agents_control/hooks/server.rb', line 28 def secret @secret end |
Instance Method Details
#occupant_hint ⇒ Object
"Something's already running somewhere" with no indication of where is a useless message: finding the process becomes a manual hunt. Name it right away.
59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/agents_control/hooks/server.rb', line 59 def occupant_hint # argv array, not a shell string: the port ultimately comes from # the user's own config, but there's no reason to trust a shell # to parse it correctly either. out, = Open3.capture3("lsof", "-nP", "-iTCP:#{@requested_port.to_i}", "-sTCP:LISTEN", "-t") pids = out.split.map(&:strip) return "" if pids.empty? " by process #{pids.join(', ')}. Stop it: agents_control stop" rescue StandardError "" end |
#start(&handler) ⇒ Object
42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/agents_control/hooks/server.rb', line 42 def start(&handler) @server = TCPServer.new(HOST, @requested_port) @port = @server.addr[1] @running = true @thread = Thread.new do accept_loop(&handler) end self rescue Errno::EADDRINUSE raise PortBusy, "port #{@requested_port} is taken#{occupant_hint}" end |
#stop ⇒ Object
74 75 76 77 78 |
# File 'lib/agents_control/hooks/server.rb', line 74 def stop @running = false @server&.close @thread&.kill end |
#url ⇒ Object
72 |
# File 'lib/agents_control/hooks/server.rb', line 72 def url = "http://#{HOST}:#{port}" |
#wait ⇒ Object
80 |
# File 'lib/agents_control/hooks/server.rb', line 80 def wait = @thread&.join |