diff --git a/core/src/main/java/com/linecorp/armeria/internal/common/ArmeriaHttpUtil.java b/core/src/main/java/com/linecorp/armeria/internal/common/ArmeriaHttpUtil.java index e768ba4a9fe..14082a2a2ae 100644 --- a/core/src/main/java/com/linecorp/armeria/internal/common/ArmeriaHttpUtil.java +++ b/core/src/main/java/com/linecorp/armeria/internal/common/ArmeriaHttpUtil.java @@ -368,7 +368,7 @@ private static String simpleConcat(String prefix, String path) { * Decodes a percent-encoded path string. */ public static String decodePath(String path) { - if (path.indexOf('%') < 0) { + if (path.indexOf('%') < 0 && path.indexOf('+') < 0) { // No need to decode because it's not percent-encoded return path; } @@ -407,6 +407,11 @@ private static String slowDecodePath(String path, boolean decodeSlash) { int dstLen = 0; for (int i = 0; i < len; i++) { final char ch = path.charAt(i); + if (ch == '+') { + buf[dstLen++] = (byte) ' '; + continue; + } + if (ch != '%') { buf[dstLen++] = (byte) ((ch & 0xFF80) == 0 ? ch : 0xFF); continue; diff --git a/core/src/test/java/com/linecorp/armeria/internal/common/ArmeriaHttpUtilTest.java b/core/src/test/java/com/linecorp/armeria/internal/common/ArmeriaHttpUtilTest.java index d478c8256ae..fbcfdca9b99 100644 --- a/core/src/test/java/com/linecorp/armeria/internal/common/ArmeriaHttpUtilTest.java +++ b/core/src/test/java/com/linecorp/armeria/internal/common/ArmeriaHttpUtilTest.java @@ -116,6 +116,12 @@ void testDecodePath(boolean isPathParam) throws Exception { } else { assertThat(decodeFunc.apply("/%2F")).isEqualTo("/%2F"); } + + // Tests for '+' decoding into ' ' + assertThat(decodeFunc.apply("/foo+bar")).isEqualTo("/foo bar"); // '+' should be decoded into space + assertThat(decodeFunc.apply("/foo+%20bar")).isEqualTo("/foo bar"); // '+' and '%20' both decoded to space + assertThat(decodeFunc.apply("/foo+bar+baz")).isEqualTo("/foo bar baz"); // multiple '+' should be decoded to space + assertThat(decodeFunc.apply("/foo%20bar+baz")).isEqualTo("/foo bar baz"); // combination of '%20' and '+' } @Test