fix parameter/multimap handling with duplicates, add test
This commit is contained in:
parent
3c7e102765
commit
0a31fca8fe
7 changed files with 135 additions and 60 deletions
|
@ -1,3 +1,3 @@
|
|||
group = org.xbib
|
||||
name = net-http
|
||||
version = 4.0.8
|
||||
version = 4.0.9
|
||||
|
|
|
@ -3,6 +3,7 @@ dependencies {
|
|||
api libs.netty.codec.http2
|
||||
testImplementation project(':net-http-client-netty')
|
||||
testImplementation project(':net-http-template-groovy')
|
||||
testImplementation libs.datastructures.json.tiny
|
||||
}
|
||||
|
||||
test {
|
||||
|
|
|
@ -35,7 +35,7 @@ public class HttpRequest extends BaseHttpRequest {
|
|||
|
||||
@Override
|
||||
public InputStream getInputStream() {
|
||||
return builder.byteBuffer != null ? new ByteBufferInputStream(builder.byteBuffer) : null;
|
||||
return builder.getBody() != null ? new ByteBufferInputStream(builder.getBody()) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -49,7 +49,7 @@ public class HttpRequest extends BaseHttpRequest {
|
|||
return "HttpRequest[method=" + builder.getMethod() +
|
||||
",version=" + builder.getVersion() +
|
||||
",parameter=" + builder.getParameter() +
|
||||
",body=" + (builder.byteBuffer != null) +
|
||||
",body=" + (builder.getBody() != null) +
|
||||
"]";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,10 +27,6 @@ public class HttpRequestBuilder extends BaseHttpRequestBuilder {
|
|||
|
||||
private FullHttpRequest httpRequest;
|
||||
|
||||
protected ByteBuffer byteBuffer;
|
||||
|
||||
protected CharBuffer charBuffer;
|
||||
|
||||
protected HttpRequestBuilder() {
|
||||
}
|
||||
|
||||
|
@ -59,25 +55,12 @@ public class HttpRequestBuilder extends BaseHttpRequestBuilder {
|
|||
if (fullHttpRequest.content() != null) {
|
||||
ByteBuf byteBuf = fullHttpRequest.content();
|
||||
byte[] bytes = ByteBufUtil.getBytes(byteBuf);
|
||||
byteBuffer = ByteBuffer.wrap(bytes);
|
||||
this.byteBuffer = ByteBuffer.wrap(bytes);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer getBody() {
|
||||
return byteBuffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharBuffer getBodyAsChars(Charset charset) {
|
||||
if (charBuffer == null) {
|
||||
charBuffer = byteBuffer != null ? charset.decode(byteBuffer) : null;
|
||||
}
|
||||
return charBuffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpRequestBuilder setAddress(HttpAddress httpAddress) {
|
||||
super.setAddress(httpAddress);
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
package org.xbib.net.http.netty.test;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.xbib.datastructures.api.Builder;
|
||||
import org.xbib.datastructures.json.tiny.JsonBuilder;
|
||||
import org.xbib.net.Parameter;
|
||||
import org.xbib.net.http.HttpHeaders;
|
||||
import org.xbib.net.http.HttpMethod;
|
||||
import org.xbib.net.http.server.HttpRequest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class HttpRequestTest {
|
||||
|
||||
@Test
|
||||
public void testFormAsMultiMap() {
|
||||
Parameter parameter = Parameter.builder().domain(Parameter.Domain.FORM)
|
||||
.enableDuplicates()
|
||||
.add("key", "key1")
|
||||
.add("key", "key2")
|
||||
.add("key", "key3")
|
||||
.add("value", "value1")
|
||||
.add("value", "")
|
||||
.add("value", "")
|
||||
.add("op", "and")
|
||||
.add("op", "")
|
||||
.add("op", "")
|
||||
.build();
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
HttpRequest httpRequest = org.xbib.net.http.server.netty.HttpRequest.builder()
|
||||
.setMethod(HttpMethod.POST)
|
||||
.setParameter(parameter)
|
||||
.setHeaders(httpHeaders)
|
||||
.build();
|
||||
assertEquals("{key=[key1, key2, key3], value=[value1, , ], op=[and, , ]}",
|
||||
httpRequest.asMultiMap().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJsonBodyAsMultiMap() throws IOException {
|
||||
// JSON keys overwrite each other
|
||||
Builder builder = JsonBuilder.builder()
|
||||
.beginMap()
|
||||
.field("key", "key1")
|
||||
.field("key", "key2")
|
||||
.field("key", "key3")
|
||||
.field("value", "value1")
|
||||
.field("value", "")
|
||||
.field("value", "")
|
||||
.field("op", "and")
|
||||
.field("op", "")
|
||||
.field("op", "")
|
||||
.endMap();
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.add("content-type", "application/json");
|
||||
HttpRequest httpRequest = org.xbib.net.http.server.netty.HttpRequest.builder()
|
||||
.setMethod(HttpMethod.POST)
|
||||
.setHeaders(httpHeaders)
|
||||
.setBody(ByteBuffer.wrap(builder.build().getBytes(StandardCharsets.UTF_8)))
|
||||
.build();
|
||||
Objects.requireNonNull(httpRequest.getBody());
|
||||
assertEquals("{key=[key3], value=[], op=[]}",
|
||||
httpRequest.asMultiMap().toString());
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package org.xbib.net.http.server;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.charset.MalformedInputException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.charset.UnmappableCharacterException;
|
||||
|
@ -184,34 +185,45 @@ public abstract class BaseHttpRequest implements HttpRequest {
|
|||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public MultiMap<String, Object> asMultiMap() {
|
||||
PercentDecoder percentDecoder = new PercentDecoder();
|
||||
MultiMap<String, Object> multiMap = new ParameterMap();
|
||||
String contentType = getHeaders().get(HttpHeaderNames.CONTENT_TYPE);
|
||||
if (getMethod() == HttpMethod.POST &&
|
||||
contentType != null && contentType.contains(HttpHeaderValues.APPLICATION_JSON)) {
|
||||
String bodyAsChars = getBodyAsChars(StandardCharsets.UTF_8).toString();
|
||||
Map<String, Object> map = Json.toMap(bodyAsChars);
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
if (entry.getValue() instanceof Iterable) {
|
||||
multiMap.putAll(entry.getKey(), (Iterable<Object>) entry.getValue());
|
||||
Parameter parameter = getParameter();
|
||||
try {
|
||||
PercentDecoder percentDecoder = new PercentDecoder();
|
||||
String contentType = getHeaders().get(HttpHeaderNames.CONTENT_TYPE);
|
||||
if (getMethod() == HttpMethod.POST &&
|
||||
contentType != null && contentType.contains(HttpHeaderValues.APPLICATION_JSON)) {
|
||||
CharBuffer charBuffer = builder.getBodyAsChars(StandardCharsets.UTF_8);
|
||||
if (charBuffer != null) {
|
||||
String bodyAsChars = charBuffer.toString();
|
||||
Map<String, Object> map = Json.toMap(bodyAsChars);
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
if (entry.getValue() instanceof Iterable) {
|
||||
multiMap.putAll(entry.getKey(), (Iterable<Object>) entry.getValue());
|
||||
} else {
|
||||
multiMap.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
multiMap.put(entry.getKey(), entry.getValue());
|
||||
logger.log(Level.WARNING, "body is null in POST request where json is declared");
|
||||
}
|
||||
} else {
|
||||
if (parameter != null) {
|
||||
toMultiMapEntry(parameter.get(Parameter.Domain.FORM),
|
||||
percentDecoder,
|
||||
HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.equals(contentType),
|
||||
multiMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
toMultiMapEntry(getParameter().get(Parameter.Domain.PATH),
|
||||
percentDecoder,
|
||||
false,
|
||||
multiMap);
|
||||
toMultiMapEntry(getParameter().get(Parameter.Domain.FORM),
|
||||
percentDecoder,
|
||||
HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.equals(contentType),
|
||||
multiMap);
|
||||
toMultiMapEntry(getParameter().get(Parameter.Domain.QUERY),
|
||||
percentDecoder,
|
||||
HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.equals(contentType),
|
||||
multiMap);
|
||||
if (parameter != null) {
|
||||
toMultiMapEntry(parameter.get(Parameter.Domain.PATH),
|
||||
percentDecoder,
|
||||
false,
|
||||
multiMap);
|
||||
toMultiMapEntry(parameter.get(Parameter.Domain.QUERY),
|
||||
percentDecoder,
|
||||
HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED.equals(contentType),
|
||||
multiMap);
|
||||
}
|
||||
} catch (ParameterException e) {
|
||||
logger.log(Level.WARNING, e.getMessage(), ExceptionFormatter.format(e));
|
||||
}
|
||||
|
@ -223,23 +235,29 @@ public abstract class BaseHttpRequest implements HttpRequest {
|
|||
PercentDecoder percentDecoder,
|
||||
boolean isFormEncoded,
|
||||
MultiMap<String, Object> multiMap) {
|
||||
if (parameter == null) {
|
||||
return;
|
||||
}
|
||||
for (Pair<String, Object> entry : parameter) {
|
||||
try {
|
||||
List<Object> list;
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof List) {
|
||||
list = (List<Object>) value;
|
||||
} else if (value != null) {
|
||||
list = List.of(value);
|
||||
Object object = entry.getValue();
|
||||
if (object instanceof List) {
|
||||
list = (List<Object>) object;
|
||||
} else if (object != null) {
|
||||
list = List.of(object);
|
||||
} else {
|
||||
list = List.of();
|
||||
}
|
||||
for (Object object : list) {
|
||||
String string = object.toString();
|
||||
for (Object o : list) {
|
||||
String string = o.toString();
|
||||
if (isFormEncoded) {
|
||||
string = string.replace('+', ' ');
|
||||
}
|
||||
multiMap.put(entry.getKey(), percentDecoder.decode(string));
|
||||
String name = entry.getKey();
|
||||
String value = percentDecoder.decode(string);
|
||||
logger.log(Level.INFO, "name = " + name + " value = " + value);
|
||||
multiMap.put(name, value);
|
||||
}
|
||||
} catch (MalformedInputException | UnmappableCharacterException e) {
|
||||
logger.log(Level.WARNING, "unable to percent decode parameter: " +
|
||||
|
@ -255,7 +273,7 @@ public abstract class BaseHttpRequest implements HttpRequest {
|
|||
|
||||
@Override
|
||||
protected Collection<Object> newValues() {
|
||||
// keep values with multiple occurences
|
||||
// keep values with multiple occurrences
|
||||
return TinyList.builder();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package org.xbib.net.http.server;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.CharBuffer;
|
||||
|
@ -57,6 +56,8 @@ public abstract class BaseHttpRequestBuilder implements HttpRequestBuilder {
|
|||
|
||||
protected ByteBuffer byteBuffer;
|
||||
|
||||
protected CharBuffer charBuffer;
|
||||
|
||||
protected boolean done;
|
||||
|
||||
protected List<Message> messages;
|
||||
|
@ -194,6 +195,13 @@ public abstract class BaseHttpRequestBuilder implements HttpRequestBuilder {
|
|||
return byteBuffer;
|
||||
}
|
||||
|
||||
public CharBuffer getBodyAsChars(Charset charset) {
|
||||
if (charBuffer == null) {
|
||||
charBuffer = byteBuffer != null ? charset.decode(byteBuffer) : null;
|
||||
}
|
||||
return charBuffer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseHttpRequestBuilder setBaseURL(URL baseURL) {
|
||||
if (done) {
|
||||
|
@ -259,11 +267,6 @@ public abstract class BaseHttpRequestBuilder implements HttpRequestBuilder {
|
|||
return requestPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharBuffer getBodyAsChars(Charset charset) {
|
||||
return byteBuffer != null ? charset.decode(byteBuffer) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaseHttpRequestBuilder setParameter(Parameter parameter) {
|
||||
if (done) {
|
||||
|
|
Loading…
Reference in a new issue