From 269780d8cfa12f83dc000ba2b56dbb99b5296938 Mon Sep 17 00:00:00 2001 From: Gustavo Henrique Santos Souza de Miranda Date: Sat, 22 Nov 2025 23:27:04 -0300 Subject: [PATCH] Add `CloseHandler` to support graceful connection termination. - Extend `test.proto` with `CloseCommand` and `CloseResponse` messages. - Introduce `CloseHandler` to process "close" actions and respond with connection termination notice. - Update `DelegateActionManager` to register `CloseHandler`. - Refactor `IPCManager` to handle "close" response headers and terminate client connections gracefully. --- .../delegate/DelegateActionManager.java | 2 ++ .../delegate/handler/CloseHandler.java | 32 ++++++++++++++++++ .../service/delegate/handler/EchoHandler.java | 4 +-- .../delegate/handler/HeartbeatHandler.java | 4 +-- .../mediamanager/service/ipc/IPCManager.java | 33 +++++++++++++++---- src/main/proto/test.proto | 6 ++++ 6 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 src/main/java/com/mediamanager/service/delegate/handler/CloseHandler.java diff --git a/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java b/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java index 1d2c0a6..5909c9c 100644 --- a/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java +++ b/src/main/java/com/mediamanager/service/delegate/DelegateActionManager.java @@ -2,6 +2,7 @@ package com.mediamanager.service.delegate; import com.google.protobuf.ByteString; import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.service.delegate.handler.CloseHandler; import com.mediamanager.service.delegate.handler.EchoHandler; import com.mediamanager.service.delegate.handler.HeartbeatHandler; import org.apache.logging.log4j.LogManager; @@ -25,6 +26,7 @@ public class DelegateActionManager { private void registerHandlers() { handlerRegistry.put("echo",new EchoHandler()); handlerRegistry.put("heartbeat",new HeartbeatHandler()); + handlerRegistry.put("close", new CloseHandler()); } public void start(){ diff --git a/src/main/java/com/mediamanager/service/delegate/handler/CloseHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/CloseHandler.java new file mode 100644 index 0000000..de59bf3 --- /dev/null +++ b/src/main/java/com/mediamanager/service/delegate/handler/CloseHandler.java @@ -0,0 +1,32 @@ +package com.mediamanager.service.delegate.handler; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import com.mediamanager.protocol.TestProtocol.CloseCommand; +import com.mediamanager.protocol.TestProtocol.CloseResponse; +import com.mediamanager.protocol.TransportProtocol; +import com.mediamanager.service.delegate.ActionHandler; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class CloseHandler implements ActionHandler { + private static final Logger logger = LogManager.getLogger(CloseHandler.class); + + @Override + public TransportProtocol.Response.Builder handle(ByteString requestPayload) + throws InvalidProtocolBufferException { + + CloseCommand.parseFrom(requestPayload); // Valida + + logger.info("Close command received - connection will close"); + + CloseResponse response = CloseResponse.newBuilder() + .setMessage("Connection closing. Goodbye!") + .build(); + + return TransportProtocol.Response.newBuilder() + .setPayload(ByteString.copyFrom(response.toByteArray())) + .setStatusCode(200) + .putHeaders("Connection", "close"); // ← Marca para fechar + } +} \ No newline at end of file diff --git a/src/main/java/com/mediamanager/service/delegate/handler/EchoHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/EchoHandler.java index 4945efc..1e7ece9 100644 --- a/src/main/java/com/mediamanager/service/delegate/handler/EchoHandler.java +++ b/src/main/java/com/mediamanager/service/delegate/handler/EchoHandler.java @@ -33,7 +33,7 @@ public class EchoHandler implements ActionHandler { // 4. Retorna Response return TransportProtocol.Response.newBuilder() .setPayload(responsePayload) - .setStatusCode(200) - .putHeaders("Content-Type", "application/x-protobuf"); + .setStatusCode(200); + } } diff --git a/src/main/java/com/mediamanager/service/delegate/handler/HeartbeatHandler.java b/src/main/java/com/mediamanager/service/delegate/handler/HeartbeatHandler.java index c385433..20e3d6d 100644 --- a/src/main/java/com/mediamanager/service/delegate/handler/HeartbeatHandler.java +++ b/src/main/java/com/mediamanager/service/delegate/handler/HeartbeatHandler.java @@ -30,7 +30,7 @@ public class HeartbeatHandler implements ActionHandler { return TransportProtocol.Response.newBuilder() .setPayload(ByteString.copyFrom(response.toByteArray())) - .setStatusCode(200) - .putHeaders("Content-Type", "application/x-protobuf"); + .setStatusCode(200); + } } \ No newline at end of file diff --git a/src/main/java/com/mediamanager/service/ipc/IPCManager.java b/src/main/java/com/mediamanager/service/ipc/IPCManager.java index 00ed070..2115066 100644 --- a/src/main/java/com/mediamanager/service/ipc/IPCManager.java +++ b/src/main/java/com/mediamanager/service/ipc/IPCManager.java @@ -223,9 +223,16 @@ public class IPCManager { logger.debug("Client {} handler thread started", clientId); try { - TransportProtocol.Request request = readRequest(channel); + // LOOP: processa múltiplas requests na mesma conexão + while (channel.isOpen()) { + TransportProtocol.Request request = readRequest(channel); + + if (request == null) { + // Cliente desconectou gracefully + logger.info("Client {} disconnected (end of stream)", clientId); + break; + } - if (request != null) { logger.info("Client {} sent request {}", clientId, request.getRequestId()); // Processa usando o DelegateActionManager @@ -233,17 +240,29 @@ public class IPCManager { // Envia resposta de volta writeResponse(channel, response); - logger.info("Client {} response sent", clientId); - } else { - logger.warn("Client {} sent null message", clientId); + + // Verifica se é comando CLOSE + String connectionHeader = response.getHeadersOrDefault("Connection", ""); + if ("close".equals(connectionHeader)) { + logger.info("Client {} requested connection close", clientId); + break; // Sai do loop e fecha + } } + } catch (IOException e) { + if (channel.isOpen()) { + logger.error("IO error handling client {}", clientId, e); + } else { + logger.debug("Client {} connection closed by peer", clientId); + } } catch (Exception e) { - logger.error("Error handling client {}", clientId, e); + logger.error("Unexpected error handling client {}", clientId, e); } finally { try { - channel.close(); + if (channel.isOpen()) { + channel.close(); + } logger.debug("Client {} channel closed", clientId); } catch (IOException e) { logger.error("Error closing client {} channel", clientId, e); diff --git a/src/main/proto/test.proto b/src/main/proto/test.proto index ea4bf62..00330cd 100644 --- a/src/main/proto/test.proto +++ b/src/main/proto/test.proto @@ -21,4 +21,10 @@ message HeartbeatCommand { message HeartbeatResponse { int64 client_timestamp = 1; int64 server_timestamp = 2; +} +message CloseCommand { + // Vazio - apenas sinaliza fechamento +} +message CloseResponse { + string message = 1; } \ No newline at end of file