System.ServiceModel.CommunicationException when parsing WCF ProtocolException - c#

I am trying to use the recommended code from this page http://blogs.msdn.com/b/nathana/archive/2011/03/31/deciphering-a-soap-fault-with-a-400-status-code.aspx as follows:
static FaultException ParseProtocolExecption(ProtocolException ex)
{
try
{
System.IO.Stream stream = (ex.InnerException as WebException).Response.GetResponseStream();
System.Xml.XmlReader xmr = System.Xml.XmlReader.Create(stream);
Message message = Message.CreateMessage(xmr, (int)stream.Length, MessageVersion.Soap12);
MessageFault mf = MessageFault.CreateFault(message, (int)stream.Length);
FaultException fe = new FaultException(mf);
message.Close();
return fe;
}
catch (Exception)
{
return new FaultException(ex.Message);
}
}
I am using VS 2012 with .NET 4.5 using WCF. When the app gets a 400 Bad Request and it passes the ProtocolException to ParseProtocolException, it throws an exception on this line:
Message message = Message.CreateMessage(xmr, (int)stream.Length, MessageVersion.Soap12);
with the System.ServiceModel.CommunicationException: "The size necessary to buffer the XML content exceeded the buffer quota."
The stream.Length = 2,704 bytes, which is not very big. I tried the solution suggested on this site http://blogs.msdn.com/b/drnick/archive/2007/08/07/increasing-the-maximum-fault-size.aspx. However, even with the MaxFaultSize = 1 Mb, it gets the same errror.
Instead of this line:
System.Xml.XmlReader xmr = System.Xml.XmlReader.Create(stream);
I've tried this:
xmr = XmlDictionaryReader.CreateTextReader(stream, XmlDictionaryReaderQuotas.Max);
which sets all the quotas to their maximum value (Int32.MaxValue); but, I still get the same error on the CreateMessage call.
A sample response stream from the System.Net.WebException is as follows:
<?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:wsa5="http://www.w3.org/2005/08/addressing" xmlns:c14n="http://www.w3.org/2001/10/xml-exc-c14n#" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:xmime5="http://www.w3.org/2005/05/xmlmime" xmlns:xop="http://www.w3.org/2004/08/xop/include" xmlns:tptz="http://www.onvif.org/ver20/ptz/wsdl" xmlns:tds2="http://www.onvif.org/ver10/schema" xmlns:tds3="http://docs.oasis-open.org/wsn/b-2" xmlns:tds4="http://docs.oasis-open.org/wsrf/bf-2" xmlns:tds5="http://docs.oasis-open.org/wsn/t-1" xmlns:timg="http://www.onvif.org/ver20/imaging/wsdl" xmlns:trt="http://www.onvif.org/ver10/media/wsdl" xmlns:tan="http://www.onvif.org/ver20/analytics/wsdl" xmlns:tds6="http://www.canon.com/ns/networkcamera/onvif/va/schema" xmlns:tmd="http://www.onvif.org/ver10/deviceIO/wsdl" xmlns:tev="http://www.onvif.org/ver10/events/wsdl" xmlns:tds9="http://docs.oasis-open.org/wsrf/r-2" xmlns:tds="http://www.onvif.org/ver10/device/wsdl" xmlns:ter="http://www.onvif.org/ver10/error">
<SOAP-ENV:Header></SOAP-ENV:Header>
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<SOAP-ENV:Code>
<SOAP-ENV:Value>SOAP-ENV:Sender</SOAP-ENV:Value>
<SOAP-ENV:Subcode>
<SOAP-ENV:Value>ter:NotAuthorized</SOAP-ENV:Value>
</SOAP-ENV:Subcode>
</SOAP-ENV:Code>
<SOAP-ENV:Reason>
<SOAP-ENV:Text xml:lang="en">Sender Not Authorized</SOAP-ENV:Text>
</SOAP-ENV:Reason>
<SOAP-ENV:Node>http://www.w3.org/2003/05/soap-envelope/node/ultimateReceiver</SOAP-ENV:Node>
<SOAP-ENV:Role>http://www.w3.org/2003/05/soap-envelope/role/ultimateReceiver</SOAP-ENV:Role>
<SOAP-ENV:Detail>The action requested requires authorization and the sender is not authorized</SOAP-ENV:Detail>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Using an async web crawler that I wrote in F# Interactive, I found that some of the namespace url's were not resolvable. I corrected the erroneous ones and then ran the crawler again to sum the lengths of the namespace pages. The total is 715,965 bytes, which is much less than the Int32.MaxValue of all the quotas in XmlDictionaryReaderQuotas. Perhaps the XmlDictionaryReader has a bug, or the error it returns is not the real problem?
I finally got the Message creation to work by removing the namespace definitions that were not actually used in the SOAP-ENV:Body (i.e., keeping only the xmlns:ter used in the Subcode element). But, of course, this doesn't really solve the problem, because the service is generating the SOAP fault; and I cannot change the service implementation (it's a 3rd party device - an Onvif camera).
Morevover, I can't make the quotas any greater; so, how else to handle this exception?

The solution is to trap the CommunicationException and then do an alternate parse of the XML in the stream that doesn't require namespace resolution:
public static FaultException ParseProtocolException(System.ServiceModel.ProtocolException ex) {
var stream = (ex.InnerException as System.Net.WebException).Response.GetResponseStream();
try {
var xmr = XmlReader.Create(stream);
var message = Message.CreateMessage(xmr, (int)stream.Length, MessageVersion.Soap12);
var mf = MessageFault.CreateFault(message, (int)stream.Length);
message.Close();
return new FaultException(mf);
} catch (CommunicationException) { // If CreateMessage has a problem parsing the XML,
// then this error will be thrown. Most likely, there is an unresolvable namespace reference.
// Do an alternate parse
stream.Seek(0, System.IO.SeekOrigin.Begin);
var soapFault = GetSoapFault(stream);
return new FaultException(soapFault.Reason);
}
}
The catch resets the stream to the beginning and then uses the following to get the specifics of the SOAP Fault using an XmlReader:
private struct SoapFault {
public string Subcode;
public string Reason;
public string Detail;
}
private static string GetTextChild(XmlReader xmr, string childName) {
return xmr.ReadToDescendant(childName) ?
xmr.ReadString() : System.String.Empty;
}
private static SoapFault GetSoapFault(System.IO.Stream s) {
var xr = XmlReader.Create(s);
var fault = new SoapFault();
if (xr.ReadToFollowing("SOAP-ENV:Subcode")) {
fault.Subcode = GetTextChild(xr, "SOAP-ENV:Value");
if (xr.ReadToFollowing("SOAP-ENV:Reason")) {
fault.Reason = GetTextChild(xr, "SOAP-ENV:Text");
if (xr.ReadToFollowing("SOAP-ENV:Detail"))
fault.Detail = GetTextChild(xr, "SOAP-ENV:Text");
}
}
return fault;
}

Related

How to use .NET LLRP Library : Org.LLRP.LTK.LLRPV1.LLRPXmlParser.ParseXMLToLLRPMessage

I am developing an application using LLRP library.
After gateway(computer) receive my message from reader(simulated reader using localhost) I would like to convert it to LLRPMessage using
Org.LLRP.LTK.LLRPV1.LLRPXmlParser.ParseXMLToLLRPMessage(_xmlReceived, out msg, out enumType);
However, I check it in the watch(VS2013 Ultimate): msg is null and enumType is 0.
May I have your advice on this?
My code is :
public static void ConvertXmlToLLRPMessage()
{
Org.LLRP.LTK.LLRPV1.DataType.Message msg; //Only Message type is accepted.
Org.LLRP.LTK.LLRPV1.ENUM_LLRP_MSG_TYPE enumType; //Only ENUM_LLRP_MSG_TYPE type is accepted.
Org.LLRP.LTK.LLRPV1.LLRPXmlParser.ParseXMLToLLRPMessage(_xmlReceived, out msg, out enumType);
Console.WriteLine();
Console.WriteLine("Print out _xmlReceived, inside ConvertXmlToLLRPMessage.");
Console.WriteLine(_xmlReceived);
Console.WriteLine();
Console.WriteLine("Out msg from ParseXMLToLLRPMessage:\n"); Console.WriteLine(msg); Console.WriteLine();
Console.WriteLine("Out enumType from ParseXMLToLLRPMessage:\n"); Console.WriteLine(enumType); Console.WriteLine();
}
_xmlReceived is the XML-converted data receive from 127.0.0.1 :5084. I have to check it and it is correct it is what the reader sent out.
On Reader side, I simulate the reader to send a message to the gateway.
On reader side, the code is :
public static void testData_PARAM_ROSpecID()
{
//create and object
PARAM_ROSpecID _rec_PARAM_ROSpecID = new PARAM_ROSpecID();
//assign value to an object
_rec_PARAM_ROSpecID.ROSpecID = 789;
//Convert obj to xml
string _xmlData = ConvertObjectToXml(_rec_PARAM_ROSpecID);
//Convert xml to byte array
byte [] _byteArray = CommServerSend.getSendBuffInByteAry(_xmlData);
//Send out.
CommServerReceive._incomingDataObj.Send(_byteArray);
return;
}
Print out of data reader send out to gateway :
<?xml version="1.0" encoding="utf-16"?>
<PARAM_ROSpecID xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ROSpecID>789</ROSpecID>
</PARAM_ROSpecID>
Print out of data gateway receive from reader:
<?xml version="1.0" encoding="utf-16"?>
<PARAM_ROSpecID xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ROSpecID>789</ROSpecID>
</PARAM_ROSpecID>
but after process it using Org.LLRP.LTK.LLRPV1.LLRPXmlParser.ParseXMLToLLRPMessage(_xmlReceived, out msg, out enumType); and print out msg and enumType:
Out msg from ParseXMLToLLRPMessage:
Out enumType from ParseXMLToLLRPMessage: 0
Please help me and reply soon.
Your effort is very much appreciated.
The null obj variable would mean that the Method was unable to extract the message from the xml passed in. I would look more closely at the xml to see if there is something wrong there.
Here is an example of how the code should look for using ParseXMLToLLRPMessage():
Org.LLRP.LTK.LLRPV1.DataType.Message obj;
ENUM_LLRP_MSG_TYPE msg_type;
// read the XML from a file and validate its an SET_READER_CONFIG
try
{
FileStream fs = new FileStream(#"setReaderConfig.xml", FileMode.Open);
StreamReader sr = new StreamReader(fs);
string s = sr.ReadToEnd();
fs.Close();
LLRPXmlParser.ParseXMLToLLRPMessage(s, out obj, out msg_type);
if (obj == null || msg_type != ENUM_LLRP_MSG_TYPE.SET_READER_CONFIG)
{
Console.WriteLine("Could not extract message from XML");
return;
}
}
catch
{
Console.WriteLine("Unable to convert to valid XML");
return;
}
// Communicate that message to the reader
MSG_SET_READER_CONFIG msg = (MSG_SET_READER_CONFIG)obj;

HttpResponseMessage fails to deserialize XML but filestream is fine

I'm trying to consume a third party webservice. I craft a request message, send it to the httpclient and receive the httpResponseMessage.
Below is a snippet from the method that handles the call. xml = true for my situation.
HttpResponseMessage response = await HttpClientInstance.SendAsync(requestMessage);
if (response.IsSuccessStatusCode)
{
if (xml)
{
try
{
//This fails with "the data at the root level is invalid"
XmlMediaTypeFormatter xmlFormatter = new XmlMediaTypeFormatter { UseXmlSerializer = true };
var content = response.Content.ReadAsAsync<T>(new Collection<MediaTypeFormatter> { xmlFormatter });
return content.Result;
}
catch (Exception tempe)
{
var content = response.Content.ReadAsAsync<T>();
return content.Result;
}
}
else
{
var content = response.Content.ReadAsAsync<T>();
return content.Result;
}
}
else
{
_logWriter.LogProcessFault(operationContext, null, GetCurrentMethod(), $"An error occurred while calling the API. URI = {endpoint}. StatusCode = {response.StatusCode}", null);
throw new Exception(response.StatusCode.ToString());
}
After running this code, an error is thrown when attempting to deserialize the XML data in the stream. It fails with an error "the data at the root level is invalid".
I commented out the XMLMediaTypeFormatter and response.Content.Read... and replaced it with this
var fileStream = File.Create("D:\\Extract\\test.txt");
await response.Content.CopyToAsync(fileStream);
fileStream.Close();
which writes out valid XML to a file.
In the immediate window I ran response.Content.ReadAsStringAsync() and the returned string value has extra backslashes escaping content.
For instance, this is what is in the generated test.txt file:
<?xml version="1.0" encoding="utf-8"?>
and this is from ReadAsStringAsync:
<?xml version=\"1.0\" encoding=\"utf-8\"?>
I believe this is what is causing the deserialization to fail. Is there a clean fix for this or perhaps I am doing something wrong elsewhere?
The issue is in the following block of code
await response.Content.CopyToAsync(fileStream);
fileStream.Close();
//This fails with "the data at the root level is invalid"
XmlMediaTypeFormatter xmlFormatter = new XmlMediaTypeFormatter { UseXmlSerializer = true };
var content = response.Content.ReadAsAsync<T>(new Collection<MediaTypeFormatter> { xmlFormatter });
HTTP responses can be consumed only once. In other words, response is not read into some buffer from which multiple reads can be serviced. It reads it directly off of the socket. So once read, it cannot be read again.
Above, you copy response into a file. That consumes the response and it is no longer available to be read again. So by the time you try to read it again when assigning to content, it reads nothing, so the XML parser (in formatter) throws xml syntax error b/c it basically receives an empty string.
Not sure why you're saving to file, but once saved to file, you can simply read the file and send its contents to xml parser, and your code should now work. And of course if you remove saving to file, you should be able to read and parse contents of the response just fine.

C# System.Net.Sockets.SocketException

I'm trying to make URLReader public string, to not dublicate it in every function. So i have something like this:
public static string URLReader(string url)
{
try
{
WebClient webClient = new WebClient();
Stream fileStream = webClient.OpenRead(url);
using (StreamReader reader = new StreamReader(fileStream))
{
return reader.ReadToEnd();
}
}
catch
{
MessageBox.Show("URL Not exists or you are not connected to internet!", "Problems!", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
return "";
}
}
I don't know why, but catch not working.. For example, if I write incorrect url, or disconnect from internet it must print for me that "URL Not exists or you are not connected to interned!" But I've give error in this line:
Stream fileStream = webClient.OpenRead(url);
Error:
The first stage of processing exceptions of type
"System.Net.Sockets.SocketException" in System.dll
For more information: The requested name is correct, but the data requested type was found
If this exception handler is available, the program can be continued safely.
P.S Error mesage translated by google translate, because I'm not english
Thanks in advance.

Getting answer from Jabber Server

I want connect with my jabber server and read server answer:
DnsEndPoint host = new DnsEndPoint("talk.google.com", 5222);
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
public void start()
{
string message = "<?xml version=1.0?><stream:streamto='gmail.com'xmlns='jabber:client'xmlns:stream='http://etherx.jabber.org/streams'version='1.0'>";
var buffer = Encoding.UTF8.GetBytes(message);
args.RemoteEndPoint = host;
args.Completed += SocketAsyncEventArgs_Completed;
args.SetBuffer(buffer, 0, buffer.Length);
bool completesAsynchronously = socket.ConnectAsync(args);
if (!completesAsynchronously)
{
SocketAsyncEventArgs_Completed(args.ConnectSocket, args);
}
}
private void SocketAsyncEventArgs_Completed(object sender, SocketAsyncEventArgs e)
{
if (e.SocketError != SocketError.Success)
{
Deployment.Current.Dispatcher.BeginInvoke(() => MessageBox.Show("Error during socket operation: " + e.SocketError));
return;
}
byte[] buffer = e.Buffer;
XDocument temp = ConvertByteArrayToXml(buffer);
}
XDocument ConvertByteArrayToXml(byte[] data)
{
XmlReaderSettings settings = new XmlReaderSettings();
using (MemoryStream stream = new MemoryStream(data))
using (XmlReader reader = XmlReader.Create(stream, settings))
{
return XDocument.Load(reader);
}
}
At Return XDocument.Load(reader) I get exeption '1.0' is an unexpected token. The expected token is '"' or '''. How can solve it?
Why I want it? Because when I authorize myself and change status it not affect it at my account.
Please do not write your own XMPP library from scratch, but choose one of the existing ones.
You're not going to be successful thinking of the XML you receive like a file, which is where your Unexpected end of file has occurred error is coming from. You must parse the XML incrementally.
This (in your declaration for message):
<?xml version=1.0?>
is an invalid XML declaration. As per the exception, you want:
<?xml version="1.0" ?>
Note that this has nothing to do with Jabber or sockets, and everything to do with XML. It's important to pay attention to exception messages and stack traces, so you can diagnose this sort of thing for yourself: you need to be able to isolate the area of the problem, so you can tackle just that one bit in isolation.
(The rest of that XML looks pretty bust, too, by the way.)

WCF - Stream parameter is missing CR of CRLF

The problem I have is that the client is sending me a string of data as a stream. WCF then normalizes (removes the CR part of CRLF) and I get a hash mismatch between server and client on that specific string.
public void SomeWcfContract(Stream input)
{
try
{
string processed = ReadStream(input);
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.OK;
}
catch (Exception ex)
{
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.InternalServerError;
}
}
private string ReadStream(Stream input)
{
string output;
using (var reader = new StreamReader(input, Encoding.UTF8))
{
output = reader.ReadToEnd();
}
return output;
}
I read a post about this here : XML deserialization 'standardising' line endings, how to stop it? (.NET)
It's the exact same problem I have but I use the standard XmlSerializer of WCF. Do I need to create my own XmlSerializer implementation or can I add the "fix" to the settings somehow?
This seems to be a VERY nasty bug with the WCF XmlDictionaryReader what happens is that when WCF serializes the incoming stream to a Message all instances of carriage return in (CRLF) is removed and replaced with LF. According to this it's a known bug in WCF.
EDIT I reported this as a bug to Microsoft : https://connect.microsoft.com/wcf/feedback/details/532196/xmldictionaryreader-is-normalizing-incoming-stream-removing-cr-of-crlf#details
This seems to fix that problem:
[WebInvoke(BodyStyle = WebMessageBodyStyle.Bare,
UriTemplate = UriTemplates.SomeWcfContract), OperationContract]
void SomeWcfContract(Stream vcard);
I guess it makes sense since this would cause the parameter not to be wrapped or something like that.

Categories