diff --git a/jooby/src/main/java/io/jooby/Context.java b/jooby/src/main/java/io/jooby/Context.java index fdf101a549..c1fe07a626 100644 --- a/jooby/src/main/java/io/jooby/Context.java +++ b/jooby/src/main/java/io/jooby/Context.java @@ -397,7 +397,7 @@ public interface Context extends Registry { /** * Header as single-value map. * - * @return Header as single-value map. + * @return Header as single-value map, with case insensitive keys. */ @NonNull Map headerMap(); diff --git a/jooby/src/main/java/io/jooby/Value.java b/jooby/src/main/java/io/jooby/Value.java index 925dc23a21..4ce8fc3406 100644 --- a/jooby/src/main/java/io/jooby/Value.java +++ b/jooby/src/main/java/io/jooby/Value.java @@ -16,7 +16,6 @@ import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.TreeMap; import java.util.function.Function; import edu.umd.cs.findbugs.annotations.NonNull; @@ -25,6 +24,7 @@ import io.jooby.exception.TypeMismatchException; import io.jooby.internal.ArrayValue; import io.jooby.internal.HashValue; +import io.jooby.internal.HeadersValue; import io.jooby.internal.MissingValue; import io.jooby.internal.MultipartNode; import io.jooby.internal.SingleValue; @@ -545,7 +545,7 @@ default boolean isObject() { * @return A hash/object value. */ static @NonNull ValueNode headers(Context ctx, @NonNull Map> values) { - HashValue node = new HashValue(ctx, null, () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER)); + HeadersValue node = new HeadersValue(ctx); node.put(values); return node; } diff --git a/jooby/src/main/java/io/jooby/internal/HashValue.java b/jooby/src/main/java/io/jooby/internal/HashValue.java index 47c977199b..88feee380a 100644 --- a/jooby/src/main/java/io/jooby/internal/HashValue.java +++ b/jooby/src/main/java/io/jooby/internal/HashValue.java @@ -20,7 +20,6 @@ import java.util.StringJoiner; import java.util.TreeMap; import java.util.function.BiConsumer; -import java.util.function.Supplier; import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; @@ -29,18 +28,12 @@ import io.jooby.ValueNode; public class HashValue implements ValueNode { - private static final Map EMPTY = Collections.emptyMap(); + protected static final Map EMPTY = Collections.emptyMap(); private Context ctx; - private Map hash = EMPTY; + protected Map hash = EMPTY; private final String name; private boolean arrayLike; - public HashValue(Context ctx, String name, Supplier> mapSupplier) { - this.ctx = ctx; - this.name = name; - this.hash = mapSupplier.get(); - } - public HashValue(Context ctx, String name) { this.ctx = ctx; this.name = name; @@ -164,7 +157,7 @@ private boolean isNumber(String value) { return true; } - private Map hash() { + protected Map hash() { if (hash == EMPTY) { hash = new LinkedHashMap<>(); } diff --git a/jooby/src/main/java/io/jooby/internal/HeadersValue.java b/jooby/src/main/java/io/jooby/internal/HeadersValue.java new file mode 100644 index 0000000000..978aaee3f1 --- /dev/null +++ b/jooby/src/main/java/io/jooby/internal/HeadersValue.java @@ -0,0 +1,44 @@ +package io.jooby.internal; + +import edu.umd.cs.findbugs.annotations.NonNull; +import io.jooby.Context; +import io.jooby.ValueNode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +public class HeadersValue extends HashValue implements ValueNode { + + public HeadersValue(final Context ctx) { + super(ctx); + } + + @Override + protected Map hash() { + if (hash == EMPTY) { + hash = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + } + return hash; + } + + @NonNull + @Override + public Map toMap() { + Map map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + toMultimap().forEach((k, v) -> map.put(k, v.get(0))); + return map; + } + + @NonNull + @Override + public Map> toMultimap() { + Map> result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + Set> entries = hash.entrySet(); + for (Map.Entry entry : entries) { + ValueNode value = entry.getValue(); + result.putAll(value.toMultimap()); + } + return result; + } +} diff --git a/tests/src/test/java/io/jooby/test/Issue2357.java b/tests/src/test/java/io/jooby/test/Issue2357.java index 5210efc1e3..1eb1cde5d8 100644 --- a/tests/src/test/java/io/jooby/test/Issue2357.java +++ b/tests/src/test/java/io/jooby/test/Issue2357.java @@ -26,6 +26,12 @@ public void headersShouldBeCaseInsensitive(ServerTestRunner runner) { Assertions.assertEquals("value1", ctx.header("x-header1").value()); Assertions.assertEquals("value1", ctx.header("X-HEADER1").value()); Assertions.assertEquals("value1", ctx.header("X-hEaDeR1").value()); + Assertions.assertEquals("value1", ctx.headerMap().get("x-header1")); + Assertions.assertEquals("value1", ctx.headerMap().get("X-HEADER1")); + Assertions.assertEquals("value1", ctx.headerMap().get("X-hEaDeR1")); + Assertions.assertEquals("value1", ctx.header().toMultimap().get("x-header1").get(0)); + Assertions.assertEquals("value1", ctx.header().toMultimap().get("X-HEADER1").get(0)); + Assertions.assertEquals("value1", ctx.header().toMultimap().get("X-hEaDeR1").get(0)); return "OK"; })) .ready(