Skip to content

Commit f632652

Browse files
authored
Merge pull request #3518 from froque/fix_case_sensitive_headermap_v2
Fixes case insensitive headerMap and header toMultimap
2 parents ef8aa61 + 5542d7d commit f632652

5 files changed

Lines changed: 56 additions & 13 deletions

File tree

jooby/src/main/java/io/jooby/Context.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ public interface Context extends Registry {
397397
/**
398398
* Header as single-value map.
399399
*
400-
* @return Header as single-value map.
400+
* @return Header as single-value map, with case insensitive keys.
401401
*/
402402
@NonNull Map<String, String> headerMap();
403403

jooby/src/main/java/io/jooby/Value.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import java.util.Map;
1717
import java.util.Optional;
1818
import java.util.Set;
19-
import java.util.TreeMap;
2019
import java.util.function.Function;
2120

2221
import edu.umd.cs.findbugs.annotations.NonNull;
@@ -25,6 +24,7 @@
2524
import io.jooby.exception.TypeMismatchException;
2625
import io.jooby.internal.ArrayValue;
2726
import io.jooby.internal.HashValue;
27+
import io.jooby.internal.HeadersValue;
2828
import io.jooby.internal.MissingValue;
2929
import io.jooby.internal.MultipartNode;
3030
import io.jooby.internal.SingleValue;
@@ -545,7 +545,7 @@ default boolean isObject() {
545545
* @return A hash/object value.
546546
*/
547547
static @NonNull ValueNode headers(Context ctx, @NonNull Map<String, Collection<String>> values) {
548-
HashValue node = new HashValue(ctx, null, () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER));
548+
HeadersValue node = new HeadersValue(ctx);
549549
node.put(values);
550550
return node;
551551
}

jooby/src/main/java/io/jooby/internal/HashValue.java

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.util.StringJoiner;
2121
import java.util.TreeMap;
2222
import java.util.function.BiConsumer;
23-
import java.util.function.Supplier;
2423

2524
import edu.umd.cs.findbugs.annotations.NonNull;
2625
import edu.umd.cs.findbugs.annotations.Nullable;
@@ -29,18 +28,12 @@
2928
import io.jooby.ValueNode;
3029

3130
public class HashValue implements ValueNode {
32-
private static final Map<String, ValueNode> EMPTY = Collections.emptyMap();
31+
protected static final Map<String, ValueNode> EMPTY = Collections.emptyMap();
3332
private Context ctx;
34-
private Map<String, ValueNode> hash = EMPTY;
33+
protected Map<String, ValueNode> hash = EMPTY;
3534
private final String name;
3635
private boolean arrayLike;
3736

38-
public HashValue(Context ctx, String name, Supplier<Map<String, ValueNode>> mapSupplier) {
39-
this.ctx = ctx;
40-
this.name = name;
41-
this.hash = mapSupplier.get();
42-
}
43-
4437
public HashValue(Context ctx, String name) {
4538
this.ctx = ctx;
4639
this.name = name;
@@ -164,7 +157,7 @@ private boolean isNumber(String value) {
164157
return true;
165158
}
166159

167-
private Map<String, ValueNode> hash() {
160+
protected Map<String, ValueNode> hash() {
168161
if (hash == EMPTY) {
169162
hash = new LinkedHashMap<>();
170163
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package io.jooby.internal;
2+
3+
import edu.umd.cs.findbugs.annotations.NonNull;
4+
import io.jooby.Context;
5+
import io.jooby.ValueNode;
6+
import java.util.List;
7+
import java.util.Map;
8+
import java.util.Set;
9+
import java.util.TreeMap;
10+
11+
public class HeadersValue extends HashValue implements ValueNode {
12+
13+
public HeadersValue(final Context ctx) {
14+
super(ctx);
15+
}
16+
17+
@Override
18+
protected Map<String, ValueNode> hash() {
19+
if (hash == EMPTY) {
20+
hash = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
21+
}
22+
return hash;
23+
}
24+
25+
@NonNull
26+
@Override
27+
public Map<String, String> toMap() {
28+
Map<String, String> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
29+
toMultimap().forEach((k, v) -> map.put(k, v.get(0)));
30+
return map;
31+
}
32+
33+
@NonNull
34+
@Override
35+
public Map<String, List<String>> toMultimap() {
36+
Map<String, List<String>> result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
37+
Set<Map.Entry<String, ValueNode>> entries = hash.entrySet();
38+
for (Map.Entry<String, ValueNode> entry : entries) {
39+
ValueNode value = entry.getValue();
40+
result.putAll(value.toMultimap());
41+
}
42+
return result;
43+
}
44+
}

tests/src/test/java/io/jooby/test/Issue2357.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ public void headersShouldBeCaseInsensitive(ServerTestRunner runner) {
2626
Assertions.assertEquals("value1", ctx.header("x-header1").value());
2727
Assertions.assertEquals("value1", ctx.header("X-HEADER1").value());
2828
Assertions.assertEquals("value1", ctx.header("X-hEaDeR1").value());
29+
Assertions.assertEquals("value1", ctx.headerMap().get("x-header1"));
30+
Assertions.assertEquals("value1", ctx.headerMap().get("X-HEADER1"));
31+
Assertions.assertEquals("value1", ctx.headerMap().get("X-hEaDeR1"));
32+
Assertions.assertEquals("value1", ctx.header().toMultimap().get("x-header1").get(0));
33+
Assertions.assertEquals("value1", ctx.header().toMultimap().get("X-HEADER1").get(0));
34+
Assertions.assertEquals("value1", ctx.header().toMultimap().get("X-hEaDeR1").get(0));
2935
return "OK";
3036
}))
3137
.ready(

0 commit comments

Comments
 (0)