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:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user