From 093646b94dca963fcf44c0615771a646f62c54c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Prante?= Date: Mon, 3 Apr 2023 17:17:58 +0200 Subject: [PATCH] update to groovy 4.0.11, netty 4.1.90, xbib net 3.1.0, use multimap for HTTP request --- gradle.properties | 2 +- gradle/test/junit5.gradle | 2 +- .../xbib/netty/http/client/api/Request.java | 3 +- .../netty/http/client/rest/RestClient.java | 24 ++-- .../org/xbib/netty/http/client/Client.java | 3 +- .../http/client/test/RequestBuilderTest.java | 18 ++- .../test/http1/FileDescriptorLeakTest.java | 16 +-- .../http/client/test/http2/XbibTest.java | 20 ++-- .../netty/http/common/HttpParameters.java | 110 ++++++++++++------ .../src/test/resources/logging.properties | 5 + .../netty/http/server/HttpServerRequest.java | 45 +++---- .../org/xbib/netty/http/server/Server.java | 3 +- .../http/server/test/http1/PostTest.java | 30 ++--- .../http/server/test/http2/PostTest.java | 12 +- .../src/test/resources/logging.properties | 5 + settings.gradle | 16 +-- 16 files changed, 177 insertions(+), 137 deletions(-) create mode 100644 netty-http-common/src/test/resources/logging.properties create mode 100644 netty-http-server/src/test/resources/logging.properties diff --git a/gradle.properties b/gradle.properties index 2b799a0..4422758 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group = org.xbib name = netty-http -version = 4.1.85.0 +version = 4.1.90.0 org.gradle.warning.mode = ALL diff --git a/gradle/test/junit5.gradle b/gradle/test/junit5.gradle index 52a3d63..1de8bec 100644 --- a/gradle/test/junit5.gradle +++ b/gradle/test/junit5.gradle @@ -7,7 +7,7 @@ dependencies { test { useJUnitPlatform() - failFast = true + failFast = false maxHeapSize '1g' systemProperty 'java.util.logging.config.file', 'src/test/resources/logging.properties' testLogging { diff --git a/netty-http-client-api/src/main/java/org/xbib/netty/http/client/api/Request.java b/netty-http-client-api/src/main/java/org/xbib/netty/http/client/api/Request.java index 9aef6c2..c5437fc 100644 --- a/netty-http-client-api/src/main/java/org/xbib/netty/http/client/api/Request.java +++ b/netty-http-client-api/src/main/java/org/xbib/netty/http/client/api/Request.java @@ -42,7 +42,7 @@ import java.util.concurrent.CompletableFuture; /** * HTTP client request. */ -public final class Request implements AutoCloseable { +public final class Request { private final URL url; @@ -202,7 +202,6 @@ public final class Request implements AutoCloseable { } } - @Override public void close() throws IOException { release(); } diff --git a/netty-http-client-rest/src/main/java/org/xbib/netty/http/client/rest/RestClient.java b/netty-http-client-rest/src/main/java/org/xbib/netty/http/client/rest/RestClient.java index f1f0612..4b8e2ca 100644 --- a/netty-http-client-rest/src/main/java/org/xbib/netty/http/client/rest/RestClient.java +++ b/netty-http-client-rest/src/main/java/org/xbib/netty/http/client/rest/RestClient.java @@ -63,21 +63,19 @@ public class RestClient { HttpMethod httpMethod) throws IOException { URL url = URL.create(urlString); RestClient restClient = new RestClient(); - try (Client client = Client.builder() + Client client = Client.builder() .setThreadCount(2) // for redirect - .build()) { - Request.Builder requestBuilder = Request.builder(httpMethod).url(url); - if (body != null) { - ByteBuf byteBuf = client.getByteBufAllocator().buffer(); - byteBuf.writeCharSequence(body, charset); - requestBuilder.content(byteBuf); - } - client.newTransport(HttpAddress.http1(url)) - .execute(requestBuilder.setResponseListener(restClient::setResponse).build()) - .close(); - } catch (Exception e) { - throw new IOException(e); + .build(); + Request.Builder requestBuilder = Request.builder(httpMethod).url(url); + if (body != null) { + ByteBuf byteBuf = client.getByteBufAllocator().buffer(); + byteBuf.writeCharSequence(body, charset); + requestBuilder.content(byteBuf); } + client.newTransport(HttpAddress.http1(url)) + .execute(requestBuilder.setResponseListener(restClient::setResponse).build()) + .close(); + client.close(); return restClient; } } diff --git a/netty-http-client/src/main/java/org/xbib/netty/http/client/Client.java b/netty-http-client/src/main/java/org/xbib/netty/http/client/Client.java index 5674bf3..af2ee58 100644 --- a/netty-http-client/src/main/java/org/xbib/netty/http/client/Client.java +++ b/netty-http-client/src/main/java/org/xbib/netty/http/client/Client.java @@ -61,7 +61,7 @@ import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; -public final class Client implements AutoCloseable { +public final class Client { private static final Logger logger = Logger.getLogger(Client.class.getName()); @@ -366,7 +366,6 @@ public final class Client implements AutoCloseable { closeAndRemove(transport); } - @Override public void close() throws IOException { shutdownGracefully(); } diff --git a/netty-http-client/src/test/java/org/xbib/netty/http/client/test/RequestBuilderTest.java b/netty-http-client/src/test/java/org/xbib/netty/http/client/test/RequestBuilderTest.java index 384b5e7..7413e5c 100644 --- a/netty-http-client/src/test/java/org/xbib/netty/http/client/test/RequestBuilderTest.java +++ b/netty-http-client/src/test/java/org/xbib/netty/http/client/test/RequestBuilderTest.java @@ -128,17 +128,21 @@ class RequestBuilderTest { .url("https://google.com? a = b") .build(); assertEquals("google.com", request.url().getHost()); - assertEquals("https://google.com?%20a%20=%20b", request.absolute()); - assertEquals("?%20a%20=%20b", request.relative()); + assertEquals("https://google.com? a = b", request.absolute()); + assertEquals("? a = b", request.relative()); + assertEquals(" a = b", request.url().getQuery()); + assertEquals(" a = b", request.url().getDecodedQuery()); assertEquals("https://google.com? a = b", request.url().toString()); - assertEquals("https://google.com?%20a%20=%20b", request.url().toExternalForm()); + assertEquals("https://google.com? a = b", request.url().toExternalForm()); request = Request.get() .url("https://google.com?%20a%20=%20b") .build(); assertEquals("google.com", request.url().getHost()); assertEquals("https://google.com?%20a%20=%20b", request.absolute()); assertEquals("?%20a%20=%20b", request.relative()); - assertEquals("https://google.com? a = b", request.url().toString()); + assertEquals("%20a%20=%20b", request.url().getQuery()); + assertEquals(" a = b", request.url().getDecodedQuery()); + assertEquals("https://google.com?%20a%20=%20b", request.url().toString()); assertEquals("https://google.com?%20a%20=%20b", request.url().toExternalForm()); } @@ -149,6 +153,10 @@ class RequestBuilderTest { requestBuilder.addParameter("param" + i, "value" + i); } Request request = requestBuilder.build(); - assertEquals(18276, request.absolute().length()); + assertEquals(37796, request.absolute().length()); + Request parsedRequest = Request.get() + .url(request.url()) + .build(); + assertEquals(37796, parsedRequest.absolute().length()); } } diff --git a/netty-http-client/src/test/java/org/xbib/netty/http/client/test/http1/FileDescriptorLeakTest.java b/netty-http-client/src/test/java/org/xbib/netty/http/client/test/http1/FileDescriptorLeakTest.java index 394e63e..19225a8 100644 --- a/netty-http-client/src/test/java/org/xbib/netty/http/client/test/http1/FileDescriptorLeakTest.java +++ b/netty-http-client/src/test/java/org/xbib/netty/http/client/test/http1/FileDescriptorLeakTest.java @@ -22,14 +22,14 @@ class FileDescriptorLeakTest { if (os instanceof UnixOperatingSystemMXBean) { logger.info("before: number of open file descriptor : " + ((UnixOperatingSystemMXBean) os).getOpenFileDescriptorCount()); } - try (Client client = Client.builder().setThreadCount(1).build()) { - Request request = Request.get().url("http://xbib.org") - .setResponseListener(resp -> { - logger.log(Level.INFO, "status = " + resp.getStatus()); - }) - .build(); - client.execute(request); - } + Client client = Client.builder().setThreadCount(1).build(); + Request request = Request.get().url("http://xbib.org") + .setResponseListener(resp -> { + logger.log(Level.INFO, "status = " + resp.getStatus()); + }) + .build(); + client.execute(request); + client.close(); if (os instanceof UnixOperatingSystemMXBean){ logger.info("after: number of open file descriptor : " + ((UnixOperatingSystemMXBean) os).getOpenFileDescriptorCount()); } diff --git a/netty-http-client/src/test/java/org/xbib/netty/http/client/test/http2/XbibTest.java b/netty-http-client/src/test/java/org/xbib/netty/http/client/test/http2/XbibTest.java index 8075f29..be38479 100644 --- a/netty-http-client/src/test/java/org/xbib/netty/http/client/test/http2/XbibTest.java +++ b/netty-http-client/src/test/java/org/xbib/netty/http/client/test/http2/XbibTest.java @@ -13,17 +13,17 @@ public class XbibTest { @Test void testXbib() throws Exception { - try (Client client = Client.builder() + Client client = Client.builder() .enableDebug() - .build()) { - Request request = Request.get() - .url("https://xbib.org/") - .setVersion("HTTP/2.0") - .setResponseListener(resp -> logger.log(Level.INFO, "got HTTP/2 response: " + - resp.getHeaders() + resp.getBodyAsString(StandardCharsets.UTF_8))) - .build(); - client.execute(request).get().close(); - } + .build(); + Request request = Request.get() + .url("https://xbib.org/") + .setVersion("HTTP/2.0") + .setResponseListener(resp -> logger.log(Level.INFO, "got HTTP/2 response: " + + resp.getHeaders() + resp.getBodyAsString(StandardCharsets.UTF_8))) + .build(); + client.execute(request).get().close(); + client.close(); } } diff --git a/netty-http-common/src/main/java/org/xbib/netty/http/common/HttpParameters.java b/netty-http-common/src/main/java/org/xbib/netty/http/common/HttpParameters.java index 29f6d0a..215cff8 100644 --- a/netty-http-common/src/main/java/org/xbib/netty/http/common/HttpParameters.java +++ b/netty-http-common/src/main/java/org/xbib/netty/http/common/HttpParameters.java @@ -1,9 +1,13 @@ package org.xbib.netty.http.common; import io.netty.handler.codec.http.HttpHeaderValues; +import org.xbib.datastructures.common.MultiMap; +import org.xbib.datastructures.common.Pair; +import org.xbib.datastructures.common.TreeMultiMap; +import org.xbib.net.PercentDecoder; import org.xbib.net.PercentEncoder; import org.xbib.net.PercentEncoders; -import org.xbib.netty.http.common.util.CaseInsensitiveParameters; + import java.nio.charset.CharacterCodingException; import java.nio.charset.Charset; import java.nio.charset.MalformedInputException; @@ -13,26 +17,26 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; /** - * A limited multi-map of HTTP request parameters. Each key references a - * limited set of parameters collected from the request during message - * signing. Parameter values are sorted as per - * OAuth specification. + * A multi-map of HTTP request parameters. Each key references a + * set of parameters collected from the request during message + * signing. * Every key/value pair will be percent-encoded upon insertion. - * This class has special semantics tailored to - * being useful for message signing; it's not a general purpose collection class - * to handle request parameters. */ -public class HttpParameters extends CaseInsensitiveParameters { +@SuppressWarnings("serial") +public class HttpParameters { - private static final String EQUALS = "="; + private static final Logger logger = Logger.getLogger(HttpParameters.class.getName()); - private static final String AMPERSAND = "&"; + private static final char EQUAL_CHAR = '='; + private static final char AMPERSAND_CHAR = '&'; - private final int sizeLimit; + private final MultiMap multiMap; - private final int elementSizeLimit; + private final PercentDecoder percentDecoder; private final PercentEncoder percentEncoder; @@ -41,24 +45,16 @@ public class HttpParameters extends CaseInsensitiveParameters { private final Charset encoding; public HttpParameters() { - this(1024, 1024, 65536, - HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED, StandardCharsets.UTF_8); + this(HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED, StandardCharsets.UTF_8); } public HttpParameters(CharSequence contentType) { - this(1024, 1024, 65536, - contentType, StandardCharsets.UTF_8); + this(contentType, StandardCharsets.UTF_8); } public HttpParameters(CharSequence contentType, Charset charset) { - this(1024, 1024, 65536, - contentType, charset); - } - - public HttpParameters(int maxParam, int sizeLimit, int elementSizeLimit, - CharSequence contentType, Charset charset) { - this.sizeLimit = sizeLimit; - this.elementSizeLimit = elementSizeLimit; + this.multiMap = new TreeMultiMap<>(); + this.percentDecoder = new PercentDecoder(); this.percentEncoder = PercentEncoders.getQueryEncoder(charset); this.contentType = contentType; this.encoding = charset; @@ -72,12 +68,26 @@ public class HttpParameters extends CaseInsensitiveParameters { return encoding; } - public Collection put(String key, Collection values, boolean percentEncode) { - remove(key); + public String get(String key) { + Collection collection = multiMap.get(key); + if (collection != null) { + Iterator iterator = collection.iterator(); + if (iterator.hasNext()) { + return iterator.next(); + } + } + return null; + } + + public Collection getAll(String key) { + return multiMap.get(key); + } + + public void put(String key, Collection values, boolean percentEncode) { + multiMap.remove(key); for (String v : values) { add(key, v, percentEncode); } - return getAll(key); } /** @@ -121,7 +131,7 @@ public class HttpParameters extends CaseInsensitiveParameters { try { String k = percentEncode ? percentEncoder.encode(key) : key; String v = percentEncode ? percentEncoder.encode(value) : value; - super.add(k, v); + multiMap.put(k, v); } catch (CharacterCodingException e) { throw new IllegalArgumentException(e); } @@ -130,10 +140,10 @@ public class HttpParameters extends CaseInsensitiveParameters { public String getAsQueryString(boolean percentEncode) throws MalformedInputException, UnmappableCharacterException { List list = new ArrayList<>(); - for (String key : super.names()) { + for (String key : multiMap.keySet()) { list.add(getAsQueryString(key, percentEncode)); } - return String.join(AMPERSAND, list); + return String.join(String.valueOf(AMPERSAND_CHAR), list); } /** @@ -164,20 +174,46 @@ public class HttpParameters extends CaseInsensitiveParameters { public String getAsQueryString(String key, boolean percentEncode) throws MalformedInputException, UnmappableCharacterException { String k = percentEncode ? percentEncoder.encode(key) : key; - Collection values = getAll(k); - if (values == null) { - return k + EQUALS; - } + Collection values = multiMap.asMap().get(key); Iterator it = values.iterator(); StringBuilder sb = new StringBuilder(); while (it.hasNext()) { String v = it.next(); v = percentEncode ? percentEncoder.encode(v) : v; - sb.append(k).append(EQUALS).append(v); + sb.append(k).append(EQUAL_CHAR).append(v); if (it.hasNext()) { - sb.append(AMPERSAND); + sb.append(AMPERSAND_CHAR); } } return sb.toString(); } + + public void addPercentEncodedBody(String body) { + if (body == null) { + return; + } + // watch out for "plus" encoding, replace it with a space character + String s = body.replace('+', ' '); + while (s != null) { + Pair pairs = indexOf(AMPERSAND_CHAR, s); + Pair pair = indexOf(EQUAL_CHAR, pairs.getKey()); + if (pair.getKey() != null && !pair.getKey().isEmpty()) { + try { + String key = percentDecoder.decode(pair.getKey()); + String value = pair.getValue() instanceof CharSequence ? percentDecoder.decode((CharSequence) pair.getValue()) : pair.getValue().toString(); + addRaw(key, value); + } catch (MalformedInputException | UnmappableCharacterException e) { + logger.log(Level.WARNING, "unable to decode parameter in body: " + e.getMessage(), e); + } + } + s = pairs.getValue() !=null ? pairs.getValue().toString() : null; + } + } + + private static Pair indexOf(char ch, String input) { + int i = input.indexOf(ch); + String k = i >= 0 ? input.substring(0, i) : input; + Object v = i >= 0 ? input.substring(i + 1) : null; + return Pair.of(k, v); + } } diff --git a/netty-http-common/src/test/resources/logging.properties b/netty-http-common/src/test/resources/logging.properties new file mode 100644 index 0000000..d9913d4 --- /dev/null +++ b/netty-http-common/src/test/resources/logging.properties @@ -0,0 +1,5 @@ +handlers=java.util.logging.FileHandler, java.util.logging.ConsoleHandler +.level=ALL +java.util.logging.ConsoleHandler.level=ALL +java.util.logging.ConsoleHandler.formatter=org.xbib.net.util.ThreadLoggingFormatter +jdk.event.security.level=INFO diff --git a/netty-http-server/src/main/java/org/xbib/netty/http/server/HttpServerRequest.java b/netty-http-server/src/main/java/org/xbib/netty/http/server/HttpServerRequest.java index 2b8d68d..4f428d9 100644 --- a/netty-http-server/src/main/java/org/xbib/netty/http/server/HttpServerRequest.java +++ b/netty-http-server/src/main/java/org/xbib/netty/http/server/HttpServerRequest.java @@ -7,10 +7,6 @@ import io.netty.handler.codec.http.HttpHeaderValues; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpUtil; -import org.xbib.datastructures.common.Pair; -import org.xbib.net.Parameter; -import org.xbib.net.ParameterBuilder; -import org.xbib.net.PercentDecoder; import org.xbib.net.URL; import org.xbib.netty.http.common.HttpParameters; import org.xbib.netty.http.server.api.Domain; @@ -363,42 +359,31 @@ public class HttpServerRequest implements ServerRequest { public ServerRequest build() { // build URL and parameters Charset charset = HttpUtil.getCharset(fullHttpRequest, StandardCharsets.UTF_8); + CharSequence mimeType = HttpUtil.getMimeType(fullHttpRequest); + this.parameters = new HttpParameters(mimeType, charset); // creates path, query params, fragment this.url = URL.builder() .charset(charset, CodingErrorAction.REPLACE) .path(fullHttpRequest.uri()) // creates path, query params, fragment .build(); - ParameterBuilder queryParameters = Parameter.builder(); - //url.getQueryParams(); - CharSequence mimeType = HttpUtil.getMimeType(fullHttpRequest); - ByteBuf byteBuf = fullHttpRequest.content(); - if (byteBuf != null) { - if (fullHttpRequest.method().equals(HttpMethod.POST)) { - String params; + url.getQueryParams().forEach(p -> parameters.add(p.getKey(), p.getValue().toString())); + if (fullHttpRequest.method().equals(HttpMethod.POST)) { + ByteBuf byteBuf = fullHttpRequest.content(); + if (byteBuf != null) { + Charset htmlCharset = HttpUtil.getCharset(fullHttpRequest, StandardCharsets.ISO_8859_1); + String body = byteBuf.toString(htmlCharset); // https://www.w3.org/TR/html4/interact/forms.html#h-17.13.4 if (mimeType != null && HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.toString().equals(mimeType.toString())) { - Charset htmlCharset = HttpUtil.getCharset(fullHttpRequest, StandardCharsets.ISO_8859_1); - params = byteBuf.toString(htmlCharset).replace('+', ' '); - if (logger.isLoggable(Level.FINER)) { - logger.log(Level.FINER, "html form, charset = " + htmlCharset + " param body = " + params); - } - queryParameters.addPercentEncodedBody(params); + logger.log(Level.INFO, "html form, charset = " + htmlCharset + " body = " + body); + parameters.addPercentEncodedBody(body); + } else { + logger.log(Level.WARNING, "unable to process POST request, mime type = " + mimeType); } + } else { + logger.log(Level.WARNING, "unable to process POST request,body is empty"); } } - // copy to HTTP parameters but percent-decoded (looks very clumsy) - PercentDecoder percentDecoder = new PercentDecoder(charset.newDecoder() - .onMalformedInput(CodingErrorAction.REPLACE) - .onUnmappableCharacter(CodingErrorAction.REPLACE)); - this.parameters = new HttpParameters(mimeType, charset); - for (Pair pair : queryParameters.build()) { - try { - parameters.addRaw(percentDecoder.decode(pair.getKey()), percentDecoder.decode(pair.getValue().toString())); - } catch (Exception e) { - // does not happen - throw new IllegalArgumentException(pair.toString()); - } - } + logger.log(Level.FINER, "HTTP server complete"); return new HttpServerRequest(this); } diff --git a/netty-http-server/src/main/java/org/xbib/netty/http/server/Server.java b/netty-http-server/src/main/java/org/xbib/netty/http/server/Server.java index 6dd02f9..7e289e7 100644 --- a/netty-http-server/src/main/java/org/xbib/netty/http/server/Server.java +++ b/netty-http-server/src/main/java/org/xbib/netty/http/server/Server.java @@ -56,7 +56,7 @@ import java.util.logging.Logger; /** * HTTP server. */ -public final class Server implements AutoCloseable { +public final class Server { private static final Logger logger = Logger.getLogger(Server.class.getName()); @@ -168,7 +168,6 @@ public final class Server implements AutoCloseable { accept().channel().closeFuture().sync(); } - @Override public void close() { try { shutdownGracefully(); diff --git a/netty-http-server/src/test/java/org/xbib/netty/http/server/test/http1/PostTest.java b/netty-http-server/src/test/java/org/xbib/netty/http/server/test/http1/PostTest.java index 050a007..8c53e4a 100644 --- a/netty-http-server/src/test/java/org/xbib/netty/http/server/test/http1/PostTest.java +++ b/netty-http-server/src/test/java/org/xbib/netty/http/server/test/http1/PostTest.java @@ -1,5 +1,6 @@ package org.xbib.netty.http.server.test.http1; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -34,7 +35,9 @@ class PostTest { HttpServerDomain domain = HttpServerDomain.builder(httpAddress) .singleEndpoint("/post", "/**", (req, resp) -> { HttpParameters parameters = req.getParameters(); - logger.log(Level.INFO, "got request " + parameters.toString() + ", sending OK"); + logger.log(Level.INFO, "server got request " + parameters + + " body = " + req.getContent(StandardCharsets.UTF_8) + + " withspace = " + parameters.get("withspace")); if ("Hello World".equals(parameters.get("withspace"))) { success2.set(true); } @@ -86,7 +89,7 @@ class PostTest { HttpServerDomain domain = HttpServerDomain.builder(httpAddress) .singleEndpoint("/post", "/**", (req, resp) -> { HttpParameters parameters = req.getParameters(); - logger.log(Level.INFO, "got request " + parameters.toString() + ", sending OK"); + logger.log(Level.INFO, "server got request " + parameters + " body = " + req.getContent(StandardCharsets.UTF_8)); if ("Hello World".equals(parameters.get("withspace"))) { success2.set(true); } @@ -128,7 +131,7 @@ class PostTest { } @Test - void testFormPostHttp1() throws Exception { + void testFormPostHttp1WithCorrectContentType() throws Exception { final AtomicBoolean success1 = new AtomicBoolean(false); final AtomicBoolean success2 = new AtomicBoolean(false); final AtomicBoolean success3 = new AtomicBoolean(false); @@ -137,7 +140,7 @@ class PostTest { HttpServerDomain domain = HttpServerDomain.builder(httpAddress) .singleEndpoint("/post", "/**", (req, resp) -> { HttpParameters parameters = req.getParameters(); - logger.log(Level.INFO, "got request " + parameters.toString() + ", sending OK"); + logger.log(Level.INFO, "server got request " + parameters + " body = " + req.getContent(StandardCharsets.UTF_8)); if ("Hello World".equals(parameters.get("withplus"))) { success2.set(true); } @@ -164,6 +167,7 @@ class PostTest { }; Request postRequest = Request.post().setVersion(HttpVersion.HTTP_1_1) .url(server.getServerConfig().getAddress().base().resolve("/post/test.txt")) + // the corrent content type .contentType(HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED, StandardCharsets.UTF_8) .addParameter("a", "b") // test 'plus' encoding @@ -185,7 +189,7 @@ class PostTest { } @Test - void testTextPlainPostHttp1() throws Exception { + void testTextPlainPostHttp1MustFail() throws Exception { final AtomicBoolean success1 = new AtomicBoolean(false); final AtomicBoolean success2 = new AtomicBoolean(false); final AtomicBoolean success3 = new AtomicBoolean(false); @@ -194,7 +198,7 @@ class PostTest { HttpServerDomain domain = HttpServerDomain.builder(httpAddress) .singleEndpoint("/post", "/**", (req, resp) -> { HttpParameters parameters = req.getParameters(); - logger.log(Level.INFO, "got request " + parameters.toString() + ", sending OK"); + logger.log(Level.INFO, "server got request " + parameters + " body = " + req.getContent(StandardCharsets.UTF_8)); if ("Hello World".equals(parameters.get("withoutplus"))) { success2.set(true); } @@ -223,7 +227,7 @@ class PostTest { .setVersion(HttpVersion.HTTP_1_1) .url(server.getServerConfig().getAddress().base().resolve("/post/test.txt")) .contentType(HttpHeaderValues.TEXT_PLAIN, StandardCharsets.UTF_8) - // you can not pass form parameters on content type "text/plain" + // you can not pass form parameters on content type "text/plain" :-) .addParameter("a", "b") .addParameter("my param", "my value") .addParameter("withoutplus", "Hello World") @@ -237,9 +241,9 @@ class PostTest { logger.log(Level.INFO, "server and client shut down"); } assertTrue(success1.get()); - assertTrue(success2.get()); - assertTrue(success3.get()); - assertTrue(success4.get()); + assertFalse(success2.get()); + assertFalse(success3.get()); + assertFalse(success4.get()); } @Test @@ -252,10 +256,11 @@ class PostTest { .singleEndpoint("/post", "/**", (req, resp) -> { HttpParameters parameters = req.getParameters(); logger.log(Level.INFO, "got request " + parameters.toString() + ", sending OK"); + // unable to decode without underflow if ("myÿvalue".equals(parameters.get("my param"))) { success1.set(true); } - if ("bÿc".equals(parameters.get("a"))) { + if ("b%25YYc".equals(parameters.get("a"))) { success2.set(true); } resp.getBuilder().setStatus(HttpResponseStatus.OK.code()).build().flush(); @@ -285,9 +290,8 @@ class PostTest { client.shutdownGracefully(); logger.log(Level.INFO, "server and client shut down"); } - assertTrue(success1.get()); + assertFalse(success1.get()); assertTrue(success2.get()); assertTrue(success3.get()); } - } diff --git a/netty-http-server/src/test/java/org/xbib/netty/http/server/test/http2/PostTest.java b/netty-http-server/src/test/java/org/xbib/netty/http/server/test/http2/PostTest.java index bc449bc..2000022 100644 --- a/netty-http-server/src/test/java/org/xbib/netty/http/server/test/http2/PostTest.java +++ b/netty-http-server/src/test/java/org/xbib/netty/http/server/test/http2/PostTest.java @@ -20,6 +20,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(NettyHttpTestExtension.class) @@ -240,9 +241,9 @@ class PostTest { logger.log(Level.INFO, "server and client shut down"); } assertTrue(success1.get()); - assertTrue(success2.get()); - assertTrue(success3.get()); - assertTrue(success4.get()); + assertFalse(success2.get()); + assertFalse(success3.get()); + assertFalse(success4.get()); } @Test @@ -255,10 +256,11 @@ class PostTest { .singleEndpoint("/post", "/**", (req, resp) -> { HttpParameters parameters = req.getParameters(); logger.log(Level.INFO, "got request " + parameters.toString() + ", sending OK"); + // unable to decode if ("myÿvalue".equals(parameters.get("my param"))) { success1.set(true); } - if ("bÿc".equals(parameters.get("a"))) { + if ("b%25YYc".equals(parameters.get("a"))) { success2.set(true); } resp.getBuilder().setStatus(HttpResponseStatus.OK.code()).build().flush(); @@ -289,7 +291,7 @@ class PostTest { client.shutdownGracefully(); logger.log(Level.INFO, "server and client shut down"); } - assertTrue(success1.get()); + assertFalse(success1.get()); assertTrue(success2.get()); assertTrue(success3.get()); } diff --git a/netty-http-server/src/test/resources/logging.properties b/netty-http-server/src/test/resources/logging.properties new file mode 100644 index 0000000..d9913d4 --- /dev/null +++ b/netty-http-server/src/test/resources/logging.properties @@ -0,0 +1,5 @@ +handlers=java.util.logging.FileHandler, java.util.logging.ConsoleHandler +.level=ALL +java.util.logging.ConsoleHandler.level=ALL +java.util.logging.ConsoleHandler.formatter=org.xbib.net.util.ThreadLoggingFormatter +jdk.event.security.level=INFO diff --git a/settings.gradle b/settings.gradle index a39d844..e611dc8 100644 --- a/settings.gradle +++ b/settings.gradle @@ -10,11 +10,11 @@ dependencyResolutionManagement { versionCatalogs { libs { version('gradle', '7.5.1') - version('groovy', '3.0.10') - version('spock', '2.0-groovy-3.0') - version('junit', '5.9.1') - version('netty', '4.1.85.Final') - version('netty-tcnative', '2.0.54.Final') + version('groovy', '4.0.11') + version('spock', '2.4-M1-groovy-4.0') + version('junit', '5.9.2') + version('netty', '4.1.90.Final') + version('netty-tcnative', '2.0.59.Final') library('groovy-core', 'org.codehaus.groovy', 'groovy').versionRef('groovy') library('spock-core', 'org.spockframework', 'spock-core').versionRef('spock') library('spock-junit4', 'org.spockframework', 'spock-junit4').versionRef('spock') @@ -27,13 +27,13 @@ dependencyResolutionManagement { library('netty-epoll', 'io.netty', 'netty-transport-native-epoll').versionRef('netty') library('netty-kqueue', 'io.netty', 'netty-transport-native-kqueue').versionRef('netty') library('netty-boringssl', 'io.netty', 'netty-tcnative-boringssl-static').versionRef('netty-tcnative') - library('net', 'org.xbib', 'net').version('3.0.2') - library('net-path', 'org.xbib', 'net-path').version('3.0.2') library('bouncycastle', 'org.bouncycastle', 'bcpkix-jdk18on').version('1.72') library('conscrypt', 'org.conscrypt', 'conscrypt-openjdk-uber').version('2.5.2') library('jackson', 'com.fasterxml.jackson.core', 'jackson-databind').version('2.12.7') - library('guice', 'org.xbib', 'guice').version('5.0.1.0') library('javassist', 'org.javassist', 'javassist').version('3.29.1-GA') + library('guice', 'org.xbib', 'guice').version('5.0.1.0') + library('net', 'org.xbib', 'net').version('3.1.0') + library('net-path', 'org.xbib', 'net-path').version('3.1.0') } } }