diff --git a/eventmesh-common/src/main/java/org/apache/eventmesh/common/protocol/http/HttpCommand.java b/eventmesh-common/src/main/java/org/apache/eventmesh/common/protocol/http/HttpCommand.java index 96c75af90f..f7ebd0cab8 100644 --- a/eventmesh-common/src/main/java/org/apache/eventmesh/common/protocol/http/HttpCommand.java +++ b/eventmesh-common/src/main/java/org/apache/eventmesh/common/protocol/http/HttpCommand.java @@ -41,6 +41,7 @@ import lombok.Data; +@Deprecated @Data public class HttpCommand implements ProtocolTransportObject { diff --git a/eventmesh-common/src/main/java/org/apache/eventmesh/common/utils/JsonUtils.java b/eventmesh-common/src/main/java/org/apache/eventmesh/common/utils/JsonUtils.java index 19614714fd..71d42e3452 100644 --- a/eventmesh-common/src/main/java/org/apache/eventmesh/common/utils/JsonUtils.java +++ b/eventmesh-common/src/main/java/org/apache/eventmesh/common/utils/JsonUtils.java @@ -25,6 +25,7 @@ import java.io.IOException; import java.lang.reflect.Type; +import java.util.Map; import java.util.Objects; import com.fasterxml.jackson.annotation.JsonInclude; @@ -52,6 +53,14 @@ public class JsonUtils { OBJECT_MAPPER.registerModule(new JavaTimeModule()); } + public static T mapToObject(Map map, Class beanClass) { + if (map == null) { + return null; + } + Object obj = OBJECT_MAPPER.convertValue(map, beanClass); + return (T) obj; + } + /** * Serialize object to json string. * diff --git a/eventmesh-common/src/main/java/org/apache/eventmesh/common/utils/NetUtils.java b/eventmesh-common/src/main/java/org/apache/eventmesh/common/utils/NetUtils.java index 2c788d29b5..2683deaa84 100644 --- a/eventmesh-common/src/main/java/org/apache/eventmesh/common/utils/NetUtils.java +++ b/eventmesh-common/src/main/java/org/apache/eventmesh/common/utils/NetUtils.java @@ -17,15 +17,10 @@ package org.apache.eventmesh.common.utils; -import static org.apache.eventmesh.common.Constants.SUCCESS_CODE; - import org.apache.eventmesh.common.Constants; -import org.apache.eventmesh.common.enums.HttpMethod; import org.apache.commons.lang3.StringUtils; -import java.io.IOException; -import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.InetSocketAddress; import java.net.URLDecoder; @@ -34,8 +29,6 @@ import java.util.HashMap; import java.util.Map; -import com.sun.net.httpserver.HttpExchange; - import lombok.extern.slf4j.Slf4j; /** @@ -81,26 +74,4 @@ public static String addressToString(Collection clients) { } return sb.toString(); } - - public static String parsePostBody(HttpExchange exchange) - throws IOException { - - if (!HttpMethod.POST.name().equalsIgnoreCase(exchange.getRequestMethod()) - && !HttpMethod.PUT.name().equalsIgnoreCase(exchange.getRequestMethod())) { - return ""; - } - StringBuilder body = new StringBuilder(1024); - try (InputStreamReader reader = new InputStreamReader(exchange.getRequestBody(), Constants.DEFAULT_CHARSET.name())) { - char[] buffer = new char[256]; - int readIndex; - while ((readIndex = reader.read(buffer)) != -1) { - body.append(buffer, 0, readIndex); - } - } - return body.toString(); - } - - public static void sendSuccessResponseHeaders(HttpExchange httpExchange) throws IOException { - httpExchange.sendResponseHeaders(SUCCESS_CODE, 0); - } } diff --git a/eventmesh-common/src/test/java/org/apache/eventmesh/common/utils/NetUtilsTest.java b/eventmesh-common/src/test/java/org/apache/eventmesh/common/utils/NetUtilsTest.java index 91c0708394..fd8bf595af 100644 --- a/eventmesh-common/src/test/java/org/apache/eventmesh/common/utils/NetUtilsTest.java +++ b/eventmesh-common/src/test/java/org/apache/eventmesh/common/utils/NetUtilsTest.java @@ -17,21 +17,13 @@ package org.apache.eventmesh.common.utils; -import org.apache.eventmesh.common.enums.HttpMethod; - -import java.io.ByteArrayInputStream; -import java.io.IOException; import java.net.InetSocketAddress; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import com.sun.net.httpserver.HttpExchange; public class NetUtilsTest { @@ -58,25 +50,4 @@ public void testAddressToString() { Assertions.assertEquals(localAddress + "|", result); } - @Test - public void testParsePostBody() throws Exception { - - HttpExchange exchange = Mockito.mock(HttpExchange.class); - String expected = "mxsm"; - ByteArrayInputStream inputStream = new ByteArrayInputStream(expected.getBytes(StandardCharsets.UTF_8)); - Mockito.when(exchange.getRequestMethod()).thenReturn(HttpMethod.POST.name()); - Mockito.when(exchange.getRequestBody()).thenReturn(inputStream); - - String actual = NetUtils.parsePostBody(exchange); - Assertions.assertEquals(expected, actual); - - } - - @Test - public void testSendSuccessResponseHeaders() throws IOException { - HttpExchange exchange = Mockito.mock(HttpExchange.class); - NetUtils.sendSuccessResponseHeaders(exchange); - Mockito.verify(exchange, Mockito.times(1)) - .sendResponseHeaders(Mockito.anyInt(), Mockito.anyLong()); - } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/AbstractHttpHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/AbstractHttpHandler.java index 9e11650142..30c81446c5 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/AbstractHttpHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/AbstractHttpHandler.java @@ -17,18 +17,133 @@ package org.apache.eventmesh.runtime.admin.handler; -import com.sun.net.httpserver.HttpHandler; +import org.apache.eventmesh.common.enums.HttpMethod; +import org.apache.eventmesh.runtime.constants.EventMeshConstants; +import org.apache.eventmesh.runtime.util.HttpRequestUtil; +import org.apache.eventmesh.runtime.util.HttpResponseUtils; -import lombok.Data; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; -/** - * An abstract class that implements the {@link HttpHandler} interface - * and provides basic functionality for HTTP request handling. - *

- * Subclasses should extend this class to implement specific HTTP request handling logic. - */ +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.DefaultFullHttpResponse; +import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.HttpHeaderValues; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.HttpVersion; +import io.netty.util.AsciiString; + +import lombok.Data; @Data public abstract class AbstractHttpHandler implements HttpHandler { + protected void write(ChannelHandlerContext ctx, byte[] result) { + ctx.writeAndFlush(HttpResponseUtils.getHttpResponse(result, ctx, HttpHeaderValues.TEXT_HTML)).addListener(ChannelFutureListener.CLOSE); + } + + protected void write(ChannelHandlerContext ctx, HttpResponse response) { + ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); + } + + protected void write(ChannelHandlerContext ctx, byte[] result, AsciiString headerValue, HttpResponseStatus httpResponseStatus) { + ctx.writeAndFlush(HttpResponseUtils.getHttpResponse(result, ctx, headerValue, httpResponseStatus)).addListener(ChannelFutureListener.CLOSE); + } + + protected void write401(ChannelHandlerContext ctx) { + DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.UNAUTHORIZED); + ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); + } + + protected void writeSuccess(ChannelHandlerContext ctx) { + HttpHeaders responseHeaders = new DefaultHttpHeaders(); + responseHeaders.add(EventMeshConstants.HANDLER_ORIGIN, "*"); + DefaultFullHttpResponse response = + new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.EMPTY_BUFFER, responseHeaders, responseHeaders); + ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); + } + + protected void writeSuccess(ChannelHandlerContext ctx, DefaultHttpHeaders responseHeaders) { + DefaultFullHttpResponse response = + new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.EMPTY_BUFFER, responseHeaders, responseHeaders); + ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); + } + + protected void preflight(ChannelHandlerContext ctx) { + HttpHeaders responseHeaders = new DefaultHttpHeaders(); + responseHeaders.add(EventMeshConstants.HANDLER_ORIGIN, "*"); + responseHeaders.add(EventMeshConstants.HANDLER_METHODS, "*"); + responseHeaders.add(EventMeshConstants.HANDLER_HEADERS, "*"); + responseHeaders.add(EventMeshConstants.HANDLER_AGE, EventMeshConstants.MAX_AGE); + DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.EMPTY_BUFFER, + responseHeaders, responseHeaders); + write(ctx, response); + } + + /** + * Converts a query string to a map of key-value pairs. + *

+ * This method takes a query string and parses it to create a map of key-value pairs, where each key and value are extracted from the query string + * separated by '='. + *

+ * If the query string is null, an empty map is returned. + * + * @param query the query string to convert to a map + * @return a map containing the key-value pairs from the query string + */ + protected Map queryToMap(String query) { + if (query == null) { + return new HashMap<>(); + } + Map result = new HashMap<>(); + for (String param : query.split("&")) { + String[] entry = param.split("="); + if (entry.length > 1) { + result.put(entry[0], entry[1]); + } else { + result.put(entry[0], ""); + } + } + return result; + } + + @Override + public void handle(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + switch (HttpMethod.valueOf(httpRequest.method().name())) { + case OPTIONS: + preflight(ctx); + break; + case GET: + get(httpRequest, ctx); + break; + case POST: + post(httpRequest, ctx); + break; + case DELETE: + delete(httpRequest, ctx); + break; + default: // do nothing + break; + } + } + + protected void post(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + } + + protected void delete(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + } + + protected void get(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + } + + protected Map parseHttpRequestBody(final HttpRequest httpRequest) throws IOException { + return HttpRequestUtil.parseHttpRequestBody(httpRequest, null, null); + } } + diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/AdminHandlerManager.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/AdminHandlerManager.java index aabbe5bbe2..744990e740 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/AdminHandlerManager.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/AdminHandlerManager.java @@ -31,7 +31,6 @@ import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; -import com.sun.net.httpserver.HttpHandler; public class AdminHandlerManager { diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/ConfigurationHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/ConfigurationHandler.java index 8d37470853..8a7d3d7ce8 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/ConfigurationHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/ConfigurationHandler.java @@ -18,23 +18,23 @@ package org.apache.eventmesh.runtime.admin.handler; import org.apache.eventmesh.common.Constants; -import org.apache.eventmesh.common.enums.HttpMethod; import org.apache.eventmesh.common.utils.JsonUtils; -import org.apache.eventmesh.runtime.admin.response.Error; import org.apache.eventmesh.runtime.admin.response.GetConfigurationResponse; import org.apache.eventmesh.runtime.common.EventHttpHandler; import org.apache.eventmesh.runtime.configuration.EventMeshGrpcConfiguration; import org.apache.eventmesh.runtime.configuration.EventMeshHTTPConfiguration; import org.apache.eventmesh.runtime.configuration.EventMeshTCPConfiguration; import org.apache.eventmesh.runtime.constants.EventMeshConstants; +import org.apache.eventmesh.runtime.util.HttpResponseUtils; -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.io.StringWriter; import java.util.Objects; -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; import lombok.extern.slf4j.Slf4j; @@ -56,7 +56,7 @@ public class ConfigurationHandler extends AbstractHttpHandler { private final EventMeshGrpcConfiguration eventMeshGrpcConfiguration; /** - * Constructs a new instance with the provided configurations and HTTP handler manager. + * Constructs a new instance with the provided configurations. * * @param eventMeshTCPConfiguration the TCP configuration for EventMesh * @param eventMeshHTTPConfiguration the HTTP configuration for EventMesh @@ -72,96 +72,34 @@ public ConfigurationHandler( this.eventMeshGrpcConfiguration = eventMeshGrpcConfiguration; } - /** - * Handles the OPTIONS request first for {@code /configuration}. - *

- * This method adds CORS (Cross-Origin Resource Sharing) response headers to the {@link HttpExchange} object and sends a 200 status code. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - void preflight(HttpExchange httpExchange) throws IOException { - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_ORIGIN, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_METHODS, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_HEADERS, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_AGE, EventMeshConstants.MAX_AGE); - httpExchange.sendResponseHeaders(200, 0); - OutputStream out = httpExchange.getResponseBody(); - out.close(); - } - - /** - * Handles the GET request for {@code /configuration}. - *

- * This method retrieves the EventMesh configuration information and returns it as a JSON response. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - void get(HttpExchange httpExchange) throws IOException { - httpExchange.getResponseHeaders().add(EventMeshConstants.CONTENT_TYPE, EventMeshConstants.APPLICATION_JSON); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_ORIGIN, "*"); - try (OutputStream out = httpExchange.getResponseBody()) { - try { - GetConfigurationResponse getConfigurationResponse = new GetConfigurationResponse( - eventMeshTCPConfiguration.getSysID(), - eventMeshTCPConfiguration.getMetaStorageAddr(), - eventMeshTCPConfiguration.getEventMeshEnv(), - eventMeshTCPConfiguration.getEventMeshIDC(), - eventMeshTCPConfiguration.getEventMeshCluster(), - eventMeshTCPConfiguration.getEventMeshServerIp(), - eventMeshTCPConfiguration.getEventMeshName(), - eventMeshTCPConfiguration.getEventMeshWebhookOrigin(), - eventMeshTCPConfiguration.isEventMeshServerSecurityEnable(), - eventMeshTCPConfiguration.isEventMeshServerMetaStorageEnable(), - // TCP Configuration - eventMeshTCPConfiguration.getEventMeshTcpServerPort(), - // HTTP Configuration - eventMeshHTTPConfiguration.getHttpServerPort(), - eventMeshHTTPConfiguration.isEventMeshServerUseTls(), - // gRPC Configuration - eventMeshGrpcConfiguration.getGrpcServerPort(), - eventMeshGrpcConfiguration.isEventMeshServerUseTls()); - - String result = JsonUtils.toJSONString(getConfigurationResponse); - httpExchange.sendResponseHeaders(200, Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET).length); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - } catch (Exception e) { - StringWriter writer = new StringWriter(); - PrintWriter printWriter = new PrintWriter(writer); - e.printStackTrace(printWriter); - printWriter.flush(); - String stackTrace = writer.toString(); - - Error error = new Error(e.toString(), stackTrace); - String result = JsonUtils.toJSONString(error); - httpExchange.sendResponseHeaders(500, Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET).length); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - } - } - } - - /** - * Handles the HTTP requests for {@code /configuration}. - *

- * It delegates the handling to {@code preflight()} or {@code get()} methods based on the request method type (OPTIONS or GET). - *

- * This method is an implementation of {@linkplain com.sun.net.httpserver.HttpHandler#handle(HttpExchange) HttpHandler.handle()} - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ @Override - public void handle(HttpExchange httpExchange) throws IOException { - switch (HttpMethod.valueOf(httpExchange.getRequestMethod())) { - case OPTIONS: - preflight(httpExchange); - break; - case GET: - get(httpExchange); - break; - default: // do nothing - break; - } + protected void get(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + HttpHeaders responseHeaders = new DefaultHttpHeaders(); + responseHeaders.add(EventMeshConstants.CONTENT_TYPE, EventMeshConstants.APPLICATION_JSON); + responseHeaders.add(EventMeshConstants.HANDLER_ORIGIN, "*"); + GetConfigurationResponse getConfigurationResponse = new GetConfigurationResponse( + eventMeshTCPConfiguration.getSysID(), + eventMeshTCPConfiguration.getMetaStorageAddr(), + eventMeshTCPConfiguration.getEventMeshEnv(), + eventMeshTCPConfiguration.getEventMeshIDC(), + eventMeshTCPConfiguration.getEventMeshCluster(), + eventMeshTCPConfiguration.getEventMeshServerIp(), + eventMeshTCPConfiguration.getEventMeshName(), + eventMeshTCPConfiguration.getEventMeshWebhookOrigin(), + eventMeshTCPConfiguration.isEventMeshServerSecurityEnable(), + eventMeshTCPConfiguration.isEventMeshServerMetaStorageEnable(), + // TCP Configuration + eventMeshTCPConfiguration.getEventMeshTcpServerPort(), + // HTTP Configuration + eventMeshHTTPConfiguration.getHttpServerPort(), + eventMeshHTTPConfiguration.isEventMeshServerUseTls(), + // gRPC Configuration + eventMeshGrpcConfiguration.getGrpcServerPort(), + eventMeshGrpcConfiguration.isEventMeshServerUseTls()); + String result = JsonUtils.toJSONString(getConfigurationResponse); + HttpResponse httpResponse = + HttpResponseUtils.getHttpResponse(Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET), ctx, responseHeaders, + HttpResponseStatus.OK); + write(ctx, httpResponse); } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/DeleteWebHookConfigHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/DeleteWebHookConfigHandler.java index 0feaa748f6..feb73094c8 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/DeleteWebHookConfigHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/DeleteWebHookConfigHandler.java @@ -19,38 +19,35 @@ import org.apache.eventmesh.common.Constants; import org.apache.eventmesh.common.utils.JsonUtils; -import org.apache.eventmesh.common.utils.NetUtils; import org.apache.eventmesh.runtime.common.EventHttpHandler; import org.apache.eventmesh.webhook.api.WebHookConfig; import org.apache.eventmesh.webhook.api.WebHookConfigOperation; -import java.io.IOException; -import java.io.OutputStream; +import java.util.Map; +import java.util.Objects; -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpRequest; import lombok.extern.slf4j.Slf4j; /** - * This class handles the HTTP requests of {@code /webhook/deleteWebHookConfig} endpoint - * and deletes an existing WebHook configuration - * according to the given {@linkplain org.apache.eventmesh.webhook.api.WebHookConfig WebHookConfig}. + * This class handles the HTTP requests of {@code /webhook/deleteWebHookConfig} endpoint and deletes an existing WebHook configuration according to + * the given {@linkplain org.apache.eventmesh.webhook.api.WebHookConfig WebHookConfig}. *

- * The implementation of - * {@linkplain org.apache.eventmesh.webhook.api.WebHookConfigOperation#deleteWebHookConfig WebHookConfigOperation} - * interface depends on the {@code eventMesh.webHook.operationMode} configuration in {@code eventmesh.properties}. + * The implementation of {@linkplain org.apache.eventmesh.webhook.api.WebHookConfigOperation#deleteWebHookConfig WebHookConfigOperation} interface + * depends on the {@code eventMesh.webHook.operationMode} configuration in {@code eventmesh.properties}. *

* For example, when {@code eventMesh.webHook.operationMode=file}, It calls the - * {@linkplain org.apache.eventmesh.webhook.admin.FileWebHookConfigOperation#deleteWebHookConfig FileWebHookConfigOperation} - * method as implementation to delete the WebHook configuration file; + * {@linkplain org.apache.eventmesh.webhook.admin.FileWebHookConfigOperation + * #deleteWebHookConfig FileWebHookConfigOperation} method as implementation to delete the WebHook configuration file; *

- * When {@code eventMesh.webHook.operationMode=nacos}, It calls the - * {@linkplain org.apache.eventmesh.webhook.admin.NacosWebHookConfigOperation#deleteWebHookConfig NacosWebHookConfigOperation} - * method as implementation to delete the WebHook configuration from Nacos. + * When {@code eventMesh.webHook.operationMode=nacos}, It calls the {@linkplain org.apache.eventmesh.webhook.admin.NacosWebHookConfigOperation + * #deleteWebHookConfig NacosWebHookConfigOperation} method as implementation to delete the WebHook configuration from Nacos. *

- * The {@linkplain org.apache.eventmesh.webhook.receive.storage.HookConfigOperationManager#deleteWebHookConfig HookConfigOperationManager} - * , another implementation of {@linkplain org.apache.eventmesh.webhook.api.WebHookConfigOperation WebHookConfigOperation} - * interface, is not used for this endpoint. + * The {@linkplain org.apache.eventmesh.webhook.receive.storage.HookConfigOperationManager#deleteWebHookConfig HookConfigOperationManager} , another + * implementation of {@linkplain org.apache.eventmesh.webhook.api.WebHookConfigOperation WebHookConfigOperation} interface, is not used for this + * endpoint. * * @see AbstractHttpHandler */ @@ -63,8 +60,6 @@ public class DeleteWebHookConfigHandler extends AbstractHttpHandler { private final WebHookConfigOperation operation; /** - * Constructs a new instance with the specified WebHook config operation and HTTP handler manager. - * * @param operation the WebHookConfigOperation implementation used to delete the WebHook config */ public DeleteWebHookConfigHandler(WebHookConfigOperation operation) { @@ -72,29 +67,16 @@ public DeleteWebHookConfigHandler(WebHookConfigOperation operation) { this.operation = operation; } - /** - * Handles requests by deleting a WebHook configuration. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - * - * @see org.apache.eventmesh.webhook.receive.storage.HookConfigOperationManager#deleteWebHookConfig - */ @Override - public void handle(HttpExchange httpExchange) throws IOException { - NetUtils.sendSuccessResponseHeaders(httpExchange); - + public void handle(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + Map body = parseHttpRequestBody(httpRequest); + Objects.requireNonNull(body, "body can not be null"); // Resolve to WebHookConfig - String requestBody = NetUtils.parsePostBody(httpExchange); - WebHookConfig webHookConfig = JsonUtils.parseObject(requestBody, WebHookConfig.class); + WebHookConfig webHookConfig = JsonUtils.mapToObject(body, WebHookConfig.class); + // Delete the existing WebHookConfig + Integer code = operation.deleteWebHookConfig(webHookConfig); // operating result + String result = 1 == code ? "deleteWebHookConfig Succeed!" : "deleteWebHookConfig Failed!"; + write(ctx, result.getBytes(Constants.DEFAULT_CHARSET)); - try (OutputStream out = httpExchange.getResponseBody()) { - // Delete the existing WebHookConfig - Integer code = operation.deleteWebHookConfig(webHookConfig); // operating result - String result = 1 == code ? "deleteWebHookConfig Succeed!" : "deleteWebHookConfig Failed!"; - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - } catch (Exception e) { - log.error("get WebHookConfigOperation implementation Failed.", e); - } } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/EventHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/EventHandler.java index f4358f9977..9e4dcadc3f 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/EventHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/EventHandler.java @@ -17,22 +17,14 @@ package org.apache.eventmesh.runtime.admin.handler; -import org.apache.eventmesh.common.Constants; -import org.apache.eventmesh.common.enums.HttpMethod; import org.apache.eventmesh.common.utils.JsonUtils; -import org.apache.eventmesh.runtime.admin.response.Error; -import org.apache.eventmesh.runtime.admin.utils.HttpExchangeUtils; import org.apache.eventmesh.runtime.common.EventHttpHandler; import org.apache.eventmesh.runtime.constants.EventMeshConstants; import org.apache.eventmesh.runtime.core.plugin.MQAdminWrapper; -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.io.StringWriter; +import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -40,20 +32,19 @@ import io.cloudevents.CloudEvent; import io.cloudevents.core.provider.EventFormatProvider; import io.cloudevents.jackson.JsonFormat; - -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpRequest; import lombok.extern.slf4j.Slf4j; /** - * This class handles the {@code /event} endpoint, - * corresponding to the {@code eventmesh-dashboard} path {@code /event}. + * This class handles the {@code /event} endpoint, corresponding to the {@code eventmesh-dashboard} path {@code /event}. *

- * It is responsible for managing operations on events, - * including retrieving the event list and creating events. + * It is responsible for managing operations on events, including retrieving the event list and creating events. *

- * The GET method supports querying events by {@code topicName}, - * and uses {@code offset} and {@code length} parameters for pagination. + * The GET method supports querying events by {@code topicName}, and uses {@code offset} and {@code length} parameters for pagination. *

* An instance of {@link MQAdminWrapper} is used to interact with the messaging system. * @@ -68,7 +59,7 @@ public class EventHandler extends AbstractHttpHandler { private final MQAdminWrapper admin; /** - * Constructs a new instance with the specified connector plugin type and HTTP handler manager. + * Constructs a new instance with the specified connector plugin type. * * @param connectorPluginType The name of event storage connector plugin. */ @@ -83,169 +74,41 @@ public EventHandler( } } - /** - * Handles the OPTIONS request first for {@code /event}. - *

- * This method adds CORS (Cross-Origin Resource Sharing) response headers to - * the {@link HttpExchange} object and sends a 200 status code. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - void preflight(HttpExchange httpExchange) throws IOException { - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_ORIGIN, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_METHODS, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_HEADERS, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_AGE, EventMeshConstants.MAX_AGE); - httpExchange.sendResponseHeaders(200, 0); - OutputStream out = httpExchange.getResponseBody(); - out.close(); - } - - /** - * Converts a query string to a map of key-value pairs. - *

- * This method takes a query string and parses it to create a map of key-value pairs, - * where each key and value are extracted from the query string separated by '='. - *

- * If the query string is null, an empty map is returned. - * - * @param query the query string to convert to a map - * @return a map containing the key-value pairs from the query string - */ - private Map queryToMap(String query) { - if (query == null) { - return new HashMap<>(); - } - Map result = new HashMap<>(); - for (String param : query.split("&")) { - String[] entry = param.split("="); - if (entry.length > 1) { - result.put(entry[0], entry[1]); - } else { - result.put(entry[0], ""); - } + @Override + protected void get(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + HttpHeaders responseHeaders = new DefaultHttpHeaders(); + responseHeaders.add(EventMeshConstants.HANDLER_ORIGIN, "*"); + String queryString = URI.create(httpRequest.uri()).getQuery(); + if (queryString == null || "".equals(queryString)) { + write401(ctx); + return; } - return result; - } - - /** - * Handles the GET request for {@code /event}. - *

- * This method retrieves the list of events from the {@link MQAdminWrapper} and returns it as a JSON response. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - void get(HttpExchange httpExchange) { - httpExchange.getResponseHeaders().add(EventMeshConstants.CONTENT_TYPE, EventMeshConstants.APPLICATION_JSON); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_ORIGIN, "*"); - - try (OutputStream out = httpExchange.getResponseBody()) { - String queryString = httpExchange.getRequestURI().getQuery(); - if (queryString == null || "".equals(queryString)) { - httpExchange.sendResponseHeaders(401, 0); - return; - } - - Map queryMap = queryToMap(queryString); - String topicName = queryMap.get("topicName"); - int offset = Integer.parseInt(queryMap.get("offset")); - int length = Integer.parseInt(queryMap.get("length")); - List eventList = admin.getEvent(topicName, offset, length); - - List eventJsonList = new ArrayList<>(); - for (CloudEvent event : eventList) { - byte[] serializedEvent = Objects.requireNonNull(EventFormatProvider + Map queryMap = queryToMap(queryString); + String topicName = queryMap.get("topicName"); + int offset = Integer.parseInt(queryMap.get("offset")); + int length = Integer.parseInt(queryMap.get("length")); + List eventList = admin.getEvent(topicName, offset, length); + + List eventJsonList = new ArrayList<>(); + for (CloudEvent event : eventList) { + byte[] serializedEvent = Objects.requireNonNull(EventFormatProvider .getInstance() .resolveFormat(JsonFormat.CONTENT_TYPE)) - .serialize(event); - eventJsonList.add(new String(serializedEvent, StandardCharsets.UTF_8)); - } - String result = JsonUtils.toJSONString(eventJsonList); - httpExchange.sendResponseHeaders(200, Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET).length); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - } catch (Exception e) { - try (OutputStream out = httpExchange.getResponseBody()) { - StringWriter writer = new StringWriter(); - PrintWriter printWriter = new PrintWriter(writer); - e.printStackTrace(printWriter); - printWriter.flush(); - String stackTrace = writer.toString(); - - Error error = new Error(e.toString(), stackTrace); - String result = JsonUtils.toJSONString(error); - httpExchange.sendResponseHeaders(500, Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET).length); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - } catch (IOException ioe) { - log.warn("out close failed...", ioe); - } - } - } - - /** - * Handles the POST request for {@code /event}. - *

- * This method creates an event based on the request data, then returns {@code 200 OK}. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - void post(HttpExchange httpExchange) { - httpExchange.getResponseHeaders().add(EventMeshConstants.CONTENT_TYPE, EventMeshConstants.APPLICATION_JSON); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_ORIGIN, "*"); - - try { - String request = HttpExchangeUtils.streamToString(httpExchange.getRequestBody()); - byte[] rawRequest = request.getBytes(Constants.DEFAULT_CHARSET); - CloudEvent event = Objects.requireNonNull(EventFormatProvider - .getInstance() - .resolveFormat(JsonFormat.CONTENT_TYPE)).deserialize(rawRequest); - admin.publish(event); - httpExchange.sendResponseHeaders(200, 0); - } catch (Exception e) { - try (OutputStream out = httpExchange.getResponseBody()) { - StringWriter writer = new StringWriter(); - PrintWriter printWriter = new PrintWriter(writer); - e.printStackTrace(printWriter); - printWriter.flush(); - String stackTrace = writer.toString(); - - Error error = new Error(e.toString(), stackTrace); - String result = JsonUtils.toJSONString(error); - httpExchange.sendResponseHeaders(500, Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET).length); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - } catch (IOException ioe) { - log.warn("out close failed...", ioe); - } + .serialize(event); + eventJsonList.add(new String(serializedEvent, StandardCharsets.UTF_8)); } } - /** - * Handles the HTTP requests for {@code /event}. - *

- * It delegates the handling to {@code preflight()}, {@code get()} or {@code post()} methods - * based on the request method type (OPTIONS, GET or POST). - *

- * This method is an implementation of {@linkplain com.sun.net.httpserver.HttpHandler#handle(HttpExchange) HttpHandler.handle()}. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ @Override - public void handle(HttpExchange httpExchange) throws IOException { - switch (HttpMethod.valueOf(httpExchange.getRequestMethod())) { - case OPTIONS: - preflight(httpExchange); - break; - case POST: - post(httpExchange); - break; - case GET: - get(httpExchange); - break; - default: - break; - } + protected void post(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + HttpHeaders responseHeaders = new DefaultHttpHeaders(); + responseHeaders.add(EventMeshConstants.HANDLER_ORIGIN, "*"); + String request = JsonUtils.toJSONString(parseHttpRequestBody(httpRequest)); + byte[] rawRequest = request.getBytes(StandardCharsets.UTF_8); + CloudEvent event = Objects.requireNonNull(EventFormatProvider + .getInstance() + .resolveFormat(JsonFormat.CONTENT_TYPE)).deserialize(rawRequest); + admin.publish(event); + writeSuccess(ctx); } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/GrpcClientHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/GrpcClientHandler.java index 6970b302bc..3712defd1a 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/GrpcClientHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/GrpcClientHandler.java @@ -18,29 +18,28 @@ package org.apache.eventmesh.runtime.admin.handler; import org.apache.eventmesh.common.Constants; -import org.apache.eventmesh.common.enums.HttpMethod; import org.apache.eventmesh.common.utils.JsonUtils; import org.apache.eventmesh.runtime.admin.request.DeleteGrpcClientRequest; -import org.apache.eventmesh.runtime.admin.response.Error; import org.apache.eventmesh.runtime.admin.response.GetClientResponse; -import org.apache.eventmesh.runtime.admin.utils.HttpExchangeUtils; import org.apache.eventmesh.runtime.boot.EventMeshGrpcServer; import org.apache.eventmesh.runtime.common.EventHttpHandler; import org.apache.eventmesh.runtime.constants.EventMeshConstants; import org.apache.eventmesh.runtime.core.protocol.grpc.consumer.ConsumerManager; import org.apache.eventmesh.runtime.core.protocol.grpc.consumer.consumergroup.ConsumerGroupClient; +import org.apache.eventmesh.runtime.util.HttpResponseUtils; -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.io.StringWriter; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; import lombok.extern.slf4j.Slf4j; @@ -60,7 +59,7 @@ public class GrpcClientHandler extends AbstractHttpHandler { private final EventMeshGrpcServer eventMeshGrpcServer; /** - * Constructs a new instance with the provided server instance and HTTP handler manager. + * Constructs a new instance with the provided server instance. * * @param eventMeshGrpcServer the gRPC server instance of EventMesh */ @@ -70,167 +69,70 @@ public GrpcClientHandler( this.eventMeshGrpcServer = eventMeshGrpcServer; } - /** - * Handles the OPTIONS request first for {@code /client/grpc}. - *

- * This method adds CORS (Cross-Origin Resource Sharing) response headers to the {@link HttpExchange} object and sends a 200 status code. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - void preflight(HttpExchange httpExchange) throws IOException { - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_ORIGIN, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_METHODS, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_HEADERS, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_AGE, EventMeshConstants.MAX_AGE); - httpExchange.sendResponseHeaders(200, 0); - OutputStream out = httpExchange.getResponseBody(); - out.close(); - } - - /** - * Handles the DELETE request for {@code /client/grpc}. - *

- * This method deletes a connected gRPC client by disconnecting their connections based on the provided host and port, then returns {@code 200 - * OK}. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - void delete(HttpExchange httpExchange) throws IOException { - try (OutputStream out = httpExchange.getResponseBody()) { - // Parse the request body string into a DeleteHTTPClientRequest object - String request = HttpExchangeUtils.streamToString(httpExchange.getRequestBody()); - DeleteGrpcClientRequest deleteGrpcClientRequest = JsonUtils.parseObject(request, DeleteGrpcClientRequest.class); - String url = Objects.requireNonNull(deleteGrpcClientRequest).getUrl(); - - ConsumerManager consumerManager = eventMeshGrpcServer.getConsumerManager(); - Map> clientTable = consumerManager.getClientTable(); - // Find the client that matches the url to be deleted - for (List clientList : clientTable.values()) { - for (ConsumerGroupClient client : clientList) { - if (Objects.equals(client.getUrl(), url)) { - // Call the deregisterClient method to close the gRPC client stream and remove it - consumerManager.deregisterClient(client); - } + @Override + protected void delete(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + Map body = parseHttpRequestBody(httpRequest); + Objects.requireNonNull(body, "body can not be null"); + DeleteGrpcClientRequest deleteGrpcClientRequest = JsonUtils.mapToObject(body, DeleteGrpcClientRequest.class); + String url = Objects.requireNonNull(deleteGrpcClientRequest).getUrl(); + ConsumerManager consumerManager = eventMeshGrpcServer.getConsumerManager(); + Map> clientTable = consumerManager.getClientTable(); + // Find the client that matches the url to be deleted + for (List clientList : clientTable.values()) { + for (ConsumerGroupClient client : clientList) { + if (Objects.equals(client.getUrl(), url)) { + // Call the deregisterClient method to close the gRPC client stream and remove it + consumerManager.deregisterClient(client); } } - - // Set the response headers and send a 200 status code empty response - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_ORIGIN, "*"); - httpExchange.sendResponseHeaders(200, 0); - } catch (Exception e) { - StringWriter writer = new StringWriter(); - PrintWriter printWriter = new PrintWriter(writer); - e.printStackTrace(printWriter); - printWriter.flush(); - String stackTrace = writer.toString(); - - Error error = new Error(e.toString(), stackTrace); - String result = JsonUtils.toJSONString(error); - httpExchange.sendResponseHeaders(500, 0); - log.error(result, e); } + DefaultHttpHeaders responseHeaders = new DefaultHttpHeaders(); + responseHeaders.add(EventMeshConstants.HANDLER_ORIGIN, "*"); + writeSuccess(ctx, responseHeaders); } - /** - * Handles the GET request for {@code /client/grpc}. - *

- * This method retrieves the list of connected gRPC clients and returns it as a JSON response. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - void list(HttpExchange httpExchange) throws IOException { - OutputStream out = httpExchange.getResponseBody(); - // Set the response headers - httpExchange.getResponseHeaders().add(EventMeshConstants.CONTENT_TYPE, EventMeshConstants.APPLICATION_JSON); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_ORIGIN, "*"); - - try { - // Get the list of gRPC clients - List getClientResponseList = new ArrayList<>(); - - ConsumerManager consumerManager = eventMeshGrpcServer.getConsumerManager(); - Map> clientTable = consumerManager.getClientTable(); - for (List clientList : clientTable.values()) { - // Convert each Client object to GetClientResponse and add to getClientResponseList - for (ConsumerGroupClient client : clientList) { - GetClientResponse getClientResponse = new GetClientResponse( - Optional.ofNullable(client.env).orElse(""), - Optional.ofNullable(client.sys).orElse(""), - Optional.ofNullable(client.url).orElse(""), - "0", - Optional.ofNullable(client.hostname).orElse(""), - 0, - Optional.ofNullable(client.apiVersion).orElse(""), - Optional.ofNullable(client.idc).orElse(""), - Optional.ofNullable(client.consumerGroup).orElse(""), - "", - "gRPC"); - getClientResponseList.add(getClientResponse); - } + @Override + protected void get(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + HttpHeaders responseHeaders = new DefaultHttpHeaders(); + responseHeaders.add(EventMeshConstants.CONTENT_TYPE, EventMeshConstants.APPLICATION_JSON); + responseHeaders.add(EventMeshConstants.HANDLER_ORIGIN, "*"); + // Get the list of gRPC clients + List getClientResponseList = new ArrayList<>(); + + ConsumerManager consumerManager = eventMeshGrpcServer.getConsumerManager(); + Map> clientTable = consumerManager.getClientTable(); + for (List clientList : clientTable.values()) { + // Convert each Client object to GetClientResponse and add to getClientResponseList + for (ConsumerGroupClient client : clientList) { + GetClientResponse getClientResponse = new GetClientResponse( + Optional.ofNullable(client.env).orElse(""), + Optional.ofNullable(client.sys).orElse(""), + Optional.ofNullable(client.url).orElse(""), + "0", + Optional.ofNullable(client.hostname).orElse(""), + 0, + Optional.ofNullable(client.apiVersion).orElse(""), + Optional.ofNullable(client.idc).orElse(""), + Optional.ofNullable(client.consumerGroup).orElse(""), + "", + "gRPC"); + getClientResponseList.add(getClientResponse); } + } - // Sort the getClientResponseList by host and port - getClientResponseList.sort((lhs, rhs) -> { - if (lhs.getHost().equals(rhs.getHost())) { - return lhs.getHost().compareTo(rhs.getHost()); - } - return Integer.compare(rhs.getPort(), lhs.getPort()); - }); - - // Convert getClientResponseList to JSON and send the response - String result = JsonUtils.toJSONString(getClientResponseList); - httpExchange.sendResponseHeaders(200, Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET).length); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - } catch (Exception e) { - StringWriter writer = new StringWriter(); - PrintWriter printWriter = new PrintWriter(writer); - e.printStackTrace(printWriter); - printWriter.flush(); - String stackTrace = writer.toString(); - - Error error = new Error(e.toString(), stackTrace); - String result = JsonUtils.toJSONString(error); - httpExchange.sendResponseHeaders(500, Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET).length); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - } finally { - if (out != null) { - try { - out.close(); - } catch (IOException e) { - log.warn("out close failed...", e); - } + // Sort the getClientResponseList by host and port + getClientResponseList.sort((lhs, rhs) -> { + if (lhs.getHost().equals(rhs.getHost())) { + return lhs.getHost().compareTo(rhs.getHost()); } - } + return Integer.compare(rhs.getPort(), lhs.getPort()); + }); + // Convert getClientResponseList to JSON and send the response + String result = JsonUtils.toJSONString(getClientResponseList); + HttpResponse httpResponse = + HttpResponseUtils.getHttpResponse(Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET), ctx, responseHeaders, + HttpResponseStatus.OK); + write(ctx, httpResponse); } - /** - * Handles the HTTP requests for {@code /client/grpc}. - *

- * It delegates the handling to {@code preflight()}, {@code list()} or {@code delete()} methods based on the request method type (OPTIONS, GET or - * DELETE). - *

- * This method is an implementation of {@linkplain com.sun.net.httpserver.HttpHandler#handle(HttpExchange) HttpHandler.handle()}. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - @Override - public void handle(HttpExchange httpExchange) throws IOException { - switch (HttpMethod.valueOf(httpExchange.getRequestMethod())) { - case OPTIONS: - preflight(httpExchange); - break; - case GET: - list(httpExchange); - break; - case DELETE: - delete(httpExchange); - break; - default: - break; - } - } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/HTTPClientHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/HTTPClientHandler.java index c4173011e5..f4e8eb7369 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/HTTPClientHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/HTTPClientHandler.java @@ -18,27 +18,27 @@ package org.apache.eventmesh.runtime.admin.handler; import org.apache.eventmesh.common.Constants; -import org.apache.eventmesh.common.enums.HttpMethod; import org.apache.eventmesh.common.utils.JsonUtils; import org.apache.eventmesh.runtime.admin.request.DeleteHTTPClientRequest; -import org.apache.eventmesh.runtime.admin.response.Error; import org.apache.eventmesh.runtime.admin.response.GetClientResponse; -import org.apache.eventmesh.runtime.admin.utils.HttpExchangeUtils; import org.apache.eventmesh.runtime.boot.EventMeshHTTPServer; import org.apache.eventmesh.runtime.common.EventHttpHandler; import org.apache.eventmesh.runtime.constants.EventMeshConstants; import org.apache.eventmesh.runtime.core.protocol.http.processor.inf.Client; +import org.apache.eventmesh.runtime.util.HttpResponseUtils; -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.io.StringWriter; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; import lombok.extern.slf4j.Slf4j; @@ -58,7 +58,7 @@ public class HTTPClientHandler extends AbstractHttpHandler { private final EventMeshHTTPServer eventMeshHTTPServer; /** - * Constructs a new instance with the provided server instance and HTTP handler manager. + * Constructs a new instance with the provided server instance. * * @param eventMeshHTTPServer the HTTP server instance of EventMesh */ @@ -68,60 +68,20 @@ public HTTPClientHandler( this.eventMeshHTTPServer = eventMeshHTTPServer; } - /** - * Handles the OPTIONS request first for {@code /client/http}. - *

- * This method adds CORS (Cross-Origin Resource Sharing) response headers to the {@link HttpExchange} object and sends a 200 status code. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - void preflight(HttpExchange httpExchange) throws IOException { - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_ORIGIN, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_METHODS, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_HEADERS, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_AGE, EventMeshConstants.MAX_AGE); - httpExchange.sendResponseHeaders(200, 0); - OutputStream out = httpExchange.getResponseBody(); - out.close(); - } - - /** - * Handles the DELETE request for {@code /client/http}. - *

- * This method deletes a connected HTTP client by disconnecting their connections based on the provided host and port, then returns {@code 200 - * OK}. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - void delete(HttpExchange httpExchange) throws IOException { - try (OutputStream out = httpExchange.getResponseBody()) { - // Parse the request body string into a DeleteHTTPClientRequest object - String request = HttpExchangeUtils.streamToString(httpExchange.getRequestBody()); - DeleteHTTPClientRequest deleteHTTPClientRequest = JsonUtils.parseObject(request, DeleteHTTPClientRequest.class); + protected void delete(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + Map body = parseHttpRequestBody(httpRequest); + if (!Objects.isNull(body)) { + DeleteHTTPClientRequest deleteHTTPClientRequest = JsonUtils.mapToObject(body, DeleteHTTPClientRequest.class); String url = Objects.requireNonNull(deleteHTTPClientRequest).getUrl(); for (List clientList : eventMeshHTTPServer.getSubscriptionManager().getLocalClientInfoMapping().values()) { // Find the client that matches the url to be deleted clientList.removeIf(client -> Objects.equals(client.getUrl(), url)); } - // Set the response headers and send a 200 status code empty response - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_ORIGIN, "*"); - httpExchange.sendResponseHeaders(200, 0); - } catch (Exception e) { - StringWriter writer = new StringWriter(); - PrintWriter printWriter = new PrintWriter(writer); - e.printStackTrace(printWriter); - printWriter.flush(); - String stackTrace = writer.toString(); - - Error error = new Error(e.toString(), stackTrace); - String result = JsonUtils.toJSONString(error); - httpExchange.sendResponseHeaders(500, 0); - log.error(result, e); + writeSuccess(ctx); } + } /** @@ -129,99 +89,52 @@ void delete(HttpExchange httpExchange) throws IOException { *

* This method retrieves the list of connected HTTP clients and returns it as a JSON response. * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request + * @throws Exception if an I/O error occurs while handling the request */ - void list(HttpExchange httpExchange) throws IOException { - OutputStream out = httpExchange.getResponseBody(); + protected void get(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + HttpHeaders responseHeaders = new DefaultHttpHeaders(); // Set the response headers - httpExchange.getResponseHeaders().add(EventMeshConstants.CONTENT_TYPE, EventMeshConstants.APPLICATION_JSON); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_ORIGIN, "*"); - - try { - // Get the list of HTTP clients - List getClientResponseList = new ArrayList<>(); - - for (List clientList : eventMeshHTTPServer.getSubscriptionManager().getLocalClientInfoMapping().values()) { - // Convert each Client object to GetClientResponse and add to getClientResponseList - for (Client client : clientList) { - GetClientResponse getClientResponse = new GetClientResponse( - Optional.ofNullable(client.getEnv()).orElse(""), - Optional.ofNullable(client.getSys()).orElse(""), - Optional.ofNullable(client.getUrl()).orElse(""), - "0", - Optional.ofNullable(client.getHostname()).orElse(""), - 0, - Optional.ofNullable(client.getApiVersion()).orElse(""), - Optional.ofNullable(client.getIdc()).orElse(""), - Optional.ofNullable(client.getConsumerGroup()).orElse(""), - "", - EventMeshConstants.PROTOCOL_HTTP.toUpperCase() - - ); - getClientResponseList.add(getClientResponse); - } + responseHeaders.add(EventMeshConstants.CONTENT_TYPE, EventMeshConstants.APPLICATION_JSON); + responseHeaders.add(EventMeshConstants.HANDLER_ORIGIN, "*"); + + // Get the list of HTTP clients + List getClientResponseList = new ArrayList<>(); + + for (List clientList : eventMeshHTTPServer.getSubscriptionManager().getLocalClientInfoMapping().values()) { + // Convert each Client object to GetClientResponse and add to getClientResponseList + for (Client client : clientList) { + GetClientResponse getClientResponse = new GetClientResponse( + Optional.ofNullable(client.getEnv()).orElse(""), + Optional.ofNullable(client.getSys()).orElse(""), + Optional.ofNullable(client.getUrl()).orElse(""), + "0", + Optional.ofNullable(client.getHostname()).orElse(""), + 0, + Optional.ofNullable(client.getApiVersion()).orElse(""), + Optional.ofNullable(client.getIdc()).orElse(""), + Optional.ofNullable(client.getConsumerGroup()).orElse(""), + "", + EventMeshConstants.PROTOCOL_HTTP.toUpperCase() + + ); + getClientResponseList.add(getClientResponse); } + } - // Sort the getClientResponseList by host and port - getClientResponseList.sort((lhs, rhs) -> { - if (lhs.getHost().equals(rhs.getHost())) { - return lhs.getHost().compareTo(rhs.getHost()); - } - return Integer.compare(rhs.getPort(), lhs.getPort()); - }); - - // Convert getClientResponseList to JSON and send the response - String result = JsonUtils.toJSONString(getClientResponseList); - httpExchange.sendResponseHeaders(200, Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET).length); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - } catch (Exception e) { - StringWriter writer = new StringWriter(); - PrintWriter printWriter = new PrintWriter(writer); - e.printStackTrace(printWriter); - printWriter.flush(); - String stackTrace = writer.toString(); - - Error error = new Error(e.toString(), stackTrace); - String result = JsonUtils.toJSONString(error); - httpExchange.sendResponseHeaders(500, Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET).length); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - } finally { - if (out != null) { - try { - out.close(); - } catch (IOException e) { - log.warn("out close failed...", e); - } + // Sort the getClientResponseList by host and port + getClientResponseList.sort((lhs, rhs) -> { + if (lhs.getHost().equals(rhs.getHost())) { + return lhs.getHost().compareTo(rhs.getHost()); } - } - } + return Integer.compare(rhs.getPort(), lhs.getPort()); + }); + + // Convert getClientResponseList to JSON and send the response + String result = JsonUtils.toJSONString(getClientResponseList); + HttpResponse httpResponse = + HttpResponseUtils.getHttpResponse(Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET), ctx, responseHeaders, + HttpResponseStatus.OK); + write(ctx, httpResponse); - /** - * Handles the HTTP requests for {@code /client/http}. - *

- * It delegates the handling to {@code preflight()}, {@code list()} or {@code delete()} methods based on the request method type (OPTIONS, GET or - * DELETE). - *

- * This method is an implementation of {@linkplain com.sun.net.httpserver.HttpHandler#handle(HttpExchange) HttpHandler.handle()}. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - @Override - public void handle(HttpExchange httpExchange) throws IOException { - switch (HttpMethod.valueOf(httpExchange.getRequestMethod())) { - case OPTIONS: - preflight(httpExchange); - break; - case GET: - list(httpExchange); - break; - case DELETE: - delete(httpExchange); - break; - default: - break; - } } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/HttpHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/HttpHandler.java new file mode 100644 index 0000000000..bfce52b12d --- /dev/null +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/HttpHandler.java @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.eventmesh.runtime.admin.handler; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpRequest; + +/** + * Admin HttpHandler + */ +public interface HttpHandler { + + void handle(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception; +} diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/InsertWebHookConfigHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/InsertWebHookConfigHandler.java index c9f8ed46f3..ce01838a7e 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/InsertWebHookConfigHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/InsertWebHookConfigHandler.java @@ -19,39 +19,36 @@ import org.apache.eventmesh.common.Constants; import org.apache.eventmesh.common.utils.JsonUtils; -import org.apache.eventmesh.common.utils.NetUtils; import org.apache.eventmesh.runtime.common.EventHttpHandler; import org.apache.eventmesh.webhook.api.WebHookConfig; import org.apache.eventmesh.webhook.api.WebHookConfigOperation; -import java.io.IOException; -import java.io.OutputStream; +import java.util.Map; +import java.util.Objects; -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpRequest; import lombok.extern.slf4j.Slf4j; /** - * This class handles the HTTP requests of {@code /webhook/insertWebHookConfig} endpoint - * and adds a new WebHook configuration - * according to the given {@linkplain org.apache.eventmesh.webhook.api.WebHookConfig WebHookConfig}, - * if there isn't an existing duplicate configuration already. + * This class handles the HTTP requests of {@code /webhook/insertWebHookConfig} endpoint and adds a new WebHook configuration according to the given + * {@linkplain org.apache.eventmesh.webhook.api.WebHookConfig WebHookConfig}, if there isn't an existing duplicate configuration already. *

- * The implementation of - * {@linkplain org.apache.eventmesh.webhook.api.WebHookConfigOperation#insertWebHookConfig WebHookConfigOperation} - * interface depends on the {@code eventMesh.webHook.operationMode} configuration in {@code eventmesh.properties}. + * The implementation of {@linkplain org.apache.eventmesh.webhook.api.WebHookConfigOperation#insertWebHookConfig WebHookConfigOperation} interface + * depends on the {@code eventMesh.webHook.operationMode} configuration in {@code eventmesh.properties}. *

* For example, when {@code eventMesh.webHook.operationMode=file}, It calls the - * {@linkplain org.apache.eventmesh.webhook.admin.FileWebHookConfigOperation#insertWebHookConfig FileWebHookConfigOperation} - * method as implementation to save the WebHook configuration as a file; + * {@linkplain org.apache.eventmesh.webhook.admin.FileWebHookConfigOperation#insertWebHookConfig + * FileWebHookConfigOperation} method as implementation to save the WebHook configuration as a file; *

* When {@code eventMesh.webHook.operationMode=nacos}, It calls the - * {@linkplain org.apache.eventmesh.webhook.admin.NacosWebHookConfigOperation#insertWebHookConfig NacosWebHookConfigOperation} - * method as implementation to save the WebHook configuration into Nacos. + * {@linkplain org.apache.eventmesh.webhook.admin.NacosWebHookConfigOperation#insertWebHookConfig + * NacosWebHookConfigOperation} method as implementation to save the WebHook configuration into Nacos. *

- * The {@linkplain org.apache.eventmesh.webhook.receive.storage.HookConfigOperationManager#insertWebHookConfig HookConfigOperationManager} - * , another implementation of {@linkplain org.apache.eventmesh.webhook.api.WebHookConfigOperation WebHookConfigOperation} - * interface, is not used for this endpoint. + * The {@linkplain org.apache.eventmesh.webhook.receive.storage.HookConfigOperationManager#insertWebHookConfig HookConfigOperationManager} , another + * implementation of {@linkplain org.apache.eventmesh.webhook.api.WebHookConfigOperation WebHookConfigOperation} interface, is not used for this + * endpoint. * * @see AbstractHttpHandler */ @@ -64,7 +61,7 @@ public class InsertWebHookConfigHandler extends AbstractHttpHandler { private final WebHookConfigOperation operation; /** - * Constructs a new instance with the specified WebHook config operation and HTTP handler manager. + * Constructs a new instance with the specified WebHook config operation. * * @param operation the WebHookConfigOperation implementation used to insert the WebHook config */ @@ -76,24 +73,18 @@ public InsertWebHookConfigHandler(WebHookConfigOperation operation) { /** * Handles requests by adding a WebHook configuration. * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request + * @throws Exception if an I/O error occurs while handling the request */ - @Override - public void handle(HttpExchange httpExchange) throws IOException { - NetUtils.sendSuccessResponseHeaders(httpExchange); - // Resolve to WebHookConfig - String requestBody = NetUtils.parsePostBody(httpExchange); - WebHookConfig webHookConfig = JsonUtils.parseObject(requestBody, WebHookConfig.class); + @Override + public void handle(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + Map body = parseHttpRequestBody(httpRequest); + Objects.requireNonNull(body, "body can not be null"); + WebHookConfig webHookConfig = JsonUtils.mapToObject(body, WebHookConfig.class); + // Add the WebHookConfig if no existing duplicate configuration is found + Integer code = operation.insertWebHookConfig(webHookConfig); // operating result + String result = 1 == code ? "insertWebHookConfig Succeed!" : "insertWebHookConfig Failed!"; + write(ctx, result.getBytes(Constants.DEFAULT_CHARSET)); - try (OutputStream out = httpExchange.getResponseBody()) { - // Add the WebHookConfig if no existing duplicate configuration is found - Integer code = operation.insertWebHookConfig(webHookConfig); // operating result - String result = 1 == code ? "insertWebHookConfig Succeed!" : "insertWebHookConfig Failed!"; - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - } catch (Exception e) { - log.error("get WebHookConfigOperation implementation Failed.", e); - } } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/MetaHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/MetaHandler.java index 66c6deaa6b..77e995b7fd 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/MetaHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/MetaHandler.java @@ -19,24 +19,25 @@ import org.apache.eventmesh.api.meta.dto.EventMeshDataInfo; import org.apache.eventmesh.common.Constants; -import org.apache.eventmesh.common.enums.HttpMethod; import org.apache.eventmesh.common.utils.JsonUtils; -import org.apache.eventmesh.runtime.admin.response.Error; import org.apache.eventmesh.runtime.admin.response.GetRegistryResponse; import org.apache.eventmesh.runtime.common.EventHttpHandler; import org.apache.eventmesh.runtime.constants.EventMeshConstants; import org.apache.eventmesh.runtime.meta.MetaStorage; +import org.apache.eventmesh.runtime.util.HttpResponseUtils; import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.io.StringWriter; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Objects; -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; import lombok.extern.slf4j.Slf4j; @@ -63,96 +64,38 @@ public MetaHandler(MetaStorage eventMeshMetaStorage) { this.eventMeshMetaStorage = eventMeshMetaStorage; } - /** - * Handles the OPTIONS request first for {@code /registry}. - *

- * This method adds CORS (Cross-Origin Resource Sharing) response headers to the {@link HttpExchange} object and sends a 200 status code. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - void preflight(HttpExchange httpExchange) throws IOException { - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_ORIGIN, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_METHODS, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_AGE, EventMeshConstants.MAX_AGE); - httpExchange.sendResponseHeaders(200, 0); - OutputStream out = httpExchange.getResponseBody(); - out.close(); - } - - /** - * Handles the GET request for {@code /registry}. - *

- * This method retrieves a list of EventMesh clusters and returns it as a JSON response. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - void get(HttpExchange httpExchange) throws IOException { - httpExchange.getResponseHeaders().add(EventMeshConstants.CONTENT_TYPE, EventMeshConstants.APPLICATION_JSON); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_ORIGIN, "*"); - - try (OutputStream out = httpExchange.getResponseBody()) { - try { - List getRegistryResponseList = new ArrayList<>(); - List eventMeshDataInfos = eventMeshMetaStorage.findAllEventMeshInfo(); - for (EventMeshDataInfo eventMeshDataInfo : eventMeshDataInfos) { - GetRegistryResponse getRegistryResponse = new GetRegistryResponse( - eventMeshDataInfo.getEventMeshClusterName(), - eventMeshDataInfo.getEventMeshName(), - eventMeshDataInfo.getEndpoint(), - eventMeshDataInfo.getLastUpdateTimestamp(), - eventMeshDataInfo.getMetadata().toString()); - getRegistryResponseList.add(getRegistryResponse); - } - getRegistryResponseList.sort(Comparator.comparing(GetRegistryResponse::getEventMeshClusterName)); - - String result = JsonUtils.toJSONString(getRegistryResponseList); - httpExchange.sendResponseHeaders(200, Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET).length); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - } catch (NullPointerException e) { - // registry not initialized, return empty list - String result = JsonUtils.toJSONString(new ArrayList<>()); - httpExchange.sendResponseHeaders(200, Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET).length); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - } catch (Exception e) { - StringWriter writer = new StringWriter(); - PrintWriter printWriter = new PrintWriter(writer); - e.printStackTrace(printWriter); - printWriter.flush(); - String stackTrace = writer.toString(); - - Error error = new Error(e.toString(), stackTrace); - String result = JsonUtils.toJSONString(error); - httpExchange.sendResponseHeaders(500, Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET).length); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); + @Override + protected void get(HttpRequest httpRequest, ChannelHandlerContext ctx) throws IOException { + HttpHeaders responseHeaders = new DefaultHttpHeaders(); + responseHeaders.add(EventMeshConstants.CONTENT_TYPE, EventMeshConstants.APPLICATION_JSON); + responseHeaders.add(EventMeshConstants.HANDLER_ORIGIN, "*"); + try { + List getRegistryResponseList = new ArrayList<>(); + List eventMeshDataInfos = eventMeshMetaStorage.findAllEventMeshInfo(); + for (EventMeshDataInfo eventMeshDataInfo : eventMeshDataInfos) { + GetRegistryResponse getRegistryResponse = new GetRegistryResponse( + eventMeshDataInfo.getEventMeshClusterName(), + eventMeshDataInfo.getEventMeshName(), + eventMeshDataInfo.getEndpoint(), + eventMeshDataInfo.getLastUpdateTimestamp(), + eventMeshDataInfo.getMetadata().toString()); + getRegistryResponseList.add(getRegistryResponse); } - } catch (IOException e) { - log.warn("out close failed...", e); + getRegistryResponseList.sort(Comparator.comparing(GetRegistryResponse::getEventMeshClusterName)); + String result = JsonUtils.toJSONString(getRegistryResponseList); + HttpResponse httpResponse = + HttpResponseUtils.getHttpResponse(Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET), ctx, responseHeaders, + HttpResponseStatus.OK); + write(ctx, httpResponse); + } catch (NullPointerException e) { + // registry not initialized, return empty list + String result = JsonUtils.toJSONString(new ArrayList<>()); + HttpResponse httpResponse = + HttpResponseUtils.getHttpResponse(Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET), ctx, responseHeaders, + HttpResponseStatus.OK); + write(ctx, httpResponse); } - } - /** - * Handles the HTTP requests for {@code /registry}. - *

- * It delegates the handling to {@code preflight()} or {@code get()} methods based on the request method type (OPTIONS or GET). - *

- * This method is an implementation of {@linkplain com.sun.net.httpserver.HttpHandler#handle(HttpExchange) HttpHandler.handle()} - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - @Override - public void handle(HttpExchange httpExchange) throws IOException { - switch (HttpMethod.valueOf(httpExchange.getRequestMethod())) { - case OPTIONS: - preflight(httpExchange); - break; - case GET: - get(httpExchange); - break; - default: - break; - } } + } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/MetricsHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/MetricsHandler.java index f1065c2fe1..cf2e46ecc6 100755 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/MetricsHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/MetricsHandler.java @@ -18,33 +18,32 @@ package org.apache.eventmesh.runtime.admin.handler; import org.apache.eventmesh.common.Constants; -import org.apache.eventmesh.common.enums.HttpMethod; import org.apache.eventmesh.common.utils.JsonUtils; import org.apache.eventmesh.metrics.api.model.HttpSummaryMetrics; import org.apache.eventmesh.metrics.api.model.TcpSummaryMetrics; -import org.apache.eventmesh.runtime.admin.response.Error; import org.apache.eventmesh.runtime.admin.response.GetMetricsResponse; import org.apache.eventmesh.runtime.boot.EventMeshHTTPServer; import org.apache.eventmesh.runtime.boot.EventMeshTCPServer; import org.apache.eventmesh.runtime.common.EventHttpHandler; import org.apache.eventmesh.runtime.constants.EventMeshConstants; +import org.apache.eventmesh.runtime.util.HttpResponseUtils; import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.io.StringWriter; import java.util.Objects; -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; import lombok.extern.slf4j.Slf4j; /** - * This class handles the {@code /metrics} endpoint, - * corresponding to the {@code eventmesh-dashboard} path {@code /metrics}. + * This class handles the {@code /metrics} endpoint, corresponding to the {@code eventmesh-dashboard} path {@code /metrics}. *

- * This handler is responsible for retrieving summary information of metrics, - * including HTTP and TCP metrics. + * This handler is responsible for retrieving summary information of metrics, including HTTP and TCP metrics. * * @see AbstractHttpHandler */ @@ -57,10 +56,10 @@ public class MetricsHandler extends AbstractHttpHandler { private final TcpSummaryMetrics tcpSummaryMetrics; /** - * Constructs a new instance with the provided EventMesh server instance and HTTP handler manager. + * Constructs a new instance with the provided EventMesh server instance. * * @param eventMeshHTTPServer the HTTP server instance of EventMesh - * @param eventMeshTcpServer the TCP server instance of EventMesh + * @param eventMeshTcpServer the TCP server instance of EventMesh */ public MetricsHandler(EventMeshHTTPServer eventMeshHTTPServer, EventMeshTCPServer eventMeshTcpServer) { @@ -69,126 +68,57 @@ public MetricsHandler(EventMeshHTTPServer eventMeshHTTPServer, this.tcpSummaryMetrics = eventMeshTcpServer.getEventMeshTcpMonitor().getTcpSummaryMetrics(); } - /** - * Handles the OPTIONS request first for {@code /metrics}. - *

- * This method adds CORS (Cross-Origin Resource Sharing) response headers to - * the {@link HttpExchange} object and sends a 200 status code. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - void preflight(HttpExchange httpExchange) throws IOException { - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_ORIGIN, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_METHODS, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_HEADERS, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_AGE, EventMeshConstants.MAX_AGE); - httpExchange.sendResponseHeaders(200, 0); - OutputStream out = httpExchange.getResponseBody(); - out.close(); - } - - /** - * Handles the GET request for {@code /metrics}. - *

- * This method retrieves the EventMesh metrics summary and returns it as a JSON response. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - void get(HttpExchange httpExchange) throws IOException { - httpExchange.getResponseHeaders().add(EventMeshConstants.CONTENT_TYPE, EventMeshConstants.APPLICATION_JSON); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_ORIGIN, "*"); - - try (OutputStream out = httpExchange.getResponseBody()) { - try { - GetMetricsResponse getMetricsResponse = new GetMetricsResponse( - httpSummaryMetrics.maxHTTPTPS(), - httpSummaryMetrics.avgHTTPTPS(), - httpSummaryMetrics.maxHTTPCost(), - httpSummaryMetrics.avgHTTPCost(), - httpSummaryMetrics.avgHTTPBodyDecodeCost(), - httpSummaryMetrics.getHttpDiscard(), - httpSummaryMetrics.maxSendBatchMsgTPS(), - httpSummaryMetrics.avgSendBatchMsgTPS(), - httpSummaryMetrics.getSendBatchMsgNumSum(), - httpSummaryMetrics.getSendBatchMsgFailNumSum(), - httpSummaryMetrics.getSendBatchMsgFailRate(), - httpSummaryMetrics.getSendBatchMsgDiscardNumSum(), - httpSummaryMetrics.maxSendMsgTPS(), - httpSummaryMetrics.avgSendMsgTPS(), - httpSummaryMetrics.getSendMsgNumSum(), - httpSummaryMetrics.getSendMsgFailNumSum(), - httpSummaryMetrics.getSendMsgFailRate(), - httpSummaryMetrics.getReplyMsgNumSum(), - httpSummaryMetrics.getReplyMsgFailNumSum(), - httpSummaryMetrics.maxPushMsgTPS(), - httpSummaryMetrics.avgPushMsgTPS(), - httpSummaryMetrics.getHttpPushMsgNumSum(), - httpSummaryMetrics.getHttpPushFailNumSum(), - httpSummaryMetrics.getHttpPushMsgFailRate(), - httpSummaryMetrics.maxHTTPPushLatency(), - httpSummaryMetrics.avgHTTPPushLatency(), - httpSummaryMetrics.getBatchMsgQueueSize(), - httpSummaryMetrics.getSendMsgQueueSize(), - httpSummaryMetrics.getPushMsgQueueSize(), - httpSummaryMetrics.getHttpRetryQueueSize(), - httpSummaryMetrics.avgBatchSendMsgCost(), - httpSummaryMetrics.avgSendMsgCost(), - httpSummaryMetrics.avgReplyMsgCost(), - - tcpSummaryMetrics.getRetrySize(), - tcpSummaryMetrics.getClient2eventMeshTPS(), - tcpSummaryMetrics.getEventMesh2mqTPS(), - tcpSummaryMetrics.getMq2eventMeshTPS(), - tcpSummaryMetrics.getEventMesh2clientTPS(), - tcpSummaryMetrics.getAllTPS(), - tcpSummaryMetrics.getAllConnections(), - tcpSummaryMetrics.getSubTopicNum()); - String result = JsonUtils.toJSONString(getMetricsResponse); - byte[] bytes = Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET); - httpExchange.sendResponseHeaders(200, bytes.length); - out.write(bytes); - } catch (Exception e) { - try (StringWriter writer = new StringWriter()) { - try (PrintWriter printWriter = new PrintWriter(writer)) { - e.printStackTrace(printWriter); - } - - String stackTrace = writer.toString(); - - Error error = new Error(e.toString(), stackTrace); - String result = JsonUtils.toJSONString(error); - byte[] bytes = Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET); - httpExchange.sendResponseHeaders(500, bytes.length); - out.write(bytes); - } - } - } - } - - /** - * Handles the HTTP requests for {@code /metrics}. - *

- * It delegates the handling to {@code preflight()} or {@code get()} methods - * based on the request method type (OPTIONS or GET). - *

- * This method is an implementation of {@linkplain com.sun.net.httpserver.HttpHandler#handle(HttpExchange) HttpHandler.handle()} - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ @Override - public void handle(HttpExchange httpExchange) throws IOException { - switch (HttpMethod.valueOf(httpExchange.getRequestMethod())) { - case OPTIONS: - preflight(httpExchange); - break; - case GET: - get(httpExchange); - break; - default: - break; - } + protected void get(HttpRequest httpRequest, ChannelHandlerContext ctx) throws IOException { + HttpHeaders responseHeaders = new DefaultHttpHeaders(); + responseHeaders.add(EventMeshConstants.CONTENT_TYPE, EventMeshConstants.APPLICATION_JSON); + responseHeaders.add(EventMeshConstants.HANDLER_ORIGIN, "*"); + GetMetricsResponse getMetricsResponse = new GetMetricsResponse( + httpSummaryMetrics.maxHTTPTPS(), + httpSummaryMetrics.avgHTTPTPS(), + httpSummaryMetrics.maxHTTPCost(), + httpSummaryMetrics.avgHTTPCost(), + httpSummaryMetrics.avgHTTPBodyDecodeCost(), + httpSummaryMetrics.getHttpDiscard(), + httpSummaryMetrics.maxSendBatchMsgTPS(), + httpSummaryMetrics.avgSendBatchMsgTPS(), + httpSummaryMetrics.getSendBatchMsgNumSum(), + httpSummaryMetrics.getSendBatchMsgFailNumSum(), + httpSummaryMetrics.getSendBatchMsgFailRate(), + httpSummaryMetrics.getSendBatchMsgDiscardNumSum(), + httpSummaryMetrics.maxSendMsgTPS(), + httpSummaryMetrics.avgSendMsgTPS(), + httpSummaryMetrics.getSendMsgNumSum(), + httpSummaryMetrics.getSendMsgFailNumSum(), + httpSummaryMetrics.getSendMsgFailRate(), + httpSummaryMetrics.getReplyMsgNumSum(), + httpSummaryMetrics.getReplyMsgFailNumSum(), + httpSummaryMetrics.maxPushMsgTPS(), + httpSummaryMetrics.avgPushMsgTPS(), + httpSummaryMetrics.getHttpPushMsgNumSum(), + httpSummaryMetrics.getHttpPushFailNumSum(), + httpSummaryMetrics.getHttpPushMsgFailRate(), + httpSummaryMetrics.maxHTTPPushLatency(), + httpSummaryMetrics.avgHTTPPushLatency(), + httpSummaryMetrics.getBatchMsgQueueSize(), + httpSummaryMetrics.getSendMsgQueueSize(), + httpSummaryMetrics.getPushMsgQueueSize(), + httpSummaryMetrics.getHttpRetryQueueSize(), + httpSummaryMetrics.avgBatchSendMsgCost(), + httpSummaryMetrics.avgSendMsgCost(), + httpSummaryMetrics.avgReplyMsgCost(), + tcpSummaryMetrics.getRetrySize(), + tcpSummaryMetrics.getClient2eventMeshTPS(), + tcpSummaryMetrics.getEventMesh2mqTPS(), + tcpSummaryMetrics.getMq2eventMeshTPS(), + tcpSummaryMetrics.getEventMesh2clientTPS(), + tcpSummaryMetrics.getAllTPS(), + tcpSummaryMetrics.getAllConnections(), + tcpSummaryMetrics.getSubTopicNum()); + String result = JsonUtils.toJSONString(getMetricsResponse); + HttpResponse httpResponse = + HttpResponseUtils.getHttpResponse(Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET), ctx, responseHeaders, + HttpResponseStatus.OK); + write(ctx, httpResponse); } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/QueryRecommendEventMeshHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/QueryRecommendEventMeshHandler.java index f8f7d166a5..1688d387db 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/QueryRecommendEventMeshHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/QueryRecommendEventMeshHandler.java @@ -27,11 +27,11 @@ import org.apache.commons.lang3.StringUtils; -import java.io.IOException; -import java.io.OutputStream; +import java.net.URI; import java.util.Map; -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpRequest; import lombok.extern.slf4j.Slf4j; @@ -56,7 +56,7 @@ public class QueryRecommendEventMeshHandler extends AbstractHttpHandler { private final EventMeshTCPServer eventMeshTCPServer; /** - * Constructs a new instance with the provided server instance and HTTP handler manager. + * Constructs a new instance with the provided server instance. * * @param eventMeshTCPServer the TCP server instance of EventMesh */ @@ -65,41 +65,29 @@ public QueryRecommendEventMeshHandler(EventMeshTCPServer eventMeshTCPServer) { this.eventMeshTCPServer = eventMeshTCPServer; } - /** - * Handles requests by calculating a recommended EventMesh server node. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ @Override - public void handle(HttpExchange httpExchange) throws IOException { + public void handle(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { String result = ""; - try (OutputStream out = httpExchange.getResponseBody()) { - if (!eventMeshTCPServer.getEventMeshTCPConfiguration().isEventMeshServerMetaStorageEnable()) { - throw new Exception("registry enable config is false, not support"); - } - String queryString = httpExchange.getRequestURI().getQuery(); - Map queryStringInfo = NetUtils.formData2Dic(queryString); - // Extract parameters from the query string - String group = queryStringInfo.get(EventMeshConstants.MANAGE_GROUP); - String purpose = queryStringInfo.get(EventMeshConstants.MANAGE_PURPOSE); - // Check the validity of the parameters - if (StringUtils.isBlank(group) || StringUtils.isBlank(purpose)) { - NetUtils.sendSuccessResponseHeaders(httpExchange); - result = "params illegal!"; - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - return; - } - - EventMeshRecommendStrategy eventMeshRecommendStrategy = new EventMeshRecommendImpl(eventMeshTCPServer); - // Calculate the recommended EventMesh node according to the given group and purpose - String recommendEventMeshResult = eventMeshRecommendStrategy.calculateRecommendEventMesh(group, purpose); - result = (recommendEventMeshResult == null) ? "null" : recommendEventMeshResult; - log.info("recommend eventmesh:{},group:{},purpose:{}", result, group, purpose); - NetUtils.sendSuccessResponseHeaders(httpExchange); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - } catch (Exception e) { - log.error("QueryRecommendEventMeshHandler fail...", e); + if (!eventMeshTCPServer.getEventMeshTCPConfiguration().isEventMeshServerMetaStorageEnable()) { + throw new Exception("registry enable config is false, not support"); + } + String queryString = URI.create(httpRequest.uri()).getQuery(); + Map queryStringInfo = NetUtils.formData2Dic(queryString); + // Extract parameters from the query string + String group = queryStringInfo.get(EventMeshConstants.MANAGE_GROUP); + String purpose = queryStringInfo.get(EventMeshConstants.MANAGE_PURPOSE); + // Check the validity of the parameters + if (StringUtils.isBlank(group) || StringUtils.isBlank(purpose)) { + result = "params illegal!"; + write(ctx, result.getBytes(Constants.DEFAULT_CHARSET)); + return; } + + EventMeshRecommendStrategy eventMeshRecommendStrategy = new EventMeshRecommendImpl(eventMeshTCPServer); + // Calculate the recommended EventMesh node according to the given group and purpose + String recommendEventMeshResult = eventMeshRecommendStrategy.calculateRecommendEventMesh(group, purpose); + result = (recommendEventMeshResult == null) ? "null" : recommendEventMeshResult; + log.info("recommend eventmesh:{},group:{},purpose:{}", result, group, purpose); + write(ctx, result.getBytes(Constants.DEFAULT_CHARSET)); } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/QueryWebHookConfigByIdHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/QueryWebHookConfigByIdHandler.java index 322d095973..58a2438bea 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/QueryWebHookConfigByIdHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/QueryWebHookConfigByIdHandler.java @@ -17,21 +17,23 @@ package org.apache.eventmesh.runtime.admin.handler; -import static org.apache.eventmesh.runtime.constants.EventMeshConstants.APPLICATION_JSON; -import static org.apache.eventmesh.runtime.constants.EventMeshConstants.CONTENT_TYPE; - import org.apache.eventmesh.common.Constants; import org.apache.eventmesh.common.utils.JsonUtils; -import org.apache.eventmesh.common.utils.NetUtils; import org.apache.eventmesh.runtime.common.EventHttpHandler; +import org.apache.eventmesh.runtime.constants.EventMeshConstants; +import org.apache.eventmesh.runtime.util.HttpResponseUtils; import org.apache.eventmesh.webhook.api.WebHookConfig; import org.apache.eventmesh.webhook.api.WebHookConfigOperation; -import java.io.IOException; -import java.io.OutputStream; +import java.util.Map; import java.util.Objects; -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; import lombok.extern.slf4j.Slf4j; @@ -72,7 +74,7 @@ public class QueryWebHookConfigByIdHandler extends AbstractHttpHandler { private final WebHookConfigOperation operation; /** - * Constructs a new instance with the specified WebHook config operation and HTTP handler manager. + * Constructs a new instance with the specified WebHook config operation. * * @param operation the WebHookConfigOperation implementation used to query the WebHook config */ @@ -81,27 +83,23 @@ public QueryWebHookConfigByIdHandler(WebHookConfigOperation operation) { this.operation = operation; } - /** - * Handles requests by retrieving a WebHook configuration. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ @Override - public void handle(HttpExchange httpExchange) throws IOException { - httpExchange.getResponseHeaders().add(CONTENT_TYPE, APPLICATION_JSON); - NetUtils.sendSuccessResponseHeaders(httpExchange); - + public void handle(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + HttpHeaders responseHeaders = new DefaultHttpHeaders(); + responseHeaders.add(EventMeshConstants.CONTENT_TYPE, EventMeshConstants.APPLICATION_JSON); + responseHeaders.add(EventMeshConstants.HANDLER_ORIGIN, "*"); // Resolve to WebHookConfig - String requestBody = NetUtils.parsePostBody(httpExchange); - WebHookConfig webHookConfig = JsonUtils.parseObject(requestBody, WebHookConfig.class); - - try (OutputStream out = httpExchange.getResponseBody()) { + Map body = parseHttpRequestBody(httpRequest); + if (!Objects.isNull(body)) { + WebHookConfig webHookConfig = JsonUtils.mapToObject(body, WebHookConfig.class); // Retrieve the WebHookConfig by callback path WebHookConfig result = operation.queryWebHookConfigById(webHookConfig); // operating result - out.write(Objects.requireNonNull(JsonUtils.toJSONString(result)).getBytes(Constants.DEFAULT_CHARSET)); - } catch (Exception e) { - log.error("get WebHookConfigOperation implementation Failed.", e); + String json = JsonUtils.toJSONString(result); + HttpResponse httpResponse = + HttpResponseUtils.getHttpResponse(Objects.requireNonNull(json).getBytes(Constants.DEFAULT_CHARSET), ctx, responseHeaders, + HttpResponseStatus.OK); + write(ctx, httpResponse); } + throw new Exception(); } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/QueryWebHookConfigByManufacturerHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/QueryWebHookConfigByManufacturerHandler.java index bd50501514..9b484f6a1f 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/QueryWebHookConfigByManufacturerHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/QueryWebHookConfigByManufacturerHandler.java @@ -17,23 +17,24 @@ package org.apache.eventmesh.runtime.admin.handler; -import static org.apache.eventmesh.runtime.constants.EventMeshConstants.APPLICATION_JSON; -import static org.apache.eventmesh.runtime.constants.EventMeshConstants.CONTENT_TYPE; - import org.apache.eventmesh.common.Constants; import org.apache.eventmesh.common.utils.JsonUtils; -import org.apache.eventmesh.common.utils.NetUtils; import org.apache.eventmesh.runtime.common.EventHttpHandler; +import org.apache.eventmesh.runtime.constants.EventMeshConstants; +import org.apache.eventmesh.runtime.util.HttpResponseUtils; import org.apache.eventmesh.webhook.api.WebHookConfig; import org.apache.eventmesh.webhook.api.WebHookConfigOperation; -import java.io.IOException; -import java.io.OutputStream; import java.util.List; +import java.util.Map; import java.util.Objects; -import com.fasterxml.jackson.databind.JsonNode; -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; import lombok.extern.slf4j.Slf4j; @@ -44,12 +45,13 @@ * The implementation of {@linkplain org.apache.eventmesh.webhook.api.WebHookConfigOperation#queryWebHookConfigByManufacturer WebHookConfigOperation} * interface depends on the {@code eventMesh.webHook.operationMode} configuration in {@code eventmesh.properties}. *

- * For example, when {@code eventMesh.webHook.operationMode=file} - * It calls the {@linkplain org.apache.eventmesh.webhook.admin.FileWebHookConfigOperation#queryWebHookConfigByManufacturer + * For example, when {@code eventMesh.webHook.operationMode=file} It calls the + * {@linkplain org.apache.eventmesh.webhook.admin.FileWebHookConfigOperation + * #queryWebHookConfigByManufacturer * FileWebHookConfigOperation} method as implementation to retrieve the WebHook configuration from a file; *

- * When {@code eventMesh.webHook.operationMode=nacos} - * It calls the {@linkplain org.apache.eventmesh.webhook.admin.NacosWebHookConfigOperation#queryWebHookConfigByManufacturer + * When {@code eventMesh.webHook.operationMode=nacos} It calls the {@linkplain org.apache.eventmesh.webhook.admin.NacosWebHookConfigOperation + * #queryWebHookConfigByManufacturer * NacosWebHookConfigOperation} method as implementation to retrieve the WebHook configuration from Nacos. *

* The {@linkplain org.apache.eventmesh.webhook.receive.storage.HookConfigOperationManager#queryWebHookConfigByManufacturer @@ -67,7 +69,7 @@ public class QueryWebHookConfigByManufacturerHandler extends AbstractHttpHandler private final transient WebHookConfigOperation operation; /** - * Constructs a new instance with the specified WebHook config operation and HTTP handler manager. + * Constructs a new instance with the specified WebHook config operation. * * @param operation the WebHookConfigOperation implementation used to query the WebHook config */ @@ -78,33 +80,24 @@ public QueryWebHookConfigByManufacturerHandler(WebHookConfigOperation operation) } - /** - * Handles requests by retrieving a list of WebHook configurations. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ @Override - public void handle(HttpExchange httpExchange) throws IOException { - Objects.requireNonNull(httpExchange, "httpExchange can not be null"); - - httpExchange.getResponseHeaders().add(CONTENT_TYPE, APPLICATION_JSON); - NetUtils.sendSuccessResponseHeaders(httpExchange); - + public void handle(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + HttpHeaders responseHeaders = new DefaultHttpHeaders(); + responseHeaders.add(EventMeshConstants.CONTENT_TYPE, EventMeshConstants.APPLICATION_JSON); + responseHeaders.add(EventMeshConstants.HANDLER_ORIGIN, "*"); // Resolve to WebHookConfig - JsonNode node = JsonUtils.getJsonNode(NetUtils.parsePostBody(httpExchange)); - Objects.requireNonNull(node, "JsonNode can not be null"); - - WebHookConfig webHookConfig = JsonUtils.parseObject(node.toString(), WebHookConfig.class); - Integer pageNum = Integer.valueOf(node.get("pageNum").toString()); - Integer pageSize = Integer.valueOf(node.get("pageSize").toString()); - - try (OutputStream out = httpExchange.getResponseBody()) { - // Retrieve the WebHookConfig list by manufacturer name - List result = operation.queryWebHookConfigByManufacturer(webHookConfig, pageNum, pageSize); // operating result - out.write(Objects.requireNonNull(JsonUtils.toJSONString(result)).getBytes(Constants.DEFAULT_CHARSET)); - } catch (Exception e) { - log.error("get WebHookConfigOperation implementation Failed.", e); - } + Map body = parseHttpRequestBody(httpRequest); + Objects.requireNonNull(body, "body can not be null"); + WebHookConfig webHookConfig = JsonUtils.mapToObject(body, WebHookConfig.class); + Integer pageNum = Integer.valueOf(body.get("pageNum").toString()); + Integer pageSize = Integer.valueOf(body.get("pageSize").toString()); + + // Retrieve the WebHookConfig list by manufacturer name + List listWebHookConfig = operation.queryWebHookConfigByManufacturer(webHookConfig, pageNum, pageSize); // operating result + String result = JsonUtils.toJSONString(listWebHookConfig); + HttpResponse httpResponse = + HttpResponseUtils.getHttpResponse(Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET), ctx, responseHeaders, + HttpResponseStatus.OK); + write(ctx, httpResponse); } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RedirectClientByIpPortHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RedirectClientByIpPortHandler.java index 3d04d96ae5..d2805112a4 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RedirectClientByIpPortHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RedirectClientByIpPortHandler.java @@ -28,13 +28,13 @@ import org.apache.commons.lang3.StringUtils; -import java.io.IOException; -import java.io.OutputStream; import java.net.InetSocketAddress; +import java.net.URI; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpRequest; import lombok.extern.slf4j.Slf4j; @@ -63,7 +63,7 @@ public class RedirectClientByIpPortHandler extends AbstractHttpHandler { private final EventMeshTCPServer eventMeshTCPServer; /** - * Constructs a new instance with the provided server instance and HTTP handler manager. + * Constructs a new instance with the provided server instance. * * @param eventMeshTCPServer the TCP server instance of EventMesh */ @@ -72,76 +72,62 @@ public RedirectClientByIpPortHandler(EventMeshTCPServer eventMeshTCPServer) { this.eventMeshTCPServer = eventMeshTCPServer; } - /** - * Handles requests by redirecting matching clients to a target EventMesh server node. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ @Override - public void handle(HttpExchange httpExchange) throws IOException { + public void handle(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { String result = ""; - try (OutputStream out = httpExchange.getResponseBody()) { - String queryString = httpExchange.getRequestURI().getQuery(); - Map queryStringInfo = NetUtils.formData2Dic(queryString); - // Extract parameters from the query string - String ip = queryStringInfo.get(EventMeshConstants.MANAGE_IP); - String port = queryStringInfo.get(EventMeshConstants.MANAGE_PORT); - String destEventMeshIp = queryStringInfo.get(EventMeshConstants.MANAGE_DEST_IP); - String destEventMeshPort = queryStringInfo.get(EventMeshConstants.MANAGE_DEST_PORT); + String queryString = URI.create(httpRequest.uri()).getQuery(); + Map queryStringInfo = NetUtils.formData2Dic(queryString); + // Extract parameters from the query string + String ip = queryStringInfo.get(EventMeshConstants.MANAGE_IP); + String port = queryStringInfo.get(EventMeshConstants.MANAGE_PORT); + String destEventMeshIp = queryStringInfo.get(EventMeshConstants.MANAGE_DEST_IP); + String destEventMeshPort = queryStringInfo.get(EventMeshConstants.MANAGE_DEST_PORT); - // Check the validity of the parameters - if (StringUtils.isBlank(ip) || !StringUtils.isNumeric(port) - || StringUtils.isBlank(destEventMeshIp) || StringUtils.isBlank(destEventMeshPort) - || !StringUtils.isNumeric(destEventMeshPort)) { - NetUtils.sendSuccessResponseHeaders(httpExchange); - result = "params illegal!"; - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - return; - } - log.info("redirectClientByIpPort in admin,ip:{},port:{},destIp:{},destPort:{}====================", ip, - port, destEventMeshIp, destEventMeshPort); - // Retrieve the mapping between Sessions and their corresponding client address - ClientSessionGroupMapping clientSessionGroupMapping = eventMeshTCPServer.getClientSessionGroupMapping(); - ConcurrentHashMap sessionMap = clientSessionGroupMapping.getSessionMap(); - StringBuilder redirectResult = new StringBuilder(); - try { - if (!sessionMap.isEmpty()) { - // Iterate through the sessionMap to find matching sessions where the client's IP and port match the given ones - for (Session session : sessionMap.values()) { - // For each matching session found, redirect the client - // to the new EventMesh node specified by given EventMesh IP and port. - if (session.getClient().getHost().equals(ip) && String.valueOf( - session.getClient().getPort()).equals(port)) { - redirectResult.append("|"); - redirectResult.append(EventMeshTcp2Client.redirectClient2NewEventMesh(eventMeshTCPServer.getTcpThreadPoolGroup(), - destEventMeshIp, Integer.parseInt(destEventMeshPort), - session, clientSessionGroupMapping)); - } + // Check the validity of the parameters + if (StringUtils.isBlank(ip) || !StringUtils.isNumeric(port) + || StringUtils.isBlank(destEventMeshIp) || StringUtils.isBlank(destEventMeshPort) + || !StringUtils.isNumeric(destEventMeshPort)) { + result = "params illegal!"; + write(ctx, result.getBytes(Constants.DEFAULT_CHARSET)); + return; + } + log.info("redirectClientByIpPort in admin,ip:{},port:{},destIp:{},destPort:{}====================", ip, + port, destEventMeshIp, destEventMeshPort); + // Retrieve the mapping between Sessions and their corresponding client address + ClientSessionGroupMapping clientSessionGroupMapping = eventMeshTCPServer.getClientSessionGroupMapping(); + ConcurrentHashMap sessionMap = clientSessionGroupMapping.getSessionMap(); + StringBuilder redirectResult = new StringBuilder(); + try { + if (!sessionMap.isEmpty()) { + // Iterate through the sessionMap to find matching sessions where the client's IP and port match the given ones + for (Session session : sessionMap.values()) { + // For each matching session found, redirect the client + // to the new EventMesh node specified by given EventMesh IP and port. + if (session.getClient().getHost().equals(ip) && String.valueOf( + session.getClient().getPort()).equals(port)) { + redirectResult.append("|"); + redirectResult.append(EventMeshTcp2Client.redirectClient2NewEventMesh(eventMeshTCPServer.getTcpThreadPoolGroup(), + destEventMeshIp, Integer.parseInt(destEventMeshPort), + session, clientSessionGroupMapping)); } } - } catch (Exception e) { - log.error("clientManage|redirectClientByIpPort|fail|ip={}|port={}|destEventMeshIp" - + - "={}|destEventMeshPort={}", ip, port, destEventMeshIp, destEventMeshPort, e); - result = String.format("redirectClientByIpPort fail! sessionMap size {%d}, {clientIp=%s clientPort=%s " - + - "destEventMeshIp=%s destEventMeshPort=%s}, result {%s}, errorMsg : %s", - sessionMap.size(), ip, port, destEventMeshIp, destEventMeshPort, redirectResult, e.getMessage()); - NetUtils.sendSuccessResponseHeaders(httpExchange); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - return; } - // Serialize the result of redirection into output stream - result = String.format("redirectClientByIpPort success! sessionMap size {%d}, {ip=%s port=%s " - + - "destEventMeshIp=%s destEventMeshPort=%s}, result {%s} ", - sessionMap.size(), ip, port, destEventMeshIp, destEventMeshPort, redirectResult); - NetUtils.sendSuccessResponseHeaders(httpExchange); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); } catch (Exception e) { - log.error("redirectClientByIpPort fail...", e); + log.error("clientManage|redirectClientByIpPort|fail|ip={}|port={}|destEventMeshIp" + + + "={}|destEventMeshPort={}", ip, port, destEventMeshIp, destEventMeshPort, e); + result = String.format("redirectClientByIpPort fail! sessionMap size {%d}, {clientIp=%s clientPort=%s " + + + "destEventMeshIp=%s destEventMeshPort=%s}, result {%s}, errorMsg : %s", + sessionMap.size(), ip, port, destEventMeshIp, destEventMeshPort, redirectResult, e.getMessage()); + write(ctx, result.getBytes(Constants.DEFAULT_CHARSET)); + return; } - + // Serialize the result of redirection into output stream + result = String.format("redirectClientByIpPort success! sessionMap size {%d}, {ip=%s port=%s " + + + "destEventMeshIp=%s destEventMeshPort=%s}, result {%s} ", + sessionMap.size(), ip, port, destEventMeshIp, destEventMeshPort, redirectResult); + write(ctx, result.getBytes(Constants.DEFAULT_CHARSET)); } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RedirectClientByPathHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RedirectClientByPathHandler.java index 2badb64cb2..fc5ad6f2d9 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RedirectClientByPathHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RedirectClientByPathHandler.java @@ -28,13 +28,13 @@ import org.apache.commons.lang3.StringUtils; -import java.io.IOException; -import java.io.OutputStream; import java.net.InetSocketAddress; +import java.net.URI; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpRequest; import lombok.extern.slf4j.Slf4j; @@ -62,7 +62,7 @@ public class RedirectClientByPathHandler extends AbstractHttpHandler { private final EventMeshTCPServer eventMeshTCPServer; /** - * Constructs a new instance with the provided server instance and HTTP handler manager. + * Constructs a new instance with the provided server instance. * * @param eventMeshTCPServer the TCP server instance of EventMesh */ @@ -71,72 +71,60 @@ public RedirectClientByPathHandler(EventMeshTCPServer eventMeshTCPServer) { this.eventMeshTCPServer = eventMeshTCPServer; } - /** - * Handles requests by redirecting matching clients to a target EventMesh server node. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ @Override - public void handle(HttpExchange httpExchange) throws IOException { + public void handle(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { String result = ""; - try (OutputStream out = httpExchange.getResponseBody()) { - String queryString = httpExchange.getRequestURI().getQuery(); - Map queryStringInfo = NetUtils.formData2Dic(queryString); - // Extract parameters from the query string - String path = queryStringInfo.get(EventMeshConstants.MANAGE_PATH); - String destEventMeshIp = queryStringInfo.get(EventMeshConstants.MANAGE_DEST_IP); - String destEventMeshPort = queryStringInfo.get(EventMeshConstants.MANAGE_DEST_PORT); + String queryString = URI.create(httpRequest.uri()).getQuery(); + Map queryStringInfo = NetUtils.formData2Dic(queryString); + // Extract parameters from the query string + String path = queryStringInfo.get(EventMeshConstants.MANAGE_PATH); + String destEventMeshIp = queryStringInfo.get(EventMeshConstants.MANAGE_DEST_IP); + String destEventMeshPort = queryStringInfo.get(EventMeshConstants.MANAGE_DEST_PORT); - // Check the validity of the parameters - if (StringUtils.isBlank(path) || StringUtils.isBlank(destEventMeshIp) - || StringUtils.isBlank(destEventMeshPort) - || !StringUtils.isNumeric(destEventMeshPort)) { - NetUtils.sendSuccessResponseHeaders(httpExchange); - result = "params illegal!"; - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - return; - } - log.info("redirectClientByPath in admin,path:{},destIp:{},destPort:{}====================", path, - destEventMeshIp, destEventMeshPort); - // Retrieve the mapping between Sessions and their corresponding client address - ClientSessionGroupMapping clientSessionGroupMapping = eventMeshTCPServer.getClientSessionGroupMapping(); - ConcurrentHashMap sessionMap = clientSessionGroupMapping.getSessionMap(); - StringBuilder redirectResult = new StringBuilder(); - try { - if (!sessionMap.isEmpty()) { - // Iterate through the sessionMap to find matching sessions where the client's path matches the given param - for (Session session : sessionMap.values()) { - // For each matching session found, redirect the client - // to the new EventMesh node specified by given EventMesh IP and port. - if (session.getClient().getPath().contains(path)) { - redirectResult.append("|"); - redirectResult.append(EventMeshTcp2Client.redirectClient2NewEventMesh(eventMeshTCPServer.getTcpThreadPoolGroup(), - destEventMeshIp, Integer.parseInt(destEventMeshPort), - session, clientSessionGroupMapping)); - } + // Check the validity of the parameters + if (StringUtils.isBlank(path) || StringUtils.isBlank(destEventMeshIp) + || StringUtils.isBlank(destEventMeshPort) + || !StringUtils.isNumeric(destEventMeshPort)) { + result = "params illegal!"; + write(ctx, result.getBytes(Constants.DEFAULT_CHARSET)); + return; + } + log.info("redirectClientByPath in admin,path:{},destIp:{},destPort:{}====================", path, + destEventMeshIp, destEventMeshPort); + // Retrieve the mapping between Sessions and their corresponding client address + ClientSessionGroupMapping clientSessionGroupMapping = eventMeshTCPServer.getClientSessionGroupMapping(); + ConcurrentHashMap sessionMap = clientSessionGroupMapping.getSessionMap(); + StringBuilder redirectResult = new StringBuilder(); + try { + if (!sessionMap.isEmpty()) { + // Iterate through the sessionMap to find matching sessions where the client's path matches the given param + for (Session session : sessionMap.values()) { + // For each matching session found, redirect the client + // to the new EventMesh node specified by given EventMesh IP and port. + if (session.getClient().getPath().contains(path)) { + redirectResult.append("|"); + redirectResult.append(EventMeshTcp2Client.redirectClient2NewEventMesh(eventMeshTCPServer.getTcpThreadPoolGroup(), + destEventMeshIp, Integer.parseInt(destEventMeshPort), + session, clientSessionGroupMapping)); } } - } catch (Exception e) { - log.error("clientManage|redirectClientByPath|fail|path={}|destEventMeshIp" - + - "={}|destEventMeshPort={}", path, destEventMeshIp, destEventMeshPort, e); - result = String.format("redirectClientByPath fail! sessionMap size {%d}, {path=%s " - + - "destEventMeshIp=%s destEventMeshPort=%s}, result {%s}, errorMsg : %s", - sessionMap.size(), path, destEventMeshIp, destEventMeshPort, redirectResult, e.getMessage()); - NetUtils.sendSuccessResponseHeaders(httpExchange); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - return; } - result = String.format("redirectClientByPath success! sessionMap size {%d}, {path=%s " - + - "destEventMeshIp=%s destEventMeshPort=%s}, result {%s} ", - sessionMap.size(), path, destEventMeshIp, destEventMeshPort, redirectResult); - NetUtils.sendSuccessResponseHeaders(httpExchange); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); } catch (Exception e) { - log.error("redirectClientByPath fail...", e); + log.error("clientManage|redirectClientByPath|fail|path={}|destEventMeshIp" + + + "={}|destEventMeshPort={}", path, destEventMeshIp, destEventMeshPort, e); + result = String.format("redirectClientByPath fail! sessionMap size {%d}, {path=%s " + + + "destEventMeshIp=%s destEventMeshPort=%s}, result {%s}, errorMsg : %s", + sessionMap.size(), path, destEventMeshIp, destEventMeshPort, redirectResult, e.getMessage()); + write(ctx, result.getBytes(Constants.DEFAULT_CHARSET)); + return; } + result = String.format("redirectClientByPath success! sessionMap size {%d}, {path=%s " + + + "destEventMeshIp=%s destEventMeshPort=%s}, result {%s} ", + sessionMap.size(), path, destEventMeshIp, destEventMeshPort, redirectResult); + write(ctx, result.getBytes(Constants.DEFAULT_CHARSET)); + } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RedirectClientBySubSystemHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RedirectClientBySubSystemHandler.java index 6e7cd25906..c7b959ece3 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RedirectClientBySubSystemHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RedirectClientBySubSystemHandler.java @@ -28,21 +28,19 @@ import org.apache.commons.lang3.StringUtils; -import java.io.IOException; -import java.io.OutputStream; import java.net.InetSocketAddress; +import java.net.URI; import java.util.Map; -import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpRequest; import lombok.extern.slf4j.Slf4j; /** - * This class handles the HTTP requests of {@code /clientManage/redirectClientBySubSystem} endpoint, - * which is used to redirect matching clients to a target EventMesh server node - * based on the provided client subsystem id in a Data Communication Network (DCN). + * This class handles the HTTP requests of {@code /clientManage/redirectClientBySubSystem} endpoint, which is used to redirect matching clients to a + * target EventMesh server node based on the provided client subsystem id in a Data Communication Network (DCN). *

* The request must specify the client's subsystem id and target EventMesh node's IP and port. *

@@ -64,79 +62,64 @@ public class RedirectClientBySubSystemHandler extends AbstractHttpHandler { private final transient EventMeshTCPServer eventMeshTCPServer; /** - * Constructs a new instance with the provided server instance and HTTP handler manager. + * Constructs a new instance with the provided server instance. * - * @param eventMeshTCPServer the TCP server instance of EventMesh + * @param eventMeshTCPServer the TCP server instance of EventMesh */ public RedirectClientBySubSystemHandler(final EventMeshTCPServer eventMeshTCPServer) { super(); this.eventMeshTCPServer = eventMeshTCPServer; } - /** - * Handles requests by redirecting matching clients to a target EventMesh server node. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ @Override - public void handle(final HttpExchange httpExchange) throws IOException { - Objects.requireNonNull(httpExchange, "httpExchange can not be null"); + public void handle(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + final Map queryStringInfo = NetUtils.formData2Dic(URI.create(httpRequest.uri()).getQuery()); + // Extract parameters from the query string + final String subSystem = queryStringInfo.get(EventMeshConstants.MANAGE_SUBSYSTEM); + final String destEventMeshIp = queryStringInfo.get(EventMeshConstants.MANAGE_DEST_IP); + final String destEventMeshPort = queryStringInfo.get(EventMeshConstants.MANAGE_DEST_PORT); - try (OutputStream out = httpExchange.getResponseBody()) { - final Map queryStringInfo = NetUtils.formData2Dic(httpExchange.getRequestURI().getQuery()); - // Extract parameters from the query string - final String subSystem = queryStringInfo.get(EventMeshConstants.MANAGE_SUBSYSTEM); - final String destEventMeshIp = queryStringInfo.get(EventMeshConstants.MANAGE_DEST_IP); - final String destEventMeshPort = queryStringInfo.get(EventMeshConstants.MANAGE_DEST_PORT); - - // Check the validity of the parameters - if (!StringUtils.isNumeric(subSystem) - || StringUtils.isBlank(destEventMeshIp) || StringUtils.isBlank(destEventMeshPort) - || !StringUtils.isNumeric(destEventMeshPort)) { - NetUtils.sendSuccessResponseHeaders(httpExchange); - out.write("params illegal!".getBytes(Constants.DEFAULT_CHARSET)); - return; - } - log.info("redirectClientBySubSystem in admin,subsys:{},destIp:{},destPort:{}====================", - subSystem, destEventMeshIp, destEventMeshPort); + // Check the validity of the parameters + if (!StringUtils.isNumeric(subSystem) + || StringUtils.isBlank(destEventMeshIp) || StringUtils.isBlank(destEventMeshPort) + || !StringUtils.isNumeric(destEventMeshPort)) { + write(ctx, "params illegal!".getBytes(Constants.DEFAULT_CHARSET)); + return; + } + log.info("redirectClientBySubSystem in admin,subsys:{},destIp:{},destPort:{}====================", + subSystem, destEventMeshIp, destEventMeshPort); - // Retrieve the mapping between Sessions and their corresponding client address - final ClientSessionGroupMapping clientSessionGroupMapping = eventMeshTCPServer.getClientSessionGroupMapping(); - final ConcurrentHashMap sessionMap = clientSessionGroupMapping.getSessionMap(); - final StringBuilder redirectResult = new StringBuilder(); - try { - if (!sessionMap.isEmpty()) { - // Iterate through the sessionMap to find matching sessions where the client's subsystem id matches the given param - for (final Session session : sessionMap.values()) { - // For each matching session found, redirect the client - // to the new EventMesh node specified by given EventMesh IP and port. - if (session.getClient().getSubsystem().equals(subSystem)) { - redirectResult.append('|') - .append(EventMeshTcp2Client.redirectClient2NewEventMesh(eventMeshTCPServer.getTcpThreadPoolGroup(), - destEventMeshIp, Integer.parseInt(destEventMeshPort), - session, clientSessionGroupMapping)); - } + // Retrieve the mapping between Sessions and their corresponding client address + final ClientSessionGroupMapping clientSessionGroupMapping = eventMeshTCPServer.getClientSessionGroupMapping(); + final ConcurrentHashMap sessionMap = clientSessionGroupMapping.getSessionMap(); + final StringBuilder redirectResult = new StringBuilder(); + try { + if (!sessionMap.isEmpty()) { + // Iterate through the sessionMap to find matching sessions where the client's subsystem id matches the given param + for (final Session session : sessionMap.values()) { + // For each matching session found, redirect the client + // to the new EventMesh node specified by given EventMesh IP and port. + if (session.getClient().getSubsystem().equals(subSystem)) { + redirectResult.append('|') + .append(EventMeshTcp2Client.redirectClient2NewEventMesh(eventMeshTCPServer.getTcpThreadPoolGroup(), + destEventMeshIp, Integer.parseInt(destEventMeshPort), + session, clientSessionGroupMapping)); } } - } catch (Exception e) { - log.error("clientManage|redirectClientBySubSystem|fail|subSystem={}|destEventMeshIp" - + "={}|destEventMeshPort={}", subSystem, destEventMeshIp, destEventMeshPort, e); + } + } catch (Exception e) { + log.error("clientManage|redirectClientBySubSystem|fail|subSystem={}|destEventMeshIp" + + "={}|destEventMeshPort={}", subSystem, destEventMeshIp, destEventMeshPort, e); - NetUtils.sendSuccessResponseHeaders(httpExchange); - out.write(String.format("redirectClientBySubSystem fail! sessionMap size {%d}, {subSystem=%s " - + "destEventMeshIp=%s destEventMeshPort=%s}, result {%s}, errorMsg : %s", + write(ctx, String.format("redirectClientBySubSystem fail! sessionMap size {%d}, {subSystem=%s " + + "destEventMeshIp=%s destEventMeshPort=%s}, result {%s}, errorMsg : %s", sessionMap.size(), subSystem, destEventMeshIp, destEventMeshPort, redirectResult, e.getMessage()) - .getBytes(Constants.DEFAULT_CHARSET)); - return; - } - NetUtils.sendSuccessResponseHeaders(httpExchange); - out.write(String.format("redirectClientBySubSystem success! sessionMap size {%d}, {subSystem=%s " - + "destEventMeshIp=%s destEventMeshPort=%s}, result {%s} ", - sessionMap.size(), subSystem, destEventMeshIp, destEventMeshPort, redirectResult) .getBytes(Constants.DEFAULT_CHARSET)); - } catch (Exception e) { - log.error("redirectClientBySubSystem fail...", e); + return; } + write(ctx, String.format("redirectClientBySubSystem success! sessionMap size {%d}, {subSystem=%s " + + "destEventMeshIp=%s destEventMeshPort=%s}, result {%s} ", + sessionMap.size(), subSystem, destEventMeshIp, destEventMeshPort, redirectResult) + .getBytes(Constants.DEFAULT_CHARSET)); } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RejectAllClientHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RejectAllClientHandler.java index 69748bc318..614c30f600 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RejectAllClientHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RejectAllClientHandler.java @@ -25,21 +25,20 @@ import org.apache.eventmesh.runtime.core.protocol.tcp.client.group.ClientSessionGroupMapping; import org.apache.eventmesh.runtime.core.protocol.tcp.client.session.Session; -import java.io.IOException; -import java.io.OutputStream; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpRequest; import lombok.extern.slf4j.Slf4j; /** - * This class handles the HTTP requests of {@code /clientManage/rejectAllClient} endpoint, - * which is used to reject ALL client connections belonging to the current EventMesh server node. + * This class handles the HTTP requests of {@code /clientManage/rejectAllClient} endpoint, which is used to reject ALL client connections belonging to + * the current EventMesh server node. *

* CAUTION: USE WITH CARE *

@@ -55,54 +54,43 @@ public class RejectAllClientHandler extends AbstractHttpHandler { private final transient EventMeshTCPServer eventMeshTCPServer; /** - * Constructs a new instance with the provided server instance and HTTP handler manager. + * Constructs a new instance with the provided server instance. * - * @param eventMeshTCPServer the TCP server instance of EventMesh + * @param eventMeshTCPServer the TCP server instance of EventMesh */ public RejectAllClientHandler(final EventMeshTCPServer eventMeshTCPServer) { super(); this.eventMeshTCPServer = eventMeshTCPServer; } - /** - * Handles requests by rejecting all clients. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ @Override - public void handle(final HttpExchange httpExchange) throws IOException { - try (OutputStream out = httpExchange.getResponseBody()) { - // Retrieve the mapping between Sessions and their corresponding client address - final ClientSessionGroupMapping clientSessionGroupMapping = eventMeshTCPServer.getClientSessionGroupMapping(); - final ConcurrentHashMap sessionMap = clientSessionGroupMapping.getSessionMap(); - final List successRemoteAddrs = new ArrayList<>(); - try { - log.info("rejectAllClient in admin===================="); - if (!sessionMap.isEmpty()) { - // Iterate through the sessionMap and close each client connection - for (final Map.Entry entry : sessionMap.entrySet()) { - final InetSocketAddress addr = EventMeshTcp2Client.serverGoodby2Client( - eventMeshTCPServer.getTcpThreadPoolGroup(), entry.getValue(), clientSessionGroupMapping); - // Add the remote client address to a list of successfully rejected addresses - if (addr != null) { - successRemoteAddrs.add(addr); - } + public void handle(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + // Retrieve the mapping between Sessions and their corresponding client address + final ClientSessionGroupMapping clientSessionGroupMapping = eventMeshTCPServer.getClientSessionGroupMapping(); + final ConcurrentHashMap sessionMap = clientSessionGroupMapping.getSessionMap(); + final List successRemoteAddrs = new ArrayList<>(); + try { + log.info("rejectAllClient in admin===================="); + if (!sessionMap.isEmpty()) { + // Iterate through the sessionMap and close each client connection + for (final Map.Entry entry : sessionMap.entrySet()) { + final InetSocketAddress addr = EventMeshTcp2Client.serverGoodby2Client( + eventMeshTCPServer.getTcpThreadPoolGroup(), entry.getValue(), clientSessionGroupMapping); + // Add the remote client address to a list of successfully rejected addresses + if (addr != null) { + successRemoteAddrs.add(addr); } } - } catch (Exception e) { - log.error("clientManage rejectAllClient fail", e); - NetUtils.sendSuccessResponseHeaders(httpExchange); - out.write(String.format("rejectAllClient fail! sessionMap size {%d}, had reject {%s}, errorMsg : %s", - sessionMap.size(), NetUtils.addressToString(successRemoteAddrs), e.getMessage()) - .getBytes(Constants.DEFAULT_CHARSET)); - return; } - NetUtils.sendSuccessResponseHeaders(httpExchange); - out.write(String.format("rejectAllClient success! sessionMap size {%d}, had reject {%s}", sessionMap.size(), - NetUtils.addressToString(successRemoteAddrs)).getBytes(Constants.DEFAULT_CHARSET)); } catch (Exception e) { - log.error("rejectAllClient fail.", e); + log.error("clientManage rejectAllClient fail", e); + write(ctx, String.format("rejectAllClient fail! sessionMap size {%d}, had reject {%s}, errorMsg : %s", + sessionMap.size(), NetUtils.addressToString(successRemoteAddrs), e.getMessage()) + .getBytes(Constants.DEFAULT_CHARSET)); + return; } + write(ctx, String.format("rejectAllClient success! sessionMap size {%d}, had reject {%s}", sessionMap.size(), + NetUtils.addressToString(successRemoteAddrs)) + .getBytes(Constants.DEFAULT_CHARSET)); } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RejectClientByIpPortHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RejectClientByIpPortHandler.java index 63ea63ff69..d9ce5f574e 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RejectClientByIpPortHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RejectClientByIpPortHandler.java @@ -28,22 +28,21 @@ import org.apache.commons.lang3.StringUtils; -import java.io.IOException; -import java.io.OutputStream; import java.net.InetSocketAddress; +import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpRequest; import lombok.extern.slf4j.Slf4j; /** - * This class handles the HTTP requests of {@code /clientManage/rejectClientByIpPort} endpoint, - * which is used to reject a client connection - * that matches the provided IP address and port. + * This class handles the HTTP requests of {@code /clientManage/rejectClientByIpPort} endpoint, which is used to reject a client connection that + * matches the provided IP address and port. *

* The request must specify the client's and target EventMesh node's IP and port. *

@@ -66,73 +65,60 @@ public class RejectClientByIpPortHandler extends AbstractHttpHandler { private final EventMeshTCPServer eventMeshTCPServer; /** - * Constructs a new instance with the provided server instance and HTTP handler manager. + * Constructs a new instance with the provided server instance. * - * @param eventMeshTCPServer the TCP server instance of EventMesh + * @param eventMeshTCPServer the TCP server instance of EventMesh */ public RejectClientByIpPortHandler(EventMeshTCPServer eventMeshTCPServer) { super(); this.eventMeshTCPServer = eventMeshTCPServer; } - /** - * Handles requests by rejecting matching clients. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ @Override - public void handle(HttpExchange httpExchange) throws IOException { + public void handle(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { String result = ""; - try (OutputStream out = httpExchange.getResponseBody()) { - String queryString = httpExchange.getRequestURI().getQuery(); - Map queryStringInfo = NetUtils.formData2Dic(queryString); - // Extract parameters from the query string - String ip = queryStringInfo.get(EventMeshConstants.MANAGE_IP); - String port = queryStringInfo.get(EventMeshConstants.MANAGE_PORT); + String queryString = URI.create(httpRequest.uri()).getQuery(); + Map queryStringInfo = NetUtils.formData2Dic(queryString); + // Extract parameters from the query string + String ip = queryStringInfo.get(EventMeshConstants.MANAGE_IP); + String port = queryStringInfo.get(EventMeshConstants.MANAGE_PORT); - // Check the validity of the parameters - if (StringUtils.isBlank(ip) || StringUtils.isBlank(port)) { - NetUtils.sendSuccessResponseHeaders(httpExchange); - result = "params illegal!"; - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - return; - } - log.info("rejectClientByIpPort in admin,ip:{},port:{}====================", ip, port); - // Retrieve the mapping between Sessions and their corresponding client address - ClientSessionGroupMapping clientSessionGroupMapping = eventMeshTCPServer.getClientSessionGroupMapping(); - ConcurrentHashMap sessionMap = clientSessionGroupMapping.getSessionMap(); - final List successRemoteAddrs = new ArrayList(); - try { - if (!sessionMap.isEmpty()) { - // Iterate through the sessionMap to find matching sessions where the remote client address matches the given IP and port - for (Map.Entry entry : sessionMap.entrySet()) { - // Reject client connection for each matching session found - if (entry.getKey().getHostString().equals(ip) && String.valueOf(entry.getKey().getPort()).equals(port)) { - InetSocketAddress addr = EventMeshTcp2Client.serverGoodby2Client(eventMeshTCPServer.getTcpThreadPoolGroup(), - entry.getValue(), clientSessionGroupMapping); - // Add the remote client address to a list of successfully rejected addresses - if (addr != null) { - successRemoteAddrs.add(addr); - } + // Check the validity of the parameters + if (StringUtils.isBlank(ip) || StringUtils.isBlank(port)) { + result = "params illegal!"; + write(ctx, result.getBytes(Constants.DEFAULT_CHARSET)); + return; + } + log.info("rejectClientByIpPort in admin,ip:{},port:{}====================", ip, port); + // Retrieve the mapping between Sessions and their corresponding client address + ClientSessionGroupMapping clientSessionGroupMapping = eventMeshTCPServer.getClientSessionGroupMapping(); + ConcurrentHashMap sessionMap = clientSessionGroupMapping.getSessionMap(); + final List successRemoteAddrs = new ArrayList(); + try { + if (!sessionMap.isEmpty()) { + // Iterate through the sessionMap to find matching sessions where the remote client address matches the given IP and port + for (Map.Entry entry : sessionMap.entrySet()) { + // Reject client connection for each matching session found + if (entry.getKey().getHostString().equals(ip) && String.valueOf(entry.getKey().getPort()).equals(port)) { + InetSocketAddress addr = EventMeshTcp2Client.serverGoodby2Client(eventMeshTCPServer.getTcpThreadPoolGroup(), + entry.getValue(), clientSessionGroupMapping); + // Add the remote client address to a list of successfully rejected addresses + if (addr != null) { + successRemoteAddrs.add(addr); } } } - } catch (Exception e) { - log.error("clientManage|rejectClientByIpPort|fail|ip={}|port={}", ip, port, e); - result = String.format("rejectClientByIpPort fail! {ip=%s port=%s}, had reject {%s}, errorMsg : %s", ip, - port, NetUtils.addressToString(successRemoteAddrs), e.getMessage()); - NetUtils.sendSuccessResponseHeaders(httpExchange); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - return; } - result = String.format("rejectClientByIpPort success! {ip=%s port=%s}, had reject {%s}", ip, port, - NetUtils.addressToString(successRemoteAddrs)); - NetUtils.sendSuccessResponseHeaders(httpExchange); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); } catch (Exception e) { - log.error("rejectClientByIpPort fail...", e); + log.error("clientManage|rejectClientByIpPort|fail|ip={}|port={}", ip, port, e); + result = String.format("rejectClientByIpPort fail! {ip=%s port=%s}, had reject {%s}, errorMsg : %s", ip, + port, NetUtils.addressToString(successRemoteAddrs), e.getMessage()); + write(ctx, result.getBytes(Constants.DEFAULT_CHARSET)); + return; } + result = String.format("rejectClientByIpPort success! {ip=%s port=%s}, had reject {%s}", ip, port, + NetUtils.addressToString(successRemoteAddrs)); + write(ctx, result.getBytes(Constants.DEFAULT_CHARSET)); } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RejectClientBySubSystemHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RejectClientBySubSystemHandler.java index 334f5718da..4ff18caf96 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RejectClientBySubSystemHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/RejectClientBySubSystemHandler.java @@ -28,16 +28,16 @@ import org.apache.commons.lang3.StringUtils; -import java.io.IOException; -import java.io.OutputStream; import java.net.InetSocketAddress; +import java.net.URI; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpRequest; import lombok.extern.slf4j.Slf4j; @@ -63,7 +63,7 @@ public class RejectClientBySubSystemHandler extends AbstractHttpHandler { private final transient EventMeshTCPServer eventMeshTCPServer; /** - * Constructs a new instance with the provided server instance and HTTP handler manager. + * Constructs a new instance with the provided server instance. * * @param eventMeshTCPServer the TCP server instance of EventMesh */ @@ -83,68 +83,56 @@ private String printClients(Collection clients) { return sb.toString(); } - /** - * Handles requests by rejecting matching clients. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ @Override - public void handle(HttpExchange httpExchange) throws IOException { + public void handle(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { String result; - try (OutputStream out = httpExchange.getResponseBody()) { - String queryString = httpExchange.getRequestURI().getQuery(); - Map queryStringInfo = NetUtils.formData2Dic(queryString); - // Extract parameter from the query string - String subSystem = queryStringInfo.get(EventMeshConstants.MANAGE_SUBSYSTEM); - - // Check the validity of the parameters - if (StringUtils.isBlank(subSystem)) { - NetUtils.sendSuccessResponseHeaders(httpExchange); - result = "params illegal!"; - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - return; - } + String queryString = URI.create(httpRequest.uri()).getQuery(); + Map queryStringInfo = NetUtils.formData2Dic(queryString); + // Extract parameter from the query string + String subSystem = queryStringInfo.get(EventMeshConstants.MANAGE_SUBSYSTEM); - log.info("rejectClientBySubSystem in admin,subsys:{}====================", subSystem); - // Retrieve the mapping between Sessions and their corresponding client address - ClientSessionGroupMapping clientSessionGroupMapping = eventMeshTCPServer.getClientSessionGroupMapping(); - ConcurrentHashMap sessionMap = clientSessionGroupMapping.getSessionMap(); - final List successRemoteAddrs = new ArrayList<>(); - try { - if (!sessionMap.isEmpty()) { - // Iterate through the sessionMap to find matching sessions where the client's subsystem id matches the given param - for (Session session : sessionMap.values()) { - // Reject client connection for each matching session found - if (session.getClient().getSubsystem().equals(subSystem)) { - InetSocketAddress addr = EventMeshTcp2Client.serverGoodby2Client(eventMeshTCPServer.getTcpThreadPoolGroup(), session, - clientSessionGroupMapping); - // Add the remote client address to a list of successfully rejected addresses - if (addr != null) { - successRemoteAddrs.add(addr); - } + // Check the validity of the parameters + if (StringUtils.isBlank(subSystem)) { + result = "params illegal!"; + write(ctx, result.getBytes(Constants.DEFAULT_CHARSET)); + return; + } + + log.info("rejectClientBySubSystem in admin,subsys:{}====================", subSystem); + // Retrieve the mapping between Sessions and their corresponding client address + ClientSessionGroupMapping clientSessionGroupMapping = eventMeshTCPServer.getClientSessionGroupMapping(); + ConcurrentHashMap sessionMap = clientSessionGroupMapping.getSessionMap(); + final List successRemoteAddrs = new ArrayList<>(); + try { + if (!sessionMap.isEmpty()) { + // Iterate through the sessionMap to find matching sessions where the client's subsystem id matches the given param + for (Session session : sessionMap.values()) { + // Reject client connection for each matching session found + if (session.getClient().getSubsystem().equals(subSystem)) { + InetSocketAddress addr = EventMeshTcp2Client.serverGoodby2Client(eventMeshTCPServer.getTcpThreadPoolGroup(), session, + clientSessionGroupMapping); + // Add the remote client address to a list of successfully rejected addresses + if (addr != null) { + successRemoteAddrs.add(addr); } } } - } catch (Exception e) { - log.error("clientManage|rejectClientBySubSystem|fail|subSystemId={}", subSystem, e); - result = String.format("rejectClientBySubSystem fail! sessionMap size {%d}, had reject {%s} , {" - + - "subSystemId=%s}, errorMsg : %s", sessionMap.size(), printClients(successRemoteAddrs), - subSystem, e.getMessage()); - NetUtils.sendSuccessResponseHeaders(httpExchange); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - return; } - // Serialize the successfully rejected client addresses into output stream - result = String.format("rejectClientBySubSystem success! sessionMap size {%d}, had reject {%s} , {" - + - "subSystemId=%s}", sessionMap.size(), printClients(successRemoteAddrs), subSystem); - NetUtils.sendSuccessResponseHeaders(httpExchange); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); } catch (Exception e) { - log.error("rejectClientBySubSystem fail...", e); + log.error("clientManage|rejectClientBySubSystem|fail|subSystemId={}", subSystem, e); + result = String.format("rejectClientBySubSystem fail! sessionMap size {%d}, had reject {%s} , {" + + + "subSystemId=%s}, errorMsg : %s", sessionMap.size(), printClients(successRemoteAddrs), + subSystem, e.getMessage()); + write(ctx, result.getBytes(Constants.DEFAULT_CHARSET)); + return; } + // Serialize the successfully rejected client addresses into output stream + result = String.format("rejectClientBySubSystem success! sessionMap size {%d}, had reject {%s} , {" + + + "subSystemId=%s}", sessionMap.size(), printClients(successRemoteAddrs), subSystem); + write(ctx, result.getBytes(Constants.DEFAULT_CHARSET)); + } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/ShowClientBySystemHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/ShowClientBySystemHandler.java index 1222fd9536..9d2c84bc11 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/ShowClientBySystemHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/ShowClientBySystemHandler.java @@ -26,13 +26,13 @@ import org.apache.eventmesh.runtime.core.protocol.tcp.client.group.ClientSessionGroupMapping; import org.apache.eventmesh.runtime.core.protocol.tcp.client.session.Session; -import java.io.IOException; -import java.io.OutputStream; import java.net.InetSocketAddress; +import java.net.URI; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpRequest; import lombok.extern.slf4j.Slf4j; @@ -55,7 +55,7 @@ public class ShowClientBySystemHandler extends AbstractHttpHandler { private final EventMeshTCPServer eventMeshTCPServer; /** - * Constructs a new instance with the provided server instance and HTTP handler manager. + * Constructs a new instance with the provided server instance. * * @param eventMeshTCPServer the TCP server instance of EventMesh */ @@ -64,42 +64,34 @@ public ShowClientBySystemHandler(EventMeshTCPServer eventMeshTCPServer) { this.eventMeshTCPServer = eventMeshTCPServer; } - /** - * Handles requests by displaying clients information. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ @Override - public void handle(HttpExchange httpExchange) throws IOException { + public void handle(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { StringBuilder result = new StringBuilder(); - try (OutputStream out = httpExchange.getResponseBody()) { - String queryString = httpExchange.getRequestURI().getQuery(); - Map queryStringInfo = NetUtils.formData2Dic(queryString); - // Extract parameter from the query string - String subSystem = queryStringInfo.get(EventMeshConstants.MANAGE_SUBSYSTEM); + String queryString = URI.create(httpRequest.uri()).getQuery(); + Map queryStringInfo = NetUtils.formData2Dic(queryString); + // Extract parameter from the query string + String subSystem = queryStringInfo.get(EventMeshConstants.MANAGE_SUBSYSTEM); - String newLine = System.getProperty("line.separator"); - log.info("showClientBySubsys,subsys:{}", subSystem); - // Retrieve the mapping between Sessions and their corresponding client address - ClientSessionGroupMapping clientSessionGroupMapping = eventMeshTCPServer.getClientSessionGroupMapping(); - ConcurrentHashMap sessionMap = clientSessionGroupMapping.getSessionMap(); - if (sessionMap != null && !sessionMap.isEmpty()) { - // Iterate through the sessionMap to find matching sessions where the client's subsystem id matches the given param - for (Session session : sessionMap.values()) { - // For each matching session found, append the client's information to the result - if (session.getClient().getSubsystem().equals(subSystem)) { - UserAgent userAgent = session.getClient(); - result.append(String.format("pid=%s | ip=%s | port=%s | path=%s | purpose=%s", - userAgent.getPid(), userAgent.getHost(), userAgent.getPort(), - userAgent.getPath(), userAgent.getPurpose())) - .append(newLine); - } + String newLine = System.getProperty("line.separator"); + log.info("showClientBySubsys,subsys:{}", subSystem); + // Retrieve the mapping between Sessions and their corresponding client address + ClientSessionGroupMapping clientSessionGroupMapping = eventMeshTCPServer.getClientSessionGroupMapping(); + ConcurrentHashMap sessionMap = clientSessionGroupMapping.getSessionMap(); + if (sessionMap != null && !sessionMap.isEmpty()) { + // Iterate through the sessionMap to find matching sessions where the client's subsystem id matches the given param + for (Session session : sessionMap.values()) { + // For each matching session found, append the client's information to the result + if (session.getClient().getSubsystem().equals(subSystem)) { + UserAgent userAgent = session.getClient(); + result.append(String.format("pid=%s | ip=%s | port=%s | path=%s | purpose=%s", + userAgent.getPid(), userAgent.getHost(), userAgent.getPort(), + userAgent.getPath(), userAgent.getPurpose())) + .append(newLine); } } - NetUtils.sendSuccessResponseHeaders(httpExchange); - out.write(result.toString().getBytes(Constants.DEFAULT_CHARSET)); } + write(ctx, result.toString().getBytes(Constants.DEFAULT_CHARSET)); + } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/ShowClientHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/ShowClientHandler.java index f660c754e5..9699a13835 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/ShowClientHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/ShowClientHandler.java @@ -18,20 +18,18 @@ package org.apache.eventmesh.runtime.admin.handler; import org.apache.eventmesh.common.Constants; -import org.apache.eventmesh.common.utils.NetUtils; import org.apache.eventmesh.runtime.boot.EventMeshTCPServer; import org.apache.eventmesh.runtime.common.EventHttpHandler; import org.apache.eventmesh.runtime.core.protocol.tcp.client.group.ClientSessionGroupMapping; import org.apache.eventmesh.runtime.core.protocol.tcp.client.session.Session; -import java.io.IOException; -import java.io.OutputStream; import java.net.InetSocketAddress; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpRequest; import lombok.extern.slf4j.Slf4j; @@ -51,7 +49,7 @@ public class ShowClientHandler extends AbstractHttpHandler { private final EventMeshTCPServer eventMeshTCPServer; /** - * Constructs a new instance with the provided server instance and HTTP handler manager. + * Constructs a new instance with the provided server instance. * * @param eventMeshTCPServer the TCP server instance of EventMesh */ @@ -60,45 +58,32 @@ public ShowClientHandler(EventMeshTCPServer eventMeshTCPServer) { this.eventMeshTCPServer = eventMeshTCPServer; } - /** - * Handles requests by displaying clients information. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ @Override - public void handle(HttpExchange httpExchange) throws IOException { + public void handle(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { StringBuilder result = new StringBuilder(); - try (OutputStream out = httpExchange.getResponseBody()) { - String newLine = System.getProperty("line.separator"); - log.info("showAllClient================="); - ClientSessionGroupMapping clientSessionGroupMapping = eventMeshTCPServer.getClientSessionGroupMapping(); + String newLine = System.getProperty("line.separator"); + log.info("showAllClient================="); + ClientSessionGroupMapping clientSessionGroupMapping = eventMeshTCPServer.getClientSessionGroupMapping(); - // Store the subsystem and the corresponding client count. - HashMap statMap = new HashMap(); + // Store the subsystem and the corresponding client count. + HashMap statMap = new HashMap(); - Map sessionMap = clientSessionGroupMapping.getSessionMap(); - if (!sessionMap.isEmpty()) { - // Iterate through each Session to count the clients in each subsystem. - for (Session session : sessionMap.values()) { - String key = session.getClient().getSubsystem(); - if (!statMap.containsKey(key)) { - statMap.put(key, new AtomicInteger(1)); - } else { - statMap.get(key).incrementAndGet(); - } - } - - // Generate the result with the number of clients for each subsystem. - for (Map.Entry entry : statMap.entrySet()) { - result.append(String.format("System=%s | ClientNum=%d", entry.getKey(), entry.getValue().intValue())).append(newLine); + Map sessionMap = clientSessionGroupMapping.getSessionMap(); + if (!sessionMap.isEmpty()) { + // Iterate through each Session to count the clients in each subsystem. + for (Session session : sessionMap.values()) { + String key = session.getClient().getSubsystem(); + if (!statMap.containsKey(key)) { + statMap.put(key, new AtomicInteger(1)); + } else { + statMap.get(key).incrementAndGet(); } } - - NetUtils.sendSuccessResponseHeaders(httpExchange); - out.write(result.toString().getBytes(Constants.DEFAULT_CHARSET)); - } catch (Exception e) { - log.error("ShowClientHandler fail...", e); + // Generate the result with the number of clients for each subsystem. + for (Map.Entry entry : statMap.entrySet()) { + result.append(String.format("System=%s | ClientNum=%d", entry.getKey(), entry.getValue().intValue())).append(newLine); + } } + write(ctx, result.toString().getBytes(Constants.DEFAULT_CHARSET)); } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/ShowListenClientByTopicHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/ShowListenClientByTopicHandler.java index db24187d00..412ae5e5ba 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/ShowListenClientByTopicHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/ShowListenClientByTopicHandler.java @@ -27,12 +27,12 @@ import org.apache.eventmesh.runtime.core.protocol.tcp.client.group.ClientSessionGroupMapping; import org.apache.eventmesh.runtime.core.protocol.tcp.client.session.Session; -import java.io.IOException; -import java.io.OutputStream; +import java.net.URI; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpRequest; import lombok.extern.slf4j.Slf4j; @@ -55,7 +55,7 @@ public class ShowListenClientByTopicHandler extends AbstractHttpHandler { private final EventMeshTCPServer eventMeshTCPServer; /** - * Constructs a new instance with the provided server instance and HTTP handler manager. + * Constructs a new instance with the provided server instance. * * @param eventMeshTCPServer the TCP server instance of EventMesh */ @@ -64,46 +64,36 @@ public ShowListenClientByTopicHandler(EventMeshTCPServer eventMeshTCPServer) { this.eventMeshTCPServer = eventMeshTCPServer; } - /** - * Handles requests by displaying clients information. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ @Override - public void handle(HttpExchange httpExchange) throws IOException { + public void handle(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { StringBuilder result = new StringBuilder(); - try (OutputStream out = httpExchange.getResponseBody()) { - String queryString = httpExchange.getRequestURI().getQuery(); - Map queryStringInfo = NetUtils.formData2Dic(queryString); - // Extract parameter from the query string - String topic = queryStringInfo.get(EventMeshConstants.MANAGE_TOPIC); + String queryString = URI.create(httpRequest.uri()).getQuery(); + Map queryStringInfo = NetUtils.formData2Dic(queryString); + // Extract parameter from the query string + String topic = queryStringInfo.get(EventMeshConstants.MANAGE_TOPIC); - String newLine = System.getProperty("line.separator"); - log.info("showListeningClientByTopic,topic:{}=================", topic); - // Retrieve the mappings of client subsystem to client group - ClientSessionGroupMapping clientSessionGroupMapping = eventMeshTCPServer.getClientSessionGroupMapping(); - ConcurrentHashMap clientGroupMap = clientSessionGroupMapping.getClientGroupMap(); - if (!clientGroupMap.isEmpty()) { - // Iterate through the client group to get matching sessions in the group by given topic - for (ClientGroupWrapper cgw : clientGroupMap.values()) { - Map listenSessions = cgw.getTopic2sessionInGroupMapping().get(topic); - if (listenSessions != null && !listenSessions.isEmpty()) { - result.append(String.format("group:%s", cgw.getGroup())).append(newLine); - // Iterate through the sessions to get each client information - for (Session session : listenSessions.values()) { - UserAgent userAgent = session.getClient(); - result.append(String.format("pid=%s | ip=%s | port=%s | path=%s | version=%s", userAgent.getPid(), userAgent - .getHost(), userAgent.getPort(), userAgent.getPath(), userAgent.getVersion())) - .append(newLine); - } + String newLine = System.getProperty("line.separator"); + log.info("showListeningClientByTopic,topic:{}=================", topic); + // Retrieve the mappings of client subsystem to client group + ClientSessionGroupMapping clientSessionGroupMapping = eventMeshTCPServer.getClientSessionGroupMapping(); + ConcurrentHashMap clientGroupMap = clientSessionGroupMapping.getClientGroupMap(); + if (!clientGroupMap.isEmpty()) { + // Iterate through the client group to get matching sessions in the group by given topic + for (ClientGroupWrapper cgw : clientGroupMap.values()) { + Map listenSessions = cgw.getTopic2sessionInGroupMapping().get(topic); + if (listenSessions != null && !listenSessions.isEmpty()) { + result.append(String.format("group:%s", cgw.getGroup())).append(newLine); + // Iterate through the sessions to get each client information + for (Session session : listenSessions.values()) { + UserAgent userAgent = session.getClient(); + result.append(String.format("pid=%s | ip=%s | port=%s | path=%s | version=%s", userAgent.getPid(), userAgent + .getHost(), userAgent.getPort(), userAgent.getPath(), userAgent.getVersion())) + .append(newLine); } } } - NetUtils.sendSuccessResponseHeaders(httpExchange); - out.write(result.toString().getBytes(Constants.DEFAULT_CHARSET)); - } catch (Exception e) { - log.error("ShowListenClientByTopicHandler fail...", e); } + write(ctx, result.toString().getBytes(Constants.DEFAULT_CHARSET)); + } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/TCPClientHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/TCPClientHandler.java index 9697089caf..a4da5bf94c 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/TCPClientHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/TCPClientHandler.java @@ -18,24 +18,18 @@ package org.apache.eventmesh.runtime.admin.handler; import org.apache.eventmesh.common.Constants; -import org.apache.eventmesh.common.enums.HttpMethod; import org.apache.eventmesh.common.protocol.tcp.UserAgent; import org.apache.eventmesh.common.utils.JsonUtils; import org.apache.eventmesh.runtime.admin.request.DeleteTCPClientRequest; -import org.apache.eventmesh.runtime.admin.response.Error; import org.apache.eventmesh.runtime.admin.response.GetClientResponse; -import org.apache.eventmesh.runtime.admin.utils.HttpExchangeUtils; import org.apache.eventmesh.runtime.boot.EventMeshTCPServer; import org.apache.eventmesh.runtime.common.EventHttpHandler; import org.apache.eventmesh.runtime.constants.EventMeshConstants; import org.apache.eventmesh.runtime.core.protocol.tcp.client.EventMeshTcp2Client; import org.apache.eventmesh.runtime.core.protocol.tcp.client.group.ClientSessionGroupMapping; import org.apache.eventmesh.runtime.core.protocol.tcp.client.session.Session; +import org.apache.eventmesh.runtime.util.HttpResponseUtils; -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.io.StringWriter; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.List; @@ -44,7 +38,12 @@ import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; import lombok.extern.slf4j.Slf4j; @@ -64,7 +63,7 @@ public class TCPClientHandler extends AbstractHttpHandler { private final EventMeshTCPServer eventMeshTCPServer; /** - * Constructs a new instance with the provided server instance and HTTP handler manager. + * Constructs a new instance with the provided server instance. * * @param eventMeshTCPServer the TCP server instance of EventMesh */ @@ -74,161 +73,77 @@ public TCPClientHandler( this.eventMeshTCPServer = eventMeshTCPServer; } - /** - * Handles the OPTIONS request first for {@code /client/tcp}. - *

- * This method adds CORS (Cross-Origin Resource Sharing) response headers to the {@link HttpExchange} object and sends a 200 status code. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - void preflight(HttpExchange httpExchange) throws IOException { - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_ORIGIN, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_METHODS, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_HEADERS, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_AGE, EventMeshConstants.MAX_AGE); - httpExchange.sendResponseHeaders(200, 0); - OutputStream out = httpExchange.getResponseBody(); - out.close(); - } - - /** - * Handles the DELETE request for {@code /client/tcp}. - *

- * This method deletes a connected TCP client by disconnecting their connections based on the provided host and port, then returns {@code 200 - * OK}. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - void delete(HttpExchange httpExchange) throws IOException { - - try (OutputStream out = httpExchange.getResponseBody()) { - // Parse the request body string into a DeleteTCPClientRequest object - String request = HttpExchangeUtils.streamToString(httpExchange.getRequestBody()); - DeleteTCPClientRequest deleteTCPClientRequest = JsonUtils.parseObject(request, DeleteTCPClientRequest.class); - String host = Objects.requireNonNull(deleteTCPClientRequest).getHost(); - int port = deleteTCPClientRequest.getPort(); - - ClientSessionGroupMapping clientSessionGroupMapping = eventMeshTCPServer.getClientSessionGroupMapping(); - ConcurrentHashMap sessionMap = clientSessionGroupMapping.getSessionMap(); - if (!sessionMap.isEmpty()) { - for (Map.Entry entry : sessionMap.entrySet()) { - // Find the Session object that matches the host and port to be deleted - if (entry.getKey().getHostString().equals(host) && entry.getKey().getPort() == port) { - // Call the serverGoodby2Client method in EventMeshTcp2Client to disconnect the client's connection - EventMeshTcp2Client.serverGoodby2Client( - eventMeshTCPServer.getTcpThreadPoolGroup(), - entry.getValue(), - clientSessionGroupMapping); - } + @Override + protected void delete(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + // Parse the request body string into a DeleteTCPClientRequest object + Map body = parseHttpRequestBody(httpRequest); + Objects.requireNonNull(body, "body can not be null"); + DeleteTCPClientRequest deleteTCPClientRequest = JsonUtils.mapToObject(body, DeleteTCPClientRequest.class); + String host = Objects.requireNonNull(deleteTCPClientRequest).getHost(); + int port = deleteTCPClientRequest.getPort(); + + ClientSessionGroupMapping clientSessionGroupMapping = eventMeshTCPServer.getClientSessionGroupMapping(); + ConcurrentHashMap sessionMap = clientSessionGroupMapping.getSessionMap(); + if (!sessionMap.isEmpty()) { + for (Map.Entry entry : sessionMap.entrySet()) { + // Find the Session object that matches the host and port to be deleted + if (entry.getKey().getHostString().equals(host) && entry.getKey().getPort() == port) { + // Call the serverGoodby2Client method in EventMeshTcp2Client to disconnect the client's connection + EventMeshTcp2Client.serverGoodby2Client( + eventMeshTCPServer.getTcpThreadPoolGroup(), + entry.getValue(), + clientSessionGroupMapping); } } - - // Set the response headers and send a 200 status code empty response - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_ORIGIN, "*"); - httpExchange.sendResponseHeaders(200, 0); - } catch (Exception e) { - StringWriter writer = new StringWriter(); - PrintWriter printWriter = new PrintWriter(writer); - e.printStackTrace(printWriter); - printWriter.flush(); - String stackTrace = writer.toString(); - - Error error = new Error(e.toString(), stackTrace); - String result = JsonUtils.toJSONString(error); - httpExchange.sendResponseHeaders(500, Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET).length); - log.error(result, e); } - } - /** - * Handles the GET request for {@code /client/tcp}. - *

- * This method retrieves the list of connected TCP clients and returns it as a JSON response. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - void list(HttpExchange httpExchange) throws IOException { - - try (OutputStream out = httpExchange.getResponseBody()) { - // Set the response headers - httpExchange.getResponseHeaders().add(EventMeshConstants.CONTENT_TYPE, EventMeshConstants.APPLICATION_JSON); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_ORIGIN, "*"); - // Get the list of connected TCP clients - ClientSessionGroupMapping clientSessionGroupMapping = eventMeshTCPServer.getClientSessionGroupMapping(); - Map sessionMap = clientSessionGroupMapping.getSessionMap(); - List getClientResponseList = new ArrayList<>(); - // Convert each Session object to GetClientResponse and add to getClientResponseList - for (Session session : sessionMap.values()) { - UserAgent userAgent = session.getClient(); - GetClientResponse getClientResponse = new GetClientResponse( - Optional.ofNullable(userAgent.getEnv()).orElse(""), - Optional.ofNullable(userAgent.getSubsystem()).orElse(""), - Optional.ofNullable(userAgent.getPath()).orElse(""), - String.valueOf(userAgent.getPid()), - Optional.ofNullable(userAgent.getHost()).orElse(""), - userAgent.getPort(), - Optional.ofNullable(userAgent.getVersion()).orElse(""), - Optional.ofNullable(userAgent.getIdc()).orElse(""), - Optional.ofNullable(userAgent.getGroup()).orElse(""), - Optional.ofNullable(userAgent.getPurpose()).orElse(""), - "TCP"); - getClientResponseList.add(getClientResponse); - } + // Set the response headers and send a 200 status code empty response + writeSuccess(ctx); - // Sort the getClientResponseList by host and port - getClientResponseList.sort((lhs, rhs) -> { - if (lhs.getHost().equals(rhs.getHost())) { - return lhs.getHost().compareTo(rhs.getHost()); - } - return Integer.compare(rhs.getPort(), lhs.getPort()); - }); - - // Convert getClientResponseList to JSON and send the response - String result = JsonUtils.toJSONString(getClientResponseList); - httpExchange.sendResponseHeaders(200, Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET).length); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - } catch (Exception e) { - StringWriter writer = new StringWriter(); - PrintWriter printWriter = new PrintWriter(writer); - e.printStackTrace(printWriter); - printWriter.flush(); - String stackTrace = writer.toString(); - - Error error = new Error(e.toString(), stackTrace); - String result = JsonUtils.toJSONString(error); - httpExchange.sendResponseHeaders(500, Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET).length); - log.error(result, e); - } } - /** - * Handles the HTTP requests for {@code /client/tcp}. - *

- * It delegates the handling to {@code preflight()}, {@code list()} or {@code delete()} methods based on the request method type (OPTIONS, GET or - * DELETE). - *

- * This method is an implementation of {@linkplain com.sun.net.httpserver.HttpHandler#handle(HttpExchange) HttpHandler.handle()}. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ @Override - public void handle(HttpExchange httpExchange) throws IOException { - switch (HttpMethod.valueOf(httpExchange.getRequestMethod())) { - case OPTIONS: - preflight(httpExchange); - break; - case GET: - list(httpExchange); - break; - case DELETE: - delete(httpExchange); - break; - default: - break; + protected void get(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + // Set the response headers + HttpHeaders responseHeaders = new DefaultHttpHeaders(); + responseHeaders.add(EventMeshConstants.CONTENT_TYPE, EventMeshConstants.APPLICATION_JSON); + responseHeaders.add(EventMeshConstants.HANDLER_ORIGIN, "*"); + // Get the list of connected TCP clients + ClientSessionGroupMapping clientSessionGroupMapping = eventMeshTCPServer.getClientSessionGroupMapping(); + Map sessionMap = clientSessionGroupMapping.getSessionMap(); + List getClientResponseList = new ArrayList<>(); + // Convert each Session object to GetClientResponse and add to getClientResponseList + for (Session session : sessionMap.values()) { + UserAgent userAgent = session.getClient(); + GetClientResponse getClientResponse = new GetClientResponse( + Optional.ofNullable(userAgent.getEnv()).orElse(""), + Optional.ofNullable(userAgent.getSubsystem()).orElse(""), + Optional.ofNullable(userAgent.getPath()).orElse(""), + String.valueOf(userAgent.getPid()), + Optional.ofNullable(userAgent.getHost()).orElse(""), + userAgent.getPort(), + Optional.ofNullable(userAgent.getVersion()).orElse(""), + Optional.ofNullable(userAgent.getIdc()).orElse(""), + Optional.ofNullable(userAgent.getGroup()).orElse(""), + Optional.ofNullable(userAgent.getPurpose()).orElse(""), + "TCP"); + getClientResponseList.add(getClientResponse); } + + // Sort the getClientResponseList by host and port + getClientResponseList.sort((lhs, rhs) -> { + if (lhs.getHost().equals(rhs.getHost())) { + return lhs.getHost().compareTo(rhs.getHost()); + } + return Integer.compare(rhs.getPort(), lhs.getPort()); + }); + + // Convert getClientResponseList to JSON and send the response + String result = JsonUtils.toJSONString(getClientResponseList); + HttpResponse httpResponse = + HttpResponseUtils.getHttpResponse(Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET), ctx, responseHeaders, + HttpResponseStatus.OK); + write(ctx, httpResponse); + } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/TopicHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/TopicHandler.java index 26231c65a7..c30533d535 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/TopicHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/TopicHandler.java @@ -19,24 +19,24 @@ import org.apache.eventmesh.api.admin.TopicProperties; import org.apache.eventmesh.common.Constants; -import org.apache.eventmesh.common.enums.HttpMethod; import org.apache.eventmesh.common.utils.JsonUtils; import org.apache.eventmesh.runtime.admin.request.CreateTopicRequest; import org.apache.eventmesh.runtime.admin.request.DeleteTopicRequest; -import org.apache.eventmesh.runtime.admin.response.Error; -import org.apache.eventmesh.runtime.admin.utils.HttpExchangeUtils; import org.apache.eventmesh.runtime.common.EventHttpHandler; import org.apache.eventmesh.runtime.constants.EventMeshConstants; import org.apache.eventmesh.runtime.core.plugin.MQAdminWrapper; +import org.apache.eventmesh.runtime.util.HttpResponseUtils; -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintWriter; -import java.io.StringWriter; import java.util.List; +import java.util.Map; import java.util.Objects; -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.DefaultHttpHeaders; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.HttpResponse; +import io.netty.handler.codec.http.HttpResponseStatus; import lombok.extern.slf4j.Slf4j; @@ -60,7 +60,7 @@ public class TopicHandler extends AbstractHttpHandler { private final MQAdminWrapper admin; /** - * Constructs a new instance with the specified connector plugin type and HTTP handler manager. + * Constructs a new instance with the specified connector plugin type. * * @param connectorPluginType The name of event storage connector plugin. */ @@ -75,147 +75,46 @@ public TopicHandler( } } - /** - * Handles the OPTIONS request first for {@code /topic}. - *

- * This method adds CORS (Cross-Origin Resource Sharing) response headers to the {@link HttpExchange} object and sends a 200 status code. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - void preflight(HttpExchange httpExchange) throws IOException { - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_ORIGIN, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_METHODS, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_HEADERS, "*"); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_AGE, EventMeshConstants.MAX_AGE); - httpExchange.sendResponseHeaders(200, 0); - OutputStream out = httpExchange.getResponseBody(); - out.close(); - } + @Override + protected void get(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + HttpHeaders responseHeaders = new DefaultHttpHeaders(); + responseHeaders.add(EventMeshConstants.CONTENT_TYPE, EventMeshConstants.APPLICATION_JSON); + responseHeaders.add(EventMeshConstants.HANDLER_ORIGIN, "*"); + List topicList = admin.getTopic(); + String result = JsonUtils.toJSONString(topicList); + HttpResponse httpResponse = + HttpResponseUtils.getHttpResponse(Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET), ctx, responseHeaders, + HttpResponseStatus.OK); + write(ctx, httpResponse); - /** - * Handles the GET request for {@code /topic}. - *

- * This method retrieves the list of topics from the {@link MQAdminWrapper} and returns it as a JSON response. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - void get(HttpExchange httpExchange) throws IOException { - - try (OutputStream out = httpExchange.getResponseBody()) { - httpExchange.getResponseHeaders().add(EventMeshConstants.CONTENT_TYPE, EventMeshConstants.APPLICATION_JSON); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_ORIGIN, "*"); - List topicList = admin.getTopic(); - String result = JsonUtils.toJSONString(topicList); - httpExchange.sendResponseHeaders(200, Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET).length); - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - } catch (Exception e) { - StringWriter writer = new StringWriter(); - PrintWriter printWriter = new PrintWriter(writer); - e.printStackTrace(printWriter); - printWriter.flush(); - String stackTrace = writer.toString(); - - Error error = new Error(e.toString(), stackTrace); - String result = JsonUtils.toJSONString(error); - httpExchange.sendResponseHeaders(500, Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET).length); - log.error(result, e); - } } - /** - * Handles the POST request for {@code /topic}. - *

- * This method creates a topic if it doesn't exist based on the request data, then returns {@code 200 OK}. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - void post(HttpExchange httpExchange) throws IOException { - - try (OutputStream out = httpExchange.getResponseBody()) { - httpExchange.getResponseHeaders().add(EventMeshConstants.CONTENT_TYPE, EventMeshConstants.APPLICATION_JSON); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_ORIGIN, "*"); - String request = HttpExchangeUtils.streamToString(httpExchange.getRequestBody()); - CreateTopicRequest createTopicRequest = JsonUtils.parseObject(request, CreateTopicRequest.class); - String topicName = Objects.requireNonNull(createTopicRequest).getName(); - admin.createTopic(topicName); - httpExchange.sendResponseHeaders(200, 0); - } catch (Exception e) { - StringWriter writer = new StringWriter(); - PrintWriter printWriter = new PrintWriter(writer); - e.printStackTrace(printWriter); - printWriter.flush(); - String stackTrace = writer.toString(); - - Error error = new Error(e.toString(), stackTrace); - String result = JsonUtils.toJSONString(error); - httpExchange.sendResponseHeaders(500, Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET).length); - log.error(result, e); - } - } + @Override + protected void post(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + Map body = parseHttpRequestBody(httpRequest); + Objects.requireNonNull(body, "body can not be null"); + HttpHeaders responseHeaders = new DefaultHttpHeaders(); + responseHeaders.add(EventMeshConstants.CONTENT_TYPE, EventMeshConstants.APPLICATION_JSON); + responseHeaders.add(EventMeshConstants.HANDLER_ORIGIN, "*"); + CreateTopicRequest createTopicRequest = JsonUtils.mapToObject(body, CreateTopicRequest.class); + String topicName = Objects.requireNonNull(createTopicRequest).getName(); + admin.createTopic(topicName); + writeSuccess(ctx); - /** - * Handles the DELETE request for {@code /topic}. - *

- * This method deletes a topic if it exists based on the request data, then returns {@code 200 OK}. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ - void delete(HttpExchange httpExchange) throws IOException { - - try (OutputStream out = httpExchange.getResponseBody()) { - httpExchange.getResponseHeaders().add(EventMeshConstants.CONTENT_TYPE, EventMeshConstants.APPLICATION_JSON); - httpExchange.getResponseHeaders().add(EventMeshConstants.HANDLER_ORIGIN, "*"); - String request = HttpExchangeUtils.streamToString(httpExchange.getRequestBody()); - DeleteTopicRequest deleteTopicRequest = JsonUtils.parseObject(request, DeleteTopicRequest.class); - String topicName = Objects.requireNonNull(deleteTopicRequest).getName(); - admin.deleteTopic(topicName); - httpExchange.sendResponseHeaders(200, 0); - } catch (Exception e) { - StringWriter writer = new StringWriter(); - PrintWriter printWriter = new PrintWriter(writer); - e.printStackTrace(printWriter); - printWriter.flush(); - String stackTrace = writer.toString(); - - Error error = new Error(e.toString(), stackTrace); - String result = JsonUtils.toJSONString(error); - httpExchange.sendResponseHeaders(500, Objects.requireNonNull(result).getBytes(Constants.DEFAULT_CHARSET).length); - log.error(result, e); - } } - /** - * Handles the HTTP requests for {@code /topic}. - *

- * It delegates the handling to {@code preflight()}, {@code get()}, {@code post()} or {@code delete()} methods based on the request method type - * (OPTIONS, GET, POST or DELETE). - *

- * This method is an implementation of {@linkplain com.sun.net.httpserver.HttpHandler#handle(HttpExchange) HttpHandler.handle()}. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - */ @Override - public void handle(HttpExchange httpExchange) throws IOException { - switch (HttpMethod.valueOf(httpExchange.getRequestMethod())) { - case OPTIONS: - preflight(httpExchange); - break; - case POST: - post(httpExchange); - break; - case DELETE: - delete(httpExchange); - break; - case GET: - get(httpExchange); - break; - default: - break; - } + protected void delete(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + Map body = parseHttpRequestBody(httpRequest); + Objects.requireNonNull(body, "body can not be null"); + HttpHeaders responseHeaders = new DefaultHttpHeaders(); + responseHeaders.add(EventMeshConstants.CONTENT_TYPE, EventMeshConstants.APPLICATION_JSON); + responseHeaders.add(EventMeshConstants.HANDLER_ORIGIN, "*"); + DeleteTopicRequest deleteTopicRequest = JsonUtils.mapToObject(body, DeleteTopicRequest.class); + String topicName = Objects.requireNonNull(deleteTopicRequest).getName(); + admin.deleteTopic(topicName); + writeSuccess(ctx); + } + } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/UpdateWebHookConfigHandler.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/UpdateWebHookConfigHandler.java index 8c4579efa5..a842c84e65 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/UpdateWebHookConfigHandler.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/admin/handler/UpdateWebHookConfigHandler.java @@ -19,38 +19,37 @@ import org.apache.eventmesh.common.Constants; import org.apache.eventmesh.common.utils.JsonUtils; -import org.apache.eventmesh.common.utils.NetUtils; import org.apache.eventmesh.runtime.common.EventHttpHandler; import org.apache.eventmesh.webhook.api.WebHookConfig; import org.apache.eventmesh.webhook.api.WebHookConfigOperation; -import java.io.IOException; -import java.io.OutputStream; +import java.util.Map; +import java.util.Objects; -import com.sun.net.httpserver.HttpExchange; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.http.HttpRequest; import lombok.extern.slf4j.Slf4j; /** - * This class handles the HTTP requests of {@code /webhook/updateWebHookConfig} endpoint - * and updates an existing WebHook configuration - * according to the given {@linkplain org.apache.eventmesh.webhook.api.WebHookConfig WebHookConfig}. + * This class handles the HTTP requests of {@code /webhook/updateWebHookConfig} endpoint and updates an existing WebHook configuration according to + * the given {@linkplain org.apache.eventmesh.webhook.api.WebHookConfig WebHookConfig}. *

- * The implementation of - * {@linkplain org.apache.eventmesh.webhook.api.WebHookConfigOperation#updateWebHookConfig WebHookConfigOperation} - * interface depends on the {@code eventMesh.webHook.operationMode} configuration in {@code eventmesh.properties}. + * The implementation of {@linkplain org.apache.eventmesh.webhook.api.WebHookConfigOperation#updateWebHookConfig WebHookConfigOperation} interface + * depends on the {@code eventMesh.webHook.operationMode} configuration in {@code eventmesh.properties}. *

* For example, when {@code eventMesh.webHook.operationMode=file}, It calls the - * {@linkplain org.apache.eventmesh.webhook.admin.FileWebHookConfigOperation#updateWebHookConfig FileWebHookConfigOperation} - * method as implementation to update the WebHook configuration in a file; + * {@linkplain org.apache.eventmesh.webhook.admin.FileWebHookConfigOperation + * #updateWebHookConfig + * FileWebHookConfigOperation} method as implementation to update the WebHook configuration in a file; *

* When {@code eventMesh.webHook.operationMode=nacos}, It calls the - * {@linkplain org.apache.eventmesh.webhook.admin.NacosWebHookConfigOperation#updateWebHookConfig NacosWebHookConfigOperation} - * method as implementation to update the WebHook configuration in Nacos. + * {@linkplain org.apache.eventmesh.webhook.admin.NacosWebHookConfigOperation#updateWebHookConfig + * NacosWebHookConfigOperation} method as implementation to update the WebHook configuration in Nacos. *

- * The {@linkplain org.apache.eventmesh.webhook.receive.storage.HookConfigOperationManager#updateWebHookConfig HookConfigOperationManager} - * , another implementation of {@linkplain org.apache.eventmesh.webhook.api.WebHookConfigOperation WebHookConfigOperation} - * interface, is not used for this endpoint. + * The {@linkplain org.apache.eventmesh.webhook.receive.storage.HookConfigOperationManager#updateWebHookConfig HookConfigOperationManager} , another + * implementation of {@linkplain org.apache.eventmesh.webhook.api.WebHookConfigOperation WebHookConfigOperation} interface, is not used for this + * endpoint. * * @see AbstractHttpHandler */ @@ -63,7 +62,7 @@ public class UpdateWebHookConfigHandler extends AbstractHttpHandler { private final WebHookConfigOperation operation; /** - * Constructs a new instance with the specified WebHook config operation and HTTP handler manager. + * Constructs a new instance with the specified WebHook config operation. * * @param operation the WebHookConfigOperation implementation used to update the WebHook config */ @@ -72,29 +71,15 @@ public UpdateWebHookConfigHandler(WebHookConfigOperation operation) { this.operation = operation; } - /** - * Handles requests by updating a WebHook configuration. - * - * @param httpExchange the exchange containing the request from the client and used to send the response - * @throws IOException if an I/O error occurs while handling the request - * - * @see org.apache.eventmesh.webhook.receive.storage.HookConfigOperationManager#updateWebHookConfig(WebHookConfig) - */ @Override - public void handle(HttpExchange httpExchange) throws IOException { - NetUtils.sendSuccessResponseHeaders(httpExchange); - + public void handle(HttpRequest httpRequest, ChannelHandlerContext ctx) throws Exception { + Map body = parseHttpRequestBody(httpRequest); + Objects.requireNonNull(body, "body can not be null"); // Resolve to WebHookConfig - String requestBody = NetUtils.parsePostBody(httpExchange); - WebHookConfig webHookConfig = JsonUtils.parseObject(requestBody, WebHookConfig.class); - - try (OutputStream out = httpExchange.getResponseBody()) { - // Update the existing WebHookConfig - Integer code = operation.updateWebHookConfig(webHookConfig); // operating result - String result = 1 == code ? "updateWebHookConfig Succeed!" : "updateWebHookConfig Failed!"; - out.write(result.getBytes(Constants.DEFAULT_CHARSET)); - } catch (Exception e) { - log.error("get WebHookConfigOperation implementation Failed.", e); - } + WebHookConfig webHookConfig = JsonUtils.mapToObject(body, WebHookConfig.class); + // Update the existing WebHookConfig + Integer code = operation.updateWebHookConfig(webHookConfig); // operating result + String result = 1 == code ? "updateWebHookConfig Succeed!" : "updateWebHookConfig Failed!"; + write(ctx, result.getBytes(Constants.DEFAULT_CHARSET)); } } diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/boot/AbstractHTTPServer.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/boot/AbstractHTTPServer.java index f978f25a89..8dd9525170 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/boot/AbstractHTTPServer.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/boot/AbstractHTTPServer.java @@ -33,6 +33,7 @@ import org.apache.eventmesh.runtime.core.protocol.http.processor.HandlerService; import org.apache.eventmesh.runtime.core.protocol.http.processor.inf.HttpRequestProcessor; import org.apache.eventmesh.runtime.metrics.http.HTTPMetricsServer; +import org.apache.eventmesh.runtime.util.HttpRequestUtil; import org.apache.eventmesh.runtime.util.RemotingHelper; import org.apache.eventmesh.runtime.util.TraceUtils; import org.apache.eventmesh.runtime.util.Utils; @@ -42,7 +43,6 @@ import org.apache.commons.lang3.StringUtils; import java.io.IOException; -import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; @@ -78,12 +78,8 @@ import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpVersion; -import io.netty.handler.codec.http.QueryStringDecoder; -import io.netty.handler.codec.http.multipart.Attribute; import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory; import io.netty.handler.codec.http.multipart.DiskAttribute; -import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder; -import io.netty.handler.codec.http.multipart.InterfaceHttpData; import io.netty.handler.ssl.SslHandler; import io.netty.handler.timeout.IdleState; import io.netty.handler.timeout.IdleStateEvent; @@ -256,29 +252,8 @@ public void sendResponse(final ChannelHandlerContext ctx, final DefaultFullHttpR * @return */ private Map parseHttpRequestBody(final HttpRequest httpRequest) throws IOException { - final long bodyDecodeStart = System.currentTimeMillis(); - final Map httpRequestBody = new HashMap<>(); - - if (HttpMethod.GET.equals(httpRequest.method())) { - new QueryStringDecoder(httpRequest.uri()) - .parameters() - .forEach((key, value) -> httpRequestBody.put(key, value.get(0))); - } else if (HttpMethod.POST.equals(httpRequest.method())) { - decodeHttpRequestBody(httpRequest, httpRequestBody); - } - metrics.getSummaryMetrics().recordDecodeTimeCost(System.currentTimeMillis() - bodyDecodeStart); - return httpRequestBody; - } - - private void decodeHttpRequestBody(HttpRequest httpRequest, Map httpRequestBody) throws IOException { - final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(DEFAULT_HTTP_DATA_FACTORY, httpRequest); - for (final InterfaceHttpData param : decoder.getBodyHttpDatas()) { - if (InterfaceHttpData.HttpDataType.Attribute == param.getHttpDataType()) { - final Attribute data = (Attribute) param; - httpRequestBody.put(data.getName(), data.getValue()); - } - } - decoder.destroy(); + return HttpRequestUtil.parseHttpRequestBody(httpRequest, () -> System.currentTimeMillis(), + (startTime) -> metrics.getSummaryMetrics().recordDecodeTimeCost(System.currentTimeMillis() - startTime)); } @Sharable diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/boot/EventMeshAdminServer.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/boot/EventMeshAdminServer.java index 5fa1a1177b..f7b16baa51 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/boot/EventMeshAdminServer.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/boot/EventMeshAdminServer.java @@ -17,26 +17,18 @@ package org.apache.eventmesh.runtime.boot; +import static org.apache.eventmesh.runtime.util.HttpResponseUtils.getHttpResponse; + +import org.apache.eventmesh.common.Constants; import org.apache.eventmesh.runtime.admin.handler.AdminHandlerManager; +import org.apache.eventmesh.runtime.admin.handler.HttpHandler; import org.apache.eventmesh.runtime.util.HttpResponseUtils; -import org.apache.eventmesh.runtime.util.Utils; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetSocketAddress; import java.net.URI; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; +import java.util.Objects; import java.util.Optional; -import java.util.Set; import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInitializer; @@ -46,20 +38,12 @@ import io.netty.channel.epoll.EpollServerSocketChannel; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.codec.http.DefaultFullHttpResponse; -import io.netty.handler.codec.http.FullHttpRequest; +import io.netty.handler.codec.http.HttpHeaderValues; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.codec.http.HttpResponseStatus; -import io.netty.handler.codec.http.HttpVersion; - -import com.sun.net.httpserver.Headers; -import com.sun.net.httpserver.HttpContext; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpPrincipal; import lombok.extern.slf4j.Slf4j; @@ -120,160 +104,20 @@ public void parseHttpRequest(ChannelHandlerContext ctx, HttpRequest httpRequest) Optional httpHandlerOpt = adminHandlerManager.getHttpHandler(uri.getPath()); if (httpHandlerOpt.isPresent()) { try { - AdminHttpExchange adminHttpExchange = new AdminHttpExchange(ctx, httpRequest); - httpHandlerOpt.get().handle(adminHttpExchange); - adminHttpExchange.writeAndFlash(); + httpHandlerOpt.get().handle(httpRequest, ctx); return; } catch (Exception e) { - log.error(e.getMessage(), e); - ctx.writeAndFlush(HttpResponseUtils.createInternalServerError()).addListener(ChannelFutureListener.CLOSE); + log.error("admin server channelRead error", e); + ctx.writeAndFlush( + getHttpResponse(Objects.requireNonNull(e.getMessage()).getBytes(Constants.DEFAULT_CHARSET), + ctx, HttpHeaderValues.APPLICATION_JSON, + HttpResponseStatus.INTERNAL_SERVER_ERROR)).addListener(ChannelFutureListener.CLOSE); } } else { ctx.writeAndFlush(HttpResponseUtils.createNotFound()).addListener(ChannelFutureListener.CLOSE); } } - class AdminHttpExchange extends HttpExchange { - - - ChannelHandlerContext ctx; - Optional httpRequest; - - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - - Map responseCode = new HashMap<>(); - - Headers responseHeader = new Headers(); - - public AdminHttpExchange(ChannelHandlerContext ctx, HttpRequest httpRequest) { - this.ctx = ctx; - if (httpRequest instanceof FullHttpRequest) { - this.httpRequest = Optional.ofNullable((FullHttpRequest) httpRequest); - } - } - - @Override - public Headers getRequestHeaders() { - Headers headers = new Headers(); - httpRequest.ifPresent(e -> { - final Map headerMap = Utils.parseHttpHeader(e); - headerMap.putAll(headerMap); - }); - - return headers; - } - - @Override - public Headers getResponseHeaders() { - return responseHeader; - } - - @Override - public URI getRequestURI() { - if (httpRequest.isPresent()) { - return URI.create(httpRequest.get().uri()); - } - return null; - } - - @Override - public String getRequestMethod() { - if (httpRequest.isPresent()) { - return httpRequest.get().method().name(); - } - return null; - } - - @Override - public HttpContext getHttpContext() { - return null; - } - - @Override - public void close() { - - } - - @Override - public InputStream getRequestBody() { - if (httpRequest.isPresent()) { - ByteBuf content = httpRequest.get().content(); - byte[] bytes = new byte[content.readableBytes()]; - try { - content.readBytes(bytes); - } finally { - content.release(); - } - return new ByteArrayInputStream(bytes); - } - return new ByteArrayInputStream(new byte[0]); - } - - @Override - public OutputStream getResponseBody() { - return outputStream; - } - - @Override - public void sendResponseHeaders(int i, long l) throws IOException { - responseCode.put(i, l); - } - - @Override - public InetSocketAddress getRemoteAddress() { - return null; - } - - @Override - public int getResponseCode() { - Set> entries = responseCode.entrySet(); - Optional> first = entries.stream().findFirst(); - return first.get().getKey(); - } - - @Override - public InetSocketAddress getLocalAddress() { - return null; - } - - @Override - public String getProtocol() { - return null; - } - - @Override - public Object getAttribute(String s) { - return null; - } - - @Override - public void setAttribute(String s, Object o) { - - } - - @Override - public void setStreams(InputStream inputStream, OutputStream outputStream) { - - } - - @Override - public HttpPrincipal getPrincipal() { - return null; - } - - public void writeAndFlash() { - byte[] bytes = outputStream.toByteArray(); - Headers responseHeaders = getResponseHeaders(); - DefaultFullHttpResponse defaultFullHttpResponse = - new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(getResponseCode()), - Unpooled.copiedBuffer(bytes)); - responseHeaders.entrySet().stream().forEach(e -> { - defaultFullHttpResponse.headers().add(e.getKey(), e.getValue()); - }); - ctx.writeAndFlush(defaultFullHttpResponse).addListener(ChannelFutureListener.CLOSE); - } - } - private class AdminServerInitializer extends ChannelInitializer { diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/util/HttpRequestUtil.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/util/HttpRequestUtil.java new file mode 100644 index 0000000000..ecfcd0e198 --- /dev/null +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/util/HttpRequestUtil.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.eventmesh.runtime.util; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import javax.annotation.Nullable; + +import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.QueryStringDecoder; +import io.netty.handler.codec.http.multipart.Attribute; +import io.netty.handler.codec.http.multipart.DefaultHttpDataFactory; +import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder; +import io.netty.handler.codec.http.multipart.InterfaceHttpData; + +public class HttpRequestUtil { + + private static final DefaultHttpDataFactory DEFAULT_HTTP_DATA_FACTORY = new DefaultHttpDataFactory(false); + + public static Map parseHttpRequestBody(final HttpRequest httpRequest, @Nullable Supplier start, @Nullable Consumer end) + throws IOException { + T t = null; + if (!Objects.isNull(start)) { + t = start.get(); + } + final Map httpRequestBody = new HashMap<>(); + if (io.netty.handler.codec.http.HttpMethod.GET.equals(httpRequest.method())) { + new QueryStringDecoder(httpRequest.uri()) + .parameters() + .forEach((key, value) -> httpRequestBody.put(key, value.get(0))); + } else if (io.netty.handler.codec.http.HttpMethod.POST.equals(httpRequest.method())) { + decodeHttpRequestBody(httpRequest, httpRequestBody); + } + if (Objects.isNull(t)) { + end.accept(t); + } + return httpRequestBody; + } + + private static void decodeHttpRequestBody(HttpRequest httpRequest, Map httpRequestBody) throws IOException { + final HttpPostRequestDecoder decoder = new HttpPostRequestDecoder(DEFAULT_HTTP_DATA_FACTORY, httpRequest); + for (final InterfaceHttpData param : decoder.getBodyHttpDatas()) { + if (InterfaceHttpData.HttpDataType.Attribute == param.getHttpDataType()) { + final Attribute data = (Attribute) param; + httpRequestBody.put(data.getName(), data.getValue()); + } + } + decoder.destroy(); + } + +} diff --git a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/util/HttpResponseUtils.java b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/util/HttpResponseUtils.java index ccaa13b0e2..0db7e58c30 100644 --- a/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/util/HttpResponseUtils.java +++ b/eventmesh-runtime/src/main/java/org/apache/eventmesh/runtime/util/HttpResponseUtils.java @@ -47,6 +47,10 @@ public static HttpResponse createInternalServerError() { private static ByteBuf createByteBuf(ChannelHandlerContext ctx, String body) { byte[] bytes = body.getBytes(Constants.DEFAULT_CHARSET); + return createByteBuf(ctx, bytes); + } + + private static ByteBuf createByteBuf(ChannelHandlerContext ctx, byte[] bytes) { ByteBuf byteBuf = ctx.alloc().buffer(bytes.length); byteBuf.writeBytes(bytes); return byteBuf; @@ -68,4 +72,20 @@ public static HttpResponse getHttpResponse(String body, ChannelHandlerContext ct responseHeaders, responseHeaders); } + public static HttpResponse getHttpResponse(byte[] bytes, ChannelHandlerContext ctx, AsciiString headerValue) { + return getHttpResponse(bytes, ctx, headerValue, HttpResponseStatus.OK); + } + + public static HttpResponse getHttpResponse(byte[] bytes, ChannelHandlerContext ctx, AsciiString headerValue, HttpResponseStatus status) { + HttpHeaders responseHeaders = new DefaultHttpHeaders(); + responseHeaders.add(HttpHeaderNames.CONTENT_TYPE, headerValue); + return new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, createByteBuf(ctx, bytes), + responseHeaders, responseHeaders); + } + + public static HttpResponse getHttpResponse(byte[] bytes, ChannelHandlerContext ctx, HttpHeaders responseHeaders, HttpResponseStatus status) { + return new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, createByteBuf(ctx, bytes), + responseHeaders, responseHeaders); + } + } diff --git a/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/admin/handler/QueryRecommendEventMeshHandlerTest.java b/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/admin/handler/QueryRecommendEventMeshHandlerTest.java deleted file mode 100644 index 4a9753c737..0000000000 --- a/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/admin/handler/QueryRecommendEventMeshHandlerTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.eventmesh.runtime.admin.handler; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.mockConstruction; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.when; - -import org.apache.eventmesh.runtime.boot.EventMeshTCPServer; -import org.apache.eventmesh.runtime.configuration.EventMeshTCPConfiguration; -import org.apache.eventmesh.runtime.core.protocol.tcp.client.recommend.EventMeshRecommendImpl; - -import org.apache.commons.lang3.StringUtils; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.net.URI; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.mockito.MockedConstruction; -import org.mockito.MockedStatic; - -import com.sun.net.httpserver.HttpExchange; - -public class QueryRecommendEventMeshHandlerTest { - - @Test - public void testHandle() throws Exception { - // mock eventMeshTCPServer - EventMeshTCPServer eventMeshTCPServer = mock(EventMeshTCPServer.class); - EventMeshTCPConfiguration tcpConfiguration = new EventMeshTCPConfiguration(); - when(eventMeshTCPServer.getEventMeshTCPConfiguration()).thenReturn(tcpConfiguration); - - URI uri = mock(URI.class); - when(uri.getQuery()).thenReturn("group=group&purpose=purpose"); - OutputStream outputStream = new ByteArrayOutputStream(); - HttpExchange httpExchange = mock(HttpExchange.class); - when(httpExchange.getRequestURI()).thenReturn(uri); - QueryRecommendEventMeshHandler handler = new QueryRecommendEventMeshHandler(eventMeshTCPServer); - - String returnValue = "result"; - - // case 1: normal case - tcpConfiguration.setEventMeshServerMetaStorageEnable(true); - when(httpExchange.getResponseBody()).thenReturn(outputStream); - try (MockedConstruction ignored = mockConstruction(EventMeshRecommendImpl.class, - (mock, context) -> when(mock.calculateRecommendEventMesh(anyString(), anyString())).thenReturn(returnValue))) { - handler.handle(httpExchange); - String response = outputStream.toString(); - Assertions.assertEquals(returnValue, response); - } - - // case 2: params illegal - outputStream = new ByteArrayOutputStream(); - when(httpExchange.getResponseBody()).thenReturn(outputStream); - try (MockedStatic dummyStatic = mockStatic(StringUtils.class)) { - dummyStatic.when(() -> StringUtils.isBlank(any())).thenReturn(Boolean.TRUE); - handler.handle(httpExchange); - String response = outputStream.toString(); - Assertions.assertEquals("params illegal!", response); - } - - // case 3: registry disable - tcpConfiguration.setEventMeshServerMetaStorageEnable(false); - outputStream = mock(ByteArrayOutputStream.class); - doThrow(new IOException()).when(outputStream).close(); - when(httpExchange.getResponseBody()).thenReturn(outputStream); - try (MockedConstruction ignored = mockConstruction(EventMeshRecommendImpl.class, - (mock, context) -> when(mock.calculateRecommendEventMesh(anyString(), anyString())).thenReturn(returnValue))) { - handler.handle(httpExchange); - String response = outputStream.toString(); - Assertions.assertNotEquals(returnValue, response); - } - } -} diff --git a/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/admin/handler/RedirectClientByIpPortHandlerTest.java b/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/admin/handler/RedirectClientByIpPortHandlerTest.java deleted file mode 100644 index 3128c46f2d..0000000000 --- a/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/admin/handler/RedirectClientByIpPortHandlerTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.eventmesh.runtime.admin.handler; - -import org.apache.eventmesh.runtime.boot.EventMeshTCPServer; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.net.URI; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mockito; - -import com.sun.net.httpserver.HttpExchange; - -public class RedirectClientByIpPortHandlerTest { - - private static transient RedirectClientByIpPortHandler redirectClientByIpPortHandler; - - @BeforeEach - public void init() { - EventMeshTCPServer mockServer = Mockito.mock(EventMeshTCPServer.class); - redirectClientByIpPortHandler = new RedirectClientByIpPortHandler(mockServer); - } - - @Test - public void testHandleParamIllegal() throws IOException { - OutputStream outputStream = new ByteArrayOutputStream(); - URI uri = URI.create("ip=127.0.0.1&port=1234&desteventMeshIp=127.0.0.1&desteventMeshPort="); - - HttpExchange mockExchange = Mockito.mock(HttpExchange.class); - Mockito.when(mockExchange.getResponseBody()).thenReturn(outputStream); - Mockito.when(mockExchange.getRequestURI()).thenReturn(uri); - - redirectClientByIpPortHandler.handle(mockExchange); - - String response = outputStream.toString(); - Assertions.assertEquals("params illegal!", response); - - } -} \ No newline at end of file diff --git a/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/admin/handler/RedirectClientByPathHandlerTest.java b/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/admin/handler/RedirectClientByPathHandlerTest.java deleted file mode 100644 index 27f1bc510d..0000000000 --- a/eventmesh-runtime/src/test/java/org/apache/eventmesh/runtime/admin/handler/RedirectClientByPathHandlerTest.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.apache.eventmesh.runtime.admin.handler; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.mockStatic; -import static org.mockito.Mockito.when; - -import org.apache.eventmesh.common.Constants; -import org.apache.eventmesh.common.protocol.tcp.UserAgent; -import org.apache.eventmesh.common.utils.NetUtils; -import org.apache.eventmesh.runtime.boot.EventMeshTCPServer; -import org.apache.eventmesh.runtime.constants.EventMeshConstants; -import org.apache.eventmesh.runtime.core.protocol.tcp.client.EventMeshTcp2Client; -import org.apache.eventmesh.runtime.core.protocol.tcp.client.group.ClientSessionGroupMapping; -import org.apache.eventmesh.runtime.core.protocol.tcp.client.session.Session; - -import org.apache.commons.lang3.StringUtils; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockedStatic; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; - -import com.sun.net.httpserver.HttpExchange; - -public class RedirectClientByPathHandlerTest { - - @Mock - private static transient EventMeshTCPServer eventMeshTCPServer; - - @BeforeEach - public void init() { - MockitoAnnotations.openMocks(this); - } - - @Test - public void testHandle() throws IOException { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - final HttpExchange mockExchange = mock(HttpExchange.class); - - ClientSessionGroupMapping mapping = mock(ClientSessionGroupMapping.class); - when(eventMeshTCPServer.getClientSessionGroupMapping()).thenReturn(mapping); - RedirectClientByPathHandler redirectClientByPathHandler = new RedirectClientByPathHandler(eventMeshTCPServer); - - // mock session map - ConcurrentHashMap sessionMap = new ConcurrentHashMap<>(); - Session session = mock(Session.class); - UserAgent agent = mock(UserAgent.class); - when(agent.getPath()).thenReturn("path"); - when(session.getClient()).thenReturn(agent); - sessionMap.put(new InetSocketAddress(8080), session); - when(mapping.getSessionMap()).thenReturn(sessionMap); - - // mock uri - URI uri = mock(URI.class); - when(uri.getQuery()).thenReturn("path=path&ip=127.0.0.1&port=1234&desteventMeshIp=127.0.0.1&desteventmeshport=8080"); - when(mockExchange.getRequestURI()).thenReturn(uri); - - try (MockedStatic netUtilsMockedStatic = Mockito.mockStatic(NetUtils.class)) { - Map queryStringInfo = new HashMap<>(); - queryStringInfo.put(EventMeshConstants.MANAGE_PATH, EventMeshConstants.MANAGE_PATH); - queryStringInfo.put(EventMeshConstants.MANAGE_DEST_IP, "localhost"); - queryStringInfo.put(EventMeshConstants.MANAGE_DEST_PORT, "8080"); - netUtilsMockedStatic.when(() -> NetUtils.formData2Dic(anyString())).thenReturn(queryStringInfo); - - // case 1: normal case - when(mockExchange.getResponseBody()).thenReturn(outputStream); - try (MockedStatic clientMockedStatic = Mockito.mockStatic(EventMeshTcp2Client.class)) { - clientMockedStatic.when(() -> EventMeshTcp2Client.redirectClient2NewEventMesh(any(), anyString(), anyInt(), any(), - any())).thenReturn("redirectResult"); - redirectClientByPathHandler.handle(mockExchange); - String response = outputStream.toString(StandardCharsets.UTF_8.name()); - Assertions.assertTrue(response.startsWith("redirectClientByPath success!")); - } - - // case 2: params illegal - outputStream = new ByteArrayOutputStream(); - when(mockExchange.getResponseBody()).thenReturn(outputStream); - try (MockedStatic dummyStatic = mockStatic(StringUtils.class)) { - dummyStatic.when(() -> StringUtils.isBlank(any())).thenReturn(Boolean.TRUE); - redirectClientByPathHandler.handle(mockExchange); - String response = outputStream.toString(StandardCharsets.UTF_8.name()); - Assertions.assertEquals("params illegal!", response); - } - - // case 3: redirectClient2NewEventMesh fail - outputStream = new ByteArrayOutputStream(); - when(mockExchange.getResponseBody()).thenReturn(outputStream); - try (MockedStatic clientMockedStatic = Mockito.mockStatic(EventMeshTcp2Client.class)) { - clientMockedStatic.when(() -> EventMeshTcp2Client.redirectClient2NewEventMesh(any(), anyString(), anyInt(), any(), - any())).thenThrow(new RuntimeException()); - redirectClientByPathHandler.handle(mockExchange); - String response = outputStream.toString(Constants.DEFAULT_CHARSET.name()); - Assertions.assertTrue(response.startsWith("redirectClientByPath fail!")); - } - } - } -}