From 1018bf771b57984cf3c7a9b2ad8c72e8c45165b9 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Wed, 14 Jun 2017 16:38:28 -0400 Subject: [PATCH] Separate DefaultRequestPath/DefaultPathSegmentContainer --- .../reactive/DefaultPathSegmentContainer.java | 284 ++++++++++++++++++ .../server/reactive/DefaultRequestPath.java | 275 ++--------------- .../http/server/reactive/PathSegment.java | 2 +- .../server/reactive/PathSegmentContainer.java | 2 +- .../DefaultPathSegmentContainerTests.java | 124 ++++++++ .../reactive/DefaultRequestPathTests.java | 94 ------ 6 files changed, 437 insertions(+), 344 deletions(-) create mode 100644 spring-web/src/main/java/org/springframework/http/server/reactive/DefaultPathSegmentContainer.java create mode 100644 spring-web/src/test/java/org/springframework/http/server/reactive/DefaultPathSegmentContainerTests.java diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/DefaultPathSegmentContainer.java b/spring-web/src/main/java/org/springframework/http/server/reactive/DefaultPathSegmentContainer.java new file mode 100644 index 00000000000..97816ea5a98 --- /dev/null +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/DefaultPathSegmentContainer.java @@ -0,0 +1,284 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.http.server.reactive; + +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.util.StringUtils; + +/** + * Default implementations of {@link PathSegmentContainer} and {@link PathSegment}. + * + * @author Rossen Stoyanchev + * @since 5.0 + */ +class DefaultPathSegmentContainer implements PathSegmentContainer { + + private static final MultiValueMap EMPTY_MAP = new LinkedMultiValueMap<>(0); + + private static final PathSegment EMPTY_PATH_SEGMENT = new DefaultPathSegment("", "", "", EMPTY_MAP); + + static final PathSegmentContainer EMPTY_PATH = + new DefaultPathSegmentContainer("", Collections.emptyList()); + + private static final PathSegmentContainer ROOT_PATH = + new DefaultPathSegmentContainer("/", Collections.singletonList(EMPTY_PATH_SEGMENT)); + + + private final String path; + + private final boolean empty; + + private final boolean absolute; + + private final List pathSegments; + + private final boolean trailingSlash; + + + DefaultPathSegmentContainer(String path, List segments) { + this.path = path; + this.absolute = path.startsWith("/"); + this.pathSegments = Collections.unmodifiableList(segments); + this.trailingSlash = path.endsWith("/") && path.length() > 1; + this.empty = !this.absolute && !this.trailingSlash && segments.stream().allMatch(PathSegment::isEmpty); + } + + + @Override + public String value() { + return this.path; + } + + @Override + public boolean isEmpty() { + return this.empty; + } + + @Override + public boolean isAbsolute() { + return this.absolute; + } + + @Override + public List pathSegments() { + return this.pathSegments; + } + + @Override + public boolean hasTrailingSlash() { + return this.trailingSlash; + } + + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + return this.path.equals(((DefaultPathSegmentContainer) other).path); + } + + @Override + public int hashCode() { + return this.path.hashCode(); + } + + @Override + public String toString() { + return "[path='" + this.path + "\']"; + } + + + static PathSegmentContainer parsePath(String path, Charset charset) { + path = StringUtils.hasText(path) ? path : ""; + if ("".equals(path)) { + return EMPTY_PATH; + } + if ("/".equals(path)) { + return ROOT_PATH; + } + List result = new ArrayList<>(); + int begin = (path.charAt(0) == '/' ? 1 : 0); + while (begin < path.length()) { + int end = path.indexOf('/', begin); + String segment = (end != -1 ? path.substring(begin, end) : path.substring(begin)); + result.add(parsePathSegment(segment, charset)); + if (end == -1) { + break; + } + begin = end + 1; + } + return new DefaultPathSegmentContainer(path, result); + } + + static PathSegment parsePathSegment(String input, Charset charset) { + if ("".equals(input)) { + return EMPTY_PATH_SEGMENT; + } + int index = input.indexOf(';'); + if (index == -1) { + String inputDecoded = StringUtils.uriDecode(input, charset); + return new DefaultPathSegment(input, inputDecoded, "", EMPTY_MAP); + } + String value = input.substring(0, index); + String valueDecoded = StringUtils.uriDecode(value, charset); + String semicolonContent = input.substring(index); + MultiValueMap parameters = parseParams(semicolonContent, charset); + return new DefaultPathSegment(value, valueDecoded, semicolonContent, parameters); + } + + private static MultiValueMap parseParams(String input, Charset charset) { + MultiValueMap result = new LinkedMultiValueMap<>(); + int begin = 1; + while (begin < input.length()) { + int end = input.indexOf(';', begin); + String param = (end != -1 ? input.substring(begin, end) : input.substring(begin)); + parseParamValues(param, charset, result); + if (end == -1) { + break; + } + begin = end + 1; + } + return result; + } + + private static void parseParamValues(String input, Charset charset, MultiValueMap output) { + if (StringUtils.hasText(input)) { + int index = input.indexOf("="); + if (index != -1) { + String name = input.substring(0, index); + String value = input.substring(index + 1); + for (String v : StringUtils.commaDelimitedListToStringArray(value)) { + name = StringUtils.uriDecode(name, charset); + if (StringUtils.hasText(name)) { + output.add(name, StringUtils.uriDecode(v, charset)); + } + } + } + else { + String name = StringUtils.uriDecode(input, charset); + if (StringUtils.hasText(name)) { + output.add(input, ""); + } + } + } + } + + + + private static class DefaultPathSegment implements PathSegment { + + private final String value; + + private final String valueDecoded; + + private final char[] valueCharsDecoded; + + private final boolean empty; + + private final String semicolonContent; + + private final MultiValueMap parameters; + + + DefaultPathSegment(String value, String valueDecoded, String semicolonContent, + MultiValueMap params) { + + Assert.isTrue(!value.contains("/"), "Invalid path segment value: " + value); + + this.value = value; + this.valueDecoded = valueDecoded; + this.valueCharsDecoded = valueDecoded.toCharArray(); + this.empty = !StringUtils.hasText(this.valueDecoded); + this.semicolonContent = semicolonContent; + this.parameters = CollectionUtils.unmodifiableMultiValueMap(params); + } + + + @Override + public String value() { + return this.value; + } + + @Override + public String valueDecoded() { + return this.valueDecoded; + } + + @Override + public char[] valueCharsDecoded() { + return this.valueCharsDecoded; + } + + @Override + public boolean isEmpty() { + return this.empty; + } + + @Override + public String semicolonContent() { + return this.semicolonContent; + } + + @Override + public MultiValueMap parameters() { + return this.parameters; + } + + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + + DefaultPathSegment segment = (DefaultPathSegment) other; + return (this.value.equals(segment.value) && + this.semicolonContent.equals(segment.semicolonContent) && + this.parameters.equals(segment.parameters)); + } + + @Override + public int hashCode() { + int result = this.value.hashCode(); + result = 31 * result + this.semicolonContent.hashCode(); + result = 31 * result + this.parameters.hashCode(); + return result; + } + + public String toString() { + return "[value='" + this.value + "\', " + + "semicolonContent='" + this.semicolonContent + "\', " + + "parameters=" + this.parameters + "']"; + } + } + +} + diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/DefaultRequestPath.java b/spring-web/src/main/java/org/springframework/http/server/reactive/DefaultRequestPath.java index c5e621232c6..67a4892d759 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/DefaultRequestPath.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/DefaultRequestPath.java @@ -18,13 +18,9 @@ package org.springframework.http.server.reactive; import java.net.URI; import java.nio.charset.Charset; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; /** @@ -35,17 +31,6 @@ import org.springframework.util.StringUtils; */ class DefaultRequestPath implements RequestPath { - private static final MultiValueMap EMPTY_MAP = new LinkedMultiValueMap<>(0); - - private static final PathSegment EMPTY_PATH_SEGMENT = new DefaultPathSegment("", "", "", EMPTY_MAP); - - private static final PathSegmentContainer EMPTY_PATH = - new DefaultPathSegmentContainer("", Collections.emptyList()); - - private static final PathSegmentContainer ROOT_PATH = - new DefaultPathSegmentContainer("/", Collections.singletonList(EMPTY_PATH_SEGMENT)); - - private final PathSegmentContainer fullPath; private final PathSegmentContainer contextPath; @@ -54,95 +39,21 @@ class DefaultRequestPath implements RequestPath { DefaultRequestPath(URI uri, String contextPath, Charset charset) { - this.fullPath = parsePath(uri.getRawPath(), charset); + this.fullPath = PathSegmentContainer.parse(uri.getRawPath(), charset); this.contextPath = initContextPath(this.fullPath, contextPath); this.pathWithinApplication = initPathWithinApplication(this.fullPath, this.contextPath); } DefaultRequestPath(RequestPath requestPath, String contextPath) { - this.fullPath = new DefaultPathSegmentContainer(requestPath.value(), requestPath.pathSegments()); + this.fullPath = requestPath; this.contextPath = initContextPath(this.fullPath, contextPath); this.pathWithinApplication = initPathWithinApplication(this.fullPath, this.contextPath); } - static PathSegmentContainer parsePath(String path, Charset charset) { - path = StringUtils.hasText(path) ? path : ""; - if ("".equals(path)) { - return EMPTY_PATH; - } - if ("/".equals(path)) { - return ROOT_PATH; - } - List result = new ArrayList<>(); - int begin = (path.charAt(0) == '/' ? 1 : 0); - while (begin < path.length()) { - int end = path.indexOf('/', begin); - String segment = (end != -1 ? path.substring(begin, end) : path.substring(begin)); - result.add(parsePathSegment(segment, charset)); - if (end == -1) { - break; - } - begin = end + 1; - } - return new DefaultPathSegmentContainer(path, result); - } - - static PathSegment parsePathSegment(String input, Charset charset) { - if ("".equals(input)) { - return EMPTY_PATH_SEGMENT; - } - int index = input.indexOf(';'); - if (index == -1) { - String inputDecoded = StringUtils.uriDecode(input, charset); - return new DefaultPathSegment(input, inputDecoded, "", EMPTY_MAP); - } - String value = input.substring(0, index); - String valueDecoded = StringUtils.uriDecode(value, charset); - String semicolonContent = input.substring(index); - MultiValueMap parameters = parseParams(semicolonContent, charset); - return new DefaultPathSegment(value, valueDecoded, semicolonContent, parameters); - } - - private static MultiValueMap parseParams(String input, Charset charset) { - MultiValueMap result = new LinkedMultiValueMap<>(); - int begin = 1; - while (begin < input.length()) { - int end = input.indexOf(';', begin); - String param = (end != -1 ? input.substring(begin, end) : input.substring(begin)); - parseParamValues(param, charset, result); - if (end == -1) { - break; - } - begin = end + 1; - } - return result; - } - - private static void parseParamValues(String input, Charset charset, MultiValueMap output) { - if (StringUtils.hasText(input)) { - int index = input.indexOf("="); - if (index != -1) { - String name = input.substring(0, index); - String value = input.substring(index + 1); - for (String v : StringUtils.commaDelimitedListToStringArray(value)) { - name = StringUtils.uriDecode(name, charset); - if (StringUtils.hasText(name)) { - output.add(name, StringUtils.uriDecode(v, charset)); - } - } - } - else { - String name = StringUtils.uriDecode(input, charset); - if (StringUtils.hasText(name)) { - output.add(input, ""); - } - } - } - } private static PathSegmentContainer initContextPath(PathSegmentContainer path, String contextPath) { if (!StringUtils.hasText(contextPath) || "/".equals(contextPath)) { - return EMPTY_PATH; + return DefaultPathSegmentContainer.EMPTY_PATH; } Assert.isTrue(contextPath.startsWith("/") && !contextPath.endsWith("/") && @@ -192,7 +103,8 @@ class DefaultRequestPath implements RequestPath { @Override public boolean isAbsolute() { - return !this.contextPath.isEmpty() && this.contextPath.isAbsolute() || this.pathWithinApplication.isAbsolute(); + return !this.contextPath.isEmpty() && this.contextPath.isAbsolute() || + this.pathWithinApplication.isAbsolute(); } @Override @@ -219,166 +131,33 @@ class DefaultRequestPath implements RequestPath { } - private static class DefaultPathSegmentContainer implements PathSegmentContainer { - - private final String path; - - private final boolean empty; - - private final boolean absolute; - - private final List pathSegments; - - private final boolean trailingSlash; - - - - DefaultPathSegmentContainer(String path, List segments) { - this.path = path; - this.absolute = path.startsWith("/"); - this.pathSegments = Collections.unmodifiableList(segments); - this.trailingSlash = path.endsWith("/") && path.length() > 1; - this.empty = !this.absolute && !this.trailingSlash && segments.stream().allMatch(PathSegment::isEmpty); + @Override + public boolean equals(Object other) { + if (this == other) { + return true; } - - - @Override - public String value() { - return this.path; - } - - @Override - public boolean isEmpty() { - return this.empty; - } - - @Override - public boolean isAbsolute() { - return this.absolute; - } - - @Override - public List pathSegments() { - return this.pathSegments; - } - - @Override - public boolean hasTrailingSlash() { - return this.trailingSlash; - } - - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } - if (other == null || getClass() != other.getClass()) { - return false; - } - return this.path.equals(((DefaultPathSegmentContainer) other).path); - } - - @Override - public int hashCode() { - return this.path.hashCode(); - } - - @Override - public String toString() { - return "[path='" + this.path + "\']"; + if (other == null || getClass() != other.getClass()) { + return false; } + DefaultRequestPath that = (DefaultRequestPath) other; + return (this.fullPath.equals(that.fullPath) && + this.contextPath.equals(that.contextPath) && + this.pathWithinApplication.equals(that.pathWithinApplication)); } + @Override + public int hashCode() { + int result = this.fullPath.hashCode(); + result = 31 * result + this.contextPath.hashCode(); + result = 31 * result + this.pathWithinApplication.hashCode(); + return result; + } - private static class DefaultPathSegment implements PathSegment { - - private final String value; - - private final String valueDecoded; - - private final char[] valueCharsDecoded; - - private final boolean empty; - - private final String semicolonContent; - - private final MultiValueMap parameters; - - - DefaultPathSegment(String value, String valueDecoded, String semicolonContent, - MultiValueMap params) { - - Assert.isTrue(!value.contains("/"), "Invalid path segment value: " + value); - - this.value = value; - this.valueDecoded = valueDecoded; - this.valueCharsDecoded = valueDecoded.toCharArray(); - this.empty = !StringUtils.hasText(this.valueDecoded); - this.semicolonContent = semicolonContent; - this.parameters = CollectionUtils.unmodifiableMultiValueMap(params); - } - - - @Override - public String value() { - return this.value; - } - - @Override - public String valueDecoded() { - return this.valueDecoded; - } - - @Override - public char[] valueCharsDecoded() { - return this.valueCharsDecoded; - } - - @Override - public boolean isEmpty() { - return this.empty; - } - - @Override - public String semicolonContent() { - return this.semicolonContent; - } - - @Override - public MultiValueMap parameters() { - return this.parameters; - } - - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } - if (other == null || getClass() != other.getClass()) { - return false; - } - - DefaultPathSegment segment = (DefaultPathSegment) other; - return (this.value.equals(segment.value) && - this.semicolonContent.equals(segment.semicolonContent) && - this.parameters.equals(segment.parameters)); - } - - @Override - public int hashCode() { - int result = this.value.hashCode(); - result = 31 * result + this.semicolonContent.hashCode(); - result = 31 * result + this.parameters.hashCode(); - return result; - } - - public String toString() { - return "[value='" + this.value + "\', " + - "semicolonContent='" + this.semicolonContent + "\', " + - "parameters=" + this.parameters + "']"; - } + @Override + public String toString() { + return "DefaultRequestPath[fullPath='" + this.fullPath + "', " + + "contextPath='" + this.contextPath.value() + "', " + + "pathWithinApplication='" + this.pathWithinApplication.value() + "']"; } } diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/PathSegment.java b/spring-web/src/main/java/org/springframework/http/server/reactive/PathSegment.java index e0cdf1c94a3..1c3be8446a0 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/PathSegment.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/PathSegment.java @@ -69,7 +69,7 @@ public interface PathSegment { * @return the parsed path segment */ static PathSegment parse(String path, Charset encoding) { - return DefaultRequestPath.parsePathSegment(path, encoding); + return DefaultPathSegmentContainer.parsePathSegment(path, encoding); } } diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/PathSegmentContainer.java b/spring-web/src/main/java/org/springframework/http/server/reactive/PathSegmentContainer.java index 942ba9cad5e..c5017af098d 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/PathSegmentContainer.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/PathSegmentContainer.java @@ -64,7 +64,7 @@ public interface PathSegmentContainer { * @return the parsed path */ static PathSegmentContainer parse(String path, Charset encoding) { - return DefaultRequestPath.parsePath(path, encoding); + return DefaultPathSegmentContainer.parsePath(path, encoding); } } diff --git a/spring-web/src/test/java/org/springframework/http/server/reactive/DefaultPathSegmentContainerTests.java b/spring-web/src/test/java/org/springframework/http/server/reactive/DefaultPathSegmentContainerTests.java new file mode 100644 index 00000000000..a4a77e77f4e --- /dev/null +++ b/spring-web/src/test/java/org/springframework/http/server/reactive/DefaultPathSegmentContainerTests.java @@ -0,0 +1,124 @@ +/* + * Copyright 2002-2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.http.server.reactive; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.Test; + +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.Assert.assertEquals; + +/** + * Unit tests for {@link DefaultPathSegmentContainer}. + * @author Rossen Stoyanchev + */ +public class DefaultPathSegmentContainerTests { + + @Test + public void pathSegment() throws Exception { + // basic + testPathSegment("cars", "", "cars", "cars", false, new LinkedMultiValueMap<>()); + + // empty + testPathSegment("", "", "", "", true, new LinkedMultiValueMap<>()); + + // spaces + testPathSegment("%20%20", "", "%20%20", " ", true, new LinkedMultiValueMap<>()); + testPathSegment("%20a%20", "", "%20a%20", " a ", false, new LinkedMultiValueMap<>()); + } + + @Test + public void pathSegmentParams() throws Exception { + // basic + LinkedMultiValueMap params = new LinkedMultiValueMap<>(); + params.add("colors", "red"); + params.add("colors", "blue"); + params.add("colors", "green"); + params.add("year", "2012"); + testPathSegment("cars", ";colors=red,blue,green;year=2012", "cars", "cars", false, params); + + // trailing semicolon + params = new LinkedMultiValueMap<>(); + params.add("p", "1"); + testPathSegment("path", ";p=1;", "path", "path", false, params); + + // params with spaces + params = new LinkedMultiValueMap<>(); + params.add("param name", "param value"); + testPathSegment("path", ";param%20name=param%20value;%20", "path", "path", false, params); + + // empty params + params = new LinkedMultiValueMap<>(); + params.add("p", "1"); + testPathSegment("path", ";;;%20;%20;p=1;%20", "path", "path", false, params); + } + + private void testPathSegment(String pathSegment, String semicolonContent, + String value, String valueDecoded, boolean empty, MultiValueMap params) { + + PathSegment segment = PathSegment.parse(pathSegment + semicolonContent, UTF_8); + + assertEquals("value: '" + pathSegment + "'", value, segment.value()); + assertEquals("valueDecoded: '" + pathSegment + "'", valueDecoded, segment.valueDecoded()); + assertEquals("isEmpty: '" + pathSegment + "'", empty, segment.isEmpty()); + assertEquals("semicolonContent: '" + pathSegment + "'", semicolonContent, segment.semicolonContent()); + assertEquals("params: '" + pathSegment + "'", params, segment.parameters()); + } + + @Test + public void path() throws Exception { + // basic + testPath("/a/b/c", "/a/b/c", false, true, Arrays.asList("a", "b", "c"), false); + + // root path + testPath("/", "/", false, true, Collections.singletonList(""), false); + + // empty path + testPath("", "", true, false, Collections.emptyList(), false); + testPath("%20%20", "%20%20", true, false, Collections.singletonList("%20%20"), false); + + // trailing slash + testPath("/a/b/", "/a/b/", false, true, Arrays.asList("a", "b"), true); + testPath("/a/b//", "/a/b//", false, true, Arrays.asList("a", "b", ""), true); + + // extra slashes and spaces + testPath("/%20", "/%20", false, true, Collections.singletonList("%20"), false); + testPath("//%20/%20", "//%20/%20", false, true, Arrays.asList("", "%20", "%20"), false); + } + + private void testPath(String input, String value, boolean empty, boolean absolute, + List segments, boolean trailingSlash) { + + PathSegmentContainer path = PathSegmentContainer.parse(input, UTF_8); + + List segmentValues = path.pathSegments().stream().map(PathSegment::value) + .collect(Collectors.toList()); + + assertEquals("value: '" + input + "'", value, path.value()); + assertEquals("empty: '" + input + "'", empty, path.isEmpty()); + assertEquals("isAbsolute: '" + input + "'", absolute, path.isAbsolute()); + assertEquals("pathSegments: " + input, segments, segmentValues); + assertEquals("hasTrailingSlash: '" + input + "'", trailingSlash, path.hasTrailingSlash()); + } + +} diff --git a/spring-web/src/test/java/org/springframework/http/server/reactive/DefaultRequestPathTests.java b/spring-web/src/test/java/org/springframework/http/server/reactive/DefaultRequestPathTests.java index e1bb75d4cad..d478095717a 100644 --- a/spring-web/src/test/java/org/springframework/http/server/reactive/DefaultRequestPathTests.java +++ b/spring-web/src/test/java/org/springframework/http/server/reactive/DefaultRequestPathTests.java @@ -16,16 +16,9 @@ package org.springframework.http.server.reactive; import java.net.URI; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; import org.junit.Test; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; - import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.assertEquals; @@ -35,93 +28,6 @@ import static org.junit.Assert.assertEquals; */ public class DefaultRequestPathTests { - @Test - public void pathSegment() throws Exception { - // basic - testPathSegment("cars", "", "cars", "cars", false, new LinkedMultiValueMap<>()); - - // empty - testPathSegment("", "", "", "", true, new LinkedMultiValueMap<>()); - - // spaces - testPathSegment("%20%20", "", "%20%20", " ", true, new LinkedMultiValueMap<>()); - testPathSegment("%20a%20", "", "%20a%20", " a ", false, new LinkedMultiValueMap<>()); - } - - @Test - public void pathSegmentParams() throws Exception { - // basic - LinkedMultiValueMap params = new LinkedMultiValueMap<>(); - params.add("colors", "red"); - params.add("colors", "blue"); - params.add("colors", "green"); - params.add("year", "2012"); - testPathSegment("cars", ";colors=red,blue,green;year=2012", "cars", "cars", false, params); - - // trailing semicolon - params = new LinkedMultiValueMap<>(); - params.add("p", "1"); - testPathSegment("path", ";p=1;", "path", "path", false, params); - - // params with spaces - params = new LinkedMultiValueMap<>(); - params.add("param name", "param value"); - testPathSegment("path", ";param%20name=param%20value;%20", "path", "path", false, params); - - // empty params - params = new LinkedMultiValueMap<>(); - params.add("p", "1"); - testPathSegment("path", ";;;%20;%20;p=1;%20", "path", "path", false, params); - } - - private void testPathSegment(String pathSegment, String semicolonContent, - String value, String valueDecoded, boolean empty, MultiValueMap params) { - - PathSegment segment = PathSegment.parse(pathSegment + semicolonContent, UTF_8); - - assertEquals("value: '" + pathSegment + "'", value, segment.value()); - assertEquals("valueDecoded: '" + pathSegment + "'", valueDecoded, segment.valueDecoded()); - assertEquals("isEmpty: '" + pathSegment + "'", empty, segment.isEmpty()); - assertEquals("semicolonContent: '" + pathSegment + "'", semicolonContent, segment.semicolonContent()); - assertEquals("params: '" + pathSegment + "'", params, segment.parameters()); - } - - @Test - public void path() throws Exception { - // basic - testPath("/a/b/c", "/a/b/c", false, true, Arrays.asList("a", "b", "c"), false); - - // root path - testPath("/", "/", false, true, Collections.singletonList(""), false); - - // empty path - testPath("", "", true, false, Collections.emptyList(), false); - testPath("%20%20", "%20%20", true, false, Collections.singletonList("%20%20"), false); - - // trailing slash - testPath("/a/b/", "/a/b/", false, true, Arrays.asList("a", "b"), true); - testPath("/a/b//", "/a/b//", false, true, Arrays.asList("a", "b", ""), true); - - // extra slashes and spaces - testPath("/%20", "/%20", false, true, Collections.singletonList("%20"), false); - testPath("//%20/%20", "//%20/%20", false, true, Arrays.asList("", "%20", "%20"), false); - } - - private void testPath(String input, String value, boolean empty, boolean absolute, - List segments, boolean trailingSlash) { - - PathSegmentContainer path = PathSegmentContainer.parse(input, UTF_8); - - List segmentValues = path.pathSegments().stream().map(PathSegment::value) - .collect(Collectors.toList()); - - assertEquals("value: '" + input + "'", value, path.value()); - assertEquals("empty: '" + input + "'", empty, path.isEmpty()); - assertEquals("isAbsolute: '" + input + "'", absolute, path.isAbsolute()); - assertEquals("pathSegments: " + input, segments, segmentValues); - assertEquals("hasTrailingSlash: '" + input + "'", trailingSlash, path.hasTrailingSlash()); - } - @Test public void requestPath() throws Exception { // basic