This is an automated email from the ASF dual-hosted git repository.
reta pushed a commit to branch 3.6.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git
The following commit(s) were added to refs/heads/3.6.x-fixes by this push:
new 12a35866d4d [CXF-9205] Remove SOAP Header attribute duplications to
its child el. (#2960)
12a35866d4d is described below
commit 12a35866d4dfa7faf142590b254060657ac2df20
Author: jrihtarsic <[email protected]>
AuthorDate: Tue Mar 17 12:42:33 2026 +0100
[CXF-9205] Remove SOAP Header attribute duplications to its child el.
(#2960)
* [CXF-9205] Remove SOAP Header attribute duplications to its child elements
* [CXF-9205] Add unit tests for the fix
* [CXF-9205] Add message property to control the SOAP Header attribute
duplications to its child elements
---------
Co-authored-by: Joze RIHTARSIC <[email protected]>
(cherry picked from commit 3c24fd99050aab17c670bead72a40bfd65e7aa13)
---
.../soap/interceptor/ReadHeadersInterceptor.java | 58 +++++++++++------
.../interceptor/ReadHeadersInterceptorTest.java | 73 +++++++++++++++++++---
2 files changed, 102 insertions(+), 29 deletions(-)
diff --git
a/rt/bindings/soap/src/main/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptor.java
b/rt/bindings/soap/src/main/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptor.java
index 02d151ba7a3..7376c059e69 100644
---
a/rt/bindings/soap/src/main/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptor.java
+++
b/rt/bindings/soap/src/main/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptor.java
@@ -73,6 +73,9 @@ public class ReadHeadersInterceptor extends
AbstractSoapInterceptor {
public static final String BODY_EVENTS = "body.events";
public static final String ENVELOPE_PREFIX = "envelope.prefix";
public static final String BODY_PREFIX = "body.prefix";
+ private static final String SOAP_ADD_NAMESPACE_CONTEXT =
"org.apache.cxf.binding.soap.addNamespaceContext";
+ private static final String SOAP_PROPAGATE_ATTRIBUTES =
"org.apache.cxf.binding.soap.propagateHeaderAttributes";
+
/**
*
*/
@@ -195,7 +198,7 @@ public class ReadHeadersInterceptor extends
AbstractSoapInterceptor {
} else {
final boolean addNC =
MessageUtils.getContextualBoolean(
- message,
"org.apache.cxf.binding.soap.addNamespaceContext", false);
+ message, SOAP_ADD_NAMESPACE_CONTEXT, false);
Map<String, String> bodyNC = addNC ? new HashMap<String,
String>() : null;
if (addNC) {
// add the Envelope-Level declarations
@@ -230,26 +233,15 @@ public class ReadHeadersInterceptor extends
AbstractSoapInterceptor {
soapBody = DOMUtils.getChildrenWithName(element,
body.getNamespaceURI(),
body.getLocalPart());
-
- for (Element elem : elemList) {
+
+ final boolean copyParentAttributes =
MessageUtils.getContextualBoolean(
+ message, SOAP_PROPAGATE_ATTRIBUTES, false);
+
+ for (Element elem : elemList) {
Element hel = DOMUtils.getFirstElement(elem);
while (hel != null) {
- // Need to add any attributes that are present on
the parent element
- // which otherwise would be lost.
- if (elem.hasAttributes()) {
- NamedNodeMap nnp = elem.getAttributes();
- for (int ct = 0; ct < nnp.getLength(); ct++) {
- Node attr = nnp.item(ct);
- Node headerAttrNode = hel.hasAttributes()
? hel.getAttributes()
-
.getNamedItemNS(attr.getNamespaceURI(), attr.getLocalName()) : null;
-
- if (headerAttrNode == null) {
- Attr attribute = hel.getOwnerDocument()
-
.createAttributeNS(attr.getNamespaceURI(), attr.getNodeName());
-
attribute.setNodeValue(attr.getNodeValue());
- hel.setAttributeNodeNS(attribute);
- }
- }
+ if (elem.hasAttributes() && copyParentAttributes){
+ propagateHeaderAttributes(elem, hel);
}
HeaderProcessor p = bus == null ? null :
bus.getExtension(HeaderManager.class)
@@ -312,6 +304,34 @@ public class ReadHeadersInterceptor extends
AbstractSoapInterceptor {
}
}
}
+
+ /**
+ * Copies the attributes of the SOAP header element to its child elements,
if
+ * those attributes are not already present on the child.
+ *
+ * This method is retained for backward‑compatibility and should not be
used
+ * in new code, as it invalidates the signatures on the child elements.
+ *
+ * @param soapHeaderEl the SOAP header element
+ * @param child the child element of the SOAP header element
+ */
+ private static void propagateHeaderAttributes(Element soapHeaderEl,
Element child) {
+ if (soapHeaderEl.hasAttributes()) {
+ NamedNodeMap nnp = soapHeaderEl.getAttributes();
+ for (int ct = 0; ct < nnp.getLength(); ct++) {
+ Node attr = nnp.item(ct);
+ Node headerAttrNode = child.hasAttributes() ?
child.getAttributes()
+ .getNamedItemNS(attr.getNamespaceURI(),
attr.getLocalName()) : null;
+
+ if (headerAttrNode == null) {
+ Attr attribute = child.getOwnerDocument()
+ .createAttributeNS(attr.getNamespaceURI(),
attr.getNodeName());
+ attribute.setNodeValue(attr.getNodeValue());
+ child.setAttributeNodeNS(attribute);
+ }
+ }
+ }
+ }
//CHECKSTYLE:ON
private void addCurrentNamespaceDecls(XMLStreamReader xmlReader,
Map<String, String> bodyNsMap) {
diff --git
a/rt/bindings/soap/src/test/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptorTest.java
b/rt/bindings/soap/src/test/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptorTest.java
index e4b097f5555..906d42a28b3 100644
---
a/rt/bindings/soap/src/test/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptorTest.java
+++
b/rt/bindings/soap/src/test/java/org/apache/cxf/binding/soap/interceptor/ReadHeadersInterceptorTest.java
@@ -20,17 +20,26 @@
package org.apache.cxf.binding.soap.interceptor;
import java.io.ByteArrayInputStream;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
import java.util.Map;
import javax.xml.stream.XMLStreamReader;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+
import org.apache.cxf.binding.soap.Soap11;
import org.apache.cxf.binding.soap.SoapMessage;
+import org.apache.cxf.headers.Header;
import org.apache.cxf.helpers.CastUtils;
import org.apache.cxf.staxutils.StaxUtils;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -39,42 +48,86 @@ import static org.junit.Assert.assertNull;
/**
*
*/
+@RunWith(Parameterized.class)
public class ReadHeadersInterceptorTest {
private static final byte[] TEST_SOAP =
("<soap:Envelope
xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'"
+ " xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'"
- + " xmlns:xs='http://www.w3.org/2001/XMLSchema'
xmlns:bar='tmp:bar'>"
+ + " xmlns:xs='http://www.w3.org/2001/XMLSchema'
xmlns:bar='tmp:bar'"
+ + " xmlns:x='http://example.com/schema'>"
+ + "<soap:Header testAttr='testValue'>"
+ + "<x:AuthToken>Token</x:AuthToken>"
+ + "</soap:Header>"
+ "<soap:Body>"
+ "<ns2:payload xmlns:ns2='urn:tmp:foo'/>"
+ "</soap:Body>"
+ "</soap:Envelope>").getBytes();
+ private final Object attributeValue;
+ private final int expectedTransferredAttributeCount;
+ private final boolean expectedAddNSContext;
private ReadHeadersInterceptor interceptor;
+ public ReadHeadersInterceptorTest(Object attribute, int expectedCount,
boolean expectedAddNSContext) {
+ this.attributeValue = attribute;
+ this.expectedTransferredAttributeCount = expectedCount;
+ this.expectedAddNSContext = expectedAddNSContext;
+ }
+
+ @Parameterized.Parameters(name = "{index}: attribute={0},
expectedCount={1}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] {
+ {"false", 0, false},
+ {"true", 1, true},
+ {"TRUE", 1, true},
+ {true, 1, true},
+ {false, 0, false},
+ {null, 0, false},
+ {"", 0, false}
+ });
+ }
+
@Before
public void setUp() {
interceptor = new ReadHeadersInterceptor(null);
}
@Test
- public void testNotAddNSContext() throws Exception {
+ public void testAddNSContext() throws Exception {
SoapMessage message = setUpMessage();
+ message.put("org.apache.cxf.binding.soap.addNamespaceContext",
attributeValue);
interceptor.handleMessage(message);
Map<String, String> nsc = CastUtils.cast((Map<?,
?>)message.get("soap.body.ns.context"));
- assertNull(nsc);
+ if (expectedAddNSContext) {
+ assertNotNull(nsc);
+ assertEquals("http://www.w3.org/2001/XMLSchema-instance",
nsc.get("xsi"));
+ assertEquals("http://www.w3.org/2001/XMLSchema", nsc.get("xs"));
+ assertEquals("tmp:bar", nsc.get("bar"));
+ } else {
+ assertNull(nsc);
+ }
}
+ /**
+ * Test to verify that SOAP header attributes are not transferred to child
elements of the header.
+ * See CXF-9205 for more details.
+ *
+ * @throws Exception may be thrown during test execution
+ */
@Test
- public void testAddNSContext() throws Exception {
+ public void testHeaderAttributesTransferredToChildren() throws Exception {
SoapMessage message = setUpMessage();
- message.put("org.apache.cxf.binding.soap.addNamespaceContext", "true");
+ message.put("org.apache.cxf.binding.soap.propagateHeaderAttributes",
attributeValue);
interceptor.handleMessage(message);
- Map<String, String> nsc = CastUtils.cast((Map<?,
?>)message.get("soap.body.ns.context"));
- assertNotNull(nsc);
- assertEquals("http://www.w3.org/2001/XMLSchema-instance",
nsc.get("xsi"));
- assertEquals("http://www.w3.org/2001/XMLSchema", nsc.get("xs"));
- assertEquals("tmp:bar", nsc.get("bar"));
+ List<Header> cxfHeaders = message.getHeaders();
+ assertEquals(1, cxfHeaders.size());
+ Element headerElement = (Element) cxfHeaders.get(0).getObject();
+ NamedNodeMap attributes = headerElement.getAttributes();
+ assertEquals("AuthToken", headerElement.getLocalName());
+ assertEquals("http://example.com/schema",
headerElement.getNamespaceURI());
+ // The test SOAP header attribute "testAttr", must not be transferred
to child element "AuthToken"
+ assertEquals(expectedTransferredAttributeCount,
attributes.getLength());
}
private SoapMessage setUpMessage() throws Exception {