I'm trying to write a debugging tool that allows the user to view WCF's new binary XML format (application/soap +msbin1) in plain text. Once I found the XmlDictionaryReader class I thought I'd be done in minutes, but it's not working as expected.
private string DecodeBinaryXML(byte[] binaryBuffer)
{
if (binaryBuffer == null)
{
return "";
}
try
{
var doc = new XmlDocument();
using (var binaryReader = XmlDictionaryReader.CreateBinaryReader(binaryBuffer, XmlDictionaryReaderQuotas.Max))
{
doc.Load(binaryReader);
binaryReader.Close();
}
var textBuffer = new StringBuilder();
var settings = new XmlWriterSettings()
{
// lots of code not relevant to the question
};
using (var writer = XmlWriter.Create(textBuffer, settings))
{
doc.Save(writer);
writer.Close();
}
return textBuffer.ToString();
}
catch (Exception ex)
{
// just display errors in the text viewer
return ex.ToString();
}
}
Every single "soap+msbin1" sample I've found online or generated myself throws a parse exception at doc.Load().
To see what was going on, I created a simple test app and attacked the problem from the other direction.
// client
static void Main(string[] args)
{
var binding = new CustomBinding(new TextMessageEncodingBindingElement(),
new HttpTransportBindingElement());
var proxy = ChannelFactory<IService1>.CreateChannel(binding,
new EndpointAddress("http://ipv4.fiddler:25381/Service1.svc"));
Console.WriteLine(proxy.Echo("asdf"));
}
// shared interface
[ServiceContract()]
public interface IService1
{
[OperationContract]
string Echo(string input);
}
// server
public class Service1 : IService1
{
public string Echo(string input)
{
return "WCF says hi to: " + input;
}
}
Running it kicks off an http request that looks like:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"
xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">http://tempuri.org/IService1/Echo</a:Action>
<a:MessageID>urn:uuid:21a33e81-bfab-424f-a2e5-5116101a7319</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">http://ipv4.fiddler:25381/Service1.svc</a:To>
</s:Header>
<s:Body>
<Echo xmlns="http://tempuri.org/">
<input>asdf</input>
</Echo>
</s:Body>
</s:Envelope>
I converted this XML into binary two different ways. First, using XmlDictionaryWriter:
$fs = [system.io.file]::Create("c:\temp\soap.bin")
$writer = [system.xml.xmldictionarywriter]::CreateBinaryWriter($fs)
$xml = [xml] (gc C:\temp\soap.xml)
$xml.Save($writer)
$writer.Close(); $fs.Close()
Then, using WCF and the same network sniffer:
@@ -1,7 +1,7 @@
// client
static void Main(string[] args)
{
- var binding = new CustomBinding(new TextMessageEncodingBindingElement(),
+ var binding = new CustomBinding(new BinaryMessageEncodingBindingElement(),
new HttpTransportBindingElement());
Method #1 gave 397 bytes of binary-looking gunk. Method #2 shows 169 bytes of very different binary gunk. Apart from a few strings that appear in both outputs, I don't see a lot of similarity in the two encodings. No wonder, then, that XmlDictionaryReader can't understand the output of a WCF service!
Is there a secret to decoding this format, or am I on the wrong path entirely?