Skip to content

Commit

Permalink
Fixes case insensitive headerMap and header toMultimap
Browse files Browse the repository at this point in the history
Makes all ways to work with headers consistent and comply with the HTTP
Headers RFC.
  • Loading branch information
froque committed Sep 6, 2024
1 parent 22a0f5a commit 5542d7d
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 13 deletions.
2 changes: 1 addition & 1 deletion jooby/src/main/java/io/jooby/Context.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, String> headerMap();

Expand Down
4 changes: 2 additions & 2 deletions jooby/src/main/java/io/jooby/Value.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -545,7 +545,7 @@ default boolean isObject() {
* @return A hash/object value.
*/
static @NonNull ValueNode headers(Context ctx, @NonNull Map<String, Collection<String>> values) {
HashValue node = new HashValue(ctx, null, () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER));
HeadersValue node = new HeadersValue(ctx);
node.put(values);
return node;
}
Expand Down
13 changes: 3 additions & 10 deletions jooby/src/main/java/io/jooby/internal/HashValue.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -29,18 +28,12 @@
import io.jooby.ValueNode;

public class HashValue implements ValueNode {
private static final Map<String, ValueNode> EMPTY = Collections.emptyMap();
protected static final Map<String, ValueNode> EMPTY = Collections.emptyMap();
private Context ctx;
private Map<String, ValueNode> hash = EMPTY;
protected Map<String, ValueNode> hash = EMPTY;
private final String name;
private boolean arrayLike;

public HashValue(Context ctx, String name, Supplier<Map<String, ValueNode>> mapSupplier) {
this.ctx = ctx;
this.name = name;
this.hash = mapSupplier.get();
}

public HashValue(Context ctx, String name) {
this.ctx = ctx;
this.name = name;
Expand Down Expand Up @@ -164,7 +157,7 @@ private boolean isNumber(String value) {
return true;
}

private Map<String, ValueNode> hash() {
protected Map<String, ValueNode> hash() {
if (hash == EMPTY) {
hash = new LinkedHashMap<>();
}
Expand Down
44 changes: 44 additions & 0 deletions jooby/src/main/java/io/jooby/internal/HeadersValue.java
Original file line number Diff line number Diff line change
@@ -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<String, ValueNode> hash() {
if (hash == EMPTY) {
hash = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
}
return hash;
}

@NonNull
@Override
public Map<String, String> toMap() {
Map<String, String> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
toMultimap().forEach((k, v) -> map.put(k, v.get(0)));
return map;
}

@NonNull
@Override
public Map<String, List<String>> toMultimap() {
Map<String, List<String>> result = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
Set<Map.Entry<String, ValueNode>> entries = hash.entrySet();
for (Map.Entry<String, ValueNode> entry : entries) {
ValueNode value = entry.getValue();
result.putAll(value.toMultimap());
}
return result;
}
}
6 changes: 6 additions & 0 deletions tests/src/test/java/io/jooby/test/Issue2357.java
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down

0 comments on commit 5542d7d

Please sign in to comment.