switch to HTTP parameter map

This commit is contained in:
Jörg Prante 2020-08-09 16:27:42 +02:00
parent d42f928113
commit 97d6f4dc6a
11 changed files with 711 additions and 212 deletions

View file

@ -1,6 +1,6 @@
group = org.xbib group = org.xbib
name = netty-http name = netty-http
version = 4.1.51.3 version = 4.1.51.4
gradle.wrapper.version = 6.4.1 gradle.wrapper.version = 6.4.1
netty.version = 4.1.51.Final netty.version = 4.1.51.Final

View file

@ -609,10 +609,7 @@ public final class Request {
if (url != null) { if (url != null) {
// add our URI parameters to the URL // add our URI parameters to the URL
URL.Builder mutator = url.mutator(); URL.Builder mutator = url.mutator();
uriParameters.forEach((k, v) -> v.forEach(vv -> { uriParameters.forEach(e -> mutator.queryParam(e.getKey(), e.getValue()));
// no percent encoding
mutator.queryParam(k, vv);
}));
// calling build() performs percent encoding // calling build() performs percent encoding
url = mutator.build(); url = mutator.build();
String scheme = url.getScheme(); String scheme = url.getScheme();

View file

@ -4,9 +4,8 @@ import io.netty.handler.codec.http.HttpHeaderValues;
import org.xbib.net.PercentDecoder; import org.xbib.net.PercentDecoder;
import org.xbib.net.PercentEncoder; import org.xbib.net.PercentEncoder;
import org.xbib.net.PercentEncoders; import org.xbib.net.PercentEncoders;
import org.xbib.netty.http.common.util.CaseInsensitiveParameters;
import org.xbib.netty.http.common.util.LimitedSet; import org.xbib.netty.http.common.util.LimitedSet;
import org.xbib.netty.http.common.util.LimitedTreeMap;
import java.nio.charset.CharacterCodingException; import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.MalformedInputException; import java.nio.charset.MalformedInputException;
@ -15,11 +14,8 @@ import java.nio.charset.UnmappableCharacterException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
/** /**
* A limited multi-map of HTTP request parameters. Each key references a * A limited multi-map of HTTP request parameters. Each key references a
@ -31,26 +27,22 @@ import java.util.SortedSet;
* being useful for message signing; it's not a general purpose collection class * being useful for message signing; it's not a general purpose collection class
* to handle request parameters. * to handle request parameters.
*/ */
public class HttpParameters implements Map<String, SortedSet<String>> { public class HttpParameters extends /*LinkedHashSetMultiMap<String, String>*/ CaseInsensitiveParameters {
private static final String EQUALS = "="; private static final String EQUALS = "=";
private static final String AMPERSAND = "&"; private static final String AMPERSAND = "&";
private final int maxParam;
private final int sizeLimit; private final int sizeLimit;
private final int elementSizeLimit; private final int elementSizeLimit;
private final LimitedTreeMap<String, String> map;
private final PercentEncoder percentEncoder; private final PercentEncoder percentEncoder;
private final PercentDecoder percentDecoder;
private final CharSequence contentType; private final CharSequence contentType;
private final Charset encoding;
public HttpParameters() { public HttpParameters() {
this(1024, 1024, 65536, this(1024, 1024, 65536,
HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED, StandardCharsets.UTF_8); HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED, StandardCharsets.UTF_8);
@ -68,96 +60,28 @@ public class HttpParameters implements Map<String, SortedSet<String>> {
public HttpParameters(int maxParam, int sizeLimit, int elementSizeLimit, public HttpParameters(int maxParam, int sizeLimit, int elementSizeLimit,
CharSequence contentType, Charset charset) { CharSequence contentType, Charset charset) {
this.maxParam = maxParam;
this.sizeLimit = sizeLimit; this.sizeLimit = sizeLimit;
this.elementSizeLimit = elementSizeLimit; this.elementSizeLimit = elementSizeLimit;
this.map = new LimitedTreeMap<>(maxParam);
this.percentEncoder = PercentEncoders.getQueryEncoder(charset); this.percentEncoder = PercentEncoders.getQueryEncoder(charset);
this.percentDecoder = new PercentDecoder(); PercentDecoder percentDecoder = new PercentDecoder();
this.contentType = contentType; this.contentType = contentType;
this.encoding = charset;
} }
@Override public CharSequence getContentType() {
public SortedSet<String> put(String key, SortedSet<String> value) { return contentType;
return map.put(key, value);
} }
@Override public Charset getEncoding() {
public SortedSet<String> get(Object key) { return encoding;
return map.get(key);
} }
@Override public Collection<String> put(String key, Collection<String> values, boolean percentEncode) {
public void putAll(Map<? extends String, ? extends SortedSet<String>> m) {
map.putAll(m);
}
@Override
public boolean containsKey(Object key) {
return map.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
if (value instanceof String) {
for (Set<String> values : map.values()) {
if (values.contains(value)) {
return true;
}
}
}
return false;
}
@Override
public int size() {
int count = 0;
for (String key : map.keySet()) {
count += map.get(key).size();
}
return count;
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public void clear() {
map.clear();
}
@Override
public SortedSet<String> remove(Object key) {
return map.remove(key);
}
@Override
public Set<String> keySet() {
return map.keySet();
}
@Override
public Collection<SortedSet<String>> values() {
return map.values();
}
@Override
public Set<Entry<String, SortedSet<String>>> entrySet() {
return map.entrySet();
}
public SortedSet<String> put(String key, SortedSet<String> values, boolean percentEncode) {
if (percentEncode) {
remove(key); remove(key);
for (String v : values) { for (String v : values) {
add(key, v, percentEncode); add(key, v, percentEncode);
} }
return get(key); return getAll(key);
} else {
return map.put(key, values);
}
} }
/** /**
@ -167,14 +91,26 @@ public class HttpParameters implements Map<String, SortedSet<String>> {
* @param value the parameter value * @param value the parameter value
* @return the value * @return the value
*/ */
public String addRaw(String key, String value) { public HttpParameters addRaw(String key, String value) {
return add(key, value, false); return add(key, value, false);
} }
public String add(String key, String value) { public HttpParameters add(String key, String value) {
return add(key, value, true); return add(key, value, true);
} }
/**
* Convenience method to allow for storing null values. {@link #put} doesn't
* allow null values, because that would be ambiguous.
*
* @param key the parameter name
* @param nullString can be anything, but probably... null?
* @return null
*/
public HttpParameters addNull(String key, String nullString) {
return addRaw(key, nullString);
}
/** /**
* Convenience method to add a single value for the parameter specified by * Convenience method to add a single value for the parameter specified by
* 'key'. * 'key'.
@ -185,36 +121,15 @@ public class HttpParameters implements Map<String, SortedSet<String>> {
* inserted into the map * inserted into the map
* @return the value * @return the value
*/ */
private String add(String key, String value, boolean percentEncode) { private HttpParameters add(String key, String value, boolean percentEncode) {
String v = null;
try { try {
String k = percentEncode ? percentEncoder.encode(key) : key; String k = percentEncode ? percentEncoder.encode(key) : key;
SortedSet<String> values = map.get(k); String v = percentEncode ? percentEncoder.encode(value) : value;
if (values == null) { super.add(k, v);
values = new LimitedSet<>(sizeLimit, elementSizeLimit);
map.put(k, values);
}
if (value != null) {
v = percentEncode ? percentEncoder.encode(value) : value;
values.add(v);
}
} catch (CharacterCodingException e) { } catch (CharacterCodingException e) {
throw new IllegalArgumentException(e); throw new IllegalArgumentException(e);
} }
return v; return this;
}
/**
* Convenience method to allow for storing null values. {@link #put} doesn't
* allow null values, because that would be ambiguous.
*
* @param key the parameter name
* @param nullString can be anything, but probably... null?
* @return null
*/
public String addNull(String key, String nullString) {
return addRaw(key, nullString);
} }
public void addAll(String[] keyValuePairs, boolean percentEncode) { public void addAll(String[] keyValuePairs, boolean percentEncode) {
@ -223,16 +138,6 @@ public class HttpParameters implements Map<String, SortedSet<String>> {
} }
} }
public void addAll(Map<? extends String, ? extends SortedSet<String>> m, boolean percentEncode) {
if (percentEncode) {
for (String key : m.keySet()) {
put(key, m.get(key), true);
}
} else {
map.putAll(m);
}
}
/** /**
* Convenience method to merge a {@code Map<String, List<String>>}. * Convenience method to merge a {@code Map<String, List<String>>}.
* *
@ -240,53 +145,20 @@ public class HttpParameters implements Map<String, SortedSet<String>> {
*/ */
public void addMap(Map<String, List<String>> m) { public void addMap(Map<String, List<String>> m) {
for (String key : m.keySet()) { for (String key : m.keySet()) {
SortedSet<String> vals = get(key); Collection<String> vals = getAll(key);
if (vals == null) { if (vals == null) {
vals = new LimitedSet<>(sizeLimit, elementSizeLimit); vals = new LimitedSet<>(sizeLimit, elementSizeLimit);
put(key, vals); for (String v : vals) {
super.add(key, v);
}
} }
vals.addAll(m.get(key)); vals.addAll(m.get(key));
} }
} }
public String getFirst(String key) {
SortedSet<String> values = map.get(key);
if (values == null || values.isEmpty()) {
return null;
}
return values.first();
}
/**
* Returns the first value from the set of all values for the given
* parameter name. If the key passed to this method contains special
* characters, you must first percent encode it, otherwise the lookup will fail
* (that's because upon storing values in this map, keys get
* percent-encoded).
*
* @param key the parameter name (must be percent encoded if it contains unsafe
* characters!)
* @return the first value found for this parameter
* @throws MalformedInputException if input is malformed
* @throws UnmappableCharacterException if characters are unmappable
*/
public String getFirstDecoded(String key)
throws MalformedInputException, UnmappableCharacterException {
SortedSet<String> values = map.get(key);
if (values == null || values.isEmpty()) {
return null;
}
String value = values.first();
return percentDecoder.decode(value);
}
public CharSequence getContentType() {
return contentType;
}
public String getAsQueryString(boolean percentEncode) throws MalformedInputException, UnmappableCharacterException { public String getAsQueryString(boolean percentEncode) throws MalformedInputException, UnmappableCharacterException {
List<String> list = new ArrayList<>(); List<String> list = new ArrayList<>();
for (String key : keySet()) { for (String key : super.names()) {
list.add(getAsQueryString(key, percentEncode)); list.add(getAsQueryString(key, percentEncode));
} }
return String.join(AMPERSAND, list); return String.join(AMPERSAND, list);
@ -320,7 +192,7 @@ public class HttpParameters implements Map<String, SortedSet<String>> {
public String getAsQueryString(String key, boolean percentEncode) public String getAsQueryString(String key, boolean percentEncode)
throws MalformedInputException, UnmappableCharacterException { throws MalformedInputException, UnmappableCharacterException {
String k = percentEncode ? percentEncoder.encode(key) : key; String k = percentEncode ? percentEncoder.encode(key) : key;
SortedSet<String> values = map.get(k); Collection<String> values = getAll(k);
if (values == null) { if (values == null) {
return k + EQUALS; return k + EQUALS;
} }
@ -336,26 +208,4 @@ public class HttpParameters implements Map<String, SortedSet<String>> {
} }
return sb.toString(); return sb.toString();
} }
public String getAsHeaderElement(String key) {
String value = getFirst(key);
if (value == null) {
return null;
}
return key + "=\"" + value + "\"";
}
public HttpParameters getOAuthParameters() {
HttpParameters oauthParams =
new HttpParameters(maxParam, sizeLimit, elementSizeLimit, contentType, StandardCharsets.UTF_8);
entrySet().stream().filter(entry -> entry.getKey().startsWith("oauth_") || entry.getKey().startsWith("x_oauth_"))
.forEach(entry -> oauthParams.put(entry.getKey(), entry.getValue()));
return oauthParams;
}
@Override
public String toString() {
return new LinkedHashMap<>(this).toString();
}
} }

View file

@ -0,0 +1,157 @@
package org.xbib.netty.http.common.util;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
* Abstract multi map.
*
* @param <K> the key type parameter
* @param <V> the value type parameter
*/
abstract class AbstractMultiMap<K, V> implements MultiMap<K, V> {
private final Map<K, Collection<V>> map;
AbstractMultiMap() {
this(null);
}
private AbstractMultiMap(MultiMap<K, V> map) {
this.map = newMap();
if (map != null) {
putAll(map);
}
}
@Override
public int size() {
return map.size();
}
@Override
public void clear() {
map.clear();
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public boolean containsKey(K key) {
return map.containsKey(key);
}
@Override
public Set<K> keySet() {
return map.keySet();
}
@Override
public boolean put(K key, V value) {
Collection<V> set = map.get(key);
if (set == null) {
set = newValues();
set.add(value);
map.put(key, set);
return true;
} else {
set.add(value);
return false;
}
}
@Override
public void putAll(K key, Iterable<V> values) {
if (values == null) {
return;
}
Collection<V> set = map.computeIfAbsent(key, k -> newValues());
for (V v : values) {
set.add(v);
}
}
@Override
public Collection<V> get(K key) {
return map.get(key);
}
@Override
public Collection<V> remove(K key) {
return map.remove(key);
}
@Override
public boolean remove(K key, V value) {
Collection<V> set = map.get(key);
return set != null && set.remove(value);
}
@Override
public void putAll(MultiMap<K, V> map) {
if (map != null) {
for (K key : map.keySet()) {
putAll(key, map.get(key));
}
}
}
@Override
public Map<K, Collection<V>> asMap() {
return map;
}
@Override
public String getString(K key) {
Collection<V> v = get(key);
return v != null ? v.iterator().next().toString() : null;
}
@Override
public String getString(K key, String defaultValue) {
Collection<V> collection = get(key);
Iterator<V> iterator = collection != null ? collection.iterator() : null;
V v = iterator != null ? iterator.next() : null;
return v != null ? v.toString() : defaultValue;
}
@Override
public Integer getInteger(K key, int defaultValue) {
Collection<V> collection = get(key);
Iterator<V> iterator = collection != null ? collection.iterator() : null;
V v = iterator != null ? iterator.next() : null;
return v != null ? Integer.parseInt(v.toString()) : defaultValue;
}
@Override
public Boolean getBoolean(K key, boolean defaultValue) {
Collection<V> collection = get(key);
Iterator<V> iterator = collection != null ? collection.iterator() : null;
V v = iterator != null ? iterator.next() : null;
return v != null ? Boolean.parseBoolean(v.toString()) : defaultValue;
}
@Override
public boolean equals(Object obj) {
return obj instanceof AbstractMultiMap && map.equals(((AbstractMultiMap) obj).map);
}
@Override
public int hashCode() {
return map.hashCode();
}
@Override
public String toString() {
return map.toString();
}
abstract Collection<V> newValues();
abstract Map<K, Collection<V>> newMap();
}

View file

@ -0,0 +1,358 @@
package org.xbib.netty.http.common.util;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
/**
* This multi-map implementation has case insensitive keys
* and can be used to hold some HTTP headers prior to making an HTTP request.
*
*/
public class CaseInsensitiveParameters implements ParameterMap {
private static final int BUCKET_SIZE = 17;
private static int hash(String name) {
int h = 0;
for (int i = name.length() - 1; i >= 0; i--) {
char c = name.charAt(i);
if (c >= 'A' && c <= 'Z') {
c += 32;
}
h = 31 * h + c;
}
if (h > 0) {
return h;
} else if (h == Integer.MIN_VALUE) {
return Integer.MAX_VALUE;
} else {
return -h;
}
}
private ParameterMap set0(Iterable<Map.Entry<String, String>> map) {
clear();
for (Map.Entry<String, String> entry : map) {
add(entry.getKey(), entry.getValue());
}
return this;
}
@Override
public ParameterMap setAll(ParameterMap headers) {
return set0(headers);
}
@Override
public int size() {
return names().size();
}
private static boolean eq(String name1, String name2) {
int nameLen = name1.length();
if (nameLen != name2.length()) {
return false;
}
for (int i = nameLen - 1; i >= 0; i--) {
char c1 = name1.charAt(i);
char c2 = name2.charAt(i);
if (c1 != c2) {
if (c1 >= 'A' && c1 <= 'Z') {
c1 += 32;
}
if (c2 >= 'A' && c2 <= 'Z') {
c2 += 32;
}
if (c1 != c2) {
return false;
}
}
}
return true;
}
private static int index(int hash) {
return hash % BUCKET_SIZE;
}
private final MapEntry[] entries = new MapEntry[BUCKET_SIZE];
private final MapEntry head = new MapEntry(-1, null, null);
public CaseInsensitiveParameters() {
head.before = head.after = head;
}
@Override
public ParameterMap add(final String name, final String strVal) {
int h = hash(name);
int i = index(h);
add0(h, i, name, strVal);
return this;
}
@Override
public ParameterMap add(String name, Iterable<String> values) {
int h = hash(name);
int i = index(h);
for (String vstr : values) {
add0(h, i, name, vstr);
}
return this;
}
@Override
public ParameterMap addAll(ParameterMap headers) {
for (Map.Entry<String, String> entry : headers.entries()) {
add(entry.getKey(), entry.getValue());
}
return this;
}
@Override
public ParameterMap addAll(Map<String, String> map) {
for (Map.Entry<String, String> entry : map.entrySet()) {
add(entry.getKey(), entry.getValue());
}
return this;
}
@Override
public ParameterMap remove(final String name) {
Objects.requireNonNull(name, "name");
int h = hash(name);
int i = index(h);
remove0(h, i, name);
return this;
}
@Override
public ParameterMap set(final String name, final String strVal) {
int h = hash(name);
int i = index(h);
remove0(h, i, name);
add0(h, i, name, strVal);
return this;
}
@Override
public ParameterMap set(final String name, final Iterable<String> values) {
Objects.requireNonNull(values, "values");
int h = hash(name);
int i = index(h);
remove0(h, i, name);
for (String v : values) {
if (v == null) {
break;
}
add0(h, i, name, v);
}
return this;
}
@Override
public ParameterMap clear() {
Arrays.fill(entries, null);
head.before = head.after = head;
return this;
}
@Override
public String get(final String name) {
Objects.requireNonNull(name, "name");
int h = hash(name);
int i = index(h);
MapEntry e = entries[i];
String value = null;
while (e != null) {
if (e.hash == h && eq(name, e.key)) {
value = e.getValue();
}
e = e.next;
}
return value;
}
@Override
public List<String> getAll(final String name) {
Objects.requireNonNull(name, "name");
LinkedList<String> values = new LinkedList<>();
int h = hash(name);
int i = index(h);
MapEntry e = entries[i];
while (e != null) {
if (e.hash == h && eq(name, e.key)) {
values.addFirst(e.getValue());
}
e = e.next;
}
return values;
}
@Override
public void forEach(Consumer<? super Map.Entry<String, String>> action) {
MapEntry e = head.after;
while (e != head) {
action.accept(e);
e = e.after;
}
}
@Override
public List<Map.Entry<String, String>> entries() {
List<Map.Entry<String, String>> all = new LinkedList<>();
MapEntry e = head.after;
while (e != head) {
all.add(e);
e = e.after;
}
return all;
}
@Override
public Iterator<Map.Entry<String, String>> iterator() {
return entries().iterator();
}
@Override
public boolean contains(String name) {
return get(name) != null;
}
@Override
public boolean isEmpty() {
return head == head.after;
}
@Override
public List<String> names() {
List<String> names = new LinkedList<>();
MapEntry e = head.after;
while (e != head) {
if (!names.contains(e.getKey())) {
names.add(e.getKey());
}
e = e.after;
}
names.sort(String.CASE_INSENSITIVE_ORDER);
return names;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : this) {
sb.append(entry).append('\n');
}
return sb.toString();
}
private void add0(int h, int i, final String name, final String value) {
MapEntry e = entries[i];
MapEntry newEntry;
entries[i] = newEntry = new MapEntry(h, name, value);
newEntry.next = e;
newEntry.addBefore(head);
}
private void remove0(int h, int i, String name) {
MapEntry e = entries[i];
if (e == null) {
return;
}
for (;;) {
if (e.hash == h && eq(name, e.key)) {
e.remove();
MapEntry next = e.next;
if (next != null) {
entries[i] = next;
e = next;
} else {
entries[i] = null;
return;
}
} else {
break;
}
}
for (;;) {
MapEntry next = e.next;
if (next == null) {
break;
}
if (next.hash == h && eq(name, next.key)) {
e.next = next.next;
next.remove();
} else {
e = next;
}
}
}
private static final class MapEntry implements Map.Entry<String, String> {
final int hash;
final String key;
String value;
MapEntry next;
MapEntry before, after;
MapEntry(int hash, String key, String value) {
this.hash = hash;
this.key = key;
this.value = value;
}
void remove() {
before.after = after;
after.before = before;
}
void addBefore(MapEntry e) {
after = e;
before = e.before;
before.after = this;
after.before = this;
}
@Override
public String getKey() {
return key;
}
@Override
public String getValue() {
return value;
}
@Override
public String setValue(String value) {
Objects.requireNonNull(value, "value");
String oldValue = this.value;
this.value = value;
return oldValue;
}
@Override
public String toString() {
return getKey() + ": " + getValue();
}
}
}

View file

@ -0,0 +1,29 @@
package org.xbib.netty.http.common.util;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
/**
* Linked multi map.
*
* @param <K> the key type parameter
* @param <V> the value type parameter
*/
public class LinkedHashSetMultiMap<K, V> extends AbstractMultiMap<K, V> {
public LinkedHashSetMultiMap() {
super();
}
@Override
Collection<V> newValues() {
return new LinkedHashSet<>();
}
@Override
Map<K, Collection<V>> newMap() {
return new LinkedHashMap<>();
}
}

View file

@ -0,0 +1,46 @@
package org.xbib.netty.http.common.util;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
/**
* MultiMap interface.
*
* @param <K> the key type parameter
* @param <V> the value type parameter
*/
public interface MultiMap<K, V> {
void clear();
int size();
boolean isEmpty();
boolean containsKey(K key);
Collection<V> get(K key);
Set<K> keySet();
boolean put(K key, V value);
void putAll(K key, Iterable<V> values);
void putAll(MultiMap<K, V> map);
Collection<V> remove(K key);
boolean remove(K key, V value);
Map<K, Collection<V>> asMap();
String getString(K key);
String getString(K key, String defaultValue);
Integer getInteger(K key, int defaultValue);
Boolean getBoolean(K key, boolean defaultValue);
}

View file

@ -0,0 +1,44 @@
package org.xbib.netty.http.common.util;
import java.util.List;
import java.util.Map;
public interface ParameterMap extends Iterable<Map.Entry<String, String>> {
String get(String name);
List<String> getAll(String name);
List<Map.Entry<String, String>> entries();
boolean contains(String name);
default boolean contains(String name, String value, boolean caseInsensitive) {
return getAll(name).stream()
.anyMatch(val -> caseInsensitive ? val.equalsIgnoreCase(value) : val.equals(value));
}
boolean isEmpty();
List<String> names();
ParameterMap add(String name, String value);
ParameterMap add(String name, Iterable<String> values);
ParameterMap addAll(ParameterMap map);
ParameterMap addAll(Map<String, String> map);
ParameterMap set(String name, String value);
ParameterMap set(String name, Iterable<String> values);
ParameterMap setAll(ParameterMap map);
ParameterMap remove(String name);
ParameterMap clear();
int size();
}

View file

@ -11,13 +11,32 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
class HttpParametersTest { class HttpParametersTest {
@Test @Test
void testParameters() throws MalformedInputException, UnmappableCharacterException { void testSimpleParameter() throws MalformedInputException, UnmappableCharacterException {
HttpParameters httpParameters = new HttpParameters(); HttpParameters httpParameters = new HttpParameters();
httpParameters.addRaw("a", "b"); httpParameters.addRaw("a", "b");
String query = httpParameters.getAsQueryString(false); String query = httpParameters.getAsQueryString(false);
assertEquals("a=b", query); assertEquals("a=b", query);
} }
@Test
void testSimpleParameters() throws MalformedInputException, UnmappableCharacterException {
HttpParameters httpParameters = new HttpParameters();
httpParameters.addRaw("a", "b");
httpParameters.addRaw("c", "d");
String query = httpParameters.getAsQueryString(false);
assertEquals("a=b&c=d", query);
}
@Test
void testMultiParameters() throws MalformedInputException, UnmappableCharacterException {
HttpParameters httpParameters = new HttpParameters();
httpParameters.addRaw("a", "b");
httpParameters.addRaw("a", "c");
httpParameters.addRaw("a", "d");
String query = httpParameters.getAsQueryString(false);
assertEquals("a=b&a=c&a=d", query);
}
@Test @Test
void testUtf8() throws MalformedInputException, UnmappableCharacterException { void testUtf8() throws MalformedInputException, UnmappableCharacterException {
HttpParameters httpParameters = new HttpParameters("text/plain; charset=utf-8"); HttpParameters httpParameters = new HttpParameters("text/plain; charset=utf-8");

View file

@ -11,7 +11,6 @@ import org.xbib.netty.http.common.HttpParameters;
import org.xbib.netty.http.common.HttpResponse; import org.xbib.netty.http.common.HttpResponse;
import org.xbib.netty.http.server.HttpServerDomain; import org.xbib.netty.http.server.HttpServerDomain;
import org.xbib.netty.http.server.Server; import org.xbib.netty.http.server.Server;
import org.xbib.netty.http.server.api.ServerResponse;
import org.xbib.netty.http.server.test.NettyHttpTestExtension; import org.xbib.netty.http.server.test.NettyHttpTestExtension;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@ -36,10 +35,10 @@ class PostTest {
.singleEndpoint("/post", "/**", (req, resp) -> { .singleEndpoint("/post", "/**", (req, resp) -> {
HttpParameters parameters = req.getParameters(); HttpParameters parameters = req.getParameters();
logger.log(Level.INFO, "got request " + parameters.toString() + ", sending OK"); logger.log(Level.INFO, "got request " + parameters.toString() + ", sending OK");
if ("Hello World".equals(parameters.getFirst("withspace"))) { if ("Hello World".equals(parameters.get("withspace"))) {
success2.set(true); success2.set(true);
} }
if ("Jörg".equals(parameters.getFirst("name"))) { if ("Jörg".equals(parameters.get("name"))) {
success3.set(true); success3.set(true);
} }
resp.getBuilder().setStatus(HttpResponseStatus.OK).build().flush(); resp.getBuilder().setStatus(HttpResponseStatus.OK).build().flush();
@ -86,10 +85,10 @@ class PostTest {
.singleEndpoint("/post", "/**", (req, resp) -> { .singleEndpoint("/post", "/**", (req, resp) -> {
HttpParameters parameters = req.getParameters(); HttpParameters parameters = req.getParameters();
logger.log(Level.INFO, "got request " + parameters.toString() + ", sending OK"); logger.log(Level.INFO, "got request " + parameters.toString() + ", sending OK");
if ("Hello World".equals(parameters.getFirst("withspace"))) { if ("Hello World".equals(parameters.get("withspace"))) {
success2.set(true); success2.set(true);
} }
if ("Jörg".equals(parameters.getFirst("name"))) { if ("Jörg".equals(parameters.get("name"))) {
success3.set(true); success3.set(true);
} }
resp.getBuilder().setStatus(HttpResponseStatus.OK).build().flush(); resp.getBuilder().setStatus(HttpResponseStatus.OK).build().flush();
@ -137,13 +136,13 @@ class PostTest {
.singleEndpoint("/post", "/**", (req, resp) -> { .singleEndpoint("/post", "/**", (req, resp) -> {
HttpParameters parameters = req.getParameters(); HttpParameters parameters = req.getParameters();
logger.log(Level.INFO, "got request " + parameters.toString() + ", sending OK"); logger.log(Level.INFO, "got request " + parameters.toString() + ", sending OK");
if ("Hello World".equals(parameters.getFirst("withplus"))) { if ("Hello World".equals(parameters.get("withplus"))) {
success2.set(true); success2.set(true);
} }
if ("Jörg".equals(parameters.getFirst("name"))) { if ("Jörg".equals(parameters.get("name"))) {
success3.set(true); success3.set(true);
} }
if ("my value".equals(parameters.getFirst("my param"))) { if ("my value".equals(parameters.get("my param"))) {
success4.set(true); success4.set(true);
} }
resp.getBuilder().setStatus(HttpResponseStatus.OK).build().flush(); resp.getBuilder().setStatus(HttpResponseStatus.OK).build().flush();
@ -194,13 +193,13 @@ class PostTest {
.singleEndpoint("/post", "/**", (req, resp) -> { .singleEndpoint("/post", "/**", (req, resp) -> {
HttpParameters parameters = req.getParameters(); HttpParameters parameters = req.getParameters();
logger.log(Level.INFO, "got request " + parameters.toString() + ", sending OK"); logger.log(Level.INFO, "got request " + parameters.toString() + ", sending OK");
if ("Hello World".equals(parameters.getFirst("withoutplus"))) { if ("Hello World".equals(parameters.get("withoutplus"))) {
success2.set(true); success2.set(true);
} }
if ("Jörg".equals(parameters.getFirst("name"))) { if ("Jörg".equals(parameters.get("name"))) {
success3.set(true); success3.set(true);
} }
if ("my value".equals(parameters.getFirst("my param"))) { if ("my value".equals(parameters.get("my param"))) {
success4.set(true); success4.set(true);
} }
resp.getBuilder().setStatus(HttpResponseStatus.OK).build().flush(); resp.getBuilder().setStatus(HttpResponseStatus.OK).build().flush();
@ -251,10 +250,10 @@ class PostTest {
.singleEndpoint("/post", "/**", (req, resp) -> { .singleEndpoint("/post", "/**", (req, resp) -> {
HttpParameters parameters = req.getParameters(); HttpParameters parameters = req.getParameters();
logger.log(Level.INFO, "got request " + parameters.toString() + ", sending OK"); logger.log(Level.INFO, "got request " + parameters.toString() + ", sending OK");
if ("myÿvalue".equals(parameters.getFirst("my param"))) { if ("myÿvalue".equals(parameters.get("my param"))) {
success1.set(true); success1.set(true);
} }
if ("bÿc".equals(parameters.getFirst("a"))) { if ("bÿc".equals(parameters.get("a"))) {
success2.set(true); success2.set(true);
} }
resp.getBuilder().setStatus(HttpResponseStatus.OK).build().flush(); resp.getBuilder().setStatus(HttpResponseStatus.OK).build().flush();

View file

@ -36,10 +36,10 @@ class PostTest {
.singleEndpoint("/post", "/**", (req, resp) -> { .singleEndpoint("/post", "/**", (req, resp) -> {
HttpParameters parameters = req.getParameters(); HttpParameters parameters = req.getParameters();
logger.log(Level.INFO, "got request " + parameters.toString() + " , sending, OK"); logger.log(Level.INFO, "got request " + parameters.toString() + " , sending, OK");
if ("Hello World".equals(parameters.getFirst("withspace"))) { if ("Hello World".equals(parameters.get("withspace"))) {
success2.set(true); success2.set(true);
} }
if ("Jörg".equals(parameters.getFirst("name"))) { if ("Jörg".equals(parameters.get("name"))) {
success3.set(true); success3.set(true);
} }
resp.getBuilder().setStatus(HttpResponseStatus.OK).build().flush(); resp.getBuilder().setStatus(HttpResponseStatus.OK).build().flush();
@ -86,10 +86,10 @@ class PostTest {
.singleEndpoint("/post", "/**", (req, resp) -> { .singleEndpoint("/post", "/**", (req, resp) -> {
HttpParameters parameters = req.getParameters(); HttpParameters parameters = req.getParameters();
logger.log(Level.INFO, "got request " + parameters.toString() + " , sending, OK"); logger.log(Level.INFO, "got request " + parameters.toString() + " , sending, OK");
if ("Hello World".equals(parameters.getFirst("withspace"))) { if ("Hello World".equals(parameters.get("withspace"))) {
success2.set(true); success2.set(true);
} }
if ("Jörg".equals(parameters.getFirst("name"))) { if ("Jörg".equals(parameters.get("name"))) {
success3.set(true); success3.set(true);
} }
resp.getBuilder().setStatus(HttpResponseStatus.OK).build().flush(); resp.getBuilder().setStatus(HttpResponseStatus.OK).build().flush();