diff --git a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java index f0432d50502..c9dc894eeca 100644 --- a/spring-web/src/main/java/org/springframework/http/HttpHeaders.java +++ b/spring-web/src/main/java/org/springframework/http/HttpHeaders.java @@ -491,6 +491,25 @@ public class HttpHeaders implements Serializable { } } + /** + * Create a new {@code HttpHeaders} mutable instance and copy all header values given as a parameter. + * @param headers the headers to copy + * @since 7.0 + */ + public static HttpHeaders copyOf(MultiValueMap headers) { + HttpHeaders httpHeadersCopy = new HttpHeaders(); + headers.forEach((key, values) -> httpHeadersCopy.put(key, new ArrayList<>(values))); + return httpHeadersCopy; + } + + /** + * Create a new {@code HttpHeaders} mutable instance and copy all header values given as a parameter. + * @param httpHeaders the headers to copy + * @since 7.0 + */ + public static HttpHeaders copyOf(HttpHeaders httpHeaders) { + return copyOf(httpHeaders.headers); + } /** * Get the list of header values for the given header name, if any. diff --git a/spring-web/src/main/java/org/springframework/http/ReadOnlyHttpHeaders.java b/spring-web/src/main/java/org/springframework/http/ReadOnlyHttpHeaders.java index eb5fafdc41d..8ec4278c569 100644 --- a/spring-web/src/main/java/org/springframework/http/ReadOnlyHttpHeaders.java +++ b/spring-web/src/main/java/org/springframework/http/ReadOnlyHttpHeaders.java @@ -49,7 +49,6 @@ class ReadOnlyHttpHeaders extends HttpHeaders { @SuppressWarnings("serial") private @Nullable List cachedAccept; - ReadOnlyHttpHeaders(MultiValueMap headers) { super(headers); } diff --git a/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java b/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java index 9045bcdfefb..0fdc071b407 100644 --- a/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java +++ b/spring-web/src/test/java/org/springframework/http/HttpHeadersTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -77,6 +77,22 @@ class HttpHeadersTests { writeable.setContentType(MediaType.APPLICATION_JSON); } + @Test + void copyOfCopiesHeaders() { + headers.setContentType(MediaType.APPLICATION_JSON); + headers.add("X-Project", "Spring"); + headers.add("X-Project", "Framework"); + HttpHeaders readOnly = HttpHeaders.readOnlyHttpHeaders(headers); + assertThat(readOnly.getContentType()).isEqualTo(MediaType.APPLICATION_JSON); + + HttpHeaders writable = HttpHeaders.copyOf(readOnly); + writable.setContentType(MediaType.TEXT_PLAIN); + // content-type value is cached by ReadOnlyHttpHeaders + assertThat(readOnly.getContentType()).isEqualTo(MediaType.APPLICATION_JSON); + assertThat(writable.getContentType()).isEqualTo(MediaType.TEXT_PLAIN); + assertThat(writable.get("X-Project")).contains("Spring", "Framework"); + } + @Test void getOrEmpty() { String key = "FOO";