4

I am trying to send a soap request and keep getting HTTP response 411 error because of larger size of soap request. In most of the cases soap request length is more that 8k.

ERROR MESSAGE

2020-02-27 08:26:09,618 WARNING [100] [org.apache.cxf.phase.PhaseInterceptorChain] (my-thread-1) Interceptor for {http://example.com}CreationService#{http://cxf.apache.org/jaxws/dispatch}Invoke has thrown exception, unwinding now: org.apache.cxf.interceptor.Fault: Could not send Message.
    at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:64) [cxf-core-3.1.6.jar:3.1.6]
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308) [cxf-core-3.1.6.jar:3.1.6]
    at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:514) [cxf-core-3.1.6.jar:3.1.6]
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:423) [cxf-core-3.1.6.jar:3.1.6]
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:324) [cxf-core-3.1.6.jar:3.1.6]
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:277) [cxf-core-3.1.6.jar:3.1.6]
    at org.apache.cxf.endpoint.ClientImpl.invokeWrapped(ClientImpl.java:312) [cxf-core-3.1.6.jar:3.1.6]
    at org.apache.cxf.jaxws.DispatchImpl.invoke(DispatchImpl.java:327) [cxf-rt-frontend-jaxws-3.1.6.jar:3.1.6]
    at org.apache.cxf.jaxws.DispatchImpl.invoke(DispatchImpl.java:246) [cxf-rt-frontend-jaxws-3.1.6.jar:3.1.6]
    ...
Caused by: org.apache.cxf.transport.http.HTTPException: HTTP response '411: Length Required' when communicating with http://192.100.110.17:8504/example/services/CreationREQ
    at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.doProcessResponseCode(HTTPConduit.java:1600) [cxf-rt-transports-http-3.1.6.jar:3.1.6]
    at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1607) [cxf-rt-transports-http-3.1.6.jar:3.1.6]
    at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1551) [cxf-rt-transports-http-3.1.6.jar:3.1.6]
    at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1348) [cxf-rt-transports-http-3.1.6.jar:3.1.6]
    at org.apache.cxf.io.CacheAndWriteOutputStream.postClose(CacheAndWriteOutputStream.java:56) [cxf-core-3.1.6.jar:3.1.6]
    at org.apache.cxf.io.CachedOutputStream.close(CachedOutputStream.java:216) [cxf-core-3.1.6.jar:3.1.6]
    at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56) [cxf-core-3.1.6.jar:3.1.6]
    at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:651) [cxf-rt-transports-http-3.1.6.jar:3.1.6]
    at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62) [cxf-core-3.1.6.jar:3.1.6]
    ... 84 more

2020-02-27 08:26:09,621 ERROR [100] [org.jboss.as.ejb3.invocation] (my-thread-1) JBAS014134: EJB invocation failed on DaoFacade component for method public abstract void com.example.addon.core.dao.facade.DaoFacadeInterface.invokeExternalService(com.example.db.models.Synchronizable) throws com.example.addon.addon.SOAPException: javax.ejb.EJBException: javax.xml.ws.WebServiceException: Could not send Message.
    ...
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [rt.jar:1.7.0_79]
    at java.lang.Thread.run(Thread.java:745) [rt.jar:1.7.0_79]
Caused by: javax.xml.ws.WebServiceException: Could not send Message.
    at org.apache.cxf.jaxws.DispatchImpl.mapException(DispatchImpl.java:272) [cxf-rt-frontend-jaxws-3.1.6.jar:3.1.6]
    at org.apache.cxf.jaxws.DispatchImpl.invoke(DispatchImpl.java:334) [cxf-rt-frontend-jaxws-3.1.6.jar:3.1.6]
    at org.apache.cxf.jaxws.DispatchImpl.invoke(DispatchImpl.java:246) [cxf-rt-frontend-jaxws-3.1.6.jar:3.1.6]
    ...
    at org.jboss.invocation.InterceptorContext.proceed(InterceptorContext.java:288) [jboss-invocation-1.1.2.Final-redhat-1.jar:1.1.2.Final-redhat-1]
    at org.jboss.as.ejb3.tx.CMTTxInterceptor.invokeInOurTx(CMTTxInterceptor.java:280) [jboss-as-ejb3-7.5.0.Final-redhat-21.jar:7.5.0.Final-redhat-21]
    ... 40 more
Caused by: org.apache.cxf.transport.http.HTTPException: HTTP response '411: Length Required' when communicating with http://192.100.110.17:8504/example/services/CreationREQ
    at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.doProcessResponseCode(HTTPConduit.java:1600) [cxf-rt-transports-http-3.1.6.jar:3.1.6]
    at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponseInternal(HTTPConduit.java:1607) [cxf-rt-transports-http-3.1.6.jar:3.1.6]
    at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.handleResponse(HTTPConduit.java:1551) [cxf-rt-transports-http-3.1.6.jar:3.1.6]
    at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.close(HTTPConduit.java:1348) [cxf-rt-transports-http-3.1.6.jar:3.1.6]
    at org.apache.cxf.io.CacheAndWriteOutputStream.postClose(CacheAndWriteOutputStream.java:56) [cxf-core-3.1.6.jar:3.1.6]
    at org.apache.cxf.io.CachedOutputStream.close(CachedOutputStream.java:216) [cxf-core-3.1.6.jar:3.1.6]
    at org.apache.cxf.transport.AbstractConduit.close(AbstractConduit.java:56) [cxf-core-3.1.6.jar:3.1.6]
    at org.apache.cxf.transport.http.HTTPConduit.close(HTTPConduit.java:651) [cxf-rt-transports-http-3.1.6.jar:3.1.6]
    at org.apache.cxf.interceptor.MessageSenderInterceptor$MessageSenderEndingInterceptor.handleMessage(MessageSenderInterceptor.java:62) [cxf-core-3.1.6.jar:3.1.6]
    at org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:308) [cxf-core-3.1.6.jar:3.1.6]
    at org.apache.cxf.endpoint.ClientImpl.doInvoke(ClientImpl.java:514) [cxf-core-3.1.6.jar:3.1.6]
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:423) [cxf-core-3.1.6.jar:3.1.6]
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:324) [cxf-core-3.1.6.jar:3.1.6]
    at org.apache.cxf.endpoint.ClientImpl.invoke(ClientImpl.java:277) [cxf-core-3.1.6.jar:3.1.6]
    at org.apache.cxf.endpoint.ClientImpl.invokeWrapped(ClientImpl.java:312) [cxf-core-3.1.6.jar:3.1.6]
    at org.apache.cxf.jaxws.DispatchImpl.invoke(DispatchImpl.java:327) [cxf-rt-frontend-jaxws-3.1.6.jar:3.1.6]
    ... 77 more

Here is my soap client code looks like:

import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.GeneralSecurityException;

import javax.xml.namespace.QName;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
import javax.xml.transform.stream.StreamSource;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.Dispatch;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPBinding;

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.jaxws.DispatchImpl;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.apache.log4j.Logger;

public class SOAPClient {

    private static final String CXF_RESPONSE_CODE = "org.apache.cxf.message.Message.RESPONSE_CODE";
    private static final String JAXRS_RESPONSE_CODE = "javax.xml.ws.http.response.code";
    private static Logger logger = Logger.getLogger(SOAPClient.class);

    /**
     * Nested class only accessible after {@link SOAPClient#builder()} method call
     * <p>
     * It provides a Fluent Interface to make the client code more readable.
     */
    public static class Builder {
        private String endpoint;
        private String namespace;
        private String serviceName;
        private Boolean basicAuthentication = FALSE;
        private String username;
        private String password;
        private Long connectionTimeout;
        private Long receiveTimeout;
        private String portName;
        private String soapAction;
        private StreamSource soapRequest;
        private OutputStream soapResponse;
        private Dispatch<SOAPMessage> dispatch;
        private SOAPMessage requestSOAPMessage;
        private SOAPMessage returnedSOAPMessage;

        public Builder endpoint(String endpoint) {
            this.endpoint = endpoint;
            return this;
        }

        public Builder namespace(String namespace) {
            this.namespace = namespace;
            return this;
        }

        public Builder serviceName(String serviceName) {
            this.serviceName = serviceName;
            return this;
        }

        public Builder portName(String portName) {
            this.portName = portName;
            return this;
        }

        public Builder soapAction(String soapAction) {
            this.soapAction = soapAction;
            return this;
        }

        public Builder basicAuthentication(Boolean basicAuthentication) {
            this.basicAuthentication = basicAuthentication;
            return this;
        }

        public Builder username(String username) {
            this.username = username;
            return this;
        }

        public Builder password(String password) {
            this.password = password;
            return this;
        }

        public Builder connectionTimeout(Long connectionTimeout) {
            this.connectionTimeout = connectionTimeout;
            return this;
        }

        public Builder receiveTimeout(Long receiveTimeout) {
            this.receiveTimeout = receiveTimeout;
            return this;
        }       

        public Builder soapRequest(File soapRequest) throws IOException {
            if (soapRequest == null)
                throw new IllegalStateException("soapRequest not set");
            InputStream is = Files.newInputStream(soapRequest.toPath());
            this.soapRequest = new StreamSource(is, StandardCharsets.UTF_8.name());
            return this;
        }

        public Builder soapRequest(InputStream soapRequest) {
            this.soapRequest = new StreamSource(soapRequest);
            return this;
        }

        public Builder soapRequest(Reader soapRequest) {
            this.soapRequest = new StreamSource(soapRequest);
            return this;
        }

        public int execute(File soapResponse) throws IOException, SOAPException, GeneralSecurityException {
            if (soapResponse == null)
                throw new IllegalStateException("soapResponse not set");
            this.soapResponse = Files.newOutputStream(soapResponse.toPath());
            return execute();
        }

        public int execute(OutputStream soapResponse) throws IOException, SOAPException, GeneralSecurityException {
            if (soapResponse == null)
                throw new IllegalStateException("soapResponse not set");
            this.soapResponse = soapResponse;
            return execute();
        }

        private int execute() throws SOAPException, IOException, GeneralSecurityException {
            createDispatch();
            addBasicAuthentication();
            addSoapAction();
            addTimeouts();
            setSOAPMessage();
            returnedSOAPMessage = dispatch.invoke(requestSOAPMessage);
            returnedSOAPMessage.writeTo(soapResponse);
            return Integer.parseInt(getResponseCode());
        }

        private String getResponseCode() {
            Object responseCode = dispatch.getResponseContext().get(CXF_RESPONSE_CODE);
            if (responseCode != null)
                return responseCode.toString();
            else {
                responseCode = dispatch.getResponseContext().get(JAXRS_RESPONSE_CODE);
                if (responseCode != null)
                    return responseCode.toString();
                else
                    return "-1";
            }
        }

        private void createDispatch() {
            if (endpoint == null || endpoint.isEmpty())
                throw new IllegalStateException("endpoint not set");

            QName serviceQName = new QName(namespace, serviceName);
            logger.debug("Creating the Service QName, " + serviceQName);

            // Add a separate name space for method if required
            QName portQName = new QName(namespace, portName);

            logger.debug("Creating port QName, " + portQName);

            Service serviceRef = Service.create(serviceQName);
            serviceRef.addPort(portQName, SOAPBinding.SOAP11HTTP_BINDING, endpoint);

            dispatch = serviceRef.createDispatch(portQName, SOAPMessage.class, Service.Mode.MESSAGE);
        }

        private void addBasicAuthentication() throws GeneralSecurityException {
            if (basicAuthentication)
                setBasicAuthentication();
        }

        private void setBasicAuthentication() throws GeneralSecurityException {
            if (username == null || username.isEmpty())
                throw new IllegalStateException("username not set with BasicAuthentication");

            if (password == null || password.isEmpty())
                throw new IllegalStateException("password not set with BasicAuthentication");

            dispatch.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, username);
            dispatch.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, JcodificaLib.decrypt(password));
        }

        private void addSoapAction() {
            if (soapAction != null)
                setSoapAction();
        }

        private void setSoapAction() {
            logger.debug("SoapAction:" + soapAction);
            dispatch.getRequestContext().put(BindingProvider.SOAPACTION_USE_PROPERTY, TRUE);
            dispatch.getRequestContext().put(BindingProvider.SOAPACTION_URI_PROPERTY, soapAction);
        }

        private void addTimeouts() {
            if (receiveTimeout != null) {
                setReceiveTimeout();
            }

            if (connectionTimeout != null) {
                setConnectionTimeout();
            }
        }

        private void setConnectionTimeout() {
            logger.debug("connectionTimeout:" + connectionTimeout);
            dispatch.getRequestContext().put("javax.xml.ws.client.connectionTimeout", connectionTimeout);
        }

        private void setReceiveTimeout() {
            logger.debug("receiveTimeout:" + receiveTimeout);
            dispatch.getRequestContext().put("javax.xml.ws.client.receiveTimeout", receiveTimeout);
        }

        private void setSOAPMessage() throws SOAPException {
            MessageFactory messageFactory = MessageFactory.newInstance();
            requestSOAPMessage = messageFactory.createMessage();
            SOAPPart soapPart = requestSOAPMessage.getSOAPPart();
            soapPart.setContent(soapRequest);
        }   

    }

    public static SOAPClient.Builder builder() {
        return new SOAPClient.Builder();
    }
}

I tried fixing the issue by enabling chunking and setting chunking threshold to 8192 as below, but I am getting the same error message:

private void setHttpClientPolicies() {
        final Client client = ((DispatchImpl<SOAPMessage>) dispatch).getClient();
        final HTTPConduit httpConduit = (HTTPConduit) client.getConduit();
        final HTTPClientPolicy httpClientPolicy = httpConduit.getClient();
        httpConduit.setClient(httpClientPolicy);

        httpClientPolicy.setAllowChunking(TRUE);
        httpClientPolicy.setChunkingThreshold(8192);
}

Any clue how to fix the issue ?

dev
  • 1,343
  • 2
  • 19
  • 40
  • Did you try [this](https://stackoverflow.com/questions/36055056/web-service-request-8kb-failed-with-apache-cxf-client-on-jboss-http-respons) solution? It seeems that the poster solved his issue. – s.fuhrm Feb 27 '20 at 19:33
  • @s.fuhrm - I had a look at it. But it is cxf configuration for spring. I'm not using spring as you can see in above soap client code. I tried looking for similar configuration but no luck. – dev Feb 28 '20 at 06:03
  • is there any chance you get logfiles from the server? It's obvious that the server is detecting a problem that we can't analyze. – s.fuhrm Feb 28 '20 at 15:26
  • @s.fuhrm - It is difficult to get the server logfile. I don't think the issue is from server side. When I try sending a `CURL` command with this large SOAP request everything works fine and I get a proper response. I suspect the issue is in client code itself. May be something to do with chunking. – dev Feb 29 '20 at 16:41
  • good you've taken a look at that. Then maybe find the difference between your SOAP request vs. the CURL request. The server maybe has additional information about a possibly wrong request - that's what I meant. – s.fuhrm Mar 01 '20 at 15:22
  • @s.fuhrm - The only difference I see is `Content-Length` in the header of CURL command. I'm trying to add a content-length in my soap client now to see if that works!!! – dev Mar 01 '20 at 16:58
  • It didn't work :( – dev Mar 03 '20 at 03:19
  • Did you tried `setAllowChunking(false)`? – Ori Marko Mar 03 '20 at 06:58
  • Yes, I disabled chunking and tried, but still getting the same error. – dev Mar 03 '20 at 09:26
  • Have you tried it with SoapUI or some other established Soap client and see if those works? – Tarun Lalwani Mar 04 '20 at 07:24
  • @TarunLalwani - Yes, I have tried it using CURL command and it works fine with it. – dev Mar 04 '20 at 11:35
  • Did you tried both adding Content-Length header and disallowing chucking together ? – e.g78 Mar 04 '20 at 16:18
  • Probably curl is making a GET request while SOAP works with POST requests. Try with CURL using POST method (not default) and let us know. PS: I think setting the ContentLength is a good way to go, you sure you did it properly? – rakwaht Mar 05 '20 at 10:11

4 Answers4

2

I suspect you may try to set the content length in the header. IE, the size of of your request using Hypertext Transfer Protocol (HTTP/1.1): Message Syntax and Routing. I would guess that if you correctly identified the size of the request the server might not choke on it. Worth a try.

Content-Length: SIZE OF REQUIRED DATA IN BYTES \n\n

It may require accessing some lower level portions of your request framework. References: https://www.rfc-editor.org/rfc/rfc7230#section-3.3.2 https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Length

Community
  • 1
  • 1
2

I would try the contrary of what you did. I suspect the server is not supporting chunking. Therefore, you should configure CXF to disable (and not enable!) chunking, like the doc suggests it:

If you are getting strange errors (generally not soap faults, but other HTTP type errors) when trying to interact with a service, try turning off chunking to see if that helps.

TacheDeChoco
  • 3,683
  • 1
  • 14
  • 17
1

SOAP request by org.apache.http.client.HttpClient(4.1) You have to specify data length. req_xml.length() // SOAP request(xml) read-in File req_xml = new File("test/xml/request.xml");

 // SOAP request send
 HttpPost post = new HttpPost("http://localhost:8080/test/api/");
 post.setEntity(new InputStreamEntity(new FileInputStream(req_xml), **req_xml.length()**));
 post.setHeader("Content-type", "text/xml; charset=UTF-8");
 post.setHeader("SOAPAction", "");
 HttpClient client = new DefaultHttpClient();
 HttpResponse response = client.execute(post);

 // SOAP response(xml) get
 String res_xml = EntityUtils.toString(response.getEntity());

Check that working code just supply xml and change web path. You have whole builder code where I don't understand are you doing post/get.By Default everything is http get.

createDispatch();
            addBasicAuthentication();
            addSoapAction();
            addTimeouts();
            setSOAPMessage();
            returnedSOAPMessage = dispatch.invoke(requestSOAPMessage);
            returnedSOAPMessage.writeTo(soapResponse);
            return Integer.parseInt(getResponseCode());
Jin Thakur
  • 2,711
  • 18
  • 15
0

Answering my own question.

The answers posted here are pointing correctly that by disabling chunking or adding Content-Length the issue can be resolved. But my challenge was that I wasn't able to disable the chunking as it can be seen in my question description.

So I was trying to add Content-Length to http header and as you can see I'm not using any Apache library to write my soap client code so it was difficult to figure out how to add the Content-Length to the HTTP Header. Lastly I found a way to do so. The code snippet can be found below:

private void setSOAPMessage() throws SOAPException {
        MessageFactory messageFactory = MessageFactory.newInstance();
        requestSOAPMessage = messageFactory.createMessage();
        SOAPPart soapPart = requestSOAPMessage.getSOAPPart();
        soapPart.setContent(soapRequest);

        // Added Content-Length to HTTP Header
        Map<String, List<String>> requestHeaderMap = new HashMap<String, List<String>>();
        requestHeaderMap.put("Content-Length", Collections.singletonList(String.valueOf(soapRequestXMLSize)));
        dispatch.getRequestContext().put(MessageContext.HTTP_REQUEST_HEADERS, requestHeaderMap);
}

Here the soapRequestXMLSize was calculated from the actual soap request xml which is stored as String in my case.

Integer soapRequestSize = soapRequestXML.length();
dev
  • 1,343
  • 2
  • 19
  • 40
  • I replied same that you have to specify length and you are answering your own question and marking it as Answer. – Jin Thakur Mar 11 '20 at 17:31
  • @JinThakur - Thank you for answering the question. If you see my discussion above with s.fuhram you can find out that we were aware of the actual issue. Also there is nothing wrong with your code snippet as I have also mentioned in my answer. The only difference is that you are using `Apache HTTPClient` library which you can clearly see, is not the case with my soap client code. Also marking my answer doesn't allocate any points to me :) I hope you understand the difference. Thanks again :) – dev Mar 12 '20 at 05:06