Reputation: 356
Can anyone explain me why the soap response below is not deserialized into a FaultException
in my C# client? I get a System.ServiceModel.ProtocolException
with message "The remote server returned an unexpected response: (400) Bad Request", with a System.Net.WebException
as a inner exception with message "The remote server returned an error: (400) Bad Request."
My client code uses several contracts defined svcutil.exe (Add service reference)
I use a Custom binding for basic or digest authentication and soap1.2. In this request I use basic authenitcation.
Code for binding is below the response soap.
HTTP/1.1 400 Bad Request
Server: gSOAP/2.7
Content-Type: application/soap+xml; charset=utf-8
Content-Length: 2087
Connection: close
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope"
xmlns:SOAP-ENC="http://www.w3.org/2003/05/soap-encoding"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:tns1="http://www.onvif.org/ver10/topics"
xmlns:tnsacti="http://www.acti.com/2009/event/topics"
xmlns:http="http://tempuri.org/http.xsd"
xmlns:wsadis="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:wsbf="http://docs.oasis-open.org/wsrf/bf-2"
xmlns:wstop="http://docs.oasis-open.org/wsn/t-1"
xmlns:wsr="http://docs.oasis-open.org/wsrf/r-2"
xmlns:tt="http://www.onvif.org/ver10/schema"
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:ns3="http://www.onvif.org/ver10/events/wsdl/PullPointSubscriptionBinding"
xmlns:ns4="http://www.onvif.org/ver10/events/wsdl/EventBinding"
xmlns:tev="http://www.onvif.org/ver10/events/wsdl"
xmlns:ns5="http://www.onvif.org/ver10/events/wsdl/SubscriptionManagerBinding"
xmlns:ns6="http://www.onvif.org/ver10/events/wsdl/NotificationProducerBinding"
xmlns:ns7="http://www.onvif.org/ver10/events/wsdl/NotificationConsumerBinding"
xmlns:wsnt="http://docs.oasis-open.org/wsn/b-2"
xmlns:tds="http://www.onvif.org/ver10/device/wsdl"
xmlns:tptz="http://www.onvif.org/ver20/ptz/wsdl"
xmlns:trt="http://www.onvif.org/ver10/media/wsdl"
xmlns:wsa="http://www.w3.org/2005/08/addressing"
xmlns:d="http://schemas.xmlsoap.org/ws/2005/04/discovery"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:ter="http://www.onvif.org/ver10/error">
<SOAP-ENV:Body>
<SOAP-ENV:Fault SOAP-ENV:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
<SOAP-ENV:Code>
<SOAP-ENV:Value>SOAP-ENV:Sender</SOAP-ENV:Value>
</SOAP-ENV:Code>
<SOAP-ENV:Reason>
<SOAP-ENV:Text xml:lang="en">Method 'GetImagingSettings' not implemented: method name or namespace not recognized
</SOAP-ENV:Text>
</SOAP-ENV:Reason>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The C# code
private T GetClient(ISecurityTokenSelector securityToken, ref ChannelFactory<T> channelFactory) {
if (ServiceAddress == null)
throw new ArgumentNullException(string.Format("The service address to the {0} is null", typeof(T).Name));
var endpointAddress = new EndpointAddress(ServiceAddress.ToString());
//Gets custom binding
var binding = BindingFactory.GetBinding(securityToken, false, false);
channelFactory = new ChannelFactory<T>(binding, endpointAddress);
if (securityToken is BasicUserNameSecurityTokenSelector) {
channelFactory.Endpoint.Behaviors.Remove(typeof(ClientCredentials));
channelFactory.Credentials.UserName.UserName = UserName;
channelFactory.Credentials.UserName.Password = Password;
}
if (securityToken is DigestSecurityTokenSelector) {
// configure the username credentials on the channel factory
var credentials = new UsernameClientCredentials(new UsernameInfo(UserName, Password));
// replace ClientCredentials with UsernameClientCredentials
channelFactory.Endpoint.Behaviors.Remove(typeof(ClientCredentials));
channelFactory.Endpoint.Behaviors.Add(credentials);
}
return channelFactory.CreateChannel();
}
private Binding GetCustomBinding(ISecurityTokenSelector token, bool mtomEncoding, bool wsAddressing) {
var binding = new CustomBinding(CreateBindingElements(mtomEncoding, wsAddressing, token));
binding.CloseTimeout = TimeSpan.FromSeconds(30.0);
binding.OpenTimeout = TimeSpan.FromSeconds(30.0);
binding.SendTimeout = TimeSpan.FromMinutes(10.0);
binding.ReceiveTimeout = TimeSpan.FromMinutes(3.0);
return binding;
}
private static IEnumerable<BindingElement> CreateBindingElements(bool mtomEncoding, bool wsAddressing, ISecurityTokenSelector token) {
if (token is DigestSecurityTokenSelector) {
TransportSecurityBindingElement transportSecurity = new TransportSecurityBindingElement();
transportSecurity.EndpointSupportingTokenParameters.SignedEncrypted.Add(new UsernameTokenParameters());
// here you can require secure transport, in which case you'd probably replace HTTP with HTTPS as well
transportSecurity.AllowInsecureTransport = true;
transportSecurity.IncludeTimestamp = false;
yield return transportSecurity;
}
var msgVer = wsAddressing ? MessageVersion.Soap12WSAddressing10 : MessageVersion.Soap12;
if (mtomEncoding) {
var encoding = new MtomMessageEncodingBindingElement(msgVer, Encoding.UTF8);
encoding.ReaderQuotas.MaxStringContentLength = Int32.MaxValue;
encoding.MaxBufferSize = Int32.MaxValue;
yield return encoding;
} else {
var encoding = new TextMessageEncodingBindingElement(msgVer, Encoding.UTF8);
encoding.ReaderQuotas.MaxStringContentLength = Int32.MaxValue; //100 * 1024 * 1024
yield return encoding;
}
HttpTransportBindingElement transport = CreateTransportBindingElement(token);
transport.MaxReceivedMessageSize = Int32.MaxValue; //100L * 1024L * 1024L
transport.KeepAliveEnabled = false;
transport.MaxBufferSize = Int32.MaxValue;
//transport.ProxyAddress = null;
//transport.BypassProxyOnLocal = true;
//transport.UseDefaultWebProxy = false;
transport.TransferMode = TransferMode.Buffered;
if (token is BasicUserNameSecurityTokenSelector)
transport.AuthenticationScheme = AuthenticationSchemes.Basic;
yield return transport;
}
private static HttpTransportBindingElement CreateTransportBindingElement(ISecurityTokenSelector token) {
if (token != null && token.UseTls) {
var transport = new HttpsTransportBindingElement();
transport.RequireClientCertificate = false;
return transport;
} else {
return new HttpTransportBindingElement();
}
}
Upvotes: 3
Views: 2236
Reputation: 24293
The receiver is picking up on the 400 Bad Request
before it gets to the content part of the reply.
The call stack shows this error is being raised at the HttpWebRequest
level and passed up.
System.ServiceModel.ProtocolException: The remote server returned an unexpected response: (400) Bad Request. ---> System.Net.WebException: The remote server returned an error: (400) Bad Request.
at System.Net.HttpWebRequest.GetResponse()
at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
--- End of inner exception stack trace ---
To get a FaultException
, it needs to have received a 200 (or similar) status code.
Upvotes: 2