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 ?
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();