When integrating with the new third party SOAP API, my colleague managed to get it working so we deployed it to test environment. However, it didn’t work on test environment with an invalid response exception. It still worked on my colleague’s computer and a few others. Fortunately, it didn’t work on my computer when I gave it a try so I could investigate what the issue was.
After inspecting all the requests and responses coming in and out when calling the SOAP API with Fiddler, I found that the content-length did not match the actual length of the content. I replayed the response in Fiddler with the correct response length and it worked. However this didn’t make much sense on why it worked on others’ machines. With further investigation, I found that the request headers contain Expect: 100-Continue. I tried replaying the request without that header and it worked.
I wasn’t sure what header Expect: 100-Continue would do so I looked it up. Basically, if you have that header in your request, once the connection to the server is established, only the header is transmitted to the server before the body is sent. This would save bandwidth in case the server rejects the request as it would have rejected the request before having to transmit the body to the server.
However, further research found that with the number of proxies, load-balancing servers, and back-end request processing servers that are implemented, requests with the Expect: 100-Continue header have an increased probability of becoming separated from one another. This would cause it to return an error. I had proxies setup similarly as the test environment while the others did not so that explained why it did not work on my computer and test environment.
Disable Expect: 100-Continue header
To disable the Expect: 100-Continue header, I added a custom HttpClientHandler in EndpointBehaviour where I disable the header.
public class InterceptingHttpMessageHandler : DelegatingHandler
{
public InterceptingHttpMessageHandler(HttpMessageHandler innerHandler)
{
InnerHandler = innerHandler;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpResponseMessage response;
request.Headers.ExpectContinue = false;
response = await base.SendAsync(request, cancellationToken);
return response;
}
}
Then binding the custom HttpClientHandler to the endpoint behaviour.
public class HttpMessageHandlerBehavior : IEndpointBehaviour
{
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
bindingParameters.Add(new Func<HttpClientHandler, HttpMessageHandler>(GetHttpMessageHandler));
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) {}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) {}
public void Validate(ServiceEndpoint endpoint) {}
public HttpMessageHandler GetHttpMessageHandler(HttpClientHandler httpClientHandler)
{
return new InterceptingHttpMessageHandler(httpClientHandler);
}
}