remove default domain, stricter security

This commit is contained in:
Jörg Prante 2023-07-30 21:57:18 +02:00
parent a5393441ed
commit a34c444aec
16 changed files with 161 additions and 127 deletions

View file

@ -1,5 +1,5 @@
group = org.xbib
name = net
version = 3.2.0
version = 3.3.0
org.gradle.warning.mode = ALL

View file

@ -21,7 +21,7 @@ public class PathDecoder {
String path = pos > 0 ? pathAndQuery.substring(0, pos) : pathAndQuery;
this.query = pos > 0 ? pathAndQuery.substring(pos + 1) : null;
this.path = PathNormalizer.normalize(path);
this.params = Parameter.builder().enablePercentDecoding();
this.params = Parameter.builder().domain(Parameter.Domain.PATH).enablePercentDecoding();
if (query != null) {
parse(query);
}

View file

@ -1,10 +1,11 @@
package org.xbib.net.path;
import org.xbib.net.Parameter;
import org.xbib.net.ParameterException;
public interface PathResolver<T> {
void resolve(String method, String path, ResultListener<T> listener);
void resolve(String method, String path, ResultListener<T> listener) throws ParameterException;
interface Builder<T> {
@ -25,7 +26,7 @@ public interface PathResolver<T> {
@FunctionalInterface
interface ResultListener<T> {
void onResult(Result<T> result);
void onResult(Result<T> result) throws ParameterException;
}
}

View file

@ -50,11 +50,11 @@ public class PathMatcher {
}
public Parameter extractUriTemplateVariables(String pattern, String path) {
ParameterBuilder queryParameters = Parameter.builder();
if (!doMatch(pattern, path, true, queryParameters)) {
ParameterBuilder uriParameters = Parameter.builder().domain(Parameter.Domain.PATH);
if (!doMatch(pattern, path, true, uriParameters)) {
throw new IllegalStateException("Pattern \"" + pattern + "\" is not a match for \"" + path + "\"");
}
return queryParameters.build();
return uriParameters.build();
}
public PathComparator getPatternComparator(String path) {

View file

@ -2,6 +2,7 @@ package org.xbib.net.path.simple;
import org.xbib.net.Parameter;
import org.xbib.net.ParameterBuilder;
import org.xbib.net.ParameterException;
import org.xbib.net.util.CharMatcher;
import java.util.ArrayList;
@ -53,7 +54,7 @@ public class PathResolver<T> implements org.xbib.net.path.PathResolver<T> {
* @param resultListener result listener
*/
@Override
public void resolve(String method, String path, ResultListener<T> resultListener) {
public void resolve(String method, String path, ResultListener<T> resultListener) throws ParameterException {
Objects.requireNonNull(method, "method");
Objects.requireNonNull(path, "path");
resolve(method, builder.pathMatcher.tokenize(path), 0, new ArrayList<>(), resultListener);
@ -68,7 +69,7 @@ public class PathResolver<T> implements org.xbib.net.path.PathResolver<T> {
List<String> pathSegments,
int index,
List<String> parameters,
ResultListener<T> resultListener) {
ResultListener<T> resultListener) throws ParameterException {
if (index < pathSegments.size()) {
String segment = pathSegments.get(index);
PathResolver<T> child = children.get(segment);

View file

@ -4,6 +4,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import org.xbib.net.Parameter;
import org.xbib.net.ParameterBuilder;
import org.xbib.net.ParameterException;
import org.xbib.net.PathNormalizer;
import java.util.ArrayList;
@ -37,7 +38,7 @@ public class PathResolver<T> implements org.xbib.net.path.PathResolver<T> {
}
@Override
public void resolve(String method, String path, ResultListener<T> listener) {
public void resolve(String method, String path, ResultListener<T> listener) throws ParameterException {
Objects.requireNonNull(method, "method");
Objects.requireNonNull(path, "path");
List<PathSegment> pathSegments = PathMatcher.tokenize(PathNormalizer.normalize(path),
@ -54,7 +55,7 @@ public class PathResolver<T> implements org.xbib.net.path.PathResolver<T> {
private ParameterBuilder resolve(List<PathSegment> pathSegments,
int index,
ParameterBuilder parameterBuilder,
ResultListener<T> listener) {
ResultListener<T> listener) throws ParameterException {
ParameterBuilder pb = parameterBuilder;
if (index < pathSegments.size()) {
PathSegment segment = pathSegments.get(index);

View file

@ -10,17 +10,17 @@ import static org.junit.jupiter.api.Assertions.assertNull;
class PathDecoderTest {
@Test
void testPlusSign() {
void testPlusSign() throws Exception {
PathDecoder decoder = new PathDecoder("/path?a=b+c", "d=e+f");
assertEquals("[b c]", decoder.getParameter().getAll("a", Parameter.Domain.DEFAULT).toString());
assertEquals("[e f]", decoder.getParameter().getAll("d", Parameter.Domain.DEFAULT).toString());
assertEquals("[b c]", decoder.getParameter().getAll("a", Parameter.Domain.PATH).toString());
assertEquals("[e f]", decoder.getParameter().getAll("d", Parameter.Domain.PATH).toString());
}
@Test
void testSlash() {
void testSlash() throws Exception {
PathDecoder decoder = new PathDecoder("path/foo/bar/?a=b+c", "d=e+f");
assertEquals("[b c]", decoder.getParameter().getAll("a", Parameter.Domain.DEFAULT).toString());
assertEquals("[e f]", decoder.getParameter().getAll("d", Parameter.Domain.DEFAULT).toString());
assertEquals("[b c]", decoder.getParameter().getAll("a", Parameter.Domain.PATH).toString());
assertEquals("[e f]", decoder.getParameter().getAll("d", Parameter.Domain.PATH).toString());
}
@Test
@ -30,23 +30,23 @@ class PathDecoderTest {
}
@Test
void testSlashes() {
void testSlashes() throws Exception {
PathDecoder decoder = new PathDecoder("//path?a=b+c", "d=e+f");
assertEquals("/path", decoder.path());
assertEquals("[b c]", decoder.getParameter().getAll("a", Parameter.Domain.DEFAULT).toString());
assertEquals("[e f]", decoder.getParameter().getAll("d", Parameter.Domain.DEFAULT).toString());
assertEquals("[b c]", decoder.getParameter().getAll("a", Parameter.Domain.PATH).toString());
assertEquals("[e f]", decoder.getParameter().getAll("d", Parameter.Domain.PATH).toString());
}
@Test
void testPlusPercent() {
void testPlusPercent() throws Exception {
PathDecoder decoder = new PathDecoder("//path?a=b%2Bc", "d=e%2Bf");
assertEquals("/path", decoder.path());
assertEquals("[b+c]", decoder.getParameter().getAll("a", Parameter.Domain.DEFAULT).toString());
assertEquals("[e+f]", decoder.getParameter().getAll("d", Parameter.Domain.DEFAULT).toString());
assertEquals("[b+c]", decoder.getParameter().getAll("a", Parameter.Domain.PATH).toString());
assertEquals("[e+f]", decoder.getParameter().getAll("d", Parameter.Domain.PATH).toString());
}
@Test
void decodeURL() {
void decodeURL() throws Exception {
String requestURI = "/pdfconverter/index.gtpl?x-fl-key=20190035592&x-source=ftp://dummy@xbib.org/upload/20190035592/20190035592.pdf&x-fl-target=ftp://dummy@xbib.org/fl/download/20190035592/Fernleihe_Kopienlieferung_null_FB201900373_BLQDMT62_20190035592_20190035592.pdf&x-fl-copy=&x-fl-ack=https://xbib.org/ack/&x-fl-pages=1-";
URL url = URL.builder().path(requestURI).build();
assertNull(url.getHost());
@ -59,7 +59,7 @@ class PathDecoderTest {
}
assertEquals("x-fl-key=20190035592&x-source=ftp://dummy@xbib.org/upload/20190035592/20190035592.pdf&x-fl-target=ftp://dummy@xbib.org/fl/download/20190035592/Fernleihe_Kopienlieferung_null_FB201900373_BLQDMT62_20190035592_20190035592.pdf&x-fl-copy=&x-fl-ack=https://xbib.org/ack/&x-fl-pages=1-", url.getDecodedQuery());
assertEquals("[x-fl-key=20190035592, x-source=ftp://dummy@xbib.org/upload/20190035592/20190035592.pdf, x-fl-target=ftp://dummy@xbib.org/fl/download/20190035592/Fernleihe_Kopienlieferung_null_FB201900373_BLQDMT62_20190035592_20190035592.pdf, x-fl-copy=, x-fl-ack=https://xbib.org/ack/, x-fl-pages=1-]", decoder.getParameter().toString());
url = URL.from(decoder.getParameter().getAll("x-fl-target", Parameter.Domain.DEFAULT).get(0).toString());
url = URL.from(decoder.getParameter().getAll("x-fl-target", Parameter.Domain.PATH).get(0).toString());
assertEquals("ftp://dummy@xbib.org/fl/download/20190035592/Fernleihe_Kopienlieferung_null_FB201900373_BLQDMT62_20190035592_20190035592.pdf", url.toString());
}
}

View file

@ -311,38 +311,38 @@ class PathMatcherTest {
}
@Test
void extractUriTemplateVariablesRegex() {
void extractUriTemplateVariablesRegex() throws Exception {
Parameter result = pathMatcher.extractUriTemplateVariables("{symbolicName:[\\w\\.]+}-{version:[\\w\\.]+}.jar",
"com.example-1.0.0.jar");
assertEquals("com.example", result.getAll("symbolicName", Parameter.Domain.DEFAULT).get(0));
assertEquals("1.0.0", result.getAll("version", Parameter.Domain.DEFAULT).get(0));
assertEquals("com.example", result.getAll("symbolicName", Parameter.Domain.PATH).get(0));
assertEquals("1.0.0", result.getAll("version", Parameter.Domain.PATH).get(0));
result = pathMatcher.extractUriTemplateVariables("{symbolicName:[\\w\\.]+}-sources-{version:[\\w\\.]+}.jar",
"com.example-sources-1.0.0.jar");
assertEquals("com.example", result.getAll("symbolicName", Parameter.Domain.DEFAULT).get(0));
assertEquals("1.0.0", result.getAll("version", Parameter.Domain.DEFAULT).get(0));
assertEquals("com.example", result.getAll("symbolicName", Parameter.Domain.PATH).get(0));
assertEquals("1.0.0", result.getAll("version", Parameter.Domain.PATH).get(0));
}
@Test
void extractUriTemplateVarsRegexQualifiers() {
void extractUriTemplateVarsRegexQualifiers() throws Exception {
Parameter result = pathMatcher.extractUriTemplateVariables(
"{symbolicName:[\\p{L}\\.]+}-sources-{version:[\\p{N}\\.]+}.jar",
"com.example-sources-1.0.0.jar");
assertEquals("com.example", result.getAll("symbolicName", Parameter.Domain.DEFAULT).get(0));
assertEquals("1.0.0", result.getAll("version", Parameter.Domain.DEFAULT).get(0));
assertEquals("com.example", result.getAll("symbolicName", Parameter.Domain.PATH).get(0));
assertEquals("1.0.0", result.getAll("version", Parameter.Domain.PATH).get(0));
result = pathMatcher.extractUriTemplateVariables(
"{symbolicName:[\\w\\.]+}-sources-{version:[\\d\\.]+}-{year:\\d{4}}{month:\\d{2}}{day:\\d{2}}.jar",
"com.example-sources-1.0.0-20100220.jar");
assertEquals("com.example", result.getAll("symbolicName", Parameter.Domain.DEFAULT).get(0));
assertEquals("1.0.0", result.getAll("version", Parameter.Domain.DEFAULT).get(0));
assertEquals("2010", result.getAll("year", Parameter.Domain.DEFAULT).get(0));
assertEquals("02", result.getAll("month", Parameter.Domain.DEFAULT).get(0));
assertEquals("20", result.getAll("day", Parameter.Domain.DEFAULT).get(0));
assertEquals("com.example", result.getAll("symbolicName", Parameter.Domain.PATH).get(0));
assertEquals("1.0.0", result.getAll("version", Parameter.Domain.PATH).get(0));
assertEquals("2010", result.getAll("year", Parameter.Domain.PATH).get(0));
assertEquals("02", result.getAll("month", Parameter.Domain.PATH).get(0));
assertEquals("20", result.getAll("day", Parameter.Domain.PATH).get(0));
result = pathMatcher.extractUriTemplateVariables(
"{symbolicName:[\\p{L}\\.]+}-sources-{version:[\\p{N}\\.\\{\\}]+}.jar",
"com.example-sources-1.0.0.{12}.jar");
assertEquals("com.example", result.getAll("symbolicName", Parameter.Domain.DEFAULT).get(0));
assertEquals("1.0.0.{12}", result.getAll("version", Parameter.Domain.DEFAULT).get(0));
assertEquals("com.example", result.getAll("symbolicName", Parameter.Domain.PATH).get(0));
assertEquals("1.0.0.{12}", result.getAll("version", Parameter.Domain.PATH).get(0));
}
@Test

View file

@ -2,6 +2,7 @@ package org.xbib.net.path.simple;
import org.junit.jupiter.api.Test;
import org.xbib.net.Parameter;
import org.xbib.net.ParameterException;
import java.util.Collections;
import java.util.Map;
@ -18,7 +19,7 @@ import static org.junit.jupiter.api.Assertions.fail;
class PathResolverTest {
@Test
void simple() {
void simple() throws ParameterException {
PathResolver<Integer> pathResolver = PathResolver.<Integer>builder()
.add("GET", "explorer", 1234)
.build();
@ -29,7 +30,7 @@ class PathResolverTest {
}
@Test
void name() {
void name() throws ParameterException {
PathResolver<Integer> pathResolver = PathResolver.<Integer>builder()
.add("GET", "/static/{file}", 1234)
.add("HEAD", "/static/{file}", 1234)
@ -39,7 +40,7 @@ class PathResolverTest {
}
@Test
void glob() {
void glob() throws ParameterException {
PathResolver<Integer> trie = PathResolver.<Integer>builder()
.add("GET", "/static/**", 1234)
.build();
@ -48,7 +49,7 @@ class PathResolverTest {
}
@Test
void sharedPrefix() {
void sharedPrefix() throws ParameterException {
PathResolver<Integer> trie = PathResolver.<Integer>builder()
.add("GET", "discovery/v1/rest", 1234)
.add("GET", "discovery/v2/rest", 4321)
@ -61,7 +62,7 @@ class PathResolverTest {
}
@Test
void prefix() {
void prefix() throws ParameterException {
PathResolver<Integer> trie = PathResolver.<Integer>builder()
.add("GET", "discovery", 1234)
.add("GET", "discovery/v1", 4321)
@ -72,7 +73,7 @@ class PathResolverTest {
}
@Test
void parameter() {
void parameter() throws ParameterException {
PathResolver<Integer> trie = PathResolver.<Integer>builder()
.add("GET", "discovery/{version}/rest", 1234)
.build();
@ -81,7 +82,7 @@ class PathResolverTest {
}
@Test
void multipleParameters() {
void multipleParameters() throws ParameterException {
PathResolver<Integer> trie = PathResolver.<Integer>builder()
.add("GET", "discovery/{discovery_version}/apis/{api}/{format}", 1234)
.build();
@ -90,7 +91,7 @@ class PathResolverTest {
}
@Test
void sharedParameterPrefix() {
void sharedParameterPrefix() throws ParameterException {
PathResolver<Integer> trie = PathResolver.<Integer>builder()
.add("GET", "discovery/{version}/rest", 1234)
.add("GET", "discovery/{version}/rpc", 4321)
@ -102,7 +103,7 @@ class PathResolverTest {
}
@Test
void testResolveParameterAfterLiteral() {
void testResolveParameterAfterLiteral() throws ParameterException {
PathResolver<Integer> trie = PathResolver.<Integer>builder()
.add("GET", "{one}/three", 1234)
.add("GET", "one/two", 4321)
@ -112,7 +113,7 @@ class PathResolverTest {
}
@Test
void testResolveBacktrack() {
void testResolveBacktrack() throws ParameterException {
PathResolver<Integer> trie = PathResolver.<Integer>builder()
.add("GET", "{one}/{two}/three/{four}", 1234)
.add("GET", "one/two/{three}/four", 4321)
@ -122,7 +123,7 @@ class PathResolverTest {
}
@Test
void pathMethodsWithDifferentParameterNames() {
void pathMethodsWithDifferentParameterNames() throws ParameterException {
PathResolver<Integer> trie = PathResolver.<Integer>builder()
.add("GET", "test/{one}", 1234)
.add("PUT", "test/{two}", 4321)
@ -142,7 +143,7 @@ class PathResolverTest {
}
@Test
void laxDuplicatePath() {
void laxDuplicatePath() throws ParameterException {
PathResolver<Integer> pathResolver = PathResolver.<Integer>builder(false)
.add("GET", "test/{one}", 1234)
.add("GET", "test/{two}", 4321)
@ -186,6 +187,8 @@ class PathResolverTest {
fail("expected NullPointerException");
} catch (NullPointerException e) {
// expected
} catch (ParameterException e) {
throw new RuntimeException(e);
}
}
@ -223,7 +226,7 @@ class PathResolverTest {
}
@Test
void testFallback() {
void testFallback() throws ParameterException {
AtomicInteger counter = new AtomicInteger(0);
org.xbib.net.path.PathResolver<Integer> trie = PathResolver.<Integer>builder()
.add("GET", "/test/{one}", 1)
@ -247,16 +250,16 @@ class PathResolverTest {
}
}
private void assertSuccessfulGetResolution(PathResolver<Integer> trie, String path, Integer value) {
private void assertSuccessfulGetResolution(PathResolver<Integer> trie, String path, Integer value) throws ParameterException {
assertSuccessfulResolution(trie, "GET", path, value);
}
private void assertSuccessfulResolution(PathResolver<Integer> trie, String method, String path, Integer value) {
private void assertSuccessfulResolution(PathResolver<Integer> trie, String method, String path, Integer value) throws ParameterException {
assertSuccessfulResolution(trie, method, path, Set.of(value), Collections.emptyMap());
}
private void assertSuccessfulGetResolution(PathResolver<Integer> trie, String path, Integer value,
Map<String, String> rawParameters) {
Map<String, String> rawParameters) throws ParameterException {
assertSuccessfulResolution(trie, "GET", path, Set.of(value), rawParameters);
}
@ -264,18 +267,18 @@ class PathResolverTest {
String method,
String path,
Set<Integer> values,
Map<String, String> rawParameters) {
Map<String, String> rawParameters) throws ParameterException {
trie.resolve(method, path, result -> {
assertTrue(values.contains(result.getValue()));
//assertThat(result.getRawParameters(), is(rawParameters));
});
}
private void assertFailedGetResolution(PathResolver<Integer> trie, String path) {
private void assertFailedGetResolution(PathResolver<Integer> trie, String path) throws ParameterException {
assertFailedGetResolution(trie, "GET", path);
}
private void assertFailedGetResolution(PathResolver<Integer> trie, String method, String path) {
private void assertFailedGetResolution(PathResolver<Integer> trie, String method, String path) throws ParameterException {
trie.resolve(method, path, r-> { fail(); });
}
}

View file

@ -3,6 +3,7 @@ package org.xbib.net.path.structure;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.xbib.net.Parameter;
import org.xbib.net.ParameterException;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
@ -20,7 +21,7 @@ import static org.junit.jupiter.api.Assertions.fail;
class PathResolverTest {
@Test
void example() {
void example() throws ParameterException {
PathResolver<Integer> pathResolver = PathResolver.<Integer>builder()
.add( "GET", "/static/{file}", 1234)
.build();
@ -29,7 +30,7 @@ class PathResolverTest {
}
@Test
void simple() {
void simple() throws ParameterException {
PathResolver<Integer> pathResolver = PathResolver.<Integer>builder()
.add("GET", "explorer", 1234)
.build();
@ -39,7 +40,7 @@ class PathResolverTest {
}
@Test
void sharedPrefix() {
void sharedPrefix() throws ParameterException {
PathResolver<Integer> pathResolver = PathResolver.<Integer>builder()
.add("GET", "discovery/v1/rest", 1234)
.add("GET", "discovery/v2/rest", 4321)
@ -52,7 +53,7 @@ class PathResolverTest {
}
@Test
void prefix() {
void prefix() throws ParameterException {
PathResolver<Integer> pathResolver = PathResolver.<Integer>builder()
.add("GET", "discovery", 1234)
.add("GET", "discovery/v1", 4321)
@ -63,7 +64,7 @@ class PathResolverTest {
}
@Test
void parameter() {
void parameter() throws ParameterException {
PathResolver<Integer> pathResolver = PathResolver.<Integer>builder()
.add("GET", "discovery/{version}/rest", 1234)
.build();
@ -72,7 +73,7 @@ class PathResolverTest {
}
@Test
void multipleParameters() {
void multipleParameters() throws ParameterException {
PathResolver<Integer> pathResolver = PathResolver.<Integer>builder()
.add("GET", "discovery/{discovery_version}/apis/{api}/{format}", 1234)
.build();
@ -81,7 +82,7 @@ class PathResolverTest {
}
@Test
void sharedParameterPrefix() {
void sharedParameterPrefix() throws ParameterException {
PathResolver<Integer> pathResolver = PathResolver.<Integer>builder()
.add("GET", "discovery/{version}/rest", 1234)
.add("GET", "discovery/{version}/rpc", 4321)
@ -97,7 +98,7 @@ class PathResolverTest {
}
@Test
void testResolveParameterAfterLiteral() {
void testResolveParameterAfterLiteral() throws ParameterException {
PathResolver<Integer> pathResolver = PathResolver.<Integer>builder()
.add("GET", "{one}/three", 1234)
.add("GET", "one/two", 4321)
@ -108,7 +109,7 @@ class PathResolverTest {
}
@Test
void testResolveBacktrack() {
void testResolveBacktrack() throws ParameterException {
PathResolver<Integer> pathResolver = PathResolver.<Integer>builder()
.add("GET", "{one}/{two}/three/{four}", 1234)
.add("GET", "one/two/{three}/four", 4321)
@ -128,7 +129,7 @@ class PathResolverTest {
}
@Test
void pathMethodsWithDifferentParameterNames() {
void pathMethodsWithDifferentParameterNames() throws ParameterException {
PathResolver<Integer> pathResolver = PathResolver.<Integer>builder()
.add("GET", "test/{one}", 1234)
.add("GET", "test/{two}", 4321)
@ -148,7 +149,7 @@ class PathResolverTest {
}
@Test
void duplicatePathParams() {
void duplicatePathParams() throws ParameterException {
PathResolver<Integer> pathResolver = PathResolver.<Integer>builder()
.add("GET", "test/{one}", 1234)
.add("GET", "test/{two}", 4321)
@ -197,11 +198,13 @@ class PathResolverTest {
fail("expected NullPointerException");
} catch (NullPointerException e) {
// expected
} catch (ParameterException e) {
throw new RuntimeException(e);
}
}
@Test
void testFallback() {
void testFallback() throws ParameterException {
AtomicInteger counter = new AtomicInteger(0);
PathResolver<Integer> trie = PathResolver.<Integer>builder()
.add( "GET", "/test/{one}", 1)
@ -216,7 +219,7 @@ class PathResolverTest {
@Disabled
@Test
void testSuffixCatchAll() {
void testSuffixCatchAll() throws ParameterException {
AtomicInteger counter = new AtomicInteger(0);
PathResolver<Integer> trie = PathResolver.<Integer>builder()
.add( "GET", "/**/*.test", 1)
@ -229,12 +232,12 @@ class PathResolverTest {
assertThat(counter.get(), equalTo(2));
}
private void assertSuccessfulResolution(PathResolver<Integer> pathResolver, String path, Integer value) {
private void assertSuccessfulResolution(PathResolver<Integer> pathResolver, String path, Integer value) throws ParameterException {
assertSuccessfulResolution(pathResolver, "GET", path, value, Parameter.builder().domain(Parameter.Domain.PATH).build());
}
private void assertSuccessfulResolution(PathResolver<Integer> pathResolver, String method, String path, Integer value,
Parameter parameter) {
Parameter parameter) throws ParameterException {
AtomicBoolean found = new AtomicBoolean(false);
pathResolver.resolve(method, path, result -> {
assertThat(result, notNullValue());
@ -246,7 +249,7 @@ class PathResolverTest {
assertTrue(found.get());
}
private void assertFailedGetResolution(PathResolver<Integer> pathResolver, String path) {
private void assertFailedGetResolution(PathResolver<Integer> pathResolver, String path) throws ParameterException {
pathResolver.resolve("GET", path, r -> assertThat(r, nullValue()));
}
}

View file

@ -16,7 +16,7 @@ import org.xbib.datastructures.common.Pair;
public class Parameter implements Iterable<Pair<String, Object>>, Comparable<Parameter> {
public enum Domain {
DEFAULT,
UNDEFINED,
QUERY,
FORM,
PATH,
@ -92,7 +92,7 @@ public class Parameter implements Iterable<Pair<String, Object>>, Comparable<Par
}
@SuppressWarnings("unchecked")
public String getAsString(String key, Domain domain) {
public String getAsString(String key, Domain domain) throws ParameterException {
Object object = get(key, domain);
if (object instanceof Collection) {
Collection<Object> collection = (Collection<Object>) object;
@ -108,7 +108,7 @@ public class Parameter implements Iterable<Pair<String, Object>>, Comparable<Par
}
@SuppressWarnings("unchecked")
public Integer getAsInteger(String key, Domain domain) {
public Integer getAsInteger(String key, Domain domain) throws ParameterException {
Object object = get(key, domain);
if (object instanceof Collection) {
Collection<Object> collection = (Collection<Object>) object;
@ -128,7 +128,7 @@ public class Parameter implements Iterable<Pair<String, Object>>, Comparable<Par
}
@SuppressWarnings("unchecked")
public Boolean getAsBoolean(String key, Domain domain) {
public Boolean getAsBoolean(String key, Domain domain) throws ParameterException {
Object object = get(key, domain);
if (object instanceof Collection) {
Collection<Object> collection = (Collection<Object>) object;
@ -147,49 +147,35 @@ public class Parameter implements Iterable<Pair<String, Object>>, Comparable<Par
}
}
public String allToString() {
StringBuilder sb = new StringBuilder();
sb.append(list.toString());
builder.parameterMap.forEach((key, value) -> sb.append(" ").append(key).append(" -> ").append(value));
return sb.toString();
}
public boolean hasElements() {
return !list.isEmpty();
}
public MultiMap<String, Object> asMultiMap() {
public MultiMap<String, Object> asMultiMap() throws ParameterException{
if (getDomain() == Domain.UNDEFINED) {
throw new ParameterException("undefined domain");
}
MultiMap<String, Object> multiMap = new LinkedHashSetMultiMap<>();
this.forEach(p -> multiMap.put(p.getKey(), p.getValue()));
return multiMap;
}
public Map<String, Object> asSingleValuedMap() {
public Map<String, Object> asSingleValuedMap() throws ParameterException {
if (getDomain() == Domain.UNDEFINED) {
throw new ParameterException("undefined domain");
}
Map<String, Object> map = new LinkedHashMap<>();
this.forEach(p -> map.put(p.getKey(), createValue(p.getValue())));
return map;
}
@SuppressWarnings("unchecked")
private static Object createValue(Object object) {
if (object instanceof Collection) {
Collection<Object> collection = (Collection<Object>) object;
if (collection.size() == 1) {
return collection.iterator().next();
} else {
return collection;
}
}
return object;
}
public List<Object> getAllDomain(Domain domain) {
public List<Object> getAllInDomain(Domain domain) {
Parameter parameter = null;
if (builder.parameterMap.containsKey(domain)) {
parameter = builder.parameterMap.get(domain);
}
if (parameter != null) {
return parameter.getAllDomain(domain);
return parameter.getAllInDomain(domain);
}
if (getDomain().equals(domain)) {
return list.stream()
@ -213,7 +199,10 @@ public class Parameter implements Iterable<Pair<String, Object>>, Comparable<Par
return false;
}
public Parameter getDomain(Domain domain) {
public Parameter get(Domain domain) throws ParameterException {
if (getDomain() == Domain.UNDEFINED) {
throw new ParameterException("undefined domain");
}
if (builder.parameterMap.containsKey(domain)) {
return builder.parameterMap.get(domain);
}
@ -223,7 +212,10 @@ public class Parameter implements Iterable<Pair<String, Object>>, Comparable<Par
return null;
}
public List<Object> getAll(String key, Domain domain) {
public List<Object> getAll(String key, Domain domain) throws Exception {
if (getDomain() == Domain.UNDEFINED) {
throw new ParameterException("undefined domain");
}
Parameter parameter = null;
if (builder.parameterMap.containsKey(domain)) {
parameter = builder.parameterMap.get(domain);
@ -255,7 +247,10 @@ public class Parameter implements Iterable<Pair<String, Object>>, Comparable<Par
return false;
}
public Object get(String key, Domain domain) {
public Object get(String key, Domain domain) throws ParameterException {
if (getDomain() == Domain.UNDEFINED) {
throw new ParameterException("undefined domain");
}
Parameter parameter = null;
if (builder.parameterMap.containsKey(domain)) {
parameter = builder.parameterMap.get(domain);
@ -278,4 +273,24 @@ public class Parameter implements Iterable<Pair<String, Object>>, Comparable<Par
public String getAsQueryString() {
return queryString;
}
private String allToString() {
StringBuilder sb = new StringBuilder();
sb.append(list.toString());
builder.parameterMap.forEach((key, value) -> sb.append(" ").append(key).append(" -> ").append(value));
return sb.toString();
}
@SuppressWarnings("unchecked")
private static Object createValue(Object object) {
if (object instanceof Collection) {
Collection<Object> collection = (Collection<Object>) object;
if (collection.size() == 1) {
return collection.iterator().next();
} else {
return collection;
}
}
return object;
}
}

View file

@ -65,7 +65,7 @@ public class ParameterBuilder implements PairValidator {
ParameterBuilder() {
this.list = new ArrayList<>();
this.parameterMap = new HashMap<>();
this.domain = Parameter.Domain.DEFAULT;
this.domain = Parameter.Domain.UNDEFINED;
this.limit = 0;
}

View file

@ -0,0 +1,9 @@
package org.xbib.net;
@SuppressWarnings("serial")
public class ParameterException extends Exception {
public ParameterException(String message) {
super(message);
}
}

View file

@ -82,7 +82,7 @@ public class URLBuilder {
.onMalformedInput(codingErrorAction)
.onUnmappableCharacter(codingErrorAction);
this.percentDecoder = new PercentDecoder(charsetDecoder);
this.queryParams = Parameter.builder();
this.queryParams = Parameter.builder().domain(Parameter.Domain.QUERY);
return this;
}
@ -301,7 +301,6 @@ public class URLBuilder {
}
}
/**
* A path segment with associated matrix params, if any.
*/

View file

@ -17,33 +17,35 @@ public class ParameterTest {
public void testEmptyBuilder() {
Parameter parameter = Parameter.builder().build();
assertNotNull(parameter);
assertFalse(parameter.containsKey("param1", Parameter.Domain.DEFAULT));
assertFalse(parameter.containsKey("param1", Parameter.Domain.UNDEFINED));
}
@Test
public void testSingleParameter() {
Parameter parameter = Parameter.builder()
.domain(Parameter.Domain.QUERY)
.add("Hello", "World")
.build();
assertNotNull(parameter);
assertTrue(parameter.containsKey("Hello", Parameter.Domain.DEFAULT));
assertTrue(parameter.containsKey("Hello", Parameter.Domain.QUERY));
}
@Test
public void testDuplicateParameter() {
public void testDuplicateParameter() throws Exception {
Parameter parameter = Parameter.builder()
.domain(Parameter.Domain.QUERY)
.enableDuplicates()
.add("Hello", "World")
.add("Hello", "World")
.add("Hello", "World")
.build();
assertNotNull(parameter);
assertTrue(parameter.containsKey("Hello", Parameter.Domain.DEFAULT));
assertEquals(List.of("World", "World", "World"), parameter.getAll("Hello", Parameter.Domain.DEFAULT));
assertTrue(parameter.containsKey("Hello", Parameter.Domain.QUERY));
assertEquals(List.of("World", "World", "World"), parameter.getAll("Hello", Parameter.Domain.QUERY));
}
@Test
public void testHttpHeaderParameter() {
public void testHttpHeaderParameter() throws Exception {
Parameter parameter = Parameter.builder()
.charset(StandardCharsets.US_ASCII)
.lowercase()
@ -116,20 +118,20 @@ public class ParameterTest {
}
@Test
void testSimpleParse() {
ParameterBuilder queryParameters = Parameter.builder();
void testSimpleParse() throws Exception {
ParameterBuilder queryParameters = Parameter.builder().domain(Parameter.Domain.QUERY);
String body = "a=b&c=d&e=f";
queryParameters.addPercentEncodedBody(body);
Parameter parameter = queryParameters.build();
assertEquals("b", parameter.getAll("a", Parameter.Domain.DEFAULT).get(0));
assertEquals("d", parameter.getAll("c", Parameter.Domain.DEFAULT).get(0));
assertEquals("f", parameter.getAll("e", Parameter.Domain.DEFAULT).get(0));
assertEquals("b", parameter.getAll("a", Parameter.Domain.QUERY).get(0));
assertEquals("d", parameter.getAll("c", Parameter.Domain.QUERY).get(0));
assertEquals("f", parameter.getAll("e", Parameter.Domain.QUERY).get(0));
}
@Test
void testParseExceedingParamLimit() {
Assertions.assertThrows(IllegalArgumentException.class, () -> {
ParameterBuilder queryParameters = Parameter.builder().limit(100);
ParameterBuilder queryParameters = Parameter.builder().domain(Parameter.Domain.QUERY).limit(100);
List<String> list = new ArrayList<>();
for (int i = 0; i < 200; i++) {
list.add("a" + i + "=b" + i);
@ -137,18 +139,19 @@ public class ParameterTest {
String body = String.join("&", list);
queryParameters.addPercentEncodedBody(body);
Parameter parameter = queryParameters.build();
assertEquals("b0", parameter.getAll("a0", Parameter.Domain.DEFAULT).get(0));
assertEquals("b99", parameter.getAll("a99", Parameter.Domain.DEFAULT).get(0));
assertEquals("[]", parameter.getAll("a100", Parameter.Domain.DEFAULT).toString());
assertEquals("b0", parameter.getAll("a0", Parameter.Domain.QUERY).get(0));
assertEquals("b99", parameter.getAll("a99", Parameter.Domain.QUERY).get(0));
assertEquals("[]", parameter.getAll("a100", Parameter.Domain.QUERY).toString());
});
}
@Test
void testDomains() {
void testDomains() throws ParameterException {
Parameter p1 = Parameter.builder().domain(Parameter.Domain.QUERY).add("a", "a").build();
Parameter p2 = Parameter.builder().domain(Parameter.Domain.FORM).add("b", "b").build();
Parameter p3 = Parameter.builder().domain(Parameter.Domain.HEADER).add("c", "c").build();
Parameter p = Parameter.builder()
.domain(Parameter.Domain.QUERY)
.add(p1)
.add(p2)
.add(p3)
@ -162,6 +165,5 @@ public class ParameterTest {
assertTrue(p.isPresent(Parameter.Domain.QUERY));
assertTrue(p.isPresent(Parameter.Domain.FORM));
assertTrue(p.isPresent(Parameter.Domain.HEADER));
assertFalse(p.isPresent(Parameter.Domain.DEFAULT));
}
}

View file

@ -327,7 +327,7 @@ class URLParserTest {
assertEquals("path", pathSegment.getSegment());
assertEquals("p2", pathSegment.getMatrixParams().get(0).getKey());
assertEquals("v2", pathSegment.getMatrixParams().get(0).getValue());
assertEquals("v3", url.getQueryParams().getAll("q1", Parameter.Domain.DEFAULT).get(0));
assertEquals("v3", url.getQueryParams().getAll("q1", Parameter.Domain.QUERY).get(0));
}
@Test