`HttpHeaders` are no longer a `MultiValueMap`
This change removes the `MultiValueMap` nature of `HttpHeaders`, since it inherits APIs that do not align well with underlying server implementations. Notably, methods that allows to iterate over the whole collection of headers are susceptible to artificially introduced duplicates when multiple casings are used for a given header, depending on the underlying implementation. This change includes a dedicated key set implementation to support iterator-based removal, and either keeps map method implementations that are relevant or introduces header-focused methods that have a similar responsibility (like `hasHeaderValues(String, List)` and `containsHeaderValue(String, String)`). In order to nudge users away from using an HttpHeaders as a Map, the `asSingleValueMap` view is deprecated. In order to offer an escape hatch to users that do make use of the `MultiValueMap` API, a similar `asMultiValueMap` view is introduced but is immediately marked as deprecated. This change also adds map-like but header-focused assertions to `HttpHeadersAssert`, since it cannot extend `AbstractMapAssert` anymore. Closes gh-33913
This commit is contained in:
parent
1e0ef99b0c
commit
0c6f5d7d29
|
@ -298,9 +298,17 @@ public final class MockServerHttpRequest extends AbstractServerHttpRequest {
|
|||
/**
|
||||
* Add the given header values.
|
||||
* @param headers the header values
|
||||
* @deprecated Use {@link #headers(HttpHeaders)}
|
||||
*/
|
||||
@Deprecated
|
||||
B headers(MultiValueMap<String, String> headers);
|
||||
|
||||
/**
|
||||
* Add the given header values.
|
||||
* @param headers the header values
|
||||
*/
|
||||
B headers(HttpHeaders headers);
|
||||
|
||||
/**
|
||||
* Set the list of acceptable {@linkplain MediaType media types}, as
|
||||
* specified by the {@code Accept} header.
|
||||
|
@ -484,11 +492,18 @@ public final class MockServerHttpRequest extends AbstractServerHttpRequest {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public BodyBuilder headers(MultiValueMap<String, String> headers) {
|
||||
this.headers.putAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BodyBuilder headers(HttpHeaders headers) {
|
||||
this.headers.putAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BodyBuilder accept(MediaType... acceptableMediaTypes) {
|
||||
this.headers.setAccept(Arrays.asList(acceptableMediaTypes));
|
||||
|
|
|
@ -129,7 +129,7 @@ public class MockPart implements Part {
|
|||
|
||||
@Override
|
||||
public Collection<String> getHeaderNames() {
|
||||
return this.headers.keySet();
|
||||
return this.headers.headerNames();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,10 +20,16 @@ import java.time.Instant;
|
|||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.assertj.core.api.AbstractMapAssert;
|
||||
import org.assertj.core.api.AbstractCollectionAssert;
|
||||
import org.assertj.core.api.AbstractObjectAssert;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.assertj.core.api.ObjectAssert;
|
||||
import org.assertj.core.presentation.Representation;
|
||||
import org.assertj.core.presentation.StandardRepresentation;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
|
||||
|
@ -34,54 +40,87 @@ import org.springframework.http.HttpHeaders;
|
|||
* @author Stephane Nicoll
|
||||
* @since 6.2
|
||||
*/
|
||||
public class HttpHeadersAssert extends AbstractMapAssert<HttpHeadersAssert, HttpHeaders, String, List<String>> {
|
||||
public class HttpHeadersAssert extends AbstractObjectAssert<HttpHeadersAssert, HttpHeaders> {
|
||||
|
||||
private static final ZoneId GMT = ZoneId.of("GMT");
|
||||
|
||||
private final AbstractCollectionAssert<?, Collection<? extends String>, String, ObjectAssert<String>> namesAssert;
|
||||
|
||||
|
||||
public HttpHeadersAssert(HttpHeaders actual) {
|
||||
super(actual, HttpHeadersAssert.class);
|
||||
as("HTTP headers");
|
||||
withRepresentation(new Representation() {
|
||||
@Override
|
||||
public String toStringOf(Object object) {
|
||||
if (object instanceof HttpHeaders headers) {
|
||||
return headers.toString();
|
||||
}
|
||||
return StandardRepresentation.STANDARD_REPRESENTATION.toStringOf(object);
|
||||
}
|
||||
});
|
||||
this.namesAssert = Assertions.assertThat(actual.headerNames())
|
||||
.as("HTTP header names");
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual HTTP headers contain a header with the given
|
||||
* {@code name}.
|
||||
* @param name the name of an expected HTTP header
|
||||
* @see #containsKey
|
||||
*/
|
||||
public HttpHeadersAssert containsHeader(String name) {
|
||||
return containsKey(name);
|
||||
this.namesAssert
|
||||
.as("check headers contain HTTP header '%s'", name)
|
||||
.contains(name);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual HTTP headers contain the headers with the given
|
||||
* {@code names}.
|
||||
* @param names the names of expected HTTP headers
|
||||
* @see #containsKeys
|
||||
*/
|
||||
public HttpHeadersAssert containsHeaders(String... names) {
|
||||
return containsKeys(names);
|
||||
this.namesAssert
|
||||
.as("check headers contain HTTP headers '%s'", Arrays.toString(names))
|
||||
.contains(names);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual HTTP headers contain only the headers with the
|
||||
* given {@code names}, in any order and in a case-insensitive manner.
|
||||
* @param names the names of expected HTTP headers
|
||||
*/
|
||||
public HttpHeadersAssert containsOnlyHeaders(String... names) {
|
||||
this.namesAssert
|
||||
.as("check headers contains only HTTP headers '%s'", Arrays.toString(names))
|
||||
.containsOnly(names);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual HTTP headers do not contain a header with the
|
||||
* given {@code name}.
|
||||
* @param name the name of an HTTP header that should not be present
|
||||
* @see #doesNotContainKey
|
||||
*/
|
||||
public HttpHeadersAssert doesNotContainHeader(String name) {
|
||||
return doesNotContainKey(name);
|
||||
this.namesAssert
|
||||
.as("check headers does not contain HTTP header '%s'", name)
|
||||
.doesNotContain(name);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the actual HTTP headers do not contain any of the headers
|
||||
* with the given {@code names}.
|
||||
* @param names the names of HTTP headers that should not be present
|
||||
* @see #doesNotContainKeys
|
||||
*/
|
||||
public HttpHeadersAssert doesNotContainsHeaders(String... names) {
|
||||
return doesNotContainKeys(names);
|
||||
this.namesAssert
|
||||
.as("check headers does not contain HTTP headers '%s'", Arrays.toString(names))
|
||||
.doesNotContain(names);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -91,7 +130,7 @@ public class HttpHeadersAssert extends AbstractMapAssert<HttpHeadersAssert, Http
|
|||
* @param value the expected value of the header
|
||||
*/
|
||||
public HttpHeadersAssert hasValue(String name, String value) {
|
||||
containsKey(name);
|
||||
containsHeader(name);
|
||||
Assertions.assertThat(this.actual.getFirst(name))
|
||||
.as("check primary value for HTTP header '%s'", name)
|
||||
.isEqualTo(value);
|
||||
|
@ -105,7 +144,7 @@ public class HttpHeadersAssert extends AbstractMapAssert<HttpHeadersAssert, Http
|
|||
* @param value the expected value of the header
|
||||
*/
|
||||
public HttpHeadersAssert hasValue(String name, long value) {
|
||||
containsKey(name);
|
||||
containsHeader(name);
|
||||
Assertions.assertThat(this.actual.getFirst(name))
|
||||
.as("check primary long value for HTTP header '%s'", name)
|
||||
.asLong().isEqualTo(value);
|
||||
|
@ -119,11 +158,149 @@ public class HttpHeadersAssert extends AbstractMapAssert<HttpHeadersAssert, Http
|
|||
* @param value the expected value of the header
|
||||
*/
|
||||
public HttpHeadersAssert hasValue(String name, Instant value) {
|
||||
containsKey(name);
|
||||
containsHeader(name);
|
||||
Assertions.assertThat(this.actual.getFirstZonedDateTime(name))
|
||||
.as("check primary date value for HTTP header '%s'", name)
|
||||
.isCloseTo(ZonedDateTime.ofInstant(value, GMT), Assertions.within(999, ChronoUnit.MILLIS));
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the given header has a full list of values exactly equal to
|
||||
* the given list of values, and in the same order.
|
||||
* @param name the considered header name (case-insensitive)
|
||||
* @param values the exhaustive list of expected values
|
||||
*/
|
||||
public HttpHeadersAssert hasExactlyValues(String name, List<String> values) {
|
||||
containsHeader(name);
|
||||
Assertions.assertThat(this.actual.get(name))
|
||||
.as("check all values of HTTP header '%s'", name)
|
||||
.containsExactlyElementsOf(values);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the given header has a full list of values exactly equal to
|
||||
* the given list of values, in any order.
|
||||
* @param name the considered header name (case-insensitive)
|
||||
* @param values the exhaustive list of expected values
|
||||
*/
|
||||
public HttpHeadersAssert hasExactlyValuesInAnyOrder(String name, List<String> values) {
|
||||
containsHeader(name);
|
||||
Assertions.assertThat(this.actual.get(name))
|
||||
.as("check all values of HTTP header '%s' in any order", name)
|
||||
.containsExactlyInAnyOrderElementsOf(values);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that headers are empty and no headers are present.
|
||||
*/
|
||||
public HttpHeadersAssert isEmpty() {
|
||||
this.namesAssert
|
||||
.as("check headers are empty")
|
||||
.isEmpty();
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that headers are not empty and at least one header is present.
|
||||
*/
|
||||
public HttpHeadersAssert isNotEmpty() {
|
||||
this.namesAssert
|
||||
.as("check headers are not empty")
|
||||
.isNotEmpty();
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that there is exactly {@code expected} headers present, when
|
||||
* considering header names in a case-insensitive manner.
|
||||
* @param expected the expected number of headers
|
||||
*/
|
||||
public HttpHeadersAssert hasSize(int expected) {
|
||||
this.namesAssert
|
||||
.as("check headers have size '%i'", expected)
|
||||
.hasSize(expected);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the number of headers present is strictly greater than the
|
||||
* given boundary, when considering header names in a case-insensitive
|
||||
* manner.
|
||||
* @param boundary the given value to compare actual header size to
|
||||
*/
|
||||
public HttpHeadersAssert hasSizeGreaterThan(int boundary) {
|
||||
this.namesAssert
|
||||
.as("check headers have size > '%i'", boundary)
|
||||
.hasSizeGreaterThan(boundary);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the number of headers present is greater or equal to the
|
||||
* given boundary, when considering header names in a case-insensitive
|
||||
* manner.
|
||||
* @param boundary the given value to compare actual header size to
|
||||
*/
|
||||
public HttpHeadersAssert hasSizeGreaterThanOrEqualTo(int boundary) {
|
||||
this.namesAssert
|
||||
.as("check headers have size >= '%i'", boundary)
|
||||
.hasSizeGreaterThanOrEqualTo(boundary);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the number of headers present is strictly less than the
|
||||
* given boundary, when considering header names in a case-insensitive
|
||||
* manner.
|
||||
* @param boundary the given value to compare actual header size to
|
||||
*/
|
||||
public HttpHeadersAssert hasSizeLessThan(int boundary) {
|
||||
this.namesAssert
|
||||
.as("check headers have size < '%i'", boundary)
|
||||
.hasSizeLessThan(boundary);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the number of headers present is less than or equal to the
|
||||
* given boundary, when considering header names in a case-insensitive
|
||||
* manner.
|
||||
* @param boundary the given value to compare actual header size to
|
||||
*/
|
||||
public HttpHeadersAssert hasSizeLessThanOrEqualTo(int boundary) {
|
||||
this.namesAssert
|
||||
.as("check headers have size <= '%i'", boundary)
|
||||
.hasSizeLessThanOrEqualTo(boundary);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the number of headers present is between the given boundaries
|
||||
* (inclusive), when considering header names in a case-insensitive manner.
|
||||
* @param lowerBoundary the lower boundary compared to which actual size
|
||||
* should be greater than or equal to
|
||||
* @param higherBoundary the higher boundary compared to which actual size
|
||||
* should be less than or equal to
|
||||
*/
|
||||
public HttpHeadersAssert hasSizeBetween(int lowerBoundary, int higherBoundary) {
|
||||
this.namesAssert
|
||||
.as("check headers have size between '%i' and '%i'", lowerBoundary, higherBoundary)
|
||||
.hasSizeBetween(lowerBoundary, higherBoundary);
|
||||
return this.myself;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify that the number actual headers is the same as in the given
|
||||
* {@code HttpHeaders}.
|
||||
* @param other the {@code HttpHeaders} to compare size with
|
||||
*/
|
||||
public HttpHeadersAssert hasSameSizeAs(HttpHeaders other) {
|
||||
this.namesAssert
|
||||
.as("check headers have same size as '%s'", other)
|
||||
.hasSize(other.size());
|
||||
return this.myself;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import javax.xml.xpath.XPathExpressionException;
|
|||
|
||||
import org.hamcrest.Matcher;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.test.web.client.MockRestServiceServer;
|
||||
|
@ -162,7 +163,7 @@ public abstract class MockRestRequestMatchers {
|
|||
public static RequestMatcher queryParam(String name, Matcher<? super String>... matchers) {
|
||||
return request -> {
|
||||
MultiValueMap<String, String> params = getQueryParams(request);
|
||||
assertValueCount("query param", name, params, matchers.length);
|
||||
assertValueCount(name, params, matchers.length);
|
||||
for (int i = 0 ; i < matchers.length; i++) {
|
||||
assertThat("Query param", params.get(name).get(i), matchers[i]);
|
||||
}
|
||||
|
@ -190,7 +191,7 @@ public abstract class MockRestRequestMatchers {
|
|||
public static RequestMatcher queryParam(String name, String... expectedValues) {
|
||||
return request -> {
|
||||
MultiValueMap<String, String> params = getQueryParams(request);
|
||||
assertValueCount("query param", name, params, expectedValues.length);
|
||||
assertValueCount(name, params, expectedValues.length);
|
||||
for (int i = 0 ; i < expectedValues.length; i++) {
|
||||
assertEquals("Query param [" + name + "]", expectedValues[i], params.get(name).get(i));
|
||||
}
|
||||
|
@ -246,7 +247,7 @@ public abstract class MockRestRequestMatchers {
|
|||
@SafeVarargs
|
||||
public static RequestMatcher header(String name, Matcher<? super String>... matchers) {
|
||||
return request -> {
|
||||
assertValueCount("header", name, request.getHeaders(), matchers.length);
|
||||
assertValueCount(name, request.getHeaders(), matchers.length);
|
||||
List<String> headerValues = request.getHeaders().get(name);
|
||||
Assert.state(headerValues != null, "No header values");
|
||||
for (int i = 0; i < matchers.length; i++) {
|
||||
|
@ -273,7 +274,7 @@ public abstract class MockRestRequestMatchers {
|
|||
*/
|
||||
public static RequestMatcher header(String name, String... expectedValues) {
|
||||
return request -> {
|
||||
assertValueCount("header", name, request.getHeaders(), expectedValues.length);
|
||||
assertValueCount(name, request.getHeaders(), expectedValues.length);
|
||||
List<String> headerValues = request.getHeaders().get(name);
|
||||
Assert.state(headerValues != null, "No header values");
|
||||
for (int i = 0; i < expectedValues.length; i++) {
|
||||
|
@ -356,11 +357,22 @@ public abstract class MockRestRequestMatchers {
|
|||
}
|
||||
|
||||
|
||||
private static void assertValueCount(
|
||||
String valueType, String name, MultiValueMap<String, String> map, int count) {
|
||||
private static void assertValueCount(String name, MultiValueMap<String, String> map, int count) {
|
||||
|
||||
List<String> values = map.get(name);
|
||||
String message = "Expected " + valueType + " <" + name + ">";
|
||||
String message = "Expected query param <" + name + ">";
|
||||
if (values == null) {
|
||||
fail(message + " to exist but was null");
|
||||
}
|
||||
else if (count > values.size()) {
|
||||
fail(message + " to have at least <" + count + "> values but found " + values);
|
||||
}
|
||||
}
|
||||
|
||||
private static void assertValueCount(String name, HttpHeaders headers, int count) {
|
||||
|
||||
List<String> values = headers.get(name);
|
||||
String message = "Expected header <" + name + ">";
|
||||
if (values == null) {
|
||||
fail(message + " to exist but was null");
|
||||
}
|
||||
|
|
|
@ -373,10 +373,10 @@ class DefaultWebTestClient implements WebTestClient {
|
|||
private ClientRequest.Builder initRequestBuilder() {
|
||||
return ClientRequest.create(this.httpMethod, initUri())
|
||||
.headers(headersToUse -> {
|
||||
if (!CollectionUtils.isEmpty(DefaultWebTestClient.this.defaultHeaders)) {
|
||||
if (!(DefaultWebTestClient.this.defaultHeaders == null || DefaultWebTestClient.this.defaultHeaders.isEmpty())) {
|
||||
headersToUse.putAll(DefaultWebTestClient.this.defaultHeaders);
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(this.headers)) {
|
||||
if (!this.headers.isEmpty()) {
|
||||
headersToUse.putAll(this.headers);
|
||||
}
|
||||
})
|
||||
|
|
|
@ -249,7 +249,7 @@ public class ExchangeResult {
|
|||
}
|
||||
|
||||
private String formatHeaders(HttpHeaders headers, String delimiter) {
|
||||
return headers.entrySet().stream()
|
||||
return headers.headerSet().stream()
|
||||
.map(entry -> entry.getKey() + ": " + entry.getValue())
|
||||
.collect(Collectors.joining(delimiter));
|
||||
}
|
||||
|
|
|
@ -213,7 +213,7 @@ public class HeaderAssertions {
|
|||
* @since 5.0.3
|
||||
*/
|
||||
public WebTestClient.ResponseSpec exists(String name) {
|
||||
if (!getHeaders().containsKey(name)) {
|
||||
if (!getHeaders().containsHeader(name)) {
|
||||
String message = getMessage(name) + " does not exist";
|
||||
this.exchangeResult.assertWithDiagnostics(() -> fail(message));
|
||||
}
|
||||
|
@ -224,7 +224,7 @@ public class HeaderAssertions {
|
|||
* Expect that the header with the given name is not present.
|
||||
*/
|
||||
public WebTestClient.ResponseSpec doesNotExist(String name) {
|
||||
if (getHeaders().containsKey(name)) {
|
||||
if (getHeaders().containsHeader(name)) {
|
||||
String message = getMessage(name) + " exists with value=[" + getHeaders().getFirst(name) + "]";
|
||||
this.exchangeResult.assertWithDiagnostics(() -> fail(message));
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ class HttpHeadersAssertTests {
|
|||
Map<String, String> map = Map.of("first", "1");
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(map).containsHeader("wrong-name"))
|
||||
.withMessageContainingAll("HTTP headers", "first", "wrong-name");
|
||||
.withMessageContainingAll("HTTP header", "first", "wrong-name");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -71,8 +71,8 @@ class HttpHeadersAssertTests {
|
|||
void doesNotContainHeaderWithNamePresent() {
|
||||
Map<String, String> map = Map.of("first", "1");
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(map).doesNotContainKey("first"))
|
||||
.withMessageContainingAll("HTTP headers", "first");
|
||||
.isThrownBy(() -> assertThat(map).doesNotContainHeader("first"))
|
||||
.withMessageContainingAll("HTTP header", "first");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -112,7 +112,7 @@ class HttpHeadersAssertTests {
|
|||
headers.addAll("header", List.of("first", "second", "third"));
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(headers).hasValue("wrong-name", "second"))
|
||||
.withMessageContainingAll("HTTP headers", "header", "wrong-name");
|
||||
.withMessageContainingAll("HTTP header", "header", "wrong-name");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -121,7 +121,7 @@ class HttpHeadersAssertTests {
|
|||
map.add("test-header", "a");
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(map).hasValue("wrong-name", "a"))
|
||||
.withMessageContainingAll("HTTP headers", "test-header", "wrong-name");
|
||||
.withMessageContainingAll("HTTP header", "test-header", "wrong-name");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -146,7 +146,7 @@ class HttpHeadersAssertTests {
|
|||
map.addAll("header", List.of("123", "456", "789"));
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(map).hasValue("wrong-name", 456))
|
||||
.withMessageContainingAll("HTTP headers", "header", "wrong-name");
|
||||
.withMessageContainingAll("HTTP header", "header", "wrong-name");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -164,7 +164,7 @@ class HttpHeadersAssertTests {
|
|||
map.setInstant("header", instant);
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(map).hasValue("wrong-name", instant.minusSeconds(30)))
|
||||
.withMessageContainingAll("HTTP headers", "header", "wrong-name");
|
||||
.withMessageContainingAll("HTTP header", "header", "wrong-name");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -174,7 +174,7 @@ class HttpHeadersAssertTests {
|
|||
map.setInstant("header", instant);
|
||||
assertThatExceptionOfType(AssertionError.class)
|
||||
.isThrownBy(() -> assertThat(map).hasValue("wrong-name", instant.minusSeconds(1)))
|
||||
.withMessageContainingAll("HTTP headers", "header", "wrong-name");
|
||||
.withMessageContainingAll("HTTP header", "header", "wrong-name");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ class ResponseCreatorsTests {
|
|||
MockClientHttpResponse response = (MockClientHttpResponse) MockRestResponseCreators.withSuccess().createResponse(null);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getHeaders()).isEmpty();
|
||||
assertThat(response.getHeaders().isEmpty()).as("isEmpty").isTrue();
|
||||
assertThat(StreamUtils.copyToByteArray(response.getBody())).isEmpty();
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,7 @@ class ResponseCreatorsTests {
|
|||
MockClientHttpResponse response = (MockClientHttpResponse) responseCreator.createResponse(null);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
|
||||
assertThat(response.getHeaders()).isEmpty();
|
||||
assertThat(response.getHeaders().isEmpty()).as("isEmpty").isTrue();
|
||||
assertThat(StreamUtils.copyToByteArray(response.getBody())).isEmpty();
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ class ResponseCreatorsTests {
|
|||
MockClientHttpResponse response = (MockClientHttpResponse) responseCreator.createResponse(null);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
|
||||
assertThat(response.getHeaders()).isEmpty();
|
||||
assertThat(response.getHeaders().isEmpty()).as("isEmpty").isTrue();
|
||||
assertThat(StreamUtils.copyToByteArray(response.getBody())).isEmpty();
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,7 @@ class ResponseCreatorsTests {
|
|||
MockClientHttpResponse response = (MockClientHttpResponse) responseCreator.createResponse(null);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
|
||||
assertThat(response.getHeaders()).isEmpty();
|
||||
assertThat(response.getHeaders().isEmpty()).as("isEmpty").isTrue();
|
||||
assertThat(StreamUtils.copyToByteArray(response.getBody())).isEmpty();
|
||||
}
|
||||
|
||||
|
@ -152,7 +152,7 @@ class ResponseCreatorsTests {
|
|||
MockClientHttpResponse response = (MockClientHttpResponse) responseCreator.createResponse(null);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.TOO_MANY_REQUESTS);
|
||||
assertThat(response.getHeaders()).doesNotContainKey(HttpHeaders.RETRY_AFTER);
|
||||
assertThat(response.getHeaders().headerNames()).doesNotContain(HttpHeaders.RETRY_AFTER);
|
||||
assertThat(StreamUtils.copyToByteArray(response.getBody())).isEmpty();
|
||||
}
|
||||
|
||||
|
@ -172,7 +172,7 @@ class ResponseCreatorsTests {
|
|||
MockClientHttpResponse response = (MockClientHttpResponse) responseCreator.createResponse(null);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
assertThat(response.getHeaders()).isEmpty();
|
||||
assertThat(response.getHeaders().isEmpty()).as("isEmpty").isTrue();
|
||||
assertThat(StreamUtils.copyToByteArray(response.getBody())).isEmpty();
|
||||
}
|
||||
|
||||
|
@ -209,7 +209,7 @@ class ResponseCreatorsTests {
|
|||
MockClientHttpResponse response = (MockClientHttpResponse) responseCreator.createResponse(null);
|
||||
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
|
||||
assertThat(response.getHeaders()).isEmpty();
|
||||
assertThat(response.getHeaders().isEmpty()).as("isEmpty").isTrue();
|
||||
assertThat(StreamUtils.copyToByteArray(response.getBody())).isEmpty();
|
||||
}
|
||||
|
||||
|
|
|
@ -100,15 +100,15 @@ public class MockServerTests {
|
|||
WebTestClient clientFromMutatedBuilder = mutatedBuilder.build();
|
||||
|
||||
client1.mutate().filters(filters -> assertThat(filters).hasSize(1));
|
||||
client1.mutate().defaultHeaders(headers -> assertThat(headers).hasSize(1));
|
||||
client1.mutate().defaultHeaders(headers -> assertThat(headers.size()).isOne());
|
||||
client1.mutate().defaultCookies(cookies -> assertThat(cookies).hasSize(1));
|
||||
|
||||
client2.mutate().filters(filters -> assertThat(filters).hasSize(2));
|
||||
client2.mutate().defaultHeaders(headers -> assertThat(headers).hasSize(2));
|
||||
client2.mutate().defaultHeaders(headers -> assertThat(headers.size()).isEqualTo(2));
|
||||
client2.mutate().defaultCookies(cookies -> assertThat(cookies).hasSize(2));
|
||||
|
||||
clientFromMutatedBuilder.mutate().filters(filters -> assertThat(filters).hasSize(2));
|
||||
clientFromMutatedBuilder.mutate().defaultHeaders(headers -> assertThat(headers).hasSize(2));
|
||||
clientFromMutatedBuilder.mutate().defaultHeaders(headers -> assertThat(headers.size()).isEqualTo(2));
|
||||
clientFromMutatedBuilder.mutate().defaultCookies(cookies -> assertThat(cookies).hasSize(2));
|
||||
}
|
||||
|
||||
|
|
|
@ -322,8 +322,8 @@ public class MockMvcTesterIntegrationTests {
|
|||
}
|
||||
|
||||
private Consumer<HttpHeaders> textContent(String charset) {
|
||||
return headers -> assertThat(headers).containsEntry(
|
||||
"Content-Type", List.of("text/plain;charset=%s".formatted(charset)));
|
||||
return headers -> assertThat(headers.hasHeaderValues("Content-Type", List.of("text/plain;charset=%s".formatted(charset))))
|
||||
.as("hasHeaderValues").isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -126,7 +126,7 @@ public class MockWebResponseBuilderTests {
|
|||
WebResponse webResponse = this.responseBuilder.build();
|
||||
|
||||
List<NameValuePair> responseHeaders = webResponse.getResponseHeaders();
|
||||
assertThat(responseHeaders).hasSize(1);
|
||||
assertThat(responseHeaders.size()).as("size").isOne();
|
||||
NameValuePair header = responseHeaders.get(0);
|
||||
assertThat(header.getName()).isEqualTo("Set-Cookie");
|
||||
assertThat(header.getValue()).isEqualTo("cookieA=valueA");
|
||||
|
|
|
@ -130,7 +130,7 @@ public class FrameworkExtensionTests {
|
|||
|
||||
@Override
|
||||
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
|
||||
for (String headerName : this.headers.keySet()) {
|
||||
for (String headerName : this.headers.headerNames()) {
|
||||
request.addHeader(headerName, this.headers.get(headerName));
|
||||
}
|
||||
return request;
|
||||
|
|
|
@ -94,7 +94,7 @@ public class FrameworkExtensionTests {
|
|||
|
||||
@Override
|
||||
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
|
||||
for (String headerName : this.headers.keySet()) {
|
||||
for (String headerName : this.headers.headerNames()) {
|
||||
request.addHeader(headerName, this.headers.get(headerName));
|
||||
}
|
||||
return request;
|
||||
|
|
|
@ -73,7 +73,7 @@ public class HttpEntity<T> {
|
|||
* Create a new, empty {@code HttpEntity}.
|
||||
*/
|
||||
protected HttpEntity() {
|
||||
this(null, null);
|
||||
this(null, (HttpHeaders) null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -81,13 +81,35 @@ public class HttpEntity<T> {
|
|||
* @param body the entity body
|
||||
*/
|
||||
public HttpEntity(T body) {
|
||||
this(body, null);
|
||||
this(body, (HttpHeaders) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code HttpEntity} with the given headers and no body.
|
||||
* @param headers the entity headers
|
||||
* @since 7.0
|
||||
*/
|
||||
public HttpEntity(HttpHeaders headers) {
|
||||
this(null, headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code HttpEntity} with the given body and headers.
|
||||
* @param body the entity body
|
||||
* @param headers the entity headers
|
||||
* @since 7.0
|
||||
*/
|
||||
public HttpEntity(@Nullable T body, @Nullable HttpHeaders headers) {
|
||||
this.body = body;
|
||||
this.headers = HttpHeaders.readOnlyHttpHeaders(headers != null ? headers : new HttpHeaders());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code HttpEntity} with the given headers and no body.
|
||||
* @param headers the entity headers
|
||||
* @deprecated Use {@link #HttpEntity(HttpHeaders)}
|
||||
*/
|
||||
@Deprecated
|
||||
public HttpEntity(MultiValueMap<String, String> headers) {
|
||||
this(null, headers);
|
||||
}
|
||||
|
@ -96,10 +118,12 @@ public class HttpEntity<T> {
|
|||
* Create a new {@code HttpEntity} with the given body and headers.
|
||||
* @param body the entity body
|
||||
* @param headers the entity headers
|
||||
* @deprecated Use {@link #HttpEntity(Object, HttpHeaders)}
|
||||
*/
|
||||
@Deprecated
|
||||
public HttpEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers) {
|
||||
this.body = body;
|
||||
this.headers = HttpHeaders.readOnlyHttpHeaders(headers != null ? headers : new HttpHeaders());
|
||||
this.headers = HttpHeaders.readOnlyHttpHeaders(headers != null ? new HttpHeaders(headers) : new HttpHeaders());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -39,6 +39,7 @@ import java.util.Iterator;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
|
@ -52,6 +53,7 @@ import org.springframework.util.CollectionUtils;
|
|||
import org.springframework.util.LinkedCaseInsensitiveMap;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
|
@ -71,23 +73,18 @@ import org.springframework.util.StringUtils;
|
|||
* {@link #HttpHeaders(MultiValueMap)} constructor like those instantiated
|
||||
* internally by the framework to adapt to existing HTTP headers data structures
|
||||
* do guarantee per-header get/set/add operations to be case-insensitive as
|
||||
* mandated by the HTTP specification. However, it is not necessarily the case
|
||||
* for operations that deal with the collection as a whole (like {@code size()},
|
||||
* {@code values()}, {@code keySet()} and {@code entrySet()}). Prefer using
|
||||
* {@link #headerSet()} for these cases.
|
||||
*
|
||||
* <p>Some backing implementations can store header names in a case-sensitive
|
||||
* manner, which will lead to duplicates during the entrySet() iteration where
|
||||
* multiple occurrences of a header name can surface depending on letter casing
|
||||
* but each such entry has the full {@code List} of values. — This can be
|
||||
* problematic for example when copying headers into a new instance by iterating
|
||||
* over the old instance's {@code entrySet()} and using
|
||||
* {@link #addAll(String, List)} rather than {@link #put(String, List)}.
|
||||
* mandated by the HTTP specification. However, it is not necessarily how
|
||||
* entries are actually stored, and this can lead to the reported {@code size()}
|
||||
* being inflated. Prefer using {@link #headerSet()} or {@link #headerNames()}
|
||||
* to ensure a case-insensitive view.
|
||||
*
|
||||
* <p>This class is meant to reference "well-known" headers supported by Spring
|
||||
* Framework. If your application or library relies on other headers defined in RFCs,
|
||||
* please use methods that accept the header name as a parameter.
|
||||
*
|
||||
* <p>Since 7.0, this class no longer implements the {@code MultiValueMap}
|
||||
* contract.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Sebastien Deleuze
|
||||
* @author Brian Clozel
|
||||
|
@ -97,7 +94,7 @@ import org.springframework.util.StringUtils;
|
|||
* @author Simon Baslé
|
||||
* @since 3.0
|
||||
*/
|
||||
public class HttpHeaders implements MultiValueMap<String, String>, Serializable {
|
||||
public class HttpHeaders implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -8578554704772377436L;
|
||||
|
||||
|
@ -472,6 +469,28 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new {@code HttpHeaders} instance by removing any read-only
|
||||
* wrapper that may have been previously applied around the given
|
||||
* {@code HttpHeaders} via {@link #readOnlyHttpHeaders(HttpHeaders)}.
|
||||
* <p>Once the writable instance is mutated, the read-only instance is
|
||||
* likely to be out of sync and should be discarded.
|
||||
* @param httpHeaders the headers to expose
|
||||
* @since 7.0
|
||||
*/
|
||||
public HttpHeaders(HttpHeaders httpHeaders) {
|
||||
Assert.notNull(httpHeaders, "HttpHeaders must not be null");
|
||||
if (httpHeaders == EMPTY) {
|
||||
this.headers = CollectionUtils.toMultiValueMap(new LinkedCaseInsensitiveMap<>(8, Locale.ENGLISH));
|
||||
}
|
||||
else {
|
||||
while (httpHeaders.headers instanceof HttpHeaders wrapped) {
|
||||
httpHeaders = wrapped;
|
||||
}
|
||||
this.headers = httpHeaders.headers;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the list of header values for the given header name, if any.
|
||||
|
@ -479,9 +498,20 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
|
|||
* @return the list of header values, or an empty list
|
||||
* @since 5.2
|
||||
*/
|
||||
public List<String> getOrEmpty(Object headerName) {
|
||||
public List<String> getOrEmpty(String headerName) {
|
||||
return getOrDefault(headerName, Collections.emptyList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of header values for the given header name, or the given
|
||||
* default list of values if the header is not present.
|
||||
* @param headerName the header name
|
||||
* @param defaultValue the fallback list if header is not present
|
||||
* @return the list of header values, or a default list of values
|
||||
*/
|
||||
public List<String> getOrDefault(String headerName, List<String> defaultValue) {
|
||||
List<String> values = get(headerName);
|
||||
return (values != null ? values : Collections.emptyList());
|
||||
return (values != null ? values : defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1723,14 +1753,13 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
|
|||
}
|
||||
|
||||
|
||||
// MultiValueMap implementation
|
||||
// MultiValueMap-like methods
|
||||
|
||||
/**
|
||||
* Return the first header value for the given header name, if any.
|
||||
* @param headerName the header name
|
||||
* @return the first header value, or {@code null} if none
|
||||
*/
|
||||
@Override
|
||||
public @Nullable String getFirst(String headerName) {
|
||||
return this.headers.getFirst(headerName);
|
||||
}
|
||||
|
@ -1743,19 +1772,33 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
|
|||
* @see #put(String, List)
|
||||
* @see #set(String, String)
|
||||
*/
|
||||
@Override
|
||||
public void add(String headerName, @Nullable String headerValue) {
|
||||
this.headers.add(headerName, headerValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAll(String key, List<? extends String> values) {
|
||||
this.headers.addAll(key, values);
|
||||
/**
|
||||
* Add all the given values under the given name.
|
||||
* <p>As values are represented as a {@code List}, duplicate values can be
|
||||
* introduced. See {@link #put(String, List)} to replace the list of values
|
||||
* instead.
|
||||
* @param headerName the header name
|
||||
* @param headerValues the values to add
|
||||
* @see #put(String, List)
|
||||
*/
|
||||
public void addAll(String headerName, List<? extends String> headerValues) {
|
||||
this.headers.addAll(headerName, headerValues);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAll(MultiValueMap<String, String> values) {
|
||||
this.headers.addAll(values);
|
||||
/**
|
||||
* Add all the values of the given {@code HttpHeaders} to the current header.
|
||||
* <p>As values are represented as a {@code List}, duplicate values can be
|
||||
* introduced. See {@link #putAll(HttpHeaders)} to replace the list of
|
||||
* values of each individual header name instead.
|
||||
* @param headers the headers to add
|
||||
* @see #putAll(HttpHeaders)
|
||||
*/
|
||||
public void addAll(HttpHeaders headers) {
|
||||
this.headers.addAll(headers.headers);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1766,132 +1809,202 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
|
|||
* @see #put(String, List)
|
||||
* @see #add(String, String)
|
||||
*/
|
||||
@Override
|
||||
public void set(String headerName, @Nullable String headerValue) {
|
||||
this.headers.set(headerName, headerValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Set all single header value from the given Map under each of their
|
||||
* corresponding name.
|
||||
* @param values the name-single-value pairs
|
||||
* @see #putAll(Map)
|
||||
*/
|
||||
public void setAll(Map<String, String> values) {
|
||||
this.headers.setAll(values);
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Return this HttpHeaders as a {@code Map} with the first values for each
|
||||
* header name.
|
||||
* <p>The difference between this method and {@link #asSingleValueMap()} is
|
||||
* that this method returns a copy of the headers, whereas the latter
|
||||
* returns a view. This copy also ensures that collection-iterating methods
|
||||
* like {@code entrySet()} are case-insensitive.
|
||||
* @return a single value representation of these headers
|
||||
*/
|
||||
public Map<String, String> toSingleValueMap() {
|
||||
return this.headers.toSingleValueMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Return this HttpHeaders as a {@code Map} with the first values for each
|
||||
* header name.
|
||||
* <p>The difference between this method and {@link #toSingleValueMap()} is
|
||||
* that this method returns a view of the headers, whereas the latter
|
||||
* returns a copy. This method is also susceptible to include multiple
|
||||
* casing variants of a given header name, see {@link #asMultiValueMap()}
|
||||
* javadoc.
|
||||
* @return a single value representation of these headers
|
||||
* @deprecated Use {@link #toSingleValueMap()} which performs a copy but
|
||||
* ensures that collection-iterating methods like {@code entrySet()} are
|
||||
* case-insensitive
|
||||
*/
|
||||
@Deprecated
|
||||
public Map<String, String> asSingleValueMap() {
|
||||
return this.headers.asSingleValueMap();
|
||||
}
|
||||
|
||||
// Map implementation
|
||||
/**
|
||||
* Return this HttpHeaders as a {@code MultiValueMap} with the full list
|
||||
* of values for each header name.
|
||||
* <p>Note that some backing server headers implementations can store header
|
||||
* names in a case-sensitive manner, which will lead to duplicates during
|
||||
* iteration in methods like {@code entrySet()}, where multiple occurrences
|
||||
* of a header name can surface depending on letter casing but each such
|
||||
* entry has the full {@code List} of values.
|
||||
* @return a MultiValueMap representation of these headers
|
||||
* @deprecated This method is provided for backward compatibility with APIs
|
||||
* that would only accept maps. Generally avoid using HttpHeaders as a Map
|
||||
* or MultiValueMap.
|
||||
*/
|
||||
@Deprecated
|
||||
public MultiValueMap<String, String> asMultiValueMap() {
|
||||
return this.headers;
|
||||
}
|
||||
|
||||
@Override
|
||||
// Map-like implementation
|
||||
|
||||
/**
|
||||
* Returns {@code true} if this HttpHeaders contains no header entry.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return this.headers.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return this.headers.containsKey(key);
|
||||
/**
|
||||
* Returns {@code true} if this HttpHeaders contains an entry for the
|
||||
* given header name.
|
||||
* @param headerName the header name
|
||||
* @since 7.0
|
||||
*/
|
||||
public boolean containsHeader(String headerName) {
|
||||
return this.headers.containsKey(headerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return this.headers.containsValue(value);
|
||||
/**
|
||||
* Returns {@code true} if this HttpHeaders contains exactly the given list
|
||||
* of values for the given header name.
|
||||
* @param headerName the header name
|
||||
* @param values the expected list of values
|
||||
* @since 7.0
|
||||
*/
|
||||
public boolean hasHeaderValues(String headerName, List<String> values) {
|
||||
return ObjectUtils.nullSafeEquals(this.headers.get(headerName), values);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> get(Object key) {
|
||||
return this.headers.get(key);
|
||||
/**
|
||||
* Returns {@code true} if this HttpHeaders contains the given header and
|
||||
* its list of values contains the given value.
|
||||
* @param headerName the header name
|
||||
* @param value the value expected to be in the list of values
|
||||
* @since 7.0
|
||||
*/
|
||||
public boolean containsHeaderValue(String headerName, String value) {
|
||||
final List<String> values = this.headers.get(headerName);
|
||||
if (values == null) {
|
||||
return false;
|
||||
}
|
||||
return values.contains(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> put(String key, List<String> value) {
|
||||
return this.headers.put(key, value);
|
||||
/**
|
||||
* Get the list of values associated with the given header name.
|
||||
* @param headerName the header name
|
||||
*/
|
||||
@Nullable
|
||||
public List<String> get(String headerName) {
|
||||
return this.headers.get(headerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> remove(Object key) {
|
||||
/**
|
||||
* Set the list of values associated with the given header name. Returns the
|
||||
* previous list of values, or {@code null} if the header was not present.
|
||||
* @param headerName the header name
|
||||
* @param headerValues the new values
|
||||
* @return the old values for the given header name
|
||||
*/
|
||||
public @Nullable List<String> put(String headerName, List<String> headerValues) {
|
||||
return this.headers.put(headerName, headerValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set header values for the given header name if that header name isn't
|
||||
* already present in this HttpHeaders and return {@code null}. If the
|
||||
* header is already present, returns the associated value list instead.
|
||||
* @param headerName the header name
|
||||
* @param headerValues the header values to set if header is not present
|
||||
* @return the previous value or {@code null}
|
||||
*/
|
||||
public @Nullable List<String> putIfAbsent(String headerName, List<String> headerValues) {
|
||||
return this.headers.putIfAbsent(headerName, headerValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put all the entries from the given HttpHeaders into this HttpHeaders.
|
||||
* @param headers the given headers
|
||||
* @see #put(String, List)
|
||||
*/
|
||||
public void putAll(HttpHeaders headers) {
|
||||
this.headers.putAll(headers.headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put all the entries from the given {@code MultiValueMap} into this
|
||||
* HttpHeaders.
|
||||
* @param headers the given headers
|
||||
* @see #put(String, List)
|
||||
*/
|
||||
public void putAll(Map<? extends String, ? extends List<String>> headers) {
|
||||
this.headers.putAll(headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a header from this HttpHeaders instance, and return the associated
|
||||
* value list or {@code null} if that header wasn't present.
|
||||
* @param key the name of the header to remove
|
||||
* @return the value list associated with the removed header name
|
||||
*/
|
||||
public @Nullable List<String> remove(String key) {
|
||||
return this.headers.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ? extends List<String>> map) {
|
||||
this.headers.putAll(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Remove all headers from this HttpHeaders instance.
|
||||
*/
|
||||
public void clear() {
|
||||
this.headers.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> putIfAbsent(String key, List<String> value) {
|
||||
return this.headers.putIfAbsent(key, value);
|
||||
}
|
||||
|
||||
// Map/MultiValueMap methods that can have duplicate header names: size/keySet/values/entrySet/forEach
|
||||
|
||||
/**
|
||||
* Return the number of headers in the collection. This can be inflated,
|
||||
* see {@link HttpHeaders class level javadoc}.
|
||||
*/
|
||||
@Override
|
||||
public int size() {
|
||||
return this.headers.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link Set} view of header names. This can include multiple
|
||||
* casing variants of a given header name, see
|
||||
* {@link HttpHeaders class level javadoc}.
|
||||
*/
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
return this.headers.keySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link Collection} view of all the header values, reconstructed
|
||||
* from iterating over the {@link #keySet()}. This can include duplicates if
|
||||
* multiple casing variants of a given header name are tracked, see
|
||||
* {@link HttpHeaders class level javadoc}.
|
||||
*/
|
||||
@Override
|
||||
public Collection<List<String>> values() {
|
||||
return this.headers.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a {@link Set} views of header entries, reconstructed from
|
||||
* iterating over the {@link #keySet()}. This can include duplicate entries
|
||||
* if multiple casing variants of a given header name are tracked, see
|
||||
* {@link HttpHeaders class level javadoc}.
|
||||
* @see #headerSet()
|
||||
*/
|
||||
@Override
|
||||
public Set<Entry<String, List<String>>> entrySet() {
|
||||
return this.headers.entrySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an action over each header, as when iterated via
|
||||
* {@link #entrySet()}. This can include duplicate entries
|
||||
* if multiple casing variants of a given header name are tracked, see
|
||||
* {@link HttpHeaders class level javadoc}.
|
||||
* {@link #headerSet()}.
|
||||
* @param action the action to be performed for each entry
|
||||
*/
|
||||
@Override
|
||||
public void forEach(BiConsumer<? super String, ? super List<String>> action) {
|
||||
this.headers.forEach(action);
|
||||
this.headerSet().forEach(e -> action.accept(e.getKey(), e.getValue()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a view of the headers as an entry {@code Set} of key-list pairs.
|
||||
* Both {@link Iterator#remove()} and {@link java.util.Map.Entry#setValue}
|
||||
* Both {@link Iterator#remove()} and {@link Entry#setValue}
|
||||
* are supported and mutate the headers.
|
||||
* <p>This collection is guaranteed to contain one entry per header name
|
||||
* even if the backing structure stores multiple casing variants of names,
|
||||
|
@ -1905,6 +2018,20 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
|
|||
return new CaseInsensitiveEntrySet(this.headers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the set of header names. Both {@link Set#remove(Object)} and
|
||||
* {@link Set#clear()} operations are supported and mutate the headers.
|
||||
* <p>This collection is guaranteed to contain only one casing variant
|
||||
* of each header name even if the backing structure stores multiple casing
|
||||
* variants of names. The first encountered variant is the one that is
|
||||
* retained.
|
||||
* @return a {@code Set} of all the headers names
|
||||
* @since 7.0
|
||||
*/
|
||||
public Set<String> headerNames() {
|
||||
return new CaseInsensitiveHeaderNameSet(this.headers);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object other) {
|
||||
|
@ -1959,7 +2086,7 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
|
|||
* @since 5.1.4
|
||||
*/
|
||||
public static String formatHeaders(MultiValueMap<String, String> headers) {
|
||||
Set<String> headerNames = toCaseInsensitiveSet(headers.keySet());
|
||||
Set<String> headerNames = new CaseInsensitiveHeaderNameSet(headers);
|
||||
String suffix = "]";
|
||||
if (headerNames.size() != headers.size()) {
|
||||
suffix = "] with native header names " + headers.keySet();
|
||||
|
@ -2027,34 +2154,28 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
|
|||
}
|
||||
|
||||
|
||||
private static Set<String> toCaseInsensitiveSet(Set<String> originalSet) {
|
||||
final Set<String> deduplicatedSet = Collections.newSetFromMap(
|
||||
new LinkedCaseInsensitiveMap<>(originalSet.size(), Locale.ROOT));
|
||||
// add/addAll (put/putAll in LinkedCaseInsensitiveMap) retain the casing of the last occurrence.
|
||||
// Here we prefer the first.
|
||||
for (String header : originalSet) {
|
||||
//noinspection RedundantCollectionOperation
|
||||
if (!deduplicatedSet.contains(header)) {
|
||||
deduplicatedSet.add(header);
|
||||
}
|
||||
}
|
||||
return deduplicatedSet;
|
||||
}
|
||||
private static final class CaseInsensitiveHeaderNameSet extends AbstractSet<String> {
|
||||
|
||||
|
||||
private static final class CaseInsensitiveEntrySet extends AbstractSet<Entry<String, List<String>>> {
|
||||
private static final Object VALUE = new Object();
|
||||
|
||||
private final MultiValueMap<String, String> headers;
|
||||
private final Set<String> deduplicatedNames;
|
||||
private final Map<String, Object> deduplicatedNames;
|
||||
|
||||
public CaseInsensitiveEntrySet(MultiValueMap<String, String> headers) {
|
||||
public CaseInsensitiveHeaderNameSet(MultiValueMap<String, String> headers) {
|
||||
this.headers = headers;
|
||||
this.deduplicatedNames = toCaseInsensitiveSet(headers.keySet());
|
||||
this.deduplicatedNames = new LinkedCaseInsensitiveMap<>(headers.size(), Locale.ROOT);
|
||||
// add/addAll (put/putAll in LinkedCaseInsensitiveMap) retain the casing of the last occurrence.
|
||||
// Here we prefer the first.
|
||||
for (String header : headers.keySet()) {
|
||||
if (!this.deduplicatedNames.containsKey(header)) {
|
||||
this.deduplicatedNames.put(header, VALUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Map.Entry<String, List<String>>> iterator() {
|
||||
return new CaseInsensitiveIterator(this.deduplicatedNames.iterator());
|
||||
public Iterator<String> iterator() {
|
||||
return new HeaderNamesIterator(this.headers, this.deduplicatedNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2062,15 +2183,87 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
|
|||
return this.deduplicatedNames.size();
|
||||
}
|
||||
|
||||
private final class CaseInsensitiveIterator implements Iterator<Map.Entry<String, List<String>>> {
|
||||
@Override
|
||||
public boolean contains(Object o) {
|
||||
return this.headers.containsKey(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean remove(Object o) {
|
||||
return this.headers.remove(o) != null && this.deduplicatedNames.remove(o) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
this.headers.clear();
|
||||
this.deduplicatedNames.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private static class HeaderNamesIterator implements Iterator<String> {
|
||||
|
||||
|
||||
private @Nullable String currentName;
|
||||
|
||||
private final MultiValueMap<String, String> headers;
|
||||
private final Iterator<String> namesIterator;
|
||||
|
||||
public HeaderNamesIterator (MultiValueMap<String, String> headers, Map<String, Object> caseInsensitiveNames) {
|
||||
this.headers = headers;
|
||||
this.namesIterator = caseInsensitiveNames.keySet().iterator();
|
||||
this.currentName = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return this.namesIterator.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String next() {
|
||||
this.currentName = this.namesIterator.next();
|
||||
return this.currentName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
if (this.currentName == null) {
|
||||
throw new IllegalStateException("No current Header in iterator");
|
||||
}
|
||||
if (!this.headers.containsKey(this.currentName)) {
|
||||
throw new IllegalStateException("Header not present: " + this.currentName);
|
||||
}
|
||||
this.headers.remove(this.currentName);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final class CaseInsensitiveEntrySet extends AbstractSet<Entry<String, List<String>>> {
|
||||
|
||||
private final MultiValueMap<String, String> headers;
|
||||
private final CaseInsensitiveHeaderNameSet nameSet;
|
||||
|
||||
public CaseInsensitiveEntrySet(MultiValueMap<String, String> headers) {
|
||||
this.headers = headers;
|
||||
this.nameSet = new CaseInsensitiveHeaderNameSet(headers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Entry<String, List<String>>> iterator() {
|
||||
return new CaseInsensitiveIterator(this.nameSet.iterator());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return this.nameSet.size();
|
||||
}
|
||||
|
||||
private final class CaseInsensitiveIterator implements Iterator<Entry<String, List<String>>> {
|
||||
|
||||
private final Iterator<String> namesIterator;
|
||||
|
||||
private @Nullable String currentName;
|
||||
|
||||
private CaseInsensitiveIterator(Iterator<String> namesIterator) {
|
||||
public CaseInsensitiveIterator(Iterator<String> namesIterator) {
|
||||
this.namesIterator = namesIterator;
|
||||
this.currentName = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2079,24 +2272,17 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
|
|||
}
|
||||
|
||||
@Override
|
||||
public Map.Entry<String, List<String>> next() {
|
||||
this.currentName = this.namesIterator.next();
|
||||
return new CaseInsensitiveEntry(this.currentName);
|
||||
public Entry<String, List<String>> next() {
|
||||
return new CaseInsensitiveEntrySet.CaseInsensitiveEntry(this.namesIterator.next());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
if (this.currentName == null) {
|
||||
throw new IllegalStateException("No current Header in iterator");
|
||||
}
|
||||
if (!CaseInsensitiveEntrySet.this.headers.containsKey(this.currentName)) {
|
||||
throw new IllegalStateException("Header not present: " + this.currentName);
|
||||
}
|
||||
CaseInsensitiveEntrySet.this.headers.remove(this.currentName);
|
||||
this.namesIterator.remove();
|
||||
}
|
||||
}
|
||||
|
||||
private final class CaseInsensitiveEntry implements Map.Entry<String, List<String>> {
|
||||
private final class CaseInsensitiveEntry implements Entry<String, List<String>> {
|
||||
|
||||
private final String key;
|
||||
|
||||
|
@ -2121,6 +2307,22 @@ public class HttpHeaders implements MultiValueMap<String, String>, Serializable
|
|||
CaseInsensitiveEntrySet.this.headers.put(this.key, value);
|
||||
return previousValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof Map.Entry<?,?> that)) {
|
||||
return false;
|
||||
}
|
||||
return ObjectUtils.nullSafeEquals(getKey(), that.getKey()) && ObjectUtils.nullSafeEquals(getValue(), that.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return ObjectUtils.nullSafeHash(getKey(), getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,17 +17,18 @@
|
|||
package org.springframework.http;
|
||||
|
||||
import java.util.AbstractMap.SimpleImmutableEntry;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
|
@ -84,8 +85,8 @@ class ReadOnlyHttpHeaders extends HttpHeaders {
|
|||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> get(Object key) {
|
||||
List<String> values = this.headers.get(key);
|
||||
public @Nullable List<String> get(String headerName) {
|
||||
List<String> values = this.headers.get(headerName);
|
||||
return (values != null ? Collections.unmodifiableList(values) : null);
|
||||
}
|
||||
|
||||
|
@ -95,12 +96,12 @@ class ReadOnlyHttpHeaders extends HttpHeaders {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void addAll(String key, List<? extends String> values) {
|
||||
public void addAll(String key, List<? extends String> headerValues) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAll(MultiValueMap<String, String> values) {
|
||||
public void addAll(HttpHeaders values) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
@ -120,27 +121,45 @@ class ReadOnlyHttpHeaders extends HttpHeaders {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public Map<String, String> asSingleValueMap() {
|
||||
return Collections.unmodifiableMap(this.headers.asSingleValueMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
return Collections.unmodifiableSet(this.headers.keySet());
|
||||
@Deprecated
|
||||
public MultiValueMap<String, String> asMultiValueMap() {
|
||||
return CollectionUtils.unmodifiableMultiValueMap(this.headers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> headerNames() {
|
||||
return Collections.unmodifiableSet(super.headerNames());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<String> put(String key, List<String> value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> remove(Object key) {
|
||||
public @Nullable List<String> putIfAbsent(String headerName, List<String> headerValues) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ? extends List<String>> map) {
|
||||
public void putAll(@Nullable HttpHeaders values) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ? extends List<String>> headers) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> remove(String key) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
@ -150,13 +169,8 @@ class ReadOnlyHttpHeaders extends HttpHeaders {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Collection<List<String>> values() {
|
||||
return Collections.unmodifiableCollection(this.headers.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<String, List<String>>> entrySet() {
|
||||
return this.headers.entrySet().stream().map(SimpleImmutableEntry::new)
|
||||
public Set<Entry<String, List<String>>> headerSet() {
|
||||
return super.headerSet().stream().map(SimpleImmutableEntry::new)
|
||||
.collect(Collectors.collectingAndThen(
|
||||
Collectors.toCollection(LinkedHashSet::new), // Retain original ordering of entries
|
||||
Collections::unmodifiableSet));
|
||||
|
|
|
@ -79,7 +79,7 @@ public class RequestEntity<T> extends HttpEntity<T> {
|
|||
* @param url the URL
|
||||
*/
|
||||
public RequestEntity(HttpMethod method, URI url) {
|
||||
this(null, null, method, url, null);
|
||||
this(null, (HttpHeaders) null, method, url, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -89,7 +89,7 @@ public class RequestEntity<T> extends HttpEntity<T> {
|
|||
* @param url the URL
|
||||
*/
|
||||
public RequestEntity(@Nullable T body, HttpMethod method, URI url) {
|
||||
this(body, null, method, url, null);
|
||||
this(body, (HttpHeaders) null, method, url, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,7 +101,7 @@ public class RequestEntity<T> extends HttpEntity<T> {
|
|||
* @since 4.3
|
||||
*/
|
||||
public RequestEntity(@Nullable T body, HttpMethod method, URI url, Type type) {
|
||||
this(body, null, method, url, type);
|
||||
this(body, (HttpHeaders) null, method, url, type);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -109,7 +109,52 @@ public class RequestEntity<T> extends HttpEntity<T> {
|
|||
* @param headers the headers
|
||||
* @param method the method
|
||||
* @param url the URL
|
||||
* @since 7.0
|
||||
*/
|
||||
public RequestEntity(HttpHeaders headers, HttpMethod method, URI url) {
|
||||
this(null, headers, method, url, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with method, URL, headers and body.
|
||||
* @param body the body
|
||||
* @param headers the headers
|
||||
* @param method the method
|
||||
* @param url the URL
|
||||
* @since 7.0
|
||||
*/
|
||||
public RequestEntity(@Nullable T body, @Nullable HttpHeaders headers,
|
||||
@Nullable HttpMethod method, URI url) {
|
||||
|
||||
this(body, headers, method, url, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with method, URL, headers, body and type.
|
||||
* @param body the body
|
||||
* @param headers the headers
|
||||
* @param method the method
|
||||
* @param url the URL
|
||||
* @param type the type used for generic type resolution
|
||||
* @since 7.0
|
||||
*/
|
||||
public RequestEntity(@Nullable T body, @Nullable HttpHeaders headers,
|
||||
@Nullable HttpMethod method, @Nullable URI url, @Nullable Type type) {
|
||||
|
||||
super(body, headers);
|
||||
this.method = method;
|
||||
this.url = url;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with method, URL and headers but without body.
|
||||
* @param headers the headers
|
||||
* @param method the method
|
||||
* @param url the URL
|
||||
* @deprecated Use {@link #RequestEntity(HttpHeaders, HttpMethod, URI)}
|
||||
*/
|
||||
@Deprecated
|
||||
public RequestEntity(MultiValueMap<String, String> headers, HttpMethod method, URI url) {
|
||||
this(null, headers, method, url, null);
|
||||
}
|
||||
|
@ -120,7 +165,9 @@ public class RequestEntity<T> extends HttpEntity<T> {
|
|||
* @param headers the headers
|
||||
* @param method the method
|
||||
* @param url the URL
|
||||
* @deprecated Use {@link #RequestEntity(Object, HttpHeaders, HttpMethod, URI)}
|
||||
*/
|
||||
@Deprecated
|
||||
public RequestEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers,
|
||||
@Nullable HttpMethod method, URI url) {
|
||||
|
||||
|
@ -135,7 +182,9 @@ public class RequestEntity<T> extends HttpEntity<T> {
|
|||
* @param url the URL
|
||||
* @param type the type used for generic type resolution
|
||||
* @since 4.3
|
||||
* @deprecated Use {@link #RequestEntity(Object, HttpHeaders, HttpMethod, URI, Type)}
|
||||
*/
|
||||
@Deprecated
|
||||
public RequestEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers,
|
||||
@Nullable HttpMethod method, @Nullable URI url, @Nullable Type type) {
|
||||
|
||||
|
@ -694,7 +743,7 @@ public class RequestEntity<T> extends HttpEntity<T> {
|
|||
private final @Nullable Map<String, ?> uriVarsMap;
|
||||
|
||||
UriTemplateRequestEntity(
|
||||
@Nullable T body, @Nullable MultiValueMap<String, String> headers,
|
||||
@Nullable T body, @Nullable HttpHeaders headers,
|
||||
@Nullable HttpMethod method, @Nullable Type type, String uriTemplate,
|
||||
Object @Nullable [] uriVarsArray, @Nullable Map<String, ?> uriVarsMap) {
|
||||
|
||||
|
|
|
@ -88,7 +88,7 @@ public class ResponseEntity<T> extends HttpEntity<T> {
|
|||
* @param status the status code
|
||||
*/
|
||||
public ResponseEntity(HttpStatusCode status) {
|
||||
this(null, null, status);
|
||||
this(null, (HttpHeaders) null, status);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -97,14 +97,51 @@ public class ResponseEntity<T> extends HttpEntity<T> {
|
|||
* @param status the status code
|
||||
*/
|
||||
public ResponseEntity(@Nullable T body, HttpStatusCode status) {
|
||||
this(body, null, status);
|
||||
this(body, (HttpHeaders) null, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code ResponseEntity} with headers and a status code.
|
||||
* @param headers the entity headers
|
||||
* @param status the status code
|
||||
* @since 7.0
|
||||
*/
|
||||
public ResponseEntity(HttpHeaders headers, HttpStatusCode status) {
|
||||
this(null, headers, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code ResponseEntity} with a body, headers, and a raw status code.
|
||||
* @param body the entity body
|
||||
* @param headers the entity headers
|
||||
* @param rawStatus the status code value
|
||||
* @since 7.0
|
||||
*/
|
||||
public ResponseEntity(@Nullable T body, @Nullable HttpHeaders headers, int rawStatus) {
|
||||
this(body, headers, HttpStatusCode.valueOf(rawStatus));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code ResponseEntity} with a body, headers, and a status code.
|
||||
* @param body the entity body
|
||||
* @param headers the entity headers
|
||||
* @param statusCode the status code
|
||||
* @since 7.0
|
||||
*/
|
||||
public ResponseEntity(@Nullable T body, @Nullable HttpHeaders headers, HttpStatusCode statusCode) {
|
||||
super(body, headers);
|
||||
Assert.notNull(statusCode, "HttpStatusCode must not be null");
|
||||
|
||||
this.status = statusCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a {@code ResponseEntity} with headers and a status code.
|
||||
* @param headers the entity headers
|
||||
* @param status the status code
|
||||
* @deprecated Use {@link #ResponseEntity(HttpHeaders, HttpStatusCode)}
|
||||
*/
|
||||
@Deprecated
|
||||
public ResponseEntity(MultiValueMap<String, String> headers, HttpStatusCode status) {
|
||||
this(null, headers, status);
|
||||
}
|
||||
|
@ -115,7 +152,9 @@ public class ResponseEntity<T> extends HttpEntity<T> {
|
|||
* @param headers the entity headers
|
||||
* @param rawStatus the status code value
|
||||
* @since 5.3.2
|
||||
* @deprecated Use {@link #ResponseEntity(Object, HttpHeaders, int)}
|
||||
*/
|
||||
@Deprecated
|
||||
public ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, int rawStatus) {
|
||||
this(body, headers, HttpStatusCode.valueOf(rawStatus));
|
||||
}
|
||||
|
@ -125,7 +164,9 @@ public class ResponseEntity<T> extends HttpEntity<T> {
|
|||
* @param body the entity body
|
||||
* @param headers the entity headers
|
||||
* @param statusCode the status code
|
||||
* @deprecated Use {@link #ResponseEntity(Object, HttpHeaders, HttpStatusCode)}
|
||||
*/
|
||||
@Deprecated
|
||||
public ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, HttpStatusCode statusCode) {
|
||||
super(body, headers);
|
||||
Assert.notNull(statusCode, "HttpStatusCode must not be null");
|
||||
|
|
|
@ -366,7 +366,7 @@ public final class MultipartBodyBuilder {
|
|||
private final ResolvableType resolvableType;
|
||||
|
||||
PublisherEntity(
|
||||
@Nullable MultiValueMap<String, String> headers, P publisher, ResolvableType resolvableType) {
|
||||
@Nullable HttpHeaders headers, P publisher, ResolvableType resolvableType) {
|
||||
|
||||
super(publisher, headers);
|
||||
Assert.notNull(publisher, "'publisher' must not be null");
|
||||
|
|
|
@ -125,7 +125,7 @@ class HttpComponentsClientHttpRequest extends AbstractClientHttpRequest {
|
|||
protected void applyHeaders() {
|
||||
HttpHeaders headers = getHeaders();
|
||||
|
||||
headers.entrySet()
|
||||
headers.headerSet()
|
||||
.stream()
|
||||
.filter(entry -> !HttpHeaders.CONTENT_LENGTH.equals(entry.getKey()))
|
||||
.forEach(entry -> entry.getValue().forEach(v -> this.httpRequest.addHeader(entry.getKey(), v)));
|
||||
|
|
|
@ -99,7 +99,7 @@ class JdkClientHttpRequest extends AbstractClientHttpRequest {
|
|||
|
||||
@Override
|
||||
protected void applyHeaders() {
|
||||
for (Map.Entry<String, List<String>> entry : getHeaders().entrySet()) {
|
||||
for (Map.Entry<String, List<String>> entry : getHeaders().headerSet()) {
|
||||
if (entry.getKey().equalsIgnoreCase(HttpHeaders.CONTENT_LENGTH)) {
|
||||
// content-length is specified when writing
|
||||
continue;
|
||||
|
@ -108,7 +108,7 @@ class JdkClientHttpRequest extends AbstractClientHttpRequest {
|
|||
this.builder.header(entry.getKey(), value);
|
||||
}
|
||||
}
|
||||
if (!getHeaders().containsKey(HttpHeaders.ACCEPT)) {
|
||||
if (!getHeaders().containsHeader(HttpHeaders.ACCEPT)) {
|
||||
this.builder.header(HttpHeaders.ACCEPT, "*/*");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -143,7 +143,7 @@ class JettyClientHttpRequest extends AbstractClientHttpRequest {
|
|||
HttpHeaders headers = getHeaders();
|
||||
this.jettyRequest.headers(fields -> {
|
||||
headers.forEach((key, value) -> value.forEach(v -> fields.add(key, v)));
|
||||
if (!headers.containsKey(HttpHeaders.ACCEPT)) {
|
||||
if (!headers.containsHeader(HttpHeaders.ACCEPT)) {
|
||||
fields.add(HttpHeaders.ACCEPT, "*/*");
|
||||
}
|
||||
});
|
||||
|
|
|
@ -74,7 +74,7 @@ public class BasicAuthenticationInterceptor implements ClientHttpRequestIntercep
|
|||
HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
|
||||
|
||||
HttpHeaders headers = request.getHeaders();
|
||||
if (!headers.containsKey(HttpHeaders.AUTHORIZATION)) {
|
||||
if (!headers.containsHeader(HttpHeaders.AUTHORIZATION)) {
|
||||
headers.setBasicAuth(this.encodedCredentials);
|
||||
}
|
||||
return execution.execute(request, body);
|
||||
|
|
|
@ -261,7 +261,7 @@ public class MultipartHttpMessageWriter extends MultipartWriterSupport
|
|||
resolvableType = ResolvableType.forClass(body.getClass());
|
||||
}
|
||||
|
||||
if (!headers.containsKey(HttpHeaders.CONTENT_DISPOSITION)) {
|
||||
if (!headers.containsHeader(HttpHeaders.CONTENT_DISPOSITION)) {
|
||||
if (body instanceof Resource resource) {
|
||||
headers.setContentDispositionFormData(name, resource.getFilename());
|
||||
}
|
||||
|
|
|
@ -150,7 +150,7 @@ public class MultipartWriterSupport extends LoggingCodecSupport {
|
|||
return Mono.fromCallable(() -> {
|
||||
@SuppressWarnings("resource")
|
||||
FastByteArrayOutputStream bos = new FastByteArrayOutputStream();
|
||||
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
|
||||
for (Map.Entry<String, List<String>> entry : headers.headerSet()) {
|
||||
byte[] headerName = entry.getKey().getBytes(getCharset());
|
||||
for (String headerValueString : entry.getValue()) {
|
||||
byte[] headerValue = headerValueString.getBytes(getCharset());
|
||||
|
|
|
@ -94,7 +94,7 @@ public class PartHttpMessageWriter extends MultipartWriterSupport implements Htt
|
|||
HttpHeaders headers = new HttpHeaders(part.headers());
|
||||
|
||||
String name = part.name();
|
||||
if (!headers.containsKey(HttpHeaders.CONTENT_DISPOSITION)) {
|
||||
if (!headers.containsHeader(HttpHeaders.CONTENT_DISPOSITION)) {
|
||||
headers.setContentDispositionFormData(name,
|
||||
(part instanceof FilePart filePart ? filePart.filename() : null));
|
||||
}
|
||||
|
|
|
@ -262,7 +262,7 @@ public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConv
|
|||
headers.setContentType(contentTypeToUse);
|
||||
}
|
||||
}
|
||||
if (headers.getContentLength() < 0 && !headers.containsKey(HttpHeaders.TRANSFER_ENCODING)) {
|
||||
if (headers.getContentLength() < 0 && !headers.containsHeader(HttpHeaders.TRANSFER_ENCODING)) {
|
||||
Long contentLength = getContentLength(t, headers.getContentType());
|
||||
if (contentLength != null) {
|
||||
headers.setContentLength(contentLength);
|
||||
|
|
|
@ -645,7 +645,7 @@ public class FormHttpMessageConverter implements HttpMessageConverter<MultiValue
|
|||
|
||||
private void writeHeaders() throws IOException {
|
||||
if (!this.headersWritten) {
|
||||
for (Map.Entry<String, List<String>> entry : this.headers.entrySet()) {
|
||||
for (Map.Entry<String, List<String>> entry : this.headers.headerSet()) {
|
||||
byte[] headerName = getBytes(entry.getKey());
|
||||
for (String headerValueString : entry.getValue()) {
|
||||
byte[] headerValue = getBytes(headerValueString);
|
||||
|
|
|
@ -291,7 +291,7 @@ public class ServletServerHttpRequest implements ServerHttpRequest {
|
|||
writer.flush();
|
||||
|
||||
byte[] bytes = bos.toByteArray();
|
||||
if (bytes.length > 0 && getHeaders().containsKey(HttpHeaders.CONTENT_LENGTH)) {
|
||||
if (bytes.length > 0 && getHeaders().containsHeader(HttpHeaders.CONTENT_LENGTH)) {
|
||||
getHeaders().setContentLength(bytes.length);
|
||||
}
|
||||
|
||||
|
|
|
@ -151,8 +151,8 @@ public class ServletServerHttpResponse implements ServerHttpResponse {
|
|||
private static final long serialVersionUID = 3410708522401046302L;
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return (super.containsKey(key) || (get(key) != null));
|
||||
public boolean containsHeader(String key) {
|
||||
return (super.containsHeader(key) || (get(key) != null));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -169,10 +169,7 @@ public class ServletServerHttpResponse implements ServerHttpResponse {
|
|||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> get(Object key) {
|
||||
Assert.isInstanceOf(String.class, key, "Key must be a String-based header name");
|
||||
|
||||
String headerName = (String) key;
|
||||
public @Nullable List<String> get(String headerName) {
|
||||
if (headerName.equalsIgnoreCase(CONTENT_TYPE)) {
|
||||
// Content-Type is written as an override so don't merge
|
||||
String value = getFirst(headerName);
|
||||
|
@ -185,7 +182,7 @@ public class ServletServerHttpResponse implements ServerHttpResponse {
|
|||
}
|
||||
boolean isEmpty1 = CollectionUtils.isEmpty(values1);
|
||||
|
||||
List<String> values2 = super.get(key);
|
||||
List<String> values2 = super.get(headerName);
|
||||
boolean isEmpty2 = CollectionUtils.isEmpty(values2);
|
||||
|
||||
if (isEmpty1 && isEmpty2) {
|
||||
|
|
|
@ -79,9 +79,24 @@ public abstract class AbstractServerHttpRequest implements ServerHttpRequest {
|
|||
* @param contextPath the context path for the request
|
||||
* @param headers the headers for the request (as {@link MultiValueMap})
|
||||
* @since 6.0.8
|
||||
* @deprecated Use {@link #AbstractServerHttpRequest(HttpMethod, URI, String, HttpHeaders)}
|
||||
*/
|
||||
@Deprecated
|
||||
public AbstractServerHttpRequest(HttpMethod method, URI uri, @Nullable String contextPath,
|
||||
MultiValueMap<String, String> headers) {
|
||||
this(method, uri, contextPath, new HttpHeaders(headers));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with the method, URI and headers for the request.
|
||||
* @param method the HTTP method for the request
|
||||
* @param uri the URI for the request
|
||||
* @param contextPath the context path for the request
|
||||
* @param headers the headers for the request (as {@link MultiValueMap})
|
||||
* @since 7.0
|
||||
*/
|
||||
public AbstractServerHttpRequest(HttpMethod method, URI uri, @Nullable String contextPath,
|
||||
HttpHeaders headers) {
|
||||
|
||||
Assert.notNull(method, "Method must not be null");
|
||||
Assert.notNull(uri, "Uri must not be null");
|
||||
|
|
|
@ -37,6 +37,7 @@ import reactor.netty5.http.server.HttpServerRequest;
|
|||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.Netty5DataBufferFactory;
|
||||
import org.springframework.http.HttpCookie;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpLogging;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.support.Netty5HeadersAdapter;
|
||||
|
@ -70,7 +71,7 @@ class ReactorNetty2ServerHttpRequest extends AbstractServerHttpRequest {
|
|||
throws URISyntaxException {
|
||||
|
||||
super(HttpMethod.valueOf(request.method().name()), initUri(request), "",
|
||||
new Netty5HeadersAdapter(request.requestHeaders()));
|
||||
new HttpHeaders(new Netty5HeadersAdapter(request.requestHeaders())));
|
||||
Assert.notNull(bufferFactory, "DataBufferFactory must not be null");
|
||||
this.request = request;
|
||||
this.bufferFactory = bufferFactory;
|
||||
|
|
|
@ -35,6 +35,7 @@ import reactor.netty.http.server.HttpServerRequest;
|
|||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.NettyDataBufferFactory;
|
||||
import org.springframework.http.HttpCookie;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpLogging;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.support.Netty4HeadersAdapter;
|
||||
|
@ -67,7 +68,7 @@ class ReactorServerHttpRequest extends AbstractServerHttpRequest {
|
|||
|
||||
super(HttpMethod.valueOf(request.method().name()),
|
||||
ReactorUriHelper.createUri(request), request.forwardedPrefix(),
|
||||
new Netty4HeadersAdapter(request.requestHeaders()));
|
||||
new HttpHeaders(new Netty4HeadersAdapter(request.requestHeaders())));
|
||||
Assert.notNull(bufferFactory, "DataBufferFactory must not be null");
|
||||
this.request = request;
|
||||
this.bufferFactory = bufferFactory;
|
||||
|
|
|
@ -24,7 +24,6 @@ import java.nio.ByteBuffer;
|
|||
import java.nio.charset.Charset;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import jakarta.servlet.AsyncContext;
|
||||
|
@ -47,7 +46,6 @@ import org.springframework.http.HttpHeaders;
|
|||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.LinkedCaseInsensitiveMap;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
@ -89,7 +87,7 @@ class ServletServerHttpRequest extends AbstractServerHttpRequest {
|
|||
this(createDefaultHttpHeaders(request), request, asyncContext, servletPath, bufferFactory, bufferSize);
|
||||
}
|
||||
|
||||
public ServletServerHttpRequest(MultiValueMap<String, String> headers, HttpServletRequest request,
|
||||
public ServletServerHttpRequest(HttpHeaders headers, HttpServletRequest request,
|
||||
AsyncContext asyncContext, String servletPath, DataBufferFactory bufferFactory, int bufferSize)
|
||||
throws IOException, URISyntaxException {
|
||||
|
||||
|
@ -112,9 +110,8 @@ class ServletServerHttpRequest extends AbstractServerHttpRequest {
|
|||
}
|
||||
|
||||
|
||||
private static MultiValueMap<String, String> createDefaultHttpHeaders(HttpServletRequest request) {
|
||||
MultiValueMap<String, String> headers =
|
||||
CollectionUtils.toMultiValueMap(new LinkedCaseInsensitiveMap<>(8, Locale.ROOT));
|
||||
private static HttpHeaders createDefaultHttpHeaders(HttpServletRequest request) {
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
for (Enumeration<?> names = request.getHeaderNames(); names.hasMoreElements(); ) {
|
||||
String name = (String) names.nextElement();
|
||||
for (Enumeration<?> values = request.getHeaders(name); values.hasMoreElements(); ) {
|
||||
|
@ -163,8 +160,7 @@ class ServletServerHttpRequest extends AbstractServerHttpRequest {
|
|||
}
|
||||
|
||||
@SuppressWarnings("NullAway")
|
||||
private static MultiValueMap<String, String> initHeaders(
|
||||
MultiValueMap<String, String> headerValues, HttpServletRequest request) {
|
||||
private static HttpHeaders initHeaders(HttpHeaders headerValues, HttpServletRequest request) {
|
||||
|
||||
HttpHeaders headers = null;
|
||||
MediaType contentType = null;
|
||||
|
|
|
@ -34,7 +34,6 @@ import org.apache.coyote.Response;
|
|||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
|
@ -91,13 +90,13 @@ public class TomcatHttpHandlerAdapter extends ServletHttpHandlerAdapter {
|
|||
super(createTomcatHttpHeaders(request), request, context, servletPath, factory, bufferSize);
|
||||
}
|
||||
|
||||
private static MultiValueMap<String, String> createTomcatHttpHeaders(HttpServletRequest request) {
|
||||
private static HttpHeaders createTomcatHttpHeaders(HttpServletRequest request) {
|
||||
RequestFacade requestFacade = getRequestFacade(request);
|
||||
org.apache.catalina.connector.Request connectorRequest = (org.apache.catalina.connector.Request)
|
||||
ReflectionUtils.getField(COYOTE_REQUEST_FIELD, requestFacade);
|
||||
Assert.state(connectorRequest != null, "No Tomcat connector request");
|
||||
Request tomcatRequest = connectorRequest.getCoyoteRequest();
|
||||
return new TomcatHeadersAdapter(tomcatRequest.getMimeHeaders());
|
||||
return new HttpHeaders(new TomcatHeadersAdapter(tomcatRequest.getMimeHeaders()));
|
||||
}
|
||||
|
||||
private static RequestFacade getRequestFacade(HttpServletRequest request) {
|
||||
|
|
|
@ -36,6 +36,7 @@ import reactor.core.publisher.Flux;
|
|||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferFactory;
|
||||
import org.springframework.http.HttpCookie;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
|
@ -65,7 +66,7 @@ class UndertowServerHttpRequest extends AbstractServerHttpRequest {
|
|||
throws URISyntaxException {
|
||||
|
||||
super(HttpMethod.valueOf(exchange.getRequestMethod().toString()), initUri(exchange), "",
|
||||
new UndertowHeadersAdapter(exchange.getRequestHeaders()));
|
||||
new HttpHeaders(new UndertowHeadersAdapter(exchange.getRequestHeaders())));
|
||||
this.exchange = exchange;
|
||||
this.body = new RequestBodyPublisher(exchange, bufferFactory);
|
||||
this.body.registerListeners(exchange);
|
||||
|
|
|
@ -623,10 +623,10 @@ final class DefaultRestClient implements RestClient {
|
|||
|
||||
private @Nullable HttpHeaders initHeaders() {
|
||||
HttpHeaders defaultHeaders = DefaultRestClient.this.defaultHeaders;
|
||||
if (CollectionUtils.isEmpty(this.headers)) {
|
||||
if (this.headers == null || this.headers.isEmpty()) {
|
||||
return defaultHeaders;
|
||||
}
|
||||
else if (CollectionUtils.isEmpty(defaultHeaders)) {
|
||||
else if (defaultHeaders == null || defaultHeaders.isEmpty()) {
|
||||
return this.headers;
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -220,7 +220,7 @@ public class DefaultCorsProcessor implements CorsProcessor {
|
|||
|
||||
private List<String> getHeadersToUse(ServerHttpRequest request, boolean isPreFlight) {
|
||||
HttpHeaders headers = request.getHeaders();
|
||||
return (isPreFlight ? headers.getAccessControlRequestHeaders() : new ArrayList<>(headers.keySet()));
|
||||
return (isPreFlight ? headers.getAccessControlRequestHeaders() : new ArrayList<>(headers.headerNames()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ public abstract class CorsUtils {
|
|||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public static boolean isCorsRequest(ServerHttpRequest request) {
|
||||
return request.getHeaders().containsKey(HttpHeaders.ORIGIN) && !isSameOrigin(request);
|
||||
return request.getHeaders().containsHeader(HttpHeaders.ORIGIN) && !isSameOrigin(request);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -52,8 +52,8 @@ public abstract class CorsUtils {
|
|||
public static boolean isPreFlightRequest(ServerHttpRequest request) {
|
||||
HttpHeaders headers = request.getHeaders();
|
||||
return (request.getMethod() == HttpMethod.OPTIONS
|
||||
&& headers.containsKey(HttpHeaders.ORIGIN)
|
||||
&& headers.containsKey(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD));
|
||||
&& headers.containsHeader(HttpHeaders.ORIGIN)
|
||||
&& headers.containsHeader(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -217,7 +217,7 @@ public class DefaultCorsProcessor implements CorsProcessor {
|
|||
|
||||
private List<String> getHeadersToUse(ServerHttpRequest request, boolean isPreFlight) {
|
||||
HttpHeaders headers = request.getHeaders();
|
||||
return (isPreFlight ? headers.getAccessControlRequestHeaders() : new ArrayList<>(headers.keySet()));
|
||||
return (isPreFlight ? headers.getAccessControlRequestHeaders() : new ArrayList<>(headers.headerNames()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.springframework.web.method.annotation;
|
|||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
|
@ -33,13 +34,14 @@ import org.springframework.web.method.support.HandlerMethodArgumentResolver;
|
|||
import org.springframework.web.method.support.ModelAndViewContainer;
|
||||
|
||||
/**
|
||||
* Resolves {@link Map} method arguments annotated with {@code @RequestHeader}.
|
||||
* For individual header values annotated with {@code @RequestHeader} see
|
||||
* Resolves {@link Map}, {@link MultiValueMap} and {@link HttpHeaders} method
|
||||
* arguments annotated with {@code @RequestHeader}. For individual header values
|
||||
* annotated with {@code @RequestHeader} see
|
||||
* {@link RequestHeaderMethodArgumentResolver} instead.
|
||||
*
|
||||
* <p>The created {@link Map} contains all request header name/value pairs.
|
||||
* The method parameter type may be a {@link MultiValueMap} to receive all
|
||||
* values for a header, not only the first one.
|
||||
* The method parameter type may be a {@link HttpHeaders} or a {@link MultiValueMap}
|
||||
* to receive all values for a header, not only the first one.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Rossen Stoyanchev
|
||||
|
@ -50,7 +52,8 @@ public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgu
|
|||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return (parameter.hasParameterAnnotation(RequestHeader.class) &&
|
||||
Map.class.isAssignableFrom(parameter.getParameterType()));
|
||||
(Map.class.isAssignableFrom(parameter.getParameterType()) ||
|
||||
HttpHeaders.class.isAssignableFrom(parameter.getParameterType())));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -58,23 +61,15 @@ public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgu
|
|||
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
|
||||
|
||||
Class<?> paramType = parameter.getParameterType();
|
||||
if (MultiValueMap.class.isAssignableFrom(paramType)) {
|
||||
MultiValueMap<String, String> result;
|
||||
if (HttpHeaders.class.isAssignableFrom(paramType)) {
|
||||
result = new HttpHeaders();
|
||||
}
|
||||
else {
|
||||
result = new LinkedMultiValueMap<>();
|
||||
}
|
||||
for (Iterator<String> iterator = webRequest.getHeaderNames(); iterator.hasNext();) {
|
||||
String headerName = iterator.next();
|
||||
String[] headerValues = webRequest.getHeaderValues(headerName);
|
||||
if (headerValues != null) {
|
||||
for (String headerValue : headerValues) {
|
||||
result.add(headerName, headerValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (HttpHeaders.class.isAssignableFrom(paramType)) {
|
||||
HttpHeaders result = new HttpHeaders();
|
||||
copyHeaderValues(webRequest, result::add);
|
||||
return result;
|
||||
}
|
||||
else if (MultiValueMap.class.isAssignableFrom(paramType)) {
|
||||
MultiValueMap<Object, Object> result = new LinkedMultiValueMap<>();
|
||||
copyHeaderValues(webRequest, result::add);
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
|
@ -90,4 +85,16 @@ public class RequestHeaderMapMethodArgumentResolver implements HandlerMethodArgu
|
|||
}
|
||||
}
|
||||
|
||||
void copyHeaderValues(NativeWebRequest webRequest, BiConsumer<String, String> consumer) {
|
||||
for (Iterator<String> iterator = webRequest.getHeaderNames(); iterator.hasNext();) {
|
||||
String headerName = iterator.next();
|
||||
String[] headerValues = webRequest.getHeaderValues(headerName);
|
||||
if (headerValues != null) {
|
||||
for (String headerValue : headerValues) {
|
||||
consumer.accept(headerName, headerValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.jspecify.annotations.Nullable;
|
|||
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.MissingRequestHeaderException;
|
||||
import org.springframework.web.bind.ServletRequestBindingException;
|
||||
|
@ -61,7 +62,8 @@ public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueMetho
|
|||
@Override
|
||||
public boolean supportsParameter(MethodParameter parameter) {
|
||||
return (parameter.hasParameterAnnotation(RequestHeader.class) &&
|
||||
!Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType()));
|
||||
!Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) &&
|
||||
!HttpHeaders.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -144,7 +144,7 @@ public class ForwardedHeaderTransformer implements Function<ServerHttpRequest, S
|
|||
protected boolean hasForwardedHeaders(ServerHttpRequest request) {
|
||||
HttpHeaders headers = request.getHeaders();
|
||||
for (String headerName : FORWARDED_HEADER_NAMES) {
|
||||
if (headers.containsKey(headerName)) {
|
||||
if (headers.containsHeader(headerName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ class HttpEntityTests {
|
|||
String body = "foo";
|
||||
HttpEntity<String> entity = new HttpEntity<>(body);
|
||||
assertThat(entity.getBody()).isSameAs(body);
|
||||
assertThat(entity.getHeaders()).isEmpty();
|
||||
assertThat(entity.getHeaders().isEmpty()).as("isEmpty").isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -62,25 +62,25 @@ class HttpEntityTests {
|
|||
|
||||
@Test
|
||||
void testEquals() {
|
||||
MultiValueMap<String, String> map1 = new LinkedMultiValueMap<>();
|
||||
map1.set("Content-Type", "text/plain");
|
||||
HttpHeaders headers1 = new HttpHeaders();
|
||||
headers1.set("Content-Type", "text/plain");
|
||||
|
||||
MultiValueMap<String, String> map2 = new LinkedMultiValueMap<>();
|
||||
map2.set("Content-Type", "application/json");
|
||||
HttpHeaders headers2 = new HttpHeaders();
|
||||
headers2.set("Content-Type", "application/json");
|
||||
|
||||
assertThat(new HttpEntity<>().equals(new HttpEntity<>())).isTrue();
|
||||
assertThat(new HttpEntity<>(map1).equals(new HttpEntity<>())).isFalse();
|
||||
assertThat(new HttpEntity<>().equals(new HttpEntity<>(map2))).isFalse();
|
||||
assertThat(new HttpEntity<>(headers1).equals(new HttpEntity<>())).isFalse();
|
||||
assertThat(new HttpEntity<>().equals(new HttpEntity<>(headers2))).isFalse();
|
||||
|
||||
assertThat(new HttpEntity<>(map1).equals(new HttpEntity<>(map1))).isTrue();
|
||||
assertThat(new HttpEntity<>(map1).equals(new HttpEntity<>(map2))).isFalse();
|
||||
assertThat(new HttpEntity<>(headers1).equals(new HttpEntity<>(headers1))).isTrue();
|
||||
assertThat(new HttpEntity<>(headers1).equals(new HttpEntity<>(headers2))).isFalse();
|
||||
|
||||
assertThat(new HttpEntity<String>(null, null).equals(new HttpEntity<>(null, null))).isTrue();
|
||||
assertThat(new HttpEntity<>("foo", null).equals(new HttpEntity<>(null, null))).isFalse();
|
||||
assertThat(new HttpEntity<String>(null, null).equals(new HttpEntity<>("bar", null))).isFalse();
|
||||
assertThat(new HttpEntity<String>(null, (HttpHeaders) null).equals(new HttpEntity<>(null, (HttpHeaders) null))).isTrue();
|
||||
assertThat(new HttpEntity<>("foo", (HttpHeaders) null).equals(new HttpEntity<>(null, (HttpHeaders) null))).isFalse();
|
||||
assertThat(new HttpEntity<String>(null, (HttpHeaders) null).equals(new HttpEntity<>("bar", (HttpHeaders) null))).isFalse();
|
||||
|
||||
assertThat(new HttpEntity<>("foo", map1).equals(new HttpEntity<>("foo", map1))).isTrue();
|
||||
assertThat(new HttpEntity<>("foo", map1).equals(new HttpEntity<>("bar", map1))).isFalse();
|
||||
assertThat(new HttpEntity<>("foo", headers1).equals(new HttpEntity<>("foo", headers1))).isTrue();
|
||||
assertThat(new HttpEntity<>("foo", headers1).equals(new HttpEntity<>("bar", headers1))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -645,7 +645,7 @@ class HttpHeadersTests {
|
|||
void keySetOperations() {
|
||||
headers.add("Alpha", "apple");
|
||||
headers.add("Bravo", "banana");
|
||||
Set<String> keySet = headers.keySet();
|
||||
Set<String> keySet = headers.headerNames();
|
||||
|
||||
// Please DO NOT simplify the following with AssertJ's fluent API.
|
||||
//
|
||||
|
@ -677,17 +677,17 @@ class HttpHeadersTests {
|
|||
// remove()
|
||||
assertThat(keySet.remove("Alpha")).isTrue();
|
||||
assertThat(keySet).hasSize(1);
|
||||
assertThat(headers).hasSize(1);
|
||||
assertThat(headers.size()).isOne();
|
||||
assertThat(keySet.remove("Alpha")).isFalse();
|
||||
assertThat(keySet).hasSize(1);
|
||||
assertThat(headers).hasSize(1);
|
||||
assertThat(headers.size()).isOne();
|
||||
|
||||
// clear()
|
||||
keySet.clear();
|
||||
assertThat(keySet).isEmpty();
|
||||
assertThat(keySet).isEmpty();
|
||||
assertThat(headers).isEmpty();
|
||||
assertThat(headers).isEmpty();
|
||||
assertThat(headers.isEmpty()).as("isEmpty").isTrue();
|
||||
assertThat(headers.isEmpty()).as("isEmpty").isTrue();
|
||||
|
||||
// Unsupported operations
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class)
|
||||
|
@ -705,10 +705,10 @@ class HttpHeadersTests {
|
|||
// --- Given ---
|
||||
headers.add("Alpha", "apple");
|
||||
headers.add("Bravo", "banana");
|
||||
assertThat(headers).containsOnlyKeys("Alpha", "Bravo");
|
||||
assertThat(headers.headerNames()).containsOnly("Alpha", "Bravo");
|
||||
|
||||
// --- When ---
|
||||
boolean removed = headers.keySet().remove("Alpha");
|
||||
boolean removed = headers.headerNames().remove("Alpha");
|
||||
|
||||
// --- Then ---
|
||||
|
||||
|
@ -718,12 +718,12 @@ class HttpHeadersTests {
|
|||
// the behavior of the entire contract.
|
||||
|
||||
assertThat(removed).isTrue();
|
||||
assertThat(headers.keySet().remove("Alpha")).isFalse();
|
||||
assertThat(headers).hasSize(1);
|
||||
assertThat(headers.containsKey("Alpha")).as("Alpha should have been removed").isFalse();
|
||||
assertThat(headers.containsKey("Bravo")).as("Bravo should be present").isTrue();
|
||||
assertThat(headers.keySet()).containsOnly("Bravo");
|
||||
assertThat(headers.entrySet()).containsOnly(entry("Bravo", List.of("banana")));
|
||||
assertThat(headers.headerNames().remove("Alpha")).isFalse();
|
||||
assertThat(headers.size()).isOne();
|
||||
assertThat(headers.containsHeader("Alpha")).as("Alpha should have been removed").isFalse();
|
||||
assertThat(headers.containsHeader("Bravo")).as("Bravo should be present").isTrue();
|
||||
assertThat(headers.headerNames()).containsOnly("Bravo");
|
||||
assertThat(headers.headerSet()).containsOnly(entry("Bravo", List.of("banana")));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -731,11 +731,11 @@ class HttpHeadersTests {
|
|||
String headerName = "MyHeader";
|
||||
String headerValue = "value";
|
||||
|
||||
assertThat(headers).isEmpty();
|
||||
assertThat(headers.isEmpty()).as("isEmpty").isTrue();
|
||||
headers.add(headerName, headerValue);
|
||||
assertThat(headers.containsKey(headerName)).isTrue();
|
||||
headers.keySet().removeIf(key -> key.equals(headerName));
|
||||
assertThat(headers).isEmpty();
|
||||
assertThat(headers.containsHeader(headerName)).isTrue();
|
||||
headers.headerNames().removeIf(key -> key.equals(headerName));
|
||||
assertThat(headers.isEmpty()).as("isEmpty").isTrue();
|
||||
headers.add(headerName, headerValue);
|
||||
assertThat(headers.get(headerName)).containsExactly(headerValue);
|
||||
}
|
||||
|
@ -745,11 +745,11 @@ class HttpHeadersTests {
|
|||
String headerName = "MyHeader";
|
||||
String headerValue = "value";
|
||||
|
||||
assertThat(headers).isEmpty();
|
||||
assertThat(headers.isEmpty()).as("isEmpty").isTrue();
|
||||
headers.add(headerName, headerValue);
|
||||
assertThat(headers.containsKey(headerName)).isTrue();
|
||||
headers.entrySet().removeIf(entry -> entry.getKey().equals(headerName));
|
||||
assertThat(headers).isEmpty();
|
||||
assertThat(headers.containsHeader(headerName)).isTrue();
|
||||
headers.headerSet().removeIf(entry -> entry.getKey().equals(headerName));
|
||||
assertThat(headers.isEmpty()).as("isEmpty").isTrue();
|
||||
headers.add(headerName, headerValue);
|
||||
assertThat(headers.get(headerName)).containsExactly(headerValue);
|
||||
}
|
||||
|
@ -764,10 +764,10 @@ class HttpHeadersTests {
|
|||
|
||||
String[] expectedKeys = new String[] { "aardvark", "beaver", "cat", "dog", "elephant" };
|
||||
|
||||
assertThat(headers.entrySet()).extracting(Entry::getKey).containsExactly(expectedKeys);
|
||||
assertThat(headers.headerSet()).extracting(Entry::getKey).containsExactly(expectedKeys);
|
||||
|
||||
HttpHeaders readOnlyHttpHeaders = HttpHeaders.readOnlyHttpHeaders(headers);
|
||||
assertThat(readOnlyHttpHeaders.entrySet()).extracting(Entry::getKey).containsExactly(expectedKeys);
|
||||
assertThat(readOnlyHttpHeaders.headerSet()).extracting(Entry::getKey).containsExactly(expectedKeys);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -784,15 +784,15 @@ class HttpHeadersTests {
|
|||
|
||||
HttpHeaders forEachHeaders = new HttpHeaders();
|
||||
readOnlyHttpHeaders.forEach(forEachHeaders::putIfAbsent);
|
||||
assertThat(forEachHeaders.entrySet()).extracting(Entry::getKey).containsExactly(expectedKeys);
|
||||
assertThat(forEachHeaders.headerSet()).extracting(Entry::getKey).containsExactly(expectedKeys);
|
||||
|
||||
HttpHeaders putAllHeaders = new HttpHeaders();
|
||||
putAllHeaders.putAll(readOnlyHttpHeaders);
|
||||
assertThat(putAllHeaders.entrySet()).extracting(Entry::getKey).containsExactly(expectedKeys);
|
||||
assertThat(putAllHeaders.headerSet()).extracting(Entry::getKey).containsExactly(expectedKeys);
|
||||
|
||||
HttpHeaders addAllHeaders = new HttpHeaders();
|
||||
addAllHeaders.addAll(readOnlyHttpHeaders);
|
||||
assertThat(addAllHeaders.entrySet()).extracting(Entry::getKey).containsExactly(expectedKeys);
|
||||
assertThat(addAllHeaders.headerSet()).extracting(Entry::getKey).containsExactly(expectedKeys);
|
||||
}
|
||||
|
||||
@Test // gh-25034
|
||||
|
|
|
@ -52,7 +52,7 @@ class RequestEntityTests {
|
|||
|
||||
assertThat(requestEntity).isNotNull();
|
||||
assertThat(requestEntity.getMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(requestEntity.getHeaders().containsKey(headerName)).isTrue();
|
||||
assertThat(requestEntity.getHeaders().containsHeader(headerName)).isTrue();
|
||||
assertThat(requestEntity.getHeaders().getFirst(headerName)).isEqualTo(headerValue);
|
||||
assertThat(requestEntity.getBody()).isEqualTo(entity);
|
||||
}
|
||||
|
@ -100,7 +100,7 @@ class RequestEntityTests {
|
|||
|
||||
assertThat(requestEntity).isNotNull();
|
||||
assertThat(requestEntity.getMethod()).isEqualTo(HttpMethod.GET);
|
||||
assertThat(requestEntity.getHeaders().containsKey(HttpHeaders.ACCEPT)).isTrue();
|
||||
assertThat(requestEntity.getHeaders().containsHeader(HttpHeaders.ACCEPT)).isTrue();
|
||||
assertThat(requestEntity.getHeaders().getFirst(HttpHeaders.ACCEPT)).isEqualTo("image/gif, image/jpeg, image/png");
|
||||
assertThat(requestEntity.getBody()).isNull();
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ class RequestEntityTests {
|
|||
ifNoneMatch(ifNoneMatch).
|
||||
contentLength(contentLength).
|
||||
contentType(contentType).
|
||||
headers(headers -> assertThat(headers).hasSize(6)).
|
||||
headers(headers -> assertThat(headers.size()).isEqualTo(6)).
|
||||
build();
|
||||
|
||||
assertThat(responseEntity).isNotNull();
|
||||
|
|
|
@ -44,7 +44,7 @@ class ResponseEntityTests {
|
|||
|
||||
assertThat(responseEntity).isNotNull();
|
||||
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(responseEntity.getHeaders().containsKey(headerName)).isTrue();
|
||||
assertThat(responseEntity.getHeaders().containsHeader(headerName)).isTrue();
|
||||
assertThat(responseEntity.getHeaders().get(headerName)).containsExactly(headerValue1, headerValue2);
|
||||
assertThat(responseEntity.getBody()).isEqualTo(entity);
|
||||
}
|
||||
|
@ -113,7 +113,7 @@ class ResponseEntityTests {
|
|||
|
||||
assertThat(responseEntity).isNotNull();
|
||||
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.CREATED);
|
||||
assertThat(responseEntity.getHeaders().containsKey(HttpHeaders.LOCATION)).isTrue();
|
||||
assertThat(responseEntity.getHeaders().containsHeader(HttpHeaders.LOCATION)).isTrue();
|
||||
assertThat(responseEntity.getHeaders().getFirst(HttpHeaders.LOCATION)).isEqualTo(location.toString());
|
||||
assertThat(responseEntity.getBody()).isNull();
|
||||
|
||||
|
@ -195,7 +195,7 @@ class ResponseEntityTests {
|
|||
location(location).
|
||||
contentLength(contentLength).
|
||||
contentType(contentType).
|
||||
headers(headers -> assertThat(headers).hasSize(5)).
|
||||
headers(headers -> assertThat(headers.size()).isEqualTo(5)).
|
||||
build();
|
||||
|
||||
assertThat(responseEntity).isNotNull();
|
||||
|
@ -236,7 +236,7 @@ class ResponseEntityTests {
|
|||
HttpHeaders responseHeaders = responseEntity.getHeaders();
|
||||
|
||||
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(responseHeaders).hasSize(1);
|
||||
assertThat(responseHeaders.size()).isOne();
|
||||
assertThat(responseHeaders.get("X-CustomHeader")).hasSize(1);
|
||||
assertThat(responseHeaders.getFirst("X-CustomHeader")).isEqualTo("vale");
|
||||
|
||||
|
@ -250,7 +250,7 @@ class ResponseEntityTests {
|
|||
ResponseEntity.ok().headers((HttpHeaders) null).build();
|
||||
|
||||
assertThat(responseEntityWithEmptyHeaders.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(responseEntityWithEmptyHeaders.getHeaders()).isEmpty();
|
||||
assertThat(responseEntityWithEmptyHeaders.getHeaders().isEmpty()).isTrue();
|
||||
assertThat(responseEntityWithNullHeaders.toString()).isEqualTo(responseEntityWithEmptyHeaders.toString());
|
||||
}
|
||||
|
||||
|
@ -265,7 +265,7 @@ class ResponseEntityTests {
|
|||
|
||||
assertThat(responseEntity).isNotNull();
|
||||
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(responseEntity.getHeaders().containsKey(HttpHeaders.CACHE_CONTROL)).isFalse();
|
||||
assertThat(responseEntity.getHeaders().containsHeader(HttpHeaders.CACHE_CONTROL)).isFalse();
|
||||
assertThat(responseEntity.getBody()).isEqualTo(entity);
|
||||
}
|
||||
|
||||
|
@ -281,7 +281,7 @@ class ResponseEntityTests {
|
|||
|
||||
assertThat(responseEntity).isNotNull();
|
||||
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(responseEntity.getHeaders().containsKey(HttpHeaders.CACHE_CONTROL)).isTrue();
|
||||
assertThat(responseEntity.getHeaders().containsHeader(HttpHeaders.CACHE_CONTROL)).isTrue();
|
||||
assertThat(responseEntity.getBody()).isEqualTo(entity);
|
||||
String cacheControlHeader = responseEntity.getHeaders().getCacheControl();
|
||||
assertThat(cacheControlHeader).isEqualTo(
|
||||
|
@ -299,7 +299,7 @@ class ResponseEntityTests {
|
|||
|
||||
assertThat(responseEntity).isNotNull();
|
||||
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(responseEntity.getHeaders().containsKey(HttpHeaders.CACHE_CONTROL)).isTrue();
|
||||
assertThat(responseEntity.getHeaders().containsHeader(HttpHeaders.CACHE_CONTROL)).isTrue();
|
||||
assertThat(responseEntity.getBody()).isEqualTo(entity);
|
||||
|
||||
String cacheControlHeader = responseEntity.getHeaders().getCacheControl();
|
||||
|
|
|
@ -98,9 +98,11 @@ abstract class AbstractHttpRequestFactoryTests extends AbstractMockWebServerTest
|
|||
|
||||
try (ClientHttpResponse response = request.execute()) {
|
||||
assertThat(response.getStatusCode()).as("Invalid status code").isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getHeaders()).as("Header not found").containsKey(headerName);
|
||||
assertThat(response.getHeaders()).as("Header value not found")
|
||||
.containsEntry(headerName, Arrays.asList(headerValue1, headerValue2));
|
||||
assertThat(response.getHeaders().containsHeader(headerName))
|
||||
.as("Header not found").isTrue();
|
||||
assertThat(response.getHeaders().hasHeaderValues(headerName, Arrays.asList(headerValue1, headerValue2)))
|
||||
.as("Header value not found")
|
||||
.isTrue();
|
||||
byte[] result = FileCopyUtils.copyToByteArray(response.getBody());
|
||||
assertThat(Arrays.equals(body, result)).as("Invalid body").isTrue();
|
||||
}
|
||||
|
|
|
@ -51,8 +51,8 @@ class BufferingClientHttpRequestFactoryTests extends AbstractHttpRequestFactoryT
|
|||
assertThat(response.getStatusCode()).as("Invalid status code").isEqualTo(HttpStatus.OK);
|
||||
assertThat(response.getStatusCode()).as("Invalid status code").isEqualTo(HttpStatus.OK);
|
||||
|
||||
assertThat(response.getHeaders().containsKey(headerName)).as("Header not found").isTrue();
|
||||
assertThat(response.getHeaders().containsKey(headerName)).as("Header not found").isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(headerName)).as("Header not found").isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(headerName)).as("Header not found").isTrue();
|
||||
|
||||
assertThat(response.getHeaders().get(headerName)).as("Header value not found").isEqualTo(Arrays.asList(headerValue1, headerValue2));
|
||||
assertThat(response.getHeaders().get(headerName)).as("Header value not found").isEqualTo(Arrays.asList(headerValue1, headerValue2));
|
||||
|
|
|
@ -103,7 +103,7 @@ class DefaultPartHttpMessageReaderTests {
|
|||
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(part -> {
|
||||
assertThat(part.headers()).isEmpty();
|
||||
assertThat(part.headers().isEmpty()).isTrue();
|
||||
part.content().subscribe(DataBufferUtils::release);
|
||||
})
|
||||
.verifyComplete();
|
||||
|
@ -266,7 +266,7 @@ class DefaultPartHttpMessageReaderTests {
|
|||
CountDownLatch latch = new CountDownLatch(1);
|
||||
StepVerifier.create(result)
|
||||
.consumeNextWith(part -> {
|
||||
assertThat(part.headers()).containsEntry("Føø", Collections.singletonList("Bår"));
|
||||
assertThat(part.headers().hasHeaderValues("Føø", Collections.singletonList("Bår"))).isTrue();
|
||||
testPart(part, null, "This is plain ASCII text.", latch);
|
||||
})
|
||||
.verifyComplete();
|
||||
|
|
|
@ -175,7 +175,7 @@ class MultipartHttpMessageWriterTests extends AbstractLeakCheckingTests {
|
|||
part = requestParts.getFirst("filePublisher");
|
||||
assertThat(part).isNotNull();
|
||||
assertThat(part.name()).isEqualTo("filePublisher");
|
||||
assertThat(part.headers()).containsEntry("foo", Collections.singletonList("bar"));
|
||||
assertThat(part.headers().hasHeaderValues("foo", Collections.singletonList("bar"))).isTrue();
|
||||
assertThat(((FilePart) part).filename()).isEqualTo("file.txt");
|
||||
value = decodeToString(part);
|
||||
assertThat(value).isEqualTo("AaBbCc");
|
||||
|
|
|
@ -42,7 +42,6 @@ import static java.nio.charset.StandardCharsets.UTF_8;
|
|||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.singletonMap;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.entry;
|
||||
import static org.springframework.core.ResolvableType.forClass;
|
||||
|
||||
/**
|
||||
|
@ -72,7 +71,7 @@ class PartEventHttpMessageReaderTests {
|
|||
Flux<PartEvent> result = this.reader.read(forClass(PartEvent.class), request, emptyMap());
|
||||
|
||||
StepVerifier.create(result)
|
||||
.assertNext(form(headers -> assertThat(headers).isEmpty(), "This is implicitly typed plain ASCII text.\r\nIt does NOT end with a linebreak."))
|
||||
.assertNext(form(headers -> assertThat(headers.isEmpty()).isTrue(), "This is implicitly typed plain ASCII text.\r\nIt does NOT end with a linebreak."))
|
||||
.assertNext(form(headers -> assertThat(headers.getContentType()).isEqualTo(TEXT_PLAIN_ASCII),
|
||||
"This is explicitly typed plain ASCII text.\r\nIt DOES end with a linebreak.\r\n"))
|
||||
.verifyComplete();
|
||||
|
@ -85,7 +84,7 @@ class PartEventHttpMessageReaderTests {
|
|||
Flux<PartEvent> result = this.reader.read(forClass(PartEvent.class), request, emptyMap());
|
||||
|
||||
StepVerifier.create(result)
|
||||
.assertNext(data(headers -> assertThat(headers).isEmpty(), bodyText("a"), true))
|
||||
.assertNext(data(headers -> assertThat(headers.isEmpty()).isTrue(), bodyText("a"), true))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
|
@ -143,8 +142,8 @@ class PartEventHttpMessageReaderTests {
|
|||
Flux<PartEvent> result = this.reader.read(forClass(PartEvent.class), request, emptyMap());
|
||||
|
||||
StepVerifier.create(result)
|
||||
.assertNext(form(headers -> assertThat(headers).contains(entry("Part", List.of("1"))), ""))
|
||||
.assertNext(data(headers -> assertThat(headers).contains(entry("Part", List.of("2"))), bodyText("a"), true))
|
||||
.assertNext(form(headers -> assertThat(headers.hasHeaderValues("Part", List.of("1"))).isTrue(), ""))
|
||||
.assertNext(data(headers -> assertThat(headers.hasHeaderValues("Part", List.of("2"))).isTrue(), bodyText("a"), true))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
|
@ -156,7 +155,7 @@ class PartEventHttpMessageReaderTests {
|
|||
Flux<PartEvent> result = this.reader.read(forClass(PartEvent.class), request, emptyMap());
|
||||
|
||||
StepVerifier.create(result, 3)
|
||||
.assertNext(form(headers -> assertThat(headers).isEmpty(),
|
||||
.assertNext(form(headers -> assertThat(headers.isEmpty()).isTrue(),
|
||||
"This is implicitly typed plain ASCII text.\r\nIt does NOT end with a linebreak."))
|
||||
.thenCancel()
|
||||
.verify();
|
||||
|
@ -238,7 +237,7 @@ class PartEventHttpMessageReaderTests {
|
|||
Flux<PartEvent> result = reader.read(forClass(PartEvent.class), request, emptyMap());
|
||||
|
||||
StepVerifier.create(result)
|
||||
.assertNext(form(headers -> assertThat(headers).isEmpty(), "This is implicitly typed plain ASCII text.\r\nIt does NOT end with a linebreak."))
|
||||
.assertNext(form(headers -> assertThat(headers.isEmpty()).isTrue(), "This is implicitly typed plain ASCII text.\r\nIt does NOT end with a linebreak."))
|
||||
.expectError(DecodingException.class)
|
||||
.verify();
|
||||
}
|
||||
|
@ -283,7 +282,7 @@ class PartEventHttpMessageReaderTests {
|
|||
Flux<PartEvent> result = this.reader.read(forClass(PartEvent.class), request, emptyMap());
|
||||
|
||||
StepVerifier.create(result)
|
||||
.assertNext(data(headers -> assertThat(headers).containsEntry("Føø", List.of("Bår")),
|
||||
.assertNext(data(headers -> assertThat(headers.hasHeaderValues("Føø", List.of("Bår"))).isTrue(),
|
||||
bodyText("This is plain ASCII text."), true))
|
||||
.verifyComplete();
|
||||
}
|
||||
|
|
|
@ -129,7 +129,7 @@ class ServletServerHttpRequestTests {
|
|||
|
||||
HttpHeaders headers = request.getHeaders();
|
||||
assertThat(headers).as("No HttpHeaders returned").isNotNull();
|
||||
assertThat(headers.containsKey(headerName)).as("Invalid headers returned").isTrue();
|
||||
assertThat(headers.containsHeader(headerName)).as("Invalid headers returned").isTrue();
|
||||
List<String> headerValues = headers.get(headerName);
|
||||
assertThat(headerValues).as("No header values returned").isNotNull();
|
||||
assertThat(headerValues.size()).as("Invalid header values returned").isEqualTo(2);
|
||||
|
@ -150,7 +150,7 @@ class ServletServerHttpRequestTests {
|
|||
|
||||
HttpHeaders headers = request.getHeaders();
|
||||
assertThat(headers).as("No HttpHeaders returned").isNotNull();
|
||||
assertThat(headers.containsKey(headerName)).as("Invalid headers returned").isTrue();
|
||||
assertThat(headers.containsHeader(headerName)).as("Invalid headers returned").isTrue();
|
||||
List<String> headerValues = headers.get(headerName);
|
||||
assertThat(headerValues.size()).as("Invalid header values returned").isEqualTo(2);
|
||||
assertThat(headerValues.contains(headerValue1)).as("Invalid header values returned").isTrue();
|
||||
|
@ -162,7 +162,7 @@ class ServletServerHttpRequestTests {
|
|||
void getHeadersWithWildcardContentType() {
|
||||
mockRequest.setContentType("*/*");
|
||||
mockRequest.removeHeader("Content-Type");
|
||||
assertThat(request.getHeaders()).as("Invalid content-type should not raise exception").isEmpty();
|
||||
assertThat(request.getHeaders().isEmpty()).as("Invalid content-type should not raise exception").isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -99,7 +99,7 @@ class ServletServerHttpResponseTests {
|
|||
|
||||
assertThat(this.response.getHeaders().getFirst(headerName)).isEqualTo(headerValue);
|
||||
assertThat(this.response.getHeaders().get(headerName)).containsExactly(headerValue);
|
||||
assertThat(this.response.getHeaders()).containsKey(headerName);
|
||||
assertThat(this.response.getHeaders().containsHeader(headerName)).isTrue();
|
||||
assertThat(this.response.getHeaders().getAccessControlAllowOrigin()).isEqualTo(headerValue);
|
||||
}
|
||||
|
||||
|
|
|
@ -117,7 +117,7 @@ class HeadersAdaptersTests {
|
|||
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.headerNames()).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));
|
||||
}
|
||||
|
@ -132,7 +132,7 @@ class HeadersAdaptersTests {
|
|||
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")
|
||||
assertThat(headers2.asMultiValueMap()).as("two keys")
|
||||
.containsKey("testheader")
|
||||
.containsKey("secondheader")
|
||||
.hasSize(2);
|
||||
|
@ -148,7 +148,10 @@ class HeadersAdaptersTests {
|
|||
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.asMultiValueMap()).as("two keys")
|
||||
.containsKey("testheader")
|
||||
.containsKey("secondheader")
|
||||
.hasSize(2);
|
||||
assertThat(headers2.toString()).as("similar toString, no 'with native headers' dump")
|
||||
.isEqualToIgnoringCase(headers.toString().substring(0, headers.toString().indexOf(']') + 1));
|
||||
}
|
||||
|
|
|
@ -110,7 +110,7 @@ class ServerHttpResponseTests {
|
|||
assertThat(response.statusCodeWritten).isFalse();
|
||||
assertThat(response.headersWritten).isFalse();
|
||||
assertThat(response.cookiesWritten).isFalse();
|
||||
assertThat(headers).doesNotContainKeys(HttpHeaders.CONTENT_TYPE, HttpHeaders.CONTENT_LENGTH,
|
||||
assertThat(headers.headerNames()).doesNotContain(HttpHeaders.CONTENT_TYPE, HttpHeaders.CONTENT_LENGTH,
|
||||
HttpHeaders.CONTENT_ENCODING);
|
||||
assertThat(response.body).isEmpty();
|
||||
}
|
||||
|
@ -178,7 +178,7 @@ class ServerHttpResponseTests {
|
|||
assertThat(response.headersWritten).isFalse();
|
||||
assertThat(response.cookiesWritten).isFalse();
|
||||
assertThat(response.isCommitted()).isFalse();
|
||||
assertThat(response.getHeaders()).isEmpty();
|
||||
assertThat(response.getHeaders().isEmpty()).isTrue();
|
||||
|
||||
// Handle the error
|
||||
response.setStatusCode(HttpStatus.SERVICE_UNAVAILABLE);
|
||||
|
|
|
@ -104,7 +104,7 @@ class ErrorResponseExceptionTests {
|
|||
assertDetail(ex, "Could not parse Content-Type.");
|
||||
assertDetailMessageCode(ex, "parseError", null);
|
||||
|
||||
assertThat(ex.getHeaders()).isEmpty();
|
||||
assertThat(ex.getHeaders().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -117,7 +117,7 @@ class ErrorResponseExceptionTests {
|
|||
assertDetail(ex, "Acceptable representations: [application/json, application/cbor].");
|
||||
assertDetailMessageCode(ex, null, new Object[] {ex.getSupportedMediaTypes()});
|
||||
|
||||
assertThat(ex.getHeaders()).hasSize(1);
|
||||
assertThat(ex.getHeaders().size()).isOne();
|
||||
assertThat(ex.getHeaders().getAccept()).isEqualTo(mediaTypes);
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@ class ErrorResponseExceptionTests {
|
|||
assertDetail(ex, "Could not parse Accept header.");
|
||||
assertDetailMessageCode(ex, "parseError", null);
|
||||
|
||||
assertThat(ex.getHeaders()).isEmpty();
|
||||
assertThat(ex.getHeaders().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -143,7 +143,7 @@ class ErrorResponseExceptionTests {
|
|||
assertStatus(ex, HttpStatus.SERVICE_UNAVAILABLE);
|
||||
assertDetail(ex, null);
|
||||
|
||||
assertThat(ex.getHeaders()).isEmpty();
|
||||
assertThat(ex.getHeaders().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -156,7 +156,7 @@ class ErrorResponseExceptionTests {
|
|||
assertDetail(ex, "Method 'PUT' is not supported.");
|
||||
assertDetailMessageCode(ex, null, new Object[] {ex.getMethod(), ex.getSupportedHttpMethods()});
|
||||
|
||||
assertThat(ex.getHeaders()).hasSize(1);
|
||||
assertThat(ex.getHeaders().size()).isOne();
|
||||
assertThat(ex.getHeaders().getAllow()).containsExactly(HttpMethod.GET, HttpMethod.POST);
|
||||
}
|
||||
|
||||
|
@ -169,7 +169,7 @@ class ErrorResponseExceptionTests {
|
|||
assertDetail(ex, "Required header 'Authorization' is not present.");
|
||||
assertDetailMessageCode(ex, null, new Object[] {ex.getHeaderName()});
|
||||
|
||||
assertThat(ex.getHeaders()).isEmpty();
|
||||
assertThat(ex.getHeaders().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -181,7 +181,7 @@ class ErrorResponseExceptionTests {
|
|||
assertDetail(ex, "Required parameter 'query' is not present.");
|
||||
assertDetailMessageCode(ex, null, new Object[] {ex.getParameterName()});
|
||||
|
||||
assertThat(ex.getHeaders()).isEmpty();
|
||||
assertThat(ex.getHeaders().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -194,7 +194,7 @@ class ErrorResponseExceptionTests {
|
|||
assertDetail(ex, "Required path parameter 'region' is not present.");
|
||||
assertDetailMessageCode(ex, null, new Object[] {ex.getVariableName()});
|
||||
|
||||
assertThat(ex.getHeaders()).isEmpty();
|
||||
assertThat(ex.getHeaders().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -206,7 +206,7 @@ class ErrorResponseExceptionTests {
|
|||
assertDetail(ex, "Required path variable 'id' is not present.");
|
||||
assertDetailMessageCode(ex, null, new Object[] {ex.getVariableName()});
|
||||
|
||||
assertThat(ex.getHeaders()).isEmpty();
|
||||
assertThat(ex.getHeaders().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -218,7 +218,7 @@ class ErrorResponseExceptionTests {
|
|||
assertDetail(ex, "Required cookie 'oreo' is not present.");
|
||||
assertDetailMessageCode(ex, null, new Object[] {ex.getCookieName()});
|
||||
|
||||
assertThat(ex.getHeaders()).isEmpty();
|
||||
assertThat(ex.getHeaders().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -231,7 +231,7 @@ class ErrorResponseExceptionTests {
|
|||
assertDetail(ex, "Invalid request parameters.");
|
||||
assertDetailMessageCode(ex, null, new Object[] {List.of("\"foo=bar, bar=baz\"")});
|
||||
|
||||
assertThat(ex.getHeaders()).isEmpty();
|
||||
assertThat(ex.getHeaders().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -243,7 +243,7 @@ class ErrorResponseExceptionTests {
|
|||
assertDetail(ex, "Required part 'file' is not present.");
|
||||
assertDetailMessageCode(ex, null, new Object[] {ex.getRequestPartName()});
|
||||
|
||||
assertThat(ex.getHeaders()).isEmpty();
|
||||
assertThat(ex.getHeaders().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -258,7 +258,7 @@ class ErrorResponseExceptionTests {
|
|||
assertDetail(ex, "Invalid request content.");
|
||||
testHelper.assertMessages(ex, ex.getAllErrors());
|
||||
|
||||
assertThat(ex.getHeaders()).isEmpty();
|
||||
assertThat(ex.getHeaders().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -306,7 +306,7 @@ class ErrorResponseExceptionTests {
|
|||
assertDetail(ex, "Could not parse Content-Type.");
|
||||
assertDetailMessageCode(ex, "parseError", null);
|
||||
|
||||
assertThat(ex.getHeaders()).isEmpty();
|
||||
assertThat(ex.getHeaders().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -319,7 +319,7 @@ class ErrorResponseExceptionTests {
|
|||
assertDetail(ex, "Acceptable representations: [application/json, application/cbor].");
|
||||
assertDetailMessageCode(ex, null, new Object[] {ex.getSupportedMediaTypes()});
|
||||
|
||||
assertThat(ex.getHeaders()).hasSize(1);
|
||||
assertThat(ex.getHeaders().size()).isOne();
|
||||
assertThat(ex.getHeaders().getAccept()).isEqualTo(mediaTypes);
|
||||
}
|
||||
|
||||
|
@ -333,7 +333,7 @@ class ErrorResponseExceptionTests {
|
|||
assertDetail(ex, "Could not parse Accept header.");
|
||||
assertDetailMessageCode(ex, "parseError", null);
|
||||
|
||||
assertThat(ex.getHeaders()).isEmpty();
|
||||
assertThat(ex.getHeaders().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -345,7 +345,7 @@ class ErrorResponseExceptionTests {
|
|||
assertDetail(ex, "Failure");
|
||||
assertDetailMessageCode(ex, null, new Object[] {ex.getReason()});
|
||||
|
||||
assertThat(ex.getHeaders()).isEmpty();
|
||||
assertThat(ex.getHeaders().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -358,7 +358,7 @@ class ErrorResponseExceptionTests {
|
|||
assertDetail(ex, "Required header 'foo' is not present.");
|
||||
assertDetailMessageCode(ex, null, new Object[] {ex.getLabel(), ex.getName()});
|
||||
|
||||
assertThat(ex.getHeaders()).isEmpty();
|
||||
assertThat(ex.getHeaders().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -373,7 +373,7 @@ class ErrorResponseExceptionTests {
|
|||
assertDetail(ex, "Invalid request parameters.");
|
||||
assertDetailMessageCode(ex, null, new Object[] {ex.getConditions()});
|
||||
|
||||
assertThat(ex.getHeaders()).isEmpty();
|
||||
assertThat(ex.getHeaders().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -388,7 +388,7 @@ class ErrorResponseExceptionTests {
|
|||
assertDetail(ex, "Invalid request content.");
|
||||
testHelper.assertMessages(ex, ex.getAllErrors());
|
||||
|
||||
assertThat(ex.getHeaders()).isEmpty();
|
||||
assertThat(ex.getHeaders().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -401,7 +401,7 @@ class ErrorResponseExceptionTests {
|
|||
assertDetail(ex, "Supported methods: [GET, POST]");
|
||||
assertDetailMessageCode(ex, null, new Object[] {ex.getHttpMethod(), supportedMethods});
|
||||
|
||||
assertThat(ex.getHeaders()).hasSize(1);
|
||||
assertThat(ex.getHeaders().size()).isOne();
|
||||
assertThat(ex.getHeaders().getAllow()).containsExactly(HttpMethod.GET, HttpMethod.POST);
|
||||
}
|
||||
|
||||
|
@ -414,7 +414,7 @@ class ErrorResponseExceptionTests {
|
|||
assertDetail(ex, "Request method 'PUT' is not supported.");
|
||||
assertDetailMessageCode(ex, null, new Object[] {ex.getHttpMethod(), Collections.emptyList()});
|
||||
|
||||
assertThat(ex.getHeaders()).isEmpty();
|
||||
assertThat(ex.getHeaders().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test // gh-30300
|
||||
|
|
|
@ -36,7 +36,7 @@ class ErrorResponseTests {
|
|||
void createWithHttpHeader() {
|
||||
ErrorResponse response = ErrorResponse.builder(new IllegalStateException(), HttpStatus.BAD_REQUEST, "test")
|
||||
.header("header", "value").build();
|
||||
assertThat(response.getHeaders()).containsOnly(entry("header", List.of("value")));
|
||||
assertThat(response.getHeaders().asMultiValueMap()).containsOnly(entry("header", List.of("value")));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -47,7 +47,7 @@ class ErrorResponseTests {
|
|||
headers.add("header", "value2");
|
||||
headers.add("another", "value3");
|
||||
}).build();
|
||||
assertThat(response.getHeaders()).containsOnly(entry("header", List.of("value", "value2")),
|
||||
assertThat(response.getHeaders().asMultiValueMap()).containsOnly(entry("header", List.of("value", "value2")),
|
||||
entry("another", List.of("value3")));
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ class RestClientResponseExceptionTests {
|
|||
|
||||
HttpHeaders responseHeaders = ex.getResponseHeaders();
|
||||
assertThat(responseHeaders).isNotNull();
|
||||
assertThat(responseHeaders).hasSize(1);
|
||||
assertThat(responseHeaders.size()).isOne();
|
||||
assertThat(responseHeaders.getFirst("FOO")).isEqualTo("bar");
|
||||
}
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ class DefaultCorsProcessorTests {
|
|||
this.processor.process(this.conf, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isFalse();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isFalse();
|
||||
assertThat(response.getHeaders().get(VARY)).contains(ORIGIN,
|
||||
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS);
|
||||
assertThat(response.getStatusCode()).isNull();
|
||||
|
@ -81,7 +81,7 @@ class DefaultCorsProcessorTests {
|
|||
this.processor.process(this.conf, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isFalse();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isFalse();
|
||||
assertThat(response.getHeaders().get(VARY)).contains(ORIGIN,
|
||||
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS);
|
||||
assertThat(response.getStatusCode()).isNull();
|
||||
|
@ -93,7 +93,7 @@ class DefaultCorsProcessorTests {
|
|||
this.processor.process(this.conf, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isFalse();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isFalse();
|
||||
assertThat(response.getHeaders().get(VARY)).contains(ORIGIN,
|
||||
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
|
||||
|
@ -105,7 +105,7 @@ class DefaultCorsProcessorTests {
|
|||
this.processor.process(null, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isFalse();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isFalse();
|
||||
assertThat(response.getStatusCode()).isNull();
|
||||
}
|
||||
|
||||
|
@ -116,10 +116,10 @@ class DefaultCorsProcessorTests {
|
|||
this.processor.process(this.conf, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_ORIGIN)).isEqualTo("*");
|
||||
assertThat(response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_MAX_AGE)).isFalse();
|
||||
assertThat(response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS)).isFalse();
|
||||
assertThat(response.getHeaders().containsHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE)).isFalse();
|
||||
assertThat(response.getHeaders().containsHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS)).isFalse();
|
||||
assertThat(response.getHeaders().get(VARY)).contains(ORIGIN,
|
||||
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS);
|
||||
assertThat(response.getStatusCode()).isNull();
|
||||
|
@ -135,9 +135,9 @@ class DefaultCorsProcessorTests {
|
|||
this.processor.process(this.conf, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_ORIGIN)).isEqualTo("https://domain2.com");
|
||||
assertThat(response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)).isTrue();
|
||||
assertThat(response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)).isEqualTo("true");
|
||||
assertThat(response.getHeaders().get(VARY)).contains(ORIGIN,
|
||||
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS);
|
||||
|
@ -156,9 +156,9 @@ class DefaultCorsProcessorTests {
|
|||
this.processor.process(this.conf, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_ORIGIN)).isEqualTo("https://domain2.com");
|
||||
assertThat(response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)).isTrue();
|
||||
assertThat(response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)).isEqualTo("true");
|
||||
assertThat(response.getHeaders().get(VARY)).contains(ORIGIN,
|
||||
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS);
|
||||
|
@ -173,7 +173,7 @@ class DefaultCorsProcessorTests {
|
|||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getStatusCode()).isNull();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
}
|
||||
|
||||
@Test // gh-26892
|
||||
|
@ -187,7 +187,7 @@ class DefaultCorsProcessorTests {
|
|||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getStatusCode()).isNull();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
}
|
||||
|
||||
@Test // gh-33682
|
||||
|
@ -202,7 +202,7 @@ class DefaultCorsProcessorTests {
|
|||
|
||||
assertThat(result).isFalse();
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isFalse();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -214,9 +214,9 @@ class DefaultCorsProcessorTests {
|
|||
this.processor.process(this.conf, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_ORIGIN)).isEqualTo("https://domain2.com");
|
||||
assertThat(response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS)).isTrue();
|
||||
assertThat(response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS)).contains("header1");
|
||||
assertThat(response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS)).contains("header2");
|
||||
assertThat(response.getHeaders().get(VARY)).contains(ORIGIN,
|
||||
|
@ -271,7 +271,7 @@ class DefaultCorsProcessorTests {
|
|||
this.processor.process(this.conf, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isFalse();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isFalse();
|
||||
assertThat(response.getHeaders().get(VARY)).contains(ORIGIN,
|
||||
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
|
||||
|
@ -284,7 +284,7 @@ class DefaultCorsProcessorTests {
|
|||
this.processor.process(this.conf, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isFalse();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isFalse();
|
||||
assertThat(response.getHeaders().get(VARY)).contains(ORIGIN,
|
||||
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
|
||||
|
@ -299,7 +299,7 @@ class DefaultCorsProcessorTests {
|
|||
this.processor.process(this.conf, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isFalse();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isFalse();
|
||||
assertThat(response.getHeaders().get(VARY)).contains(ORIGIN,
|
||||
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS);
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
|
||||
|
@ -320,11 +320,11 @@ class DefaultCorsProcessorTests {
|
|||
this.processor.process(this.conf, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_ORIGIN)).isEqualTo("*");
|
||||
assertThat(response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)).isTrue();
|
||||
assertThat(response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS)).isEqualTo("GET,PUT");
|
||||
assertThat(response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_MAX_AGE)).isFalse();
|
||||
assertThat(response.getHeaders().containsHeader(HttpHeaders.ACCESS_CONTROL_MAX_AGE)).isFalse();
|
||||
assertThat(response.getHeaders().get(VARY)).contains(ORIGIN,
|
||||
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS);
|
||||
assertThat(response.getStatusCode()).isNull();
|
||||
|
@ -345,9 +345,9 @@ class DefaultCorsProcessorTests {
|
|||
this.processor.process(this.conf, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_ORIGIN)).isEqualTo("https://domain2.com");
|
||||
assertThat(response.getHeaders().containsKey(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)).isTrue();
|
||||
assertThat(response.getHeaders().getFirst(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS)).isEqualTo("true");
|
||||
assertThat(response.getHeaders().get(VARY)).contains(ORIGIN,
|
||||
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS);
|
||||
|
@ -372,7 +372,7 @@ class DefaultCorsProcessorTests {
|
|||
this.processor.process(this.conf, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_ORIGIN)).isEqualTo("https://domain2.com");
|
||||
assertThat(response.getHeaders().get(VARY)).contains(ORIGIN,
|
||||
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS);
|
||||
|
@ -398,8 +398,8 @@ class DefaultCorsProcessorTests {
|
|||
this.processor.process(this.conf, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsKey(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isTrue();
|
||||
assertThat(response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_ORIGIN)).isEqualTo("https://domain2.com");
|
||||
assertThat(response.getHeaders().get(VARY)).contains(ORIGIN,
|
||||
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS);
|
||||
|
@ -420,8 +420,8 @@ class DefaultCorsProcessorTests {
|
|||
this.processor.process(this.conf, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_HEADERS)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_HEADERS)).isTrue();
|
||||
assertThat(response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_HEADERS)).contains("Header1");
|
||||
assertThat(response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_HEADERS)).contains("Header2");
|
||||
assertThat(response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_HEADERS)).doesNotContain("Header3");
|
||||
|
@ -442,8 +442,8 @@ class DefaultCorsProcessorTests {
|
|||
this.processor.process(this.conf, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_HEADERS)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_HEADERS)).isTrue();
|
||||
assertThat(response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_HEADERS)).contains("Header1");
|
||||
assertThat(response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_HEADERS)).contains("Header2");
|
||||
assertThat(response.getHeaders().getFirst(ACCESS_CONTROL_ALLOW_HEADERS)).doesNotContain("*");
|
||||
|
@ -464,8 +464,8 @@ class DefaultCorsProcessorTests {
|
|||
this.processor.process(this.conf, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_HEADERS)).isFalse();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_HEADERS)).isFalse();
|
||||
assertThat(response.getHeaders().get(VARY)).contains(ORIGIN,
|
||||
ACCESS_CONTROL_REQUEST_METHOD, ACCESS_CONTROL_REQUEST_HEADERS);
|
||||
assertThat(response.getStatusCode()).isNull();
|
||||
|
@ -479,7 +479,7 @@ class DefaultCorsProcessorTests {
|
|||
this.processor.process(null, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isFalse();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isFalse();
|
||||
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.FORBIDDEN);
|
||||
}
|
||||
|
||||
|
@ -513,8 +513,8 @@ class DefaultCorsProcessorTests {
|
|||
this.processor.process(this.conf, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsKey(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isFalse();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isFalse();
|
||||
assertThat(response.getStatusCode()).isNull();
|
||||
}
|
||||
|
||||
|
@ -530,8 +530,8 @@ class DefaultCorsProcessorTests {
|
|||
this.processor.process(this.conf, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsKey(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isFalse();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isFalse();
|
||||
assertThat(response.getStatusCode()).isNull();
|
||||
}
|
||||
|
||||
|
@ -548,8 +548,8 @@ class DefaultCorsProcessorTests {
|
|||
this.processor.process(this.conf, exchange);
|
||||
|
||||
ServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().containsKey(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsKey(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(ACCESS_CONTROL_ALLOW_ORIGIN)).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader(DefaultCorsProcessor.ACCESS_CONTROL_ALLOW_PRIVATE_NETWORK)).isTrue();
|
||||
assertThat(response.getStatusCode()).isNull();
|
||||
}
|
||||
|
||||
|
|
|
@ -240,7 +240,7 @@ class ForwardedHeaderTransformerTests {
|
|||
|
||||
private void assertForwardedHeadersRemoved(ServerHttpRequest request) {
|
||||
ForwardedHeaderTransformer.FORWARDED_HEADER_NAMES
|
||||
.forEach(name -> assertThat(request.getHeaders().containsKey(name)).isFalse());
|
||||
.forEach(name -> assertThat(request.getHeaders().containsHeader(name)).isFalse());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -170,7 +170,8 @@ class KotlinSerializationCborHttpMessageConverterTests {
|
|||
|
||||
this.converter.write(serializableBean, null, outputMessage)
|
||||
|
||||
assertThat(outputMessage.headers).containsEntry("Content-Type", listOf("application/cbor"))
|
||||
@Suppress("DEPRECATION")
|
||||
assertThat(outputMessage.headers.asMultiValueMap()).containsEntry("Content-Type", listOf("application/cbor"))
|
||||
assertThat(outputMessage.bodyAsBytes.isNotEmpty()).isTrue()
|
||||
}
|
||||
|
||||
|
@ -181,7 +182,8 @@ class KotlinSerializationCborHttpMessageConverterTests {
|
|||
|
||||
this.converter.write(serializableBean, null, outputMessage)
|
||||
|
||||
assertThat(outputMessage.headers).containsEntry("Content-Type", listOf("application/cbor"))
|
||||
@Suppress("DEPRECATION")
|
||||
assertThat(outputMessage.headers.asMultiValueMap()).containsEntry("Content-Type", listOf("application/cbor"))
|
||||
assertThat(outputMessage.bodyAsBytes.isNotEmpty()).isTrue()
|
||||
}
|
||||
|
||||
|
@ -191,7 +193,8 @@ class KotlinSerializationCborHttpMessageConverterTests {
|
|||
|
||||
this.converter.write(serializableBeanArray, null, outputMessage)
|
||||
|
||||
assertThat(outputMessage.headers).containsEntry("Content-Type", listOf("application/cbor"))
|
||||
@Suppress("DEPRECATION")
|
||||
assertThat(outputMessage.headers.asMultiValueMap()).containsEntry("Content-Type", listOf("application/cbor"))
|
||||
assertThat(outputMessage.bodyAsBytes.isNotEmpty()).isTrue()
|
||||
}
|
||||
|
||||
|
@ -202,7 +205,8 @@ class KotlinSerializationCborHttpMessageConverterTests {
|
|||
|
||||
this.converter.write(listOf(serializableBean), ResolvableType.forType(typeOf<List<SerializableBean>>().javaType), null, outputMessage, null)
|
||||
|
||||
assertThat(outputMessage.headers).containsEntry("Content-Type", listOf("application/cbor"))
|
||||
@Suppress("DEPRECATION")
|
||||
assertThat(outputMessage.headers.asMultiValueMap()).containsEntry("Content-Type", listOf("application/cbor"))
|
||||
assertThat(outputMessage.bodyAsBytes.isNotEmpty()).isTrue()
|
||||
}
|
||||
|
||||
|
|
|
@ -258,7 +258,8 @@ class KotlinSerializationJsonHttpMessageConverterTests {
|
|||
|
||||
val result = outputMessage.getBodyAsString(StandardCharsets.UTF_8)
|
||||
|
||||
assertThat(outputMessage.headers).containsEntry("Content-Type", listOf("application/json"))
|
||||
@Suppress("DEPRECATION")
|
||||
assertThat(outputMessage.headers.asMultiValueMap()).containsEntry("Content-Type", listOf("application/json"))
|
||||
assertThat(result)
|
||||
.contains("\"bytes\":[1,2]")
|
||||
.contains("\"array\":[\"Foo\",\"Bar\"]")
|
||||
|
@ -277,7 +278,8 @@ class KotlinSerializationJsonHttpMessageConverterTests {
|
|||
|
||||
val result = outputMessage.getBodyAsString(StandardCharsets.UTF_8)
|
||||
|
||||
assertThat(outputMessage.headers).containsEntry("Content-Type", listOf("application/json"))
|
||||
@Suppress("DEPRECATION")
|
||||
assertThat(outputMessage.headers.asMultiValueMap()).containsEntry("Content-Type", listOf("application/json"))
|
||||
assertThat(result)
|
||||
.contains("\"bytes\":[1,2]")
|
||||
.contains("\"array\":[\"Foo\",\"Bar\"]")
|
||||
|
@ -299,7 +301,8 @@ class KotlinSerializationJsonHttpMessageConverterTests {
|
|||
|
||||
val result = outputMessage.getBodyAsString(StandardCharsets.UTF_8)
|
||||
|
||||
assertThat(outputMessage.headers).containsEntry("Content-Type", listOf("application/json"))
|
||||
@Suppress("DEPRECATION")
|
||||
assertThat(outputMessage.headers.asMultiValueMap()).containsEntry("Content-Type", listOf("application/json"))
|
||||
assertThat(result).isEqualTo(expectedJson)
|
||||
}
|
||||
|
||||
|
@ -317,7 +320,8 @@ class KotlinSerializationJsonHttpMessageConverterTests {
|
|||
|
||||
val result = outputMessage.getBodyAsString(StandardCharsets.UTF_8)
|
||||
|
||||
assertThat(outputMessage.headers).containsEntry("Content-Type", listOf("application/json"))
|
||||
@Suppress("DEPRECATION")
|
||||
assertThat(outputMessage.headers.asMultiValueMap()).containsEntry("Content-Type", listOf("application/json"))
|
||||
assertThat(result).isEqualTo(expectedJson)
|
||||
}
|
||||
|
||||
|
@ -331,7 +335,8 @@ class KotlinSerializationJsonHttpMessageConverterTests {
|
|||
|
||||
val result = outputMessage.getBodyAsString(StandardCharsets.UTF_16BE)
|
||||
|
||||
assertThat(outputMessage.headers).containsEntry("Content-Type", listOf(contentType.toString()))
|
||||
@Suppress("DEPRECATION")
|
||||
assertThat(outputMessage.headers.asMultiValueMap()).containsEntry("Content-Type", listOf(contentType.toString()))
|
||||
assertThat(result).isEqualTo("\"H\u00e9llo W\u00f6rld\"")
|
||||
}
|
||||
|
||||
|
@ -367,7 +372,8 @@ class KotlinSerializationJsonHttpMessageConverterTests {
|
|||
|
||||
val result = outputMessage.getBodyAsString(StandardCharsets.UTF_8)
|
||||
|
||||
assertThat(outputMessage.headers).containsEntry("Content-Type", listOf("application/json"))
|
||||
@Suppress("DEPRECATION")
|
||||
assertThat(outputMessage.headers.asMultiValueMap()).containsEntry("Content-Type", listOf("application/json"))
|
||||
assertThat(result).isEqualTo("1.0")
|
||||
}
|
||||
|
||||
|
@ -384,7 +390,8 @@ class KotlinSerializationJsonHttpMessageConverterTests {
|
|||
|
||||
val result = outputMessage.getBodyAsString(StandardCharsets.UTF_8)
|
||||
|
||||
assertThat(outputMessage.headers).containsEntry("Content-Type", listOf("application/json"))
|
||||
@Suppress("DEPRECATION")
|
||||
assertThat(outputMessage.headers.asMultiValueMap()).containsEntry("Content-Type", listOf("application/json"))
|
||||
assertThat(result).isEqualTo(expectedJson)
|
||||
}
|
||||
|
||||
|
|
|
@ -186,7 +186,8 @@ class KotlinSerializationProtobufHttpMessageConverterTests {
|
|||
|
||||
this.converter.write(serializableBean, null, outputMessage)
|
||||
|
||||
assertThat(outputMessage.headers).containsEntry("Content-Type", listOf("application/x-protobuf"))
|
||||
@Suppress("DEPRECATION")
|
||||
assertThat(outputMessage.headers.asMultiValueMap()).containsEntry("Content-Type", listOf("application/x-protobuf"))
|
||||
assertThat(outputMessage.bodyAsBytes.isNotEmpty()).isTrue()
|
||||
}
|
||||
|
||||
|
@ -197,7 +198,8 @@ class KotlinSerializationProtobufHttpMessageConverterTests {
|
|||
|
||||
this.converter.write(serializableBean, null, outputMessage)
|
||||
|
||||
assertThat(outputMessage.headers).containsEntry("Content-Type", listOf("application/x-protobuf"))
|
||||
@Suppress("DEPRECATION")
|
||||
assertThat(outputMessage.headers.asMultiValueMap()).containsEntry("Content-Type", listOf("application/x-protobuf"))
|
||||
assertThat(outputMessage.bodyAsBytes.isNotEmpty()).isTrue()
|
||||
}
|
||||
|
||||
|
@ -207,7 +209,8 @@ class KotlinSerializationProtobufHttpMessageConverterTests {
|
|||
|
||||
this.converter.write(serializableBeanArray, null, outputMessage)
|
||||
|
||||
assertThat(outputMessage.headers).containsEntry("Content-Type", listOf("application/x-protobuf"))
|
||||
@Suppress("DEPRECATION")
|
||||
assertThat(outputMessage.headers.asMultiValueMap()).containsEntry("Content-Type", listOf("application/x-protobuf"))
|
||||
assertThat(outputMessage.bodyAsBytes.isNotEmpty()).isTrue()
|
||||
}
|
||||
|
||||
|
@ -218,7 +221,8 @@ class KotlinSerializationProtobufHttpMessageConverterTests {
|
|||
|
||||
this.converter.write(listOf(serializableBean), ResolvableType.forType(typeOf<List<SerializableBean>>().javaType), null, outputMessage, null)
|
||||
|
||||
assertThat(outputMessage.headers).containsEntry("Content-Type", listOf("application/x-protobuf"))
|
||||
@Suppress("DEPRECATION")
|
||||
assertThat(outputMessage.headers.asMultiValueMap()).containsEntry("Content-Type", listOf("application/x-protobuf"))
|
||||
assertThat(outputMessage.bodyAsBytes.isNotEmpty()).isTrue()
|
||||
}
|
||||
|
||||
|
|
|
@ -299,9 +299,18 @@ public final class MockServerHttpRequest extends AbstractServerHttpRequest {
|
|||
/**
|
||||
* Add the given header values.
|
||||
* @param headers the header values
|
||||
* @deprecated Use {@link #headers(HttpHeaders)}
|
||||
*/
|
||||
@Deprecated
|
||||
B headers(MultiValueMap<String, String> headers);
|
||||
|
||||
/**
|
||||
* Add the given header values.
|
||||
* @param headers the header values
|
||||
* @since 7.0
|
||||
*/
|
||||
B headers(HttpHeaders headers);
|
||||
|
||||
/**
|
||||
* Set the list of acceptable {@linkplain MediaType media types}, as
|
||||
* specified by the {@code Accept} header.
|
||||
|
@ -491,11 +500,18 @@ public final class MockServerHttpRequest extends AbstractServerHttpRequest {
|
|||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public BodyBuilder headers(MultiValueMap<String, String> headers) {
|
||||
this.headers.putAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BodyBuilder headers(HttpHeaders headers) {
|
||||
this.headers.putAll(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BodyBuilder accept(MediaType... acceptableMediaTypes) {
|
||||
this.headers.setAccept(Arrays.asList(acceptableMediaTypes));
|
||||
|
|
|
@ -129,7 +129,7 @@ public class MockPart implements Part {
|
|||
|
||||
@Override
|
||||
public Collection<String> getHeaderNames() {
|
||||
return this.headers.keySet();
|
||||
return this.headers.headerNames();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -495,10 +495,10 @@ final class DefaultWebClient implements WebClient {
|
|||
}
|
||||
|
||||
private void initHeaders(HttpHeaders out) {
|
||||
if (!CollectionUtils.isEmpty(defaultHeaders)) {
|
||||
if (defaultHeaders != null && !defaultHeaders.isEmpty()) {
|
||||
out.putAll(defaultHeaders);
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(this.headers)) {
|
||||
if (this.headers != null && !this.headers.isEmpty()) {
|
||||
out.putAll(this.headers);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,7 +58,7 @@ public class WebClientRequestException extends WebClientException {
|
|||
*/
|
||||
private static HttpHeaders copy(HttpHeaders headers) {
|
||||
HttpHeaders result = new HttpHeaders();
|
||||
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
|
||||
for (Map.Entry<String, List<String>> entry : headers.headerSet()) {
|
||||
for (String value : entry.getValue()) {
|
||||
result.add(entry.getKey(), value);
|
||||
}
|
||||
|
|
|
@ -150,7 +150,7 @@ public class WebClientResponseException extends WebClientException {
|
|||
}
|
||||
else {
|
||||
HttpHeaders result = new HttpHeaders();
|
||||
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
|
||||
for (Map.Entry<String, List<String>> entry : headers.headerSet()) {
|
||||
for (String value : entry.getValue()) {
|
||||
result.add(entry.getKey(), value);
|
||||
}
|
||||
|
|
|
@ -357,6 +357,12 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder {
|
|||
dst.putAll(src);
|
||||
}
|
||||
}
|
||||
|
||||
private static void copy(HttpHeaders src, HttpHeaders dst) {
|
||||
if (!src.isEmpty()) {
|
||||
dst.putAll(src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ public class ExtendedWebExchangeDataBinder extends WebExchangeDataBinder {
|
|||
vars.forEach((key, value) -> addValueIfNotPresent(map, "URI variable", key, value));
|
||||
}
|
||||
HttpHeaders headers = exchange.getRequest().getHeaders();
|
||||
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
|
||||
for (Map.Entry<String, List<String>> entry : headers.headerSet()) {
|
||||
String name = entry.getKey();
|
||||
if (!this.headerPredicate.test(entry.getKey())) {
|
||||
continue;
|
||||
|
|
|
@ -55,17 +55,22 @@ public class RequestHeaderMapMethodArgumentResolver extends HandlerMethodArgumen
|
|||
}
|
||||
|
||||
private boolean allParams(RequestHeader annotation, Class<?> type) {
|
||||
return Map.class.isAssignableFrom(type);
|
||||
return Map.class.isAssignableFrom(type) || HttpHeaders.class.isAssignableFrom(type);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public Object resolveArgumentValue(
|
||||
MethodParameter methodParameter, BindingContext context, ServerWebExchange exchange) {
|
||||
|
||||
boolean isMultiValueMap = MultiValueMap.class.isAssignableFrom(methodParameter.getParameterType());
|
||||
HttpHeaders headers = exchange.getRequest().getHeaders();
|
||||
return (isMultiValueMap ? headers : headers.toSingleValueMap());
|
||||
if (isMultiValueMap) {
|
||||
return headers.asMultiValueMap();
|
||||
}
|
||||
boolean isHttpHeaders = HttpHeaders.class.isAssignableFrom(methodParameter.getParameterType());
|
||||
return (isHttpHeaders ? headers : headers.toSingleValueMap());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
|||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.RequestHeader;
|
||||
import org.springframework.web.server.MissingRequestValueException;
|
||||
|
@ -68,7 +69,7 @@ public class RequestHeaderMethodArgumentResolver extends AbstractNamedValueSyncA
|
|||
}
|
||||
|
||||
private boolean singleParam(RequestHeader annotation, Class<?> type) {
|
||||
return !Map.class.isAssignableFrom(type);
|
||||
return !Map.class.isAssignableFrom(type) && !HttpHeaders.class.isAssignableFrom(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -473,7 +473,7 @@ public class ViewResolutionResultHandler extends HandlerResultHandlerSupport imp
|
|||
|
||||
@Override
|
||||
public HttpHeaders getHeaders() {
|
||||
if (!super.getHeaders().containsKey(HttpHeaders.CONTENT_TYPE)) {
|
||||
if (!super.getHeaders().containsHeader(HttpHeaders.CONTENT_TYPE)) {
|
||||
return super.getHeaders();
|
||||
}
|
||||
// Content-type is set, ignore further updates
|
||||
|
|
|
@ -92,7 +92,7 @@ public class JettyWebSocketClient implements WebSocketClient, Lifecycle {
|
|||
ClientUpgradeRequest upgradeRequest = new ClientUpgradeRequest();
|
||||
upgradeRequest.setSubProtocols(handler.getSubProtocols());
|
||||
if (headers != null) {
|
||||
headers.keySet().forEach(header -> upgradeRequest.setHeader(header, headers.getValuesAsList(header)));
|
||||
headers.headerNames().forEach(header -> upgradeRequest.setHeader(header, headers.getValuesAsList(header)));
|
||||
}
|
||||
|
||||
final AtomicReference<HandshakeInfo> handshakeInfo = new AtomicReference<>();
|
||||
|
|
|
@ -179,12 +179,12 @@ public class StandardWebSocketClient implements WebSocketClient {
|
|||
|
||||
@Override
|
||||
public void beforeRequest(Map<String, List<String>> requestHeaders) {
|
||||
requestHeaders.putAll(this.requestHeaders);
|
||||
this.requestHeaders.forEach(requestHeaders::put);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterResponse(HandshakeResponse response) {
|
||||
this.responseHeaders.putAll(response.getHeaders());
|
||||
response.getHeaders().forEach(this.responseHeaders::put);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -242,7 +242,7 @@ public class UndertowWebSocketClient implements WebSocketClient {
|
|||
|
||||
@Override
|
||||
public void beforeRequest(Map<String, List<String>> headers) {
|
||||
headers.putAll(this.requestHeaders);
|
||||
this.requestHeaders.forEach(headers::put);
|
||||
if (this.delegate != null) {
|
||||
this.delegate.beforeRequest(headers);
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ class DefaultClientRequestBuilderTests {
|
|||
|
||||
assertThat(result.url()).isEqualTo(DEFAULT_URL);
|
||||
assertThat(result.method()).isEqualTo(GET);
|
||||
assertThat(result.headers()).hasSize(1);
|
||||
assertThat(result.headers().size()).isOne();
|
||||
assertThat(result.headers().getFirst("foo")).isEqualTo("baar");
|
||||
assertThat(result.cookies()).hasSize(1);
|
||||
assertThat(result.cookies().getFirst("baz")).isEqualTo("quux");
|
||||
|
|
|
@ -88,7 +88,7 @@ class DefaultClientResponseBuilderTests {
|
|||
|
||||
|
||||
assertThat(result.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
|
||||
assertThat(result.headers().asHttpHeaders()).hasSize(3);
|
||||
assertThat(result.headers().asHttpHeaders().size()).isEqualTo(3);
|
||||
assertThat(result.headers().asHttpHeaders().getFirst("foo")).isEqualTo("baar");
|
||||
assertThat(result.headers().asHttpHeaders().getFirst("bar")).isEqualTo("baz");
|
||||
assertThat(result.cookies()).hasSize(1);
|
||||
|
|
|
@ -338,7 +338,7 @@ class DefaultClientResponseTests {
|
|||
WebClientResponseException exception = resultMono.block();
|
||||
assertThat(exception.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
assertThat(exception.getMessage()).isEqualTo("404 Not Found from UNKNOWN https://example.org:9999/app/path");
|
||||
assertThat(exception.getHeaders()).containsExactly(entry("Content-Type", List.of("text/plain")));
|
||||
assertThat(exception.getHeaders().asMultiValueMap()).containsExactly(entry("Content-Type", List.of("text/plain")));
|
||||
assertThat(exception.getResponseBodyAsByteArray()).isEqualTo(bytes);
|
||||
}
|
||||
|
||||
|
@ -397,7 +397,7 @@ class DefaultClientResponseTests {
|
|||
WebClientResponseException exception = (WebClientResponseException) t;
|
||||
assertThat(exception.getStatusCode()).isEqualTo(HttpStatus.NOT_FOUND);
|
||||
assertThat(exception.getMessage()).isEqualTo("404 Not Found");
|
||||
assertThat(exception.getHeaders()).containsExactly(entry("Content-Type",List.of("text/plain")));
|
||||
assertThat(exception.getHeaders().asMultiValueMap()).containsExactly(entry("Content-Type",List.of("text/plain")));
|
||||
assertThat(exception.getResponseBodyAsByteArray()).isEqualTo(bytes);
|
||||
})
|
||||
.verify();
|
||||
|
|
|
@ -295,17 +295,17 @@ public class DefaultWebClientTests {
|
|||
|
||||
WebClient.Builder builder1 = client1.mutate();
|
||||
builder1.filters(filters -> assertThat(filters).hasSize(1));
|
||||
builder1.defaultHeaders(headers -> assertThat(headers).hasSize(1));
|
||||
builder1.defaultHeaders(headers -> assertThat(headers.size()).isOne());
|
||||
builder1.defaultCookies(cookies -> assertThat(cookies).hasSize(1));
|
||||
|
||||
WebClient.Builder builder2 = client2.mutate();
|
||||
builder2.filters(filters -> assertThat(filters).hasSize(2));
|
||||
builder2.defaultHeaders(headers -> assertThat(headers).hasSize(2));
|
||||
builder2.defaultHeaders(headers -> assertThat(headers.size()).isEqualTo(2));
|
||||
builder2.defaultCookies(cookies -> assertThat(cookies).hasSize(2));
|
||||
|
||||
WebClient.Builder builder1a = client1a.mutate();
|
||||
builder1a.filters(filters -> assertThat(filters).hasSize(2));
|
||||
builder1a.defaultHeaders(headers -> assertThat(headers).hasSize(2));
|
||||
builder1a.defaultHeaders(headers -> assertThat(headers.size()).isEqualTo(2));
|
||||
builder1a.defaultCookies(cookies -> assertThat(cookies).hasSize(2));
|
||||
}
|
||||
|
||||
|
|
|
@ -103,13 +103,13 @@ class ExchangeFilterFunctionsTests {
|
|||
ClientResponse response = mock();
|
||||
|
||||
ExchangeFunction exchange = r -> {
|
||||
assertThat(r.headers().containsKey(HttpHeaders.AUTHORIZATION)).isTrue();
|
||||
assertThat(r.headers().containsHeader(HttpHeaders.AUTHORIZATION)).isTrue();
|
||||
assertThat(r.headers().getFirst(HttpHeaders.AUTHORIZATION)).startsWith("Basic ");
|
||||
return Mono.just(response);
|
||||
};
|
||||
|
||||
ExchangeFilterFunction auth = ExchangeFilterFunctions.basicAuthentication("foo", "bar");
|
||||
assertThat(request.headers().containsKey(HttpHeaders.AUTHORIZATION)).isFalse();
|
||||
assertThat(request.headers().containsHeader(HttpHeaders.AUTHORIZATION)).isFalse();
|
||||
ClientResponse result = auth.filter(request, exchange).block();
|
||||
assertThat(result).isEqualTo(response);
|
||||
}
|
||||
|
@ -133,13 +133,13 @@ class ExchangeFilterFunctionsTests {
|
|||
ClientResponse response = mock();
|
||||
|
||||
ExchangeFunction exchange = r -> {
|
||||
assertThat(r.headers().containsKey(HttpHeaders.AUTHORIZATION)).isTrue();
|
||||
assertThat(r.headers().containsHeader(HttpHeaders.AUTHORIZATION)).isTrue();
|
||||
assertThat(r.headers().getFirst(HttpHeaders.AUTHORIZATION)).startsWith("Basic ");
|
||||
return Mono.just(response);
|
||||
};
|
||||
|
||||
ExchangeFilterFunction auth = ExchangeFilterFunctions.basicAuthentication();
|
||||
assertThat(request.headers().containsKey(HttpHeaders.AUTHORIZATION)).isFalse();
|
||||
assertThat(request.headers().containsHeader(HttpHeaders.AUTHORIZATION)).isFalse();
|
||||
ClientResponse result = auth.filter(request, exchange).block();
|
||||
assertThat(result).isEqualTo(response);
|
||||
}
|
||||
|
@ -151,12 +151,12 @@ class ExchangeFilterFunctionsTests {
|
|||
ClientResponse response = mock();
|
||||
|
||||
ExchangeFunction exchange = r -> {
|
||||
assertThat(r.headers().containsKey(HttpHeaders.AUTHORIZATION)).isFalse();
|
||||
assertThat(r.headers().containsHeader(HttpHeaders.AUTHORIZATION)).isFalse();
|
||||
return Mono.just(response);
|
||||
};
|
||||
|
||||
ExchangeFilterFunction auth = ExchangeFilterFunctions.basicAuthentication();
|
||||
assertThat(request.headers().containsKey(HttpHeaders.AUTHORIZATION)).isFalse();
|
||||
assertThat(request.headers().containsHeader(HttpHeaders.AUTHORIZATION)).isFalse();
|
||||
ClientResponse result = auth.filter(request, exchange).block();
|
||||
assertThat(result).isEqualTo(response);
|
||||
}
|
||||
|
|
|
@ -204,7 +204,7 @@ class WebClientDataBufferAllocatingTests extends AbstractDataBufferAllocatingTes
|
|||
StepVerifier.create(result)
|
||||
.assertNext(entity -> {
|
||||
assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.CREATED);
|
||||
assertThat(entity.getHeaders()).containsEntry("Foo", Collections.singletonList("bar"));
|
||||
assertThat(entity.getHeaders().hasHeaderValues("Foo", Collections.singletonList("bar"))).isTrue();
|
||||
assertThat(entity.getBody()).isNull();
|
||||
})
|
||||
.expectComplete()
|
||||
|
|
|
@ -91,7 +91,7 @@ class WebClientObservationTests {
|
|||
|
||||
assertThatHttpObservation().hasLowCardinalityKeyValue("outcome", "SUCCESS")
|
||||
.hasLowCardinalityKeyValue("uri", "/base/resource/{id}");
|
||||
assertThat(clientRequest.headers()).containsEntry("foo", Collections.singletonList("bar"));
|
||||
assertThat(clientRequest.headers().hasHeaderValues("foo", Collections.singletonList("bar"))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -73,7 +73,7 @@ class DefaultServerRequestBuilderTests {
|
|||
assertThat(result.uri()).isEqualTo(uri);
|
||||
assertThat(result.requestPath().pathWithinApplication().value()).isEqualTo("/bar");
|
||||
assertThat(result.requestPath().contextPath().value()).isEqualTo("/foo");
|
||||
assertThat(result.headers().asHttpHeaders()).hasSize(1);
|
||||
assertThat(result.headers().asHttpHeaders().size()).isOne();
|
||||
assertThat(result.headers().asHttpHeaders().getFirst("foo")).isEqualTo("baar");
|
||||
assertThat(result.cookies()).hasSize(1);
|
||||
assertThat(result.cookies().getFirst("baz").getValue()).isEqualTo("quux");
|
||||
|
|
|
@ -417,7 +417,7 @@ class ResourceWebHandlerTests {
|
|||
this.handler.handle(exchange).block(TIMEOUT);
|
||||
|
||||
HttpHeaders headers = exchange.getResponse().getHeaders();
|
||||
assertThat(headers.containsKey("Last-Modified")).isTrue();
|
||||
assertThat(headers.containsHeader("Last-Modified")).isTrue();
|
||||
assertThat(resourceLastModifiedDate("test/foo.css") / 1000).isEqualTo(headers.getLastModified() / 1000);
|
||||
}
|
||||
|
||||
|
@ -448,7 +448,7 @@ class ResourceWebHandlerTests {
|
|||
|
||||
MockServerHttpResponse response = exchange.getResponse();
|
||||
assertThat(response.getHeaders().getCacheControl()).isEqualTo("no-store");
|
||||
assertThat(response.getHeaders().containsKey("Last-Modified")).isTrue();
|
||||
assertThat(response.getHeaders().containsHeader("Last-Modified")).isTrue();
|
||||
assertThat(resourceLastModifiedDate("test/foo.css") / 1000).isEqualTo(response.getHeaders().getLastModified() / 1000);
|
||||
}
|
||||
|
||||
|
@ -561,7 +561,7 @@ class ResourceWebHandlerTests {
|
|||
HttpHeaders headers = exchange.getResponse().getHeaders();
|
||||
assertThat(headers.getContentType()).isEqualTo(MediaType.parseMediaType("text/css"));
|
||||
assertThat(headers.getContentLength()).isEqualTo(17);
|
||||
assertThat(headers.containsKey("Last-Modified")).isFalse();
|
||||
assertThat(headers.containsHeader("Last-Modified")).isFalse();
|
||||
assertResponseBody(exchange, "h1 { color:red; }");
|
||||
}
|
||||
|
||||
|
|
|
@ -123,7 +123,7 @@ public abstract class AbstractRequestMappingIntegrationTests extends AbstractHtt
|
|||
}
|
||||
|
||||
private void addHeaders(RequestEntity.HeadersBuilder<?> builder, HttpHeaders headers) {
|
||||
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
|
||||
for (Map.Entry<String, List<String>> entry : headers.headerSet()) {
|
||||
for (String value : entry.getValue()) {
|
||||
builder.header(entry.getKey(), value);
|
||||
}
|
||||
|
|
|
@ -157,7 +157,7 @@ class ResponseEntityResultHandlerTests {
|
|||
this.resultHandler.handleResult(exchange, result).block(Duration.ofSeconds(5));
|
||||
|
||||
assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.NO_CONTENT);
|
||||
assertThat(exchange.getResponse().getHeaders()).isEmpty();
|
||||
assertThat(exchange.getResponse().getHeaders().isEmpty()).isTrue();
|
||||
assertResponseBodyIsEmpty(exchange);
|
||||
}
|
||||
|
||||
|
@ -171,7 +171,7 @@ class ResponseEntityResultHandlerTests {
|
|||
this.resultHandler.handleResult(exchange, result).block(Duration.ofSeconds(5));
|
||||
|
||||
assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(exchange.getResponse().getHeaders()).hasSize(1);
|
||||
assertThat(exchange.getResponse().getHeaders().size()).isOne();
|
||||
assertThat(exchange.getResponse().getHeaders().getFirst("Allow")).isEqualTo("GET,POST,OPTIONS");
|
||||
assertResponseBodyIsEmpty(exchange);
|
||||
}
|
||||
|
@ -186,7 +186,7 @@ class ResponseEntityResultHandlerTests {
|
|||
this.resultHandler.handleResult(exchange, result).block(Duration.ofSeconds(5));
|
||||
|
||||
assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.CREATED);
|
||||
assertThat(exchange.getResponse().getHeaders()).hasSize(1);
|
||||
assertThat(exchange.getResponse().getHeaders().size()).isOne();
|
||||
assertThat(exchange.getResponse().getHeaders().getLocation()).isEqualTo(location);
|
||||
assertResponseBodyIsEmpty(exchange);
|
||||
}
|
||||
|
@ -236,7 +236,7 @@ class ResponseEntityResultHandlerTests {
|
|||
this.resultHandler.handleResult(exchange, result).block(Duration.ofSeconds(5));
|
||||
|
||||
assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
|
||||
assertThat(exchange.getResponse().getHeaders()).hasSize(3);
|
||||
assertThat(exchange.getResponse().getHeaders().size()).isEqualTo(3);
|
||||
assertThat(exchange.getResponse().getHeaders().get("foo")).containsExactly("bar");
|
||||
assertThat(exchange.getResponse().getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);
|
||||
assertResponseBody(exchange,
|
||||
|
@ -256,7 +256,7 @@ class ResponseEntityResultHandlerTests {
|
|||
this.resultHandler.handleResult(exchange, result).block(Duration.ofSeconds(5));
|
||||
|
||||
assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.BAD_REQUEST);
|
||||
assertThat(exchange.getResponse().getHeaders()).hasSize(2);
|
||||
assertThat(exchange.getResponse().getHeaders().size()).isEqualTo(2);
|
||||
assertThat(exchange.getResponse().getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_PROBLEM_JSON);
|
||||
assertResponseBody(exchange,
|
||||
"{\"type\":\"about:blank\"," +
|
||||
|
@ -385,7 +385,7 @@ class ResponseEntityResultHandlerTests {
|
|||
this.resultHandler.handleResult(exchange, result).block(Duration.ofSeconds(5));
|
||||
|
||||
assertThat(exchange.getResponse().getStatusCode()).isEqualTo(HttpStatus.OK);
|
||||
assertThat(exchange.getResponse().getHeaders()).hasSize(1);
|
||||
assertThat(exchange.getResponse().getHeaders().size()).isOne();
|
||||
assertThat(exchange.getResponse().getHeaders().getContentType()).isEqualTo(MediaType.APPLICATION_JSON);
|
||||
assertResponseBodyIsEmpty(exchange);
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ class DefaultRenderingBuilderTests {
|
|||
assertThat(rendering.view()).isEqualTo("abc");
|
||||
assertThat(rendering.modelAttributes()).isEqualTo(Collections.emptyMap());
|
||||
assertThat(rendering.status()).isNull();
|
||||
assertThat(rendering.headers()).isEmpty();
|
||||
assertThat(rendering.headers().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -97,7 +97,7 @@ class DefaultRenderingBuilderTests {
|
|||
void header() {
|
||||
Rendering rendering = Rendering.view("foo").header("foo", "bar").build();
|
||||
|
||||
assertThat(rendering.headers()).hasSize(1);
|
||||
assertThat(rendering.headers().size()).isOne();
|
||||
assertThat(rendering.headers().get("foo")).isEqualTo(Collections.singletonList("bar"));
|
||||
}
|
||||
|
||||
|
|
|
@ -606,7 +606,7 @@ class DefaultServerRequest implements ServerRequest {
|
|||
|
||||
@Override
|
||||
public boolean containsHeader(String name) {
|
||||
return this.headers.containsKey(name);
|
||||
return this.headers.containsHeader(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -647,7 +647,7 @@ class DefaultServerRequest implements ServerRequest {
|
|||
|
||||
@Override
|
||||
public Collection<String> getHeaderNames() {
|
||||
return this.headers.keySet();
|
||||
return this.headers.headerNames();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -489,7 +489,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
|
|||
*/
|
||||
private void addContentDispositionHeader(ServletServerHttpRequest request, ServletServerHttpResponse response) {
|
||||
HttpHeaders headers = response.getHeaders();
|
||||
if (headers.containsKey(HttpHeaders.CONTENT_DISPOSITION)) {
|
||||
if (headers.containsHeader(HttpHeaders.CONTENT_DISPOSITION)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -224,7 +224,7 @@ public class HttpEntityMethodProcessor extends AbstractMessageConverterMethodPro
|
|||
HttpHeaders entityHeaders = httpEntity.getHeaders();
|
||||
if (!entityHeaders.isEmpty()) {
|
||||
entityHeaders.forEach((key, value) -> {
|
||||
if (HttpHeaders.VARY.equals(key) && outputHeaders.containsKey(HttpHeaders.VARY)) {
|
||||
if (HttpHeaders.VARY.equals(key) && outputHeaders.containsHeader(HttpHeaders.VARY)) {
|
||||
List<String> values = getVaryRequestHeadersToAdd(outputHeaders, entityHeaders);
|
||||
if (!values.isEmpty()) {
|
||||
outputHeaders.setVary(values);
|
||||
|
|
|
@ -301,7 +301,7 @@ class ResponseEntityExceptionHandlerTests {
|
|||
ResponseEntity<Object> responseEntity =
|
||||
testException(new NoHandlerFoundException("GET", "/resource", requestHeaders));
|
||||
|
||||
assertThat(responseEntity.getHeaders()).isEmpty();
|
||||
assertThat(responseEntity.getHeaders().isEmpty()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -3576,7 +3576,21 @@ class ServletAnnotationControllerHandlerMethodTests extends AbstractServletHandl
|
|||
@RequestMapping("/httpHeaders")
|
||||
public void httpHeaders(@RequestHeader HttpHeaders headers, Writer writer) throws IOException {
|
||||
assertThat(headers.getContentType()).as("Invalid Content-Type").isEqualTo(new MediaType("text", "html"));
|
||||
multiValueMap(headers, writer);
|
||||
for (Iterator<Map.Entry<String, List<String>>> it1 = headers.headerSet().iterator(); it1.hasNext();) {
|
||||
Map.Entry<String, List<String>> entry = it1.next();
|
||||
writer.write(entry.getKey() + "=[");
|
||||
for (Iterator<String> it2 = entry.getValue().iterator(); it2.hasNext();) {
|
||||
String value = it2.next();
|
||||
writer.write(value);
|
||||
if (it2.hasNext()) {
|
||||
writer.write(',');
|
||||
}
|
||||
}
|
||||
writer.write(']');
|
||||
if (it1.hasNext()) {
|
||||
writer.write(',');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
package org.springframework.web.socket;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -244,30 +243,30 @@ public class WebSocketHttpHeaders extends HttpHeaders {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return this.headers.containsKey(key);
|
||||
public boolean containsHeader(String key) {
|
||||
return this.headers.containsHeader(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return this.headers.containsValue(value);
|
||||
public @Nullable List<String> get(String headerName) {
|
||||
return this.headers.get(headerName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> get(Object key) {
|
||||
return this.headers.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> put(String key, List<String> value) {
|
||||
public @Nullable List<String> put(String key, List<String> value) {
|
||||
return this.headers.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> remove(Object key) {
|
||||
public @Nullable List<String> remove(String key) {
|
||||
return this.headers.remove(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(HttpHeaders headers) {
|
||||
this.headers.putAll(headers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends String, ? extends List<String>> m) {
|
||||
this.headers.putAll(m);
|
||||
|
@ -279,18 +278,13 @@ public class WebSocketHttpHeaders extends HttpHeaders {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
return this.headers.keySet();
|
||||
public Set<String> headerNames() {
|
||||
return this.headers.headerNames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<List<String>> values() {
|
||||
return this.headers.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<String, List<String>>> entrySet() {
|
||||
return this.headers.entrySet();
|
||||
public Set<Map.Entry<String, List<String>>> headerSet() {
|
||||
return this.headers.headerSet();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -299,8 +293,8 @@ public class WebSocketHttpHeaders extends HttpHeaders {
|
|||
}
|
||||
|
||||
@Override
|
||||
public List<String> putIfAbsent(String key, List<String> value) {
|
||||
return this.headers.putIfAbsent(key, value);
|
||||
public @Nullable List<String> putIfAbsent(String headerName, List<String> headerValues) {
|
||||
return this.headers.putIfAbsent(headerName, headerValues);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -216,7 +216,7 @@ public class StandardWebSocketClient extends AbstractWebSocketClient {
|
|||
|
||||
@Override
|
||||
public void beforeRequest(Map<String, List<String>> requestHeaders) {
|
||||
requestHeaders.putAll(this.headers);
|
||||
this.headers.forEach(requestHeaders::put);
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Handshake request headers: " + requestHeaders);
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ class StandardWebSocketClientTests {
|
|||
|
||||
WebSocketSession session = this.wsClient.execute(this.wsHandler, this.headers, uri).get();
|
||||
|
||||
assertThat(session.getHandshakeHeaders()).hasSize(1);
|
||||
assertThat(session.getHandshakeHeaders().size()).isOne();
|
||||
assertThat(session.getHandshakeHeaders().getFirst("foo")).isEqualTo("bar");
|
||||
}
|
||||
|
||||
|
|
|
@ -108,12 +108,12 @@ class SockJsClientTests {
|
|||
this.sockJsClient.execute(handler, headers, URI.create(URL)).whenComplete(this.connectCallback);
|
||||
|
||||
HttpHeaders httpHeaders = headersCaptor.getValue();
|
||||
assertThat(httpHeaders).hasSize(2);
|
||||
assertThat(httpHeaders.size()).isEqualTo(2);
|
||||
assertThat(httpHeaders.getFirst("foo")).isEqualTo("bar");
|
||||
assertThat(httpHeaders.getFirst("auth")).isEqualTo("123");
|
||||
|
||||
httpHeaders = this.xhrTransport.getRequest().getHttpRequestHeaders();
|
||||
assertThat(httpHeaders).hasSize(2);
|
||||
assertThat(httpHeaders.size()).isEqualTo(2);
|
||||
assertThat(httpHeaders.getFirst("foo")).isEqualTo("bar");
|
||||
assertThat(httpHeaders.getFirst("auth")).isEqualTo("123");
|
||||
}
|
||||
|
@ -129,9 +129,9 @@ class SockJsClientTests {
|
|||
this.sockJsClient.setHttpHeaderNames("auth");
|
||||
this.sockJsClient.execute(handler, headers, URI.create(URL)).whenComplete(this.connectCallback);
|
||||
|
||||
assertThat(headersCaptor.getValue()).hasSize(1);
|
||||
assertThat(headersCaptor.getValue().size()).isEqualTo(1);
|
||||
assertThat(headersCaptor.getValue().getFirst("auth")).isEqualTo("123");
|
||||
assertThat(this.xhrTransport.getRequest().getHttpRequestHeaders()).hasSize(1);
|
||||
assertThat(this.xhrTransport.getRequest().getHttpRequestHeaders().size()).isEqualTo(1);
|
||||
assertThat(this.xhrTransport.getRequest().getHttpRequestHeaders().getFirst("auth")).isEqualTo("123");
|
||||
}
|
||||
|
||||
|
|
|
@ -69,7 +69,7 @@ class XhrTransportTests {
|
|||
transport.sendMessageResponseToReturn = new ResponseEntity<>(HttpStatus.NO_CONTENT);
|
||||
URI url = URI.create("https://example.com");
|
||||
transport.executeSendRequest(url, requestHeaders, new TextMessage("payload"));
|
||||
assertThat(transport.actualSendRequestHeaders).hasSize(2);
|
||||
assertThat(transport.actualSendRequestHeaders.size()).isEqualTo(2);
|
||||
assertThat(transport.actualSendRequestHeaders.getFirst("foo")).isEqualTo("bar");
|
||||
assertThat(transport.actualSendRequestHeaders.getContentType()).isEqualTo(MediaType.APPLICATION_JSON);
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ class XhrTransportTests {
|
|||
verify(request).getHttpRequestHeaders();
|
||||
verifyNoMoreInteractions(request);
|
||||
|
||||
assertThat(transport.actualHandshakeHeaders).hasSize(1);
|
||||
assertThat(transport.actualHandshakeHeaders.size()).isOne();
|
||||
assertThat(transport.actualHandshakeHeaders.getOrigin()).isEqualTo("foo");
|
||||
|
||||
assertThat(transport.actualSession.isDisconnected()).isFalse();
|
||||
|
|
Loading…
Reference in New Issue