This is an automated email from the ASF dual-hosted git repository.

robertlazarski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/axis-axis2-java-core.git

commit 921c4581fb05b8dc4fb2b921cd818c296a11c919
Author: Robert Lazarski <[email protected]>
AuthorDate: Sat Feb 14 08:10:09 2026 -1000

    Fix AXIS2-3879 + AXIS2-4146: Respect user-set HTTP status codes on responses
    
    The transport sender unconditionally set HTTP 500 for all fault responses,
    overwriting any user-specified status code (e.g., 503, 400). This change
    checks Constants.HTTP_RESPONSE_STATE on the message context before falling
    back to the default, and propagates the property through fault contexts.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
---
 .../apache/axis2/util/MessageContextBuilder.java   |  3 ++
 .../http/AbstractHTTPTransportSender.java          | 45 +++++++++++++++-
 .../apache/axis2/transport/http/AxisServlet.java   |  7 ++-
 .../transport/http/HTTPTransportSenderTest.java    | 62 ++++++++++++++++++++++
 .../http/mock/MockHttpServletResponse.java         |  8 +--
 5 files changed, 119 insertions(+), 6 deletions(-)

diff --git 
a/modules/kernel/src/org/apache/axis2/util/MessageContextBuilder.java 
b/modules/kernel/src/org/apache/axis2/util/MessageContextBuilder.java
index ccc4d9b612..9bf82f965c 100644
--- a/modules/kernel/src/org/apache/axis2/util/MessageContextBuilder.java
+++ b/modules/kernel/src/org/apache/axis2/util/MessageContextBuilder.java
@@ -114,6 +114,9 @@ public class MessageContextBuilder {
         newmsgCtx.setProperty(Constants.OUT_TRANSPORT_INFO,
                               
inMessageContext.getProperty(Constants.OUT_TRANSPORT_INFO));
 
+        newmsgCtx.setProperty(Constants.HTTP_RESPONSE_STATE,
+                              
inMessageContext.getProperty(Constants.HTTP_RESPONSE_STATE));
+
         handleCorrelationID(inMessageContext,newmsgCtx);
         return newmsgCtx;
     }
diff --git 
a/modules/transport/http/src/main/java/org/apache/axis2/transport/http/AbstractHTTPTransportSender.java
 
b/modules/transport/http/src/main/java/org/apache/axis2/transport/http/AbstractHTTPTransportSender.java
index 06c9bcc094..8d89e9d6dc 100644
--- 
a/modules/transport/http/src/main/java/org/apache/axis2/transport/http/AbstractHTTPTransportSender.java
+++ 
b/modules/transport/http/src/main/java/org/apache/axis2/transport/http/AbstractHTTPTransportSender.java
@@ -271,9 +271,11 @@ public abstract class AbstractHTTPTransportSender extends 
AbstractHandler implem
             servletBasedOutTransportInfo =
                     (ServletBasedOutTransportInfo) transportInfo;
 
-            // if sending a fault, set HTTP status code to 500
+            // if sending a fault, set HTTP status code (respecting user-set 
status codes)
             if (msgContext.isFault()) {
-                
servletBasedOutTransportInfo.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+                int faultStatus = resolveHttpStatusCode(msgContext,
+                        HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+                servletBasedOutTransportInfo.setStatus(faultStatus);
             }
 
             Object customHeaders = 
msgContext.getProperty(HTTPConstants.HTTP_HEADERS);
@@ -299,6 +301,12 @@ public abstract class AbstractHTTPTransportSender extends 
AbstractHandler implem
                 }
             }
         } else if (transportInfo instanceof AxisHttpResponse) {
+            if (msgContext.isFault()) {
+                int faultStatus = resolveHttpStatusCode(msgContext,
+                        HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+                ((AxisHttpResponse) transportInfo).setStatus(faultStatus);
+            }
+
             Object customHeaders = 
msgContext.getProperty(HTTPConstants.HTTP_HEADERS);
             if (customHeaders != null) {
                 if (customHeaders instanceof List) {
@@ -360,6 +368,39 @@ public abstract class AbstractHTTPTransportSender extends 
AbstractHandler implem
         }
     }
 
+    /**
+     * Resolves the HTTP status code to use for a response. Checks the message 
context
+     * (and the inbound message context as a fallback) for a user-specified 
status code
+     * via {@link Constants#HTTP_RESPONSE_STATE}. If none is found, returns 
the default.
+     *
+     * @param msgContext the current (outbound) message context
+     * @param defaultStatus the default HTTP status code to use if no user 
override is found
+     * @return the resolved HTTP status code
+     */
+    private static int resolveHttpStatusCode(MessageContext msgContext, int 
defaultStatus) {
+        // Check the outbound message context first
+        String statusStr = (String) 
msgContext.getProperty(Constants.HTTP_RESPONSE_STATE);
+
+        // Fall back to the inbound message context (createFaultMessageContext 
may not
+        // have copied this property in older code paths)
+        if (statusStr == null) {
+            MessageContext inMsgCtx =
+                    (MessageContext) 
msgContext.getProperty(MessageContext.IN_MESSAGE_CONTEXT);
+            if (inMsgCtx != null) {
+                statusStr = (String) 
inMsgCtx.getProperty(Constants.HTTP_RESPONSE_STATE);
+            }
+        }
+
+        if (statusStr != null) {
+            try {
+                return Integer.parseInt(statusStr);
+            } catch (NumberFormatException e) {
+                log.error("Invalid HTTP status code value: " + statusStr, e);
+            }
+        }
+        return defaultStatus;
+    }
+
     private void writeMessageWithCommons(MessageContext messageContext,
                                          EndpointReference toEPR, 
OMOutputFormat format)
             throws AxisFault {
diff --git 
a/modules/transport/http/src/main/java/org/apache/axis2/transport/http/AxisServlet.java
 
b/modules/transport/http/src/main/java/org/apache/axis2/transport/http/AxisServlet.java
index 96f99098f5..8fc6ebef78 100644
--- 
a/modules/transport/http/src/main/java/org/apache/axis2/transport/http/AxisServlet.java
+++ 
b/modules/transport/http/src/main/java/org/apache/axis2/transport/http/AxisServlet.java
@@ -512,7 +512,12 @@ public class AxisServlet extends HttpServlet {
             if (valueElement != null) {
                 if 
(SOAP12Constants.FAULT_CODE_SENDER.equals(valueElement.getTextAsQName().getLocalPart())
                         && !msgContext.isDoingREST()) {
-                    response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+                    // Only set SOAP 1.2 Sender → 400 if the user hasn't 
already set a custom status
+                    if (msgContext.getProperty(Constants.HTTP_RESPONSE_STATE) 
== null) {
+                        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+                        faultContext.setProperty(Constants.HTTP_RESPONSE_STATE,
+                                
String.valueOf(HttpServletResponse.SC_BAD_REQUEST));
+                    }
                 }
             }
         }
diff --git 
a/modules/transport/http/src/test/java/org/apache/axis2/transport/http/HTTPTransportSenderTest.java
 
b/modules/transport/http/src/test/java/org/apache/axis2/transport/http/HTTPTransportSenderTest.java
index a3353da4e8..173b1541e5 100644
--- 
a/modules/transport/http/src/test/java/org/apache/axis2/transport/http/HTTPTransportSenderTest.java
+++ 
b/modules/transport/http/src/test/java/org/apache/axis2/transport/http/HTTPTransportSenderTest.java
@@ -149,6 +149,68 @@ public abstract class HTTPTransportSenderTest extends 
TestCase  {
 
     }
     
+    /**
+     * AXIS2-3879: Fault with a user-specified custom status code should use 
that code,
+     * not the default 500.
+     */
+    public void testFaultWithCustomStatusCode() throws Exception {
+        MockHttpServletResponse httpResponse = new MockHttpServletResponse();
+        ServletBasedOutTransportInfo info = new 
ServletBasedOutTransportInfo(httpResponse);
+        configAndRunFault(httpResponse, info, "503", getTransportSender());
+        assertEquals("Custom status code should be respected", 503, 
httpResponse.getStatus());
+    }
+
+    /**
+     * AXIS2-3879: Fault without a custom status code should default to 500.
+     */
+    public void testFaultDefaultsTo500() throws Exception {
+        MockHttpServletResponse httpResponse = new MockHttpServletResponse();
+        ServletBasedOutTransportInfo info = new 
ServletBasedOutTransportInfo(httpResponse);
+        configAndRunFault(httpResponse, info, null, getTransportSender());
+        assertEquals("Default fault status should be 500", 500, 
httpResponse.getStatus());
+    }
+
+    /**
+     * AXIS2-4146: User-set status code 400 should not be overwritten to 500.
+     */
+    public void testFaultWithStatus400NotOverwritten() throws Exception {
+        MockHttpServletResponse httpResponse = new MockHttpServletResponse();
+        ServletBasedOutTransportInfo info = new 
ServletBasedOutTransportInfo(httpResponse);
+        configAndRunFault(httpResponse, info, "400", getTransportSender());
+        assertEquals("Status 400 should not be overwritten to 500", 400, 
httpResponse.getStatus());
+    }
+
+    private static void configAndRunFault(MockHttpServletResponse outResponse,
+            OutTransportInfo outTransportInfo, String customStatus,
+            TransportSender sender) throws Exception {
+        ConfigurationContext confContext = ConfigurationContextFactory
+                .createEmptyConfigurationContext();
+        TransportOutDescription transportOut = new 
TransportOutDescription("http");
+        Parameter param = new Parameter(HTTPConstants.OMIT_SOAP_12_ACTION, 
false);
+        SOAPEnvelope envelope = getFaultEnvelope();
+        MessageContext msgContext = new MessageContext();
+
+        transportOut.addParameter(param);
+        msgContext.setEnvelope(envelope);
+        msgContext.setProperty(MessageContext.TRANSPORT_OUT,
+                outResponse.getByteArrayOutputStream());
+        msgContext.setProperty(Constants.OUT_TRANSPORT_INFO, outTransportInfo);
+        msgContext.setTransportOut(transportOut);
+        msgContext.setConfigurationContext(confContext);
+        if (customStatus != null) {
+            msgContext.setProperty(Constants.HTTP_RESPONSE_STATE, 
customStatus);
+        }
+        sender.init(confContext, transportOut);
+        sender.invoke(msgContext);
+    }
+
+    static SOAPEnvelope getFaultEnvelope() {
+        SOAPFactory soapFac = OMAbstractFactory.getSOAP11Factory();
+        SOAPEnvelope envelope = soapFac.getDefaultFaultEnvelope();
+        envelope.getBody().getFault().getReason().setText("test fault");
+        return envelope;
+    }
+
     static SOAPEnvelope getEnvelope() throws IOException {
         SOAPFactory soapFac = OMAbstractFactory.getSOAP11Factory();
         OMFactory omFac = OMAbstractFactory.getOMFactory();
diff --git 
a/modules/transport/http/src/test/java/org/apache/axis2/transport/http/mock/MockHttpServletResponse.java
 
b/modules/transport/http/src/test/java/org/apache/axis2/transport/http/mock/MockHttpServletResponse.java
index a3c1e75375..cc5473ba2b 100644
--- 
a/modules/transport/http/src/test/java/org/apache/axis2/transport/http/mock/MockHttpServletResponse.java
+++ 
b/modules/transport/http/src/test/java/org/apache/axis2/transport/http/mock/MockHttpServletResponse.java
@@ -49,8 +49,9 @@ public class MockHttpServletResponse implements 
HttpServletResponse, OutTranspor
     private OutputStream outStream;
     private boolean committed;
     private HeaderGroup headerGroup;
-    private ByteArrayOutputStream byteArrayOutputStream;   
-    
+    private ByteArrayOutputStream byteArrayOutputStream;
+    private int status = 200;
+
     public MockHttpServletResponse() {
        headerGroup = new HeaderGroup();
         byteArrayOutputStream = new ByteArrayOutputStream();
@@ -203,6 +204,7 @@ public class MockHttpServletResponse implements 
HttpServletResponse, OutTranspor
 
     @Override
     public void setStatus(int sc) {
+        this.status = sc;
     }
 
     @Override
@@ -222,7 +224,7 @@ public class MockHttpServletResponse implements 
HttpServletResponse, OutTranspor
 
     @Override
     public int getStatus() {
-        throw new UnsupportedOperationException();
+        return status;
     }
 
     @Override

Reply via email to