From e3adbd436937c7cdacd0347179cfe3812548879e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Prante?= Date: Mon, 21 Aug 2023 17:18:11 +0200 Subject: [PATCH] make server HTTP request serializable in json --- gradle.properties | 2 +- .../http/server/netty/HttpRequestBuilder.java | 6 ++ .../http/netty/test/NettyHttp2ServerTest.java | 4 +- .../http/netty/test/NettyHttpServerTest.java | 23 +++++-- .../xbib/net/http/server/BaseHttpRequest.java | 65 +++++++++++++++++++ .../http/server/BaseHttpRequestBuilder.java | 51 +++++++++++++++ .../org/xbib/net/http/server/HttpRequest.java | 2 + settings.gradle | 2 +- 8 files changed, 144 insertions(+), 11 deletions(-) diff --git a/gradle.properties b/gradle.properties index 937b6e8..c5bb41f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = org.xbib name = net-http -version = 3.6.1 +version = 3.6.2 org.gradle.warning.mode = ALL diff --git a/net-http-server-netty/src/main/java/org/xbib/net/http/server/netty/HttpRequestBuilder.java b/net-http-server-netty/src/main/java/org/xbib/net/http/server/netty/HttpRequestBuilder.java index 9925eef..47bda31 100644 --- a/net-http-server-netty/src/main/java/org/xbib/net/http/server/netty/HttpRequestBuilder.java +++ b/net-http-server-netty/src/main/java/org/xbib/net/http/server/netty/HttpRequestBuilder.java @@ -5,6 +5,7 @@ import io.netty.buffer.ByteBufUtil; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.multipart.FileUpload; import java.io.IOException; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.xbib.net.Parameter; @@ -33,6 +34,11 @@ public class HttpRequestBuilder extends BaseHttpRequestBuilder { protected HttpRequestBuilder() { } + public HttpRequestBuilder parse(Map map) { + super.parse(map); + return this; + } + public HttpRequestBuilder setHttpRequest(io.netty.handler.codec.http.HttpRequest httpRequest) { if (httpRequest != null) { setVersion(HttpVersion.valueOf(httpRequest.protocolVersion().text())); diff --git a/net-http-server-netty/src/test/java/org/xbib/net/http/netty/test/NettyHttp2ServerTest.java b/net-http-server-netty/src/test/java/org/xbib/net/http/netty/test/NettyHttp2ServerTest.java index 3df275d..e200fbc 100644 --- a/net-http-server-netty/src/test/java/org/xbib/net/http/netty/test/NettyHttp2ServerTest.java +++ b/net-http-server-netty/src/test/java/org/xbib/net/http/netty/test/NettyHttp2ServerTest.java @@ -52,9 +52,7 @@ public class NettyHttp2ServerTest { .header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN) .charset(StandardCharsets.UTF_8) .body("Hello, here is my response: " + - ctx.getRequest().getParameter() + " " + - ctx.getRequest().getLocalAddress() + " " + - ctx.getRequest().getRemoteAddress()) + ctx.getRequest().asJson()) .done(); }) .build()) diff --git a/net-http-server-netty/src/test/java/org/xbib/net/http/netty/test/NettyHttpServerTest.java b/net-http-server-netty/src/test/java/org/xbib/net/http/netty/test/NettyHttpServerTest.java index 112c65c..f889ce6 100644 --- a/net-http-server-netty/src/test/java/org/xbib/net/http/netty/test/NettyHttpServerTest.java +++ b/net-http-server-netty/src/test/java/org/xbib/net/http/netty/test/NettyHttpServerTest.java @@ -1,6 +1,9 @@ package org.xbib.net.http.netty.test; import io.netty.bootstrap.Bootstrap; + +import java.io.IOException; +import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import org.junit.jupiter.api.Test; @@ -9,7 +12,9 @@ import org.xbib.net.URL; import org.xbib.net.http.HttpAddress; import org.xbib.net.http.HttpHeaderNames; import org.xbib.net.http.HttpHeaderValues; +import org.xbib.net.http.HttpMethod; import org.xbib.net.http.HttpResponseStatus; +import org.xbib.net.http.client.HttpRequestBuilder; import org.xbib.net.http.client.netty.HttpRequest; import org.xbib.net.http.client.netty.NettyHttpClient; import org.xbib.net.http.client.netty.NettyHttpClientConfig; @@ -22,6 +27,7 @@ import org.xbib.net.http.server.route.HttpRouter; import org.xbib.net.http.server.service.BaseHttpService; import org.xbib.net.http.server.netty.NettyHttpServer; import org.xbib.net.http.server.netty.NettyHttpServerConfig; +import org.xbib.net.util.JsonUtil; import java.nio.charset.StandardCharsets; import java.util.logging.Logger; @@ -50,11 +56,7 @@ public class NettyHttpServerTest { ctx.status(HttpResponseStatus.OK) .header(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.TEXT_PLAIN) .charset(StandardCharsets.UTF_8) - .body("domain" + - " parameter = " + ctx.getRequest().getParameter().toString() + - " local address = " + ctx.getRequest().getLocalAddress() + - " remote address = " + ctx.getRequest().getRemoteAddress() + - " attributes = " + ctx.getAttributes()) + .body(ctx.getRequest().asJson()) .done(); }) .build()) @@ -81,10 +83,19 @@ public class NettyHttpServerTest { HttpRequest request = HttpRequest.get() .setURL(url) .setResponseListener(resp -> { + String body = resp.getBodyAsChars(StandardCharsets.UTF_8).toString(); logger.log(Level.INFO, "got response:" + " status = " + resp.getStatus() + " header = " + resp.getHeaders() + - " body = " + resp.getBodyAsChars(StandardCharsets.UTF_8)); + " body = " + body); + try { + Map map = JsonUtil.toMap(body); + org.xbib.net.http.server.netty.HttpRequest httpRequest = org.xbib.net.http.server.netty.HttpRequest.builder() + .parse(map).build(); + logger.log(Level.INFO, "parsed http request = " + httpRequest.asJson()); + } catch (IOException e) { + throw new RuntimeException(e); + } received.set(true); }) .build(); diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/BaseHttpRequest.java b/net-http-server/src/main/java/org/xbib/net/http/server/BaseHttpRequest.java index bd83f0a..0f20e1b 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/BaseHttpRequest.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/BaseHttpRequest.java @@ -1,9 +1,19 @@ package org.xbib.net.http.server; +import java.io.IOException; import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.xbib.datastructures.common.Pair; +import org.xbib.datastructures.json.tiny.JsonBuilder; import org.xbib.net.Attributes; import org.xbib.net.Parameter; +import org.xbib.net.ParameterException; import org.xbib.net.URL; import org.xbib.net.http.HttpHeaders; import org.xbib.net.http.HttpMethod; @@ -101,4 +111,59 @@ public abstract class BaseHttpRequest implements HttpRequest { public Attributes getAttributes() { return attributes; } + + @Override + public String asJson() { + JsonBuilder jsonBuilder = JsonBuilder.builder(); + try { + jsonBuilder.beginMap(); + Map local = Map.of("host", builder.localAddress.getHostString(), "port", builder.localAddress.getPort()); + jsonBuilder.buildKey("local").buildMap(local); + Map remote = Map.of("host", builder.remoteAddress.getHostString(), "port", builder.remoteAddress.getPort()); + jsonBuilder.buildKey("remote").buildMap(remote); + jsonBuilder.buildKey("baseurl").buildValue(builder.baseURL.toString()); + jsonBuilder.buildKey("version").buildValue(builder.getVersion().toString()); + jsonBuilder.buildKey("method").buildValue(builder.getMethod().toString()); + Map headerMap = builder.getHeaders().entries().stream() + .collect(Collectors.toMap(Pair::getKey, Pair::getValue, (x, y) -> y, LinkedHashMap::new)); + jsonBuilder.buildKey("header").buildMap(headerMap); + jsonBuilder.buildKey("requesturi").buildValue(builder.getRequestURI()); + jsonBuilder.buildKey("requestpath").buildValue(builder.getRequestPath()); + Parameter queryParameter = builder.parameter.get(Parameter.Domain.QUERY); + Map queryParameterMap = queryParameter != null ? + queryParameter.asMultiMap().asMap().entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (x, y) -> y, LinkedHashMap::new)) : Map.of(); + Parameter pathParameter = builder.parameter.get(Parameter.Domain.PATH); + Map pathParameterMap = pathParameter != null ? + pathParameter.asMultiMap().asMap().entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (x, y) -> y, LinkedHashMap::new)) : Map.of(); + Parameter formParameter = builder.parameter.get(Parameter.Domain.FORM); + Map formParameterMap = formParameter != null ? + formParameter.asMultiMap().asMap().entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (x, y) -> y, LinkedHashMap::new)) : Map.of(); + Parameter cookieParameter = builder.parameter.get(Parameter.Domain.COOKIE); + Map cookieParameterMap = cookieParameter != null ? + cookieParameter.asMultiMap().asMap().entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (x, y) -> y, LinkedHashMap::new)) : Map.of(); + Parameter headerParameter = builder.parameter.get(Parameter.Domain.HEADER); + Map headerParameterMap = headerParameter != null ? + headerParameter.asMultiMap().asMap().entrySet().stream() + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (x, y) -> y, LinkedHashMap::new)) : Map.of(); + jsonBuilder.buildKey("parameter").buildMap(Map.of("query", queryParameterMap, + "path", pathParameterMap, + "form", formParameterMap, + "cookie", cookieParameterMap, + "header", headerParameterMap)); + jsonBuilder.buildKey("sequenceid").buildValue(builder.sequenceId); + jsonBuilder.buildKey("streamid").buildValue(builder.streamId); + jsonBuilder.buildKey("requestid").buildValue(builder.requestId); + // body may be large + //jsonBuilder.buildKey("encoding").buildValue("ISO-8859-1"); + //jsonBuilder.buildKey("body").buildValue(StandardCharsets.ISO_8859_1.decode(builder.getBody()).toString()); + jsonBuilder.endMap(); + } catch (IOException | ParameterException e) { + // ignore + } + return jsonBuilder.build(); + } } diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/BaseHttpRequestBuilder.java b/net-http-server/src/main/java/org/xbib/net/http/server/BaseHttpRequestBuilder.java index 959f144..1944d82 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/BaseHttpRequestBuilder.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/BaseHttpRequestBuilder.java @@ -1,13 +1,20 @@ package org.xbib.net.http.server; +import java.net.InetAddress; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Locale; +import java.util.Map; import java.util.Objects; + +import org.xbib.datastructures.common.Maps; import org.xbib.net.Parameter; +import org.xbib.net.ParameterBuilder; import org.xbib.net.URL; import org.xbib.net.URLBuilder; import org.xbib.net.http.HttpAddress; @@ -59,6 +66,50 @@ public abstract class BaseHttpRequestBuilder implements HttpRequestBuilder { this.messages = new ArrayList<>(); } + @SuppressWarnings("unchecked") + public BaseHttpRequestBuilder parse(Map map) { + Map localMap = (Map) map.get("local"); + String localHost = Maps.getString(localMap, "host"); + int localPort = Maps.getInteger(localMap, "port", -1); + setLocalAddress(new InetSocketAddress(localHost, localPort)); + Map remoteMap = (Map) map.get("remote"); + String remoteHost = Maps.getString(remoteMap, "host"); + int remotePort = Maps.getInteger(remoteMap, "port", -1); + setRemoteAddress(new InetSocketAddress(remoteHost, remotePort)); + setBaseURL(URL.from(Maps.getString(map, "baseurl"))); + setVersion(HttpVersion.valueOf(Maps.getString(map, "version"))); + setMethod(HttpMethod.valueOf(Maps.getString(map, "method"))); + HttpHeaders httpHeaders = new HttpHeaders(); + Map headerMap = (Map) map.get("header"); + if (headerMap != null) { + headerMap.forEach((k, v) -> { + if (v instanceof Iterable ) { + httpHeaders.add(k, (Iterable) v); + } else { + httpHeaders.add(k, v.toString()); + } + }); + } + setHeaders(httpHeaders); + setRequestURI(Maps.getString(map, "requesturi")); + setRequestPath(Maps.getString(map, "requestpath")); + ParameterBuilder parameterBuilder = Parameter.builder().domain(Parameter.Domain.QUERY); + Map parameterMap = (Map) map.get("parameter"); + Arrays.asList(Parameter.Domain.QUERY, Parameter.Domain.PATH, Parameter.Domain.FORM, Parameter.Domain.COOKIE, Parameter.Domain.HEADER).forEach(d -> { + Map m = (Map) parameterMap.get(d.name().toLowerCase(Locale.ROOT)); + if (m != null) { + ParameterBuilder p = Parameter.builder().domain(d); + m.forEach(p::add); + parameterBuilder.add(p.build()); + } + }); + setParameter(parameterBuilder.build()); + setSequenceId(Maps.getInteger(map, "sequenceid", 0)); + setStreamId(Maps.getInteger(map, "streamid", -1)); + setRequestId(Maps.getLong(map, "requestid", -1L)); + return this; + } + @Override public BaseHttpRequestBuilder setContext(HttpRouterContext httpRouterContext) { if (done) { diff --git a/net-http-server/src/main/java/org/xbib/net/http/server/HttpRequest.java b/net-http-server/src/main/java/org/xbib/net/http/server/HttpRequest.java index 1e241d6..916389b 100644 --- a/net-http-server/src/main/java/org/xbib/net/http/server/HttpRequest.java +++ b/net-http-server/src/main/java/org/xbib/net/http/server/HttpRequest.java @@ -43,4 +43,6 @@ public interface HttpRequest extends Request { List getMessages(); Attributes getAttributes(); + + String asJson(); } diff --git a/settings.gradle b/settings.gradle index 6c16693..822de8a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -6,7 +6,7 @@ dependencyResolutionManagement { version('groovy', '4.0.13') version('netty', '4.1.96.Final') version('netty-tcnative', '2.0.61.Final') - version('datastructures', '2.3.0') + version('datastructures', '2.3.1') version('config', '5.0.3') version('net', '3.3.3') library('junit-jupiter-api', 'org.junit.jupiter', 'junit-jupiter-api').versionRef('junit')