update to groovy 4.0.11, netty 4.1.90, xbib net 3.1.0, use multimap for HTTP request
This commit is contained in:
parent
9f55b8692c
commit
093646b94d
16 changed files with 177 additions and 137 deletions
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
* <a href="http://oauth.net/core/1.0a/#anchor13">OAuth specification</a>.
|
||||
* 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<String, String> 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<String> put(String key, Collection<String> values, boolean percentEncode) {
|
||||
remove(key);
|
||||
public String get(String key) {
|
||||
Collection<String> collection = multiMap.get(key);
|
||||
if (collection != null) {
|
||||
Iterator<String> iterator = collection.iterator();
|
||||
if (iterator.hasNext()) {
|
||||
return iterator.next();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Collection<String> getAll(String key) {
|
||||
return multiMap.get(key);
|
||||
}
|
||||
|
||||
public void put(String key, Collection<String> 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<String> 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<String> values = getAll(k);
|
||||
if (values == null) {
|
||||
return k + EQUALS;
|
||||
}
|
||||
Collection<String> values = multiMap.asMap().get(key);
|
||||
Iterator<String> 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<String, Object> pairs = indexOf(AMPERSAND_CHAR, s);
|
||||
Pair<String, Object> 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<String, Object> 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);
|
||||
}
|
||||
}
|
||||
|
|
5
netty-http-common/src/test/resources/logging.properties
Normal file
5
netty-http-common/src/test/resources/logging.properties
Normal file
|
@ -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
|
|
@ -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<String, Object> 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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
5
netty-http-server/src/test/resources/logging.properties
Normal file
5
netty-http-server/src/test/resources/logging.properties
Normal file
|
@ -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
|
|
@ -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')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue