Merge branch '6.1.x'
This commit is contained in:
@@ -23,16 +23,23 @@ import java.lang.annotation.Target;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import io.netty.handler.codec.http.DefaultHttpHeaders;
|
||||
import io.undertow.util.HeaderMap;
|
||||
import io.undertow.util.HttpString;
|
||||
import org.apache.hc.client5.http.classic.methods.HttpGet;
|
||||
import org.apache.tomcat.util.http.MimeHeaders;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.support.HttpComponentsHeadersAdapter;
|
||||
import org.springframework.http.support.JettyHeadersAdapter;
|
||||
import org.springframework.http.support.Netty4HeadersAdapter;
|
||||
import org.springframework.http.support.Netty5HeadersAdapter;
|
||||
@@ -49,15 +56,109 @@ import static org.junit.jupiter.params.provider.Arguments.argumentSet;
|
||||
*
|
||||
* @author Brian Clozel
|
||||
* @author Sam Brannen
|
||||
* @author Simon Baslé
|
||||
*/
|
||||
class HeadersAdaptersTests {
|
||||
|
||||
@ParameterizedPopulatedHeadersTest
|
||||
void toSingleValueMapIsCaseInsensitive(MultiValueMap<String, String> headers) {
|
||||
assertThat(headers.toSingleValueMap()).as("toSingleValueMap")
|
||||
.containsEntry("TestHeader", "first")
|
||||
.containsEntry("SecondHeader", "value")
|
||||
.hasSize(2);
|
||||
}
|
||||
|
||||
@ParameterizedHeadersTest
|
||||
void shouldRemoveCaseInsensitiveFromKeySet(MultiValueMap<String, String> headers) {
|
||||
headers.add("TestHeader", "first");
|
||||
headers.add("TestHEADER", "second");
|
||||
headers.add("TestHeader", "third");
|
||||
|
||||
Iterator<String> iterator = headers.keySet().iterator();
|
||||
iterator.next();
|
||||
iterator.remove();
|
||||
|
||||
assertThat(headers)
|
||||
.doesNotContainKey("TestHeader")
|
||||
.doesNotContainKey("TestHEADER")
|
||||
.doesNotContainKey("testheader")
|
||||
.hasSize(0);
|
||||
}
|
||||
|
||||
@ParameterizedPopulatedHeadersTest
|
||||
void toString(MultiValueMap<String, String> headers) {
|
||||
String expectedFirstHeader = "TestHeader:\"first\", \"second\", \"third\"";
|
||||
String expectedSecondHeader = "SecondHeader:\"value\"";
|
||||
int minimumLength = expectedFirstHeader.length() + expectedSecondHeader.length() + 4;
|
||||
|
||||
String result = headers.toString();
|
||||
assertThat(result)
|
||||
.startsWith("[").endsWith("]")
|
||||
// Using contains here because some native headers iterate over names in reverse insertion order
|
||||
.contains(expectedFirstHeader)
|
||||
.contains(expectedSecondHeader)
|
||||
.hasSizeGreaterThanOrEqualTo(minimumLength);
|
||||
|
||||
if (result.length() > minimumLength) {
|
||||
String expectedEnd = " with native header names " + headers.keySet();
|
||||
assertThat(result).as("toString() with a dump of native duplicate headers")
|
||||
.endsWith(expectedEnd)
|
||||
.hasSize(minimumLength + expectedEnd.length());
|
||||
}
|
||||
}
|
||||
|
||||
@ParameterizedPopulatedHeadersTest
|
||||
void copyUsingHeaderSetAddAllIsCaseInsensitive(MultiValueMap<String, String> headers) {
|
||||
HttpHeaders headers2 = new HttpHeaders();
|
||||
for (Map.Entry<String, List<String>> entry : new HttpHeaders(headers).headerSet()) {
|
||||
headers2.addAll(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
assertThat(headers2.get("TestHeader")).as("TestHeader")
|
||||
.containsExactly("first", "second", "third");
|
||||
// Using the headerSet approach, we keep the first encountered casing of any given key
|
||||
assertThat(headers2.keySet()).as("first casing variant").containsExactlyInAnyOrder("TestHeader", "SecondHeader");
|
||||
assertThat(headers2.toString()).as("similar toString, no 'with native headers' dump")
|
||||
.isEqualTo(headers.toString().substring(0, headers.toString().indexOf(']') + 1));
|
||||
}
|
||||
|
||||
@ParameterizedPopulatedHeadersTest
|
||||
void copyUsingEntrySetPutRemovesDuplicates(MultiValueMap<String, String> headers) {
|
||||
HttpHeaders headers2 = new HttpHeaders();
|
||||
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
|
||||
headers2.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
assertThat(headers2.get("TestHeader")).as("TestHeader")
|
||||
.containsExactly("first", "second", "third");
|
||||
// Ordering and casing are not guaranteed using the entrySet+put approach
|
||||
assertThat(headers2).as("two keys")
|
||||
.containsKey("testheader")
|
||||
.containsKey("secondheader")
|
||||
.hasSize(2);
|
||||
assertThat(headers2.toString()).as("no 'with native headers' dump")
|
||||
.doesNotContain("with native headers");
|
||||
}
|
||||
|
||||
@ParameterizedPopulatedHeadersTest
|
||||
void copyUsingPutAllRemovesDuplicates(MultiValueMap<String, String> headers) {
|
||||
HttpHeaders headers2 = new HttpHeaders();
|
||||
headers2.putAll(headers);
|
||||
|
||||
assertThat(headers2.get("TestHeader")).as("TestHeader")
|
||||
.containsExactly("first", "second", "third");
|
||||
// Ordering and casing are not guaranteed using the putAll approach
|
||||
assertThat(headers2).as("two keys").containsOnlyKeys("testheader", "secondheader");
|
||||
assertThat(headers2.toString()).as("similar toString, no 'with native headers' dump")
|
||||
.isEqualToIgnoringCase(headers.toString().substring(0, headers.toString().indexOf(']') + 1));
|
||||
}
|
||||
|
||||
@ParameterizedPopulatedHeadersTest
|
||||
void getWithUnknownHeaderShouldReturnNull(MultiValueMap<String, String> headers) {
|
||||
assertThat(headers.get("Unknown")).isNull();
|
||||
}
|
||||
|
||||
@ParameterizedHeadersTest
|
||||
@ParameterizedPopulatedHeadersTest
|
||||
void getFirstWithUnknownHeaderShouldReturnNull(MultiValueMap<String, String> headers) {
|
||||
assertThat(headers.getFirst("Unknown")).isNull();
|
||||
}
|
||||
@@ -77,9 +178,8 @@ class HeadersAdaptersTests {
|
||||
assertThat(headers.keySet()).hasSize(2);
|
||||
}
|
||||
|
||||
@ParameterizedHeadersTest
|
||||
@ParameterizedPopulatedHeadersTest
|
||||
void containsKeyShouldBeCaseInsensitive(MultiValueMap<String, String> headers) {
|
||||
headers.add("TestHeader", "first");
|
||||
assertThat(headers.containsKey("testheader")).isTrue();
|
||||
}
|
||||
|
||||
@@ -126,6 +226,39 @@ class HeadersAdaptersTests {
|
||||
assertThatThrownBy(names::remove).isInstanceOf(IllegalStateException.class);
|
||||
}
|
||||
|
||||
@ParameterizedPopulatedHeadersTest
|
||||
void headerSetEntryCanSetList(MultiValueMap<String, String> headers) {
|
||||
for (Map.Entry<String, List<String>> entry : new HttpHeaders(headers).headerSet()) {
|
||||
entry.setValue(List.of(entry.getKey()));
|
||||
}
|
||||
|
||||
assertThat(headers).hasSize(2);
|
||||
assertThat(headers.get("TestHeader")).containsExactly("TestHeader");
|
||||
assertThat(headers.get("SecondHeader")).containsExactly("SecondHeader");
|
||||
}
|
||||
|
||||
@ParameterizedPopulatedHeadersTest
|
||||
void headerSetIteratorCanRemove(MultiValueMap<String, String> headers) {
|
||||
Iterator<Map.Entry<String, List<String>>> iterator = new HttpHeaders(headers).headerSet().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
iterator.next();
|
||||
iterator.remove();
|
||||
}
|
||||
|
||||
assertThat(headers).isEmpty();
|
||||
}
|
||||
|
||||
|
||||
static <T> T withHeaders(T nativeHeader, Function<T, BiConsumer<String, String>> addMethod) {
|
||||
BiConsumer<String, String> add = addMethod.apply(nativeHeader);
|
||||
add.accept("TestHeader", "first");
|
||||
add.accept("TestHEADER", "second");
|
||||
add.accept("SecondHeader", "value");
|
||||
add.accept("TestHeader", "third");
|
||||
|
||||
return nativeHeader;
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@ParameterizedTest
|
||||
@@ -137,10 +270,33 @@ class HeadersAdaptersTests {
|
||||
return Stream.of(
|
||||
argumentSet("Map", CollectionUtils.toMultiValueMap(new LinkedCaseInsensitiveMap<>(8, Locale.ENGLISH))),
|
||||
argumentSet("Netty", new Netty4HeadersAdapter(new DefaultHttpHeaders())),
|
||||
argumentSet("Netty", new Netty5HeadersAdapter(io.netty5.handler.codec.http.headers.HttpHeaders.newHeaders())),
|
||||
argumentSet("Netty5", new Netty5HeadersAdapter(io.netty5.handler.codec.http.headers.HttpHeaders.newHeaders())),
|
||||
argumentSet("Tomcat", new TomcatHeadersAdapter(new MimeHeaders())),
|
||||
argumentSet("Undertow", new UndertowHeadersAdapter(new HeaderMap())),
|
||||
argumentSet("Jetty", new JettyHeadersAdapter(HttpFields.build()))
|
||||
argumentSet("Jetty", new JettyHeadersAdapter(HttpFields.build())),
|
||||
argumentSet("HttpComponents", new HttpComponentsHeadersAdapter(new HttpGet("https://example.com")))
|
||||
);
|
||||
}
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.METHOD)
|
||||
@ParameterizedTest
|
||||
@MethodSource("nativeHeadersWithCasedEntries")
|
||||
@interface ParameterizedPopulatedHeadersTest {
|
||||
}
|
||||
|
||||
static Stream<Arguments> nativeHeadersWithCasedEntries() {
|
||||
return Stream.of(
|
||||
argumentSet("Netty", new Netty4HeadersAdapter(withHeaders(new DefaultHttpHeaders(), h -> h::add))),
|
||||
argumentSet("Netty5", new Netty5HeadersAdapter(withHeaders(io.netty5.handler.codec.http.headers.HttpHeaders.newHeaders(),
|
||||
h -> h::add))),
|
||||
argumentSet("Tomcat", new TomcatHeadersAdapter(withHeaders(new MimeHeaders(),
|
||||
h -> (k, v) -> h.addValue(k).setString(v)))),
|
||||
argumentSet("Undertow", new UndertowHeadersAdapter(withHeaders(new HeaderMap(),
|
||||
h -> (k, v) -> h.add(HttpString.tryFromString(k), v)))),
|
||||
argumentSet("Jetty", new JettyHeadersAdapter(withHeaders(HttpFields.build(), h -> h::add))),
|
||||
argumentSet("HttpComponents", new HttpComponentsHeadersAdapter(withHeaders(new HttpGet("https://example.com"),
|
||||
h -> h::addHeader)))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user