diff --git a/httpclient5-sse/src/main/java/org/apache/hc/client5/http/sse/impl/DefaultEventSource.java b/httpclient5-sse/src/main/java/org/apache/hc/client5/http/sse/impl/DefaultEventSource.java index 41169a7c3a..9212c1fe12 100644 --- a/httpclient5-sse/src/main/java/org/apache/hc/client5/http/sse/impl/DefaultEventSource.java +++ b/httpclient5-sse/src/main/java/org/apache/hc/client5/http/sse/impl/DefaultEventSource.java @@ -32,8 +32,9 @@ import java.net.URI; import java.util.Map; import java.util.Objects; +import java.util.TreeMap; import java.util.concurrent.CancellationException; -import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.Executor; import java.util.concurrent.Future; import java.util.concurrent.RejectedExecutionException; @@ -178,7 +179,8 @@ public DefaultEventSource(final CloseableHttpAsyncClient client, final SseParser parser) { this.client = Objects.requireNonNull(client, "client"); this.uri = Objects.requireNonNull(uri, "uri"); - this.headers = new ConcurrentHashMap<>(Objects.requireNonNull(headers, "headers")); + this.headers = new ConcurrentSkipListMap<>(String.CASE_INSENSITIVE_ORDER); + this.headers.putAll(Objects.requireNonNull(headers, "headers")); this.listener = listener != null ? listener : (id, type, data) -> { /* no-op */ }; if (scheduler != null) { @@ -273,7 +275,9 @@ public void removeHeader(final String name) { */ @Override public Map getHeaders() { - return new ConcurrentHashMap<>(headers); + final Map copy = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + copy.putAll(headers); + return copy; } /** diff --git a/httpclient5-sse/src/test/java/org/apache/hc/client5/http/sse/impl/DefaultEventSourceTest.java b/httpclient5-sse/src/test/java/org/apache/hc/client5/http/sse/impl/DefaultEventSourceTest.java index 894d6d35e9..902f644e14 100644 --- a/httpclient5-sse/src/test/java/org/apache/hc/client5/http/sse/impl/DefaultEventSourceTest.java +++ b/httpclient5-sse/src/test/java/org/apache/hc/client5/http/sse/impl/DefaultEventSourceTest.java @@ -26,12 +26,15 @@ */ package org.apache.hc.client5.http.sse.impl; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import java.net.URI; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.AbstractExecutorService; import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; @@ -265,4 +268,43 @@ public V get(final long timeout, final TimeUnit unit) { return null; } } + + @Test + void headerNamesAreCaseInsensitive() { + final DefaultEventSource eventSource = new DefaultEventSource( + new CapturingClient(), + URI.create("http://localhost/sse"), + Collections.emptyMap(), + null); + + eventSource.setHeader("Authorization", "one"); + eventSource.setHeader("authorization", "two"); + + final Map headers = eventSource.getHeaders(); + + assertEquals(1, headers.size()); + assertEquals("two", headers.get("AUTHORIZATION")); + + eventSource.removeHeader("AUTHORIZATION"); + + assertTrue(eventSource.getHeaders().isEmpty()); + } + + @Test + void initialHeaderNamesAreCaseInsensitive() { + final Map initialHeaders = new LinkedHashMap<>(); + initialHeaders.put("X-Test", "one"); + initialHeaders.put("x-test", "two"); + + final DefaultEventSource eventSource = new DefaultEventSource( + new CapturingClient(), + URI.create("http://localhost/sse"), + initialHeaders, + null); + + final Map headers = eventSource.getHeaders(); + + assertEquals(1, headers.size()); + assertEquals("two", headers.get("X-TEST")); + } }