Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lua/amp/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ function M._on_client_connect()
M.state.connected = true

if not was_connected then
logger.info("init", "Connected to Amp")
-- Use print() directly to avoid [INFO] init: prefix from logger
print("● Connected to Amp CLI")
end
end

Expand Down
25 changes: 18 additions & 7 deletions lua/amp/server/client.lua
Original file line number Diff line number Diff line change
Expand Up @@ -208,25 +208,36 @@ end
---@param code number|nil Close code (default: 1000)
---@param reason string|nil Close reason
function M.close_client(client, code, reason)
if client.state == "closed" or client.state == "closing" then
-- Already closing/closed? Bail early.
if client.state == "closed" or client.tcp_handle:is_closing() then
return
end

code = code or 1000
reason = reason or ""

if client.handshake_complete then
client.state = "closing"

-- If handshake complete and code is allowed to be sent, send a Close frame
-- Per RFC 6455, code 1006 MUST NOT be sent in a Close frame
local can_send_close = client.handshake_complete and code ~= 1006

if can_send_close then
local close_frame = frame.create_close_frame(code, reason)
client.tcp_handle:write(close_frame, function()
client.tcp_handle:write(close_frame, function(err)
-- Regardless of write result, ensure handle is closed exactly once
if not client.tcp_handle:is_closing() then
client.tcp_handle:close()
end
client.state = "closed"
client.tcp_handle:close()
end)
else
-- For abnormal closure (1006) or no handshake, just drop TCP immediately
if not client.tcp_handle:is_closing() then
client.tcp_handle:close()
end
client.state = "closed"
client.tcp_handle:close()
end

client.state = "closing"
end

---Check if a client connection is alive
Expand Down
10 changes: 7 additions & 3 deletions lua/amp/server/tcp.lua
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,11 @@ function M._remove_client(server, client)
server.clients[client.id] = nil

if not client.tcp_handle:is_closing() then
client.state = "closing"
client.tcp_handle:close()
-- Note: close() is async in libuv, but we mark as "closed" immediately
-- since the client is removed from the active list and won't be used again
client.state = "closed"
end
end
end
Expand Down Expand Up @@ -264,10 +268,10 @@ function M.start_ping_timer(server, interval)
if client_manager.is_client_alive(client, interval * 2) then
client_manager.send_ping(client, "ping")
else
-- Client appears dead, close it
server.on_error("Client " .. client.id .. " appears dead, closing")
client_manager.close_client(client, 1006, "Connection timeout")
-- Client appears dead, drop the connection (no Close frame for 1006)
server.on_error(("Client %s appears dead, closing"):format(client.id))
M._remove_client(server, client)
server.on_disconnect(client, 1006, "Connection timeout")
end
end
end
Expand Down