MessageHeaderAccessor handle self-copy correctly
1. Revert changes in setHeader from 5.2.9 that caused regression on self-copy. 2. Update copyHeaders to ensure it still gets a copy of native headers. 3. Exit if source and target are the same instance, as an optimization. Closes gh-26155
This commit is contained in:
parent
42216b77df
commit
d46091565b
|
|
@ -378,13 +378,14 @@ public class MessageHeaderAccessor {
|
|||
* {@link #copyHeadersIfAbsent(Map)} to avoid overwriting values.
|
||||
*/
|
||||
public void copyHeaders(@Nullable Map<String, ?> headersToCopy) {
|
||||
if (headersToCopy != null) {
|
||||
headersToCopy.forEach((key, value) -> {
|
||||
if (!isReadOnly(key)) {
|
||||
setHeader(key, value);
|
||||
}
|
||||
});
|
||||
if (headersToCopy == null || this.headers == headersToCopy) {
|
||||
return;
|
||||
}
|
||||
headersToCopy.forEach((key, value) -> {
|
||||
if (!isReadOnly(key)) {
|
||||
setHeader(key, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -392,13 +393,14 @@ public class MessageHeaderAccessor {
|
|||
* <p>This operation will <em>not</em> overwrite any existing values.
|
||||
*/
|
||||
public void copyHeadersIfAbsent(@Nullable Map<String, ?> headersToCopy) {
|
||||
if (headersToCopy != null) {
|
||||
headersToCopy.forEach((key, value) -> {
|
||||
if (!isReadOnly(key)) {
|
||||
setHeaderIfAbsent(key, value);
|
||||
}
|
||||
});
|
||||
if (headersToCopy == null || this.headers == headersToCopy) {
|
||||
return;
|
||||
}
|
||||
headersToCopy.forEach((key, value) -> {
|
||||
if (!isReadOnly(key)) {
|
||||
setHeaderIfAbsent(key, value);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected boolean isReadOnly(String headerName) {
|
||||
|
|
|
|||
|
|
@ -75,6 +75,8 @@ public class NativeMessageHeaderAccessor extends MessageHeaderAccessor {
|
|||
@SuppressWarnings("unchecked")
|
||||
Map<String, List<String>> map = (Map<String, List<String>>) getHeader(NATIVE_HEADERS);
|
||||
if (map != null) {
|
||||
// setHeader checks for equality but we need copy of native headers
|
||||
setHeader(NATIVE_HEADERS, null);
|
||||
setHeader(NATIVE_HEADERS, new LinkedMultiValueMap<>(map));
|
||||
}
|
||||
}
|
||||
|
|
@ -103,6 +105,8 @@ public class NativeMessageHeaderAccessor extends MessageHeaderAccessor {
|
|||
if (isMutable()) {
|
||||
Map<String, List<String>> map = getNativeHeaders();
|
||||
if (map != null) {
|
||||
// setHeader checks for equality but we need immutable wrapper
|
||||
setHeader(NATIVE_HEADERS, null);
|
||||
setHeader(NATIVE_HEADERS, Collections.unmodifiableMap(map));
|
||||
}
|
||||
super.setImmutable();
|
||||
|
|
@ -110,31 +114,19 @@ public class NativeMessageHeaderAccessor extends MessageHeaderAccessor {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setHeader(String name, @Nullable Object value) {
|
||||
if (name.equalsIgnoreCase(NATIVE_HEADERS)) {
|
||||
// Force removal since setHeader checks for equality
|
||||
super.setHeader(NATIVE_HEADERS, null);
|
||||
}
|
||||
super.setHeader(name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void copyHeaders(@Nullable Map<String, ?> headersToCopy) {
|
||||
if (headersToCopy != null) {
|
||||
Map<String, List<String>> nativeHeaders = getNativeHeaders();
|
||||
Map<String, List<String>> map = (Map<String, List<String>>) headersToCopy.get(NATIVE_HEADERS);
|
||||
if (map != null) {
|
||||
if (nativeHeaders != null) {
|
||||
nativeHeaders.putAll(map);
|
||||
}
|
||||
else {
|
||||
nativeHeaders = new LinkedMultiValueMap<>(map);
|
||||
}
|
||||
}
|
||||
super.copyHeaders(headersToCopy);
|
||||
setHeader(NATIVE_HEADERS, nativeHeaders);
|
||||
if (headersToCopy == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, List<String>> map = (Map<String, List<String>>) headersToCopy.get(NATIVE_HEADERS);
|
||||
if (map != null && map != getNativeHeaders()) {
|
||||
map.forEach(this::setNativeHeaderValues);
|
||||
}
|
||||
|
||||
// setHeader checks for equality, native headers should be equal by now
|
||||
super.copyHeaders(headersToCopy);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -201,6 +193,30 @@ public class NativeMessageHeaderAccessor extends MessageHeaderAccessor {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Variant of {@link #addNativeHeader(String, String)} for all values.
|
||||
* @since 5.2.12
|
||||
*/
|
||||
public void setNativeHeaderValues(String name, @Nullable List<String> values) {
|
||||
Assert.state(isMutable(), "Already immutable");
|
||||
Map<String, List<String>> map = getNativeHeaders();
|
||||
if (values == null) {
|
||||
if (map != null && map.get(name) != null) {
|
||||
setModified(true);
|
||||
map.remove(name);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (map == null) {
|
||||
map = new LinkedMultiValueMap<>(3);
|
||||
setHeader(NATIVE_HEADERS, map);
|
||||
}
|
||||
if (!ObjectUtils.nullSafeEquals(values, getHeader(name))) {
|
||||
setModified(true);
|
||||
map.put(name, new ArrayList<>(values));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the specified native header value to existing values.
|
||||
* <p>In order for this to work, the accessor must be {@link #isMutable()
|
||||
|
|
|
|||
|
|
@ -241,4 +241,14 @@ public class NativeMessageHeaderAccessorTests {
|
|||
assertThat(((NativeMessageHeaderAccessor) accessor).getNativeHeader("foo")).containsExactly("bar", "baz");
|
||||
}
|
||||
|
||||
@Test // gh-26155
|
||||
void copySelf() {
|
||||
NativeMessageHeaderAccessor accessor = new NativeMessageHeaderAccessor();
|
||||
accessor.addNativeHeader("foo", "bar");
|
||||
accessor.setHeader("otherHeader", "otherHeaderValue");
|
||||
accessor.setLeaveMutable(true);
|
||||
|
||||
// Does not fail with ConcurrentModificationException
|
||||
accessor.copyHeaders(accessor.getMessageHeaders());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue