Calculating The Size Of SOAP Messages

3 commentsWritten on March 11th, 2009 by
Categories: WCF

I recently needed something to log the size of incoming and outgoing SOAP messages, and my first implementation of calculating the size looked like this:

        private static double GetMessageLengthInKB(Message message)

        {

            return Math.Round(Encoding.UTF8.GetBytes(message.ToString()).Length / 1024d, 2);

        }

There is a big problem with this. Well, at least one that i know of, possibly more. The ToString() method on the Message class returns the nicely formatted content of the message. Including all whitespace. This obviously increases the reported size of the SOAP message by a significant number, even though it's not sent over the wire with all that whitespace. A better way to calculate the size is like this:

        private static double GetMessageLengthInKB(Message message)

        {

            var writerSettings = new XmlWriterSettings { Encoding = Encoding.UTF8, Indent = false };

 

            using (var memoryStream = new MemoryStream())

            using (var writer = XmlDictionaryWriter.Create(memoryStream, writerSettings))

            {

                message.WriteMessage(writer);

                writer.Flush();

                return Math.Round(memoryStream.Position / 1024d, 2);

            }

        }

One thing to keep in mind is that you need to be careful with writing the contents of a SOAP message. If you write the content of a SOAP message without copying it first, you'll run into other problems further along the WCF pipeline. So the MessageInspector now looks like this:

    public class MessageInspector : IDispatchMessageInspector

    {

        private readonly ILog logger = LogManager.GetLogger(typeof(MessageInspector));

 

        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)

        {

            if (logger.IsInfoEnabled)

            {

                var bufferedCopy = request.CreateBufferedCopy(int.MaxValue);

 

                var sizeLog = string.Format("request message size: ~{0} KB", GetMessageLengthInKB(bufferedCopy.CreateMessage()));

                logger.Info(sizeLog);

 

                request = bufferedCopy.CreateMessage();

            }

 

            return null;

        }

 

        public void BeforeSendReply(ref Message reply, object correlationState)

        {

            if (logger.IsInfoEnabled)

            {

                var bufferedCopy = reply.CreateBufferedCopy(int.MaxValue);

 

                var sizeLog = string.Format("response message size: ~{0} KB", GetMessageLengthInKB(bufferedCopy.CreateMessage()));

                logger.Info(sizeLog);

 

                reply = bufferedCopy.CreateMessage();

            }

        }

 

        private static double GetMessageLengthInKB(Message message)

        {

            var writerSettings = new XmlWriterSettings { Encoding = Encoding.UTF8, Indent = false };

 

            using (var memoryStream = new MemoryStream())

            using (var writer = XmlDictionaryWriter.Create(memoryStream, writerSettings))

            {

                message.WriteMessage(writer);

                writer.Flush();

                return Math.Round(memoryStream.Position / 1024d, 2);

            }

        }

    }

As you can see, you're better off creating a buffered copy of a message, and then creating a new message out of that copy before doing anything with it.

  • Pingback: Dew Drop - March 12, 2009 | Alvin Ashcraft's Morning Dew

  • xr280xr

    Thanks for the example, I appreciate it. I have to point out though that there is a conceptual misuse of the var keyword here. Prior to .NET 3.0, C# was a strictly strong typed language but due to the introduction of LINQ, the var keyword was also introduced. Even though it works that way, the most specific type should always be used when declaring references in C#. So in this case: XmlWriterSettingsar writerSettings = new XmlWriterSettings(), MessageBuffer bufferedCopy = reply.CreateBufferedCopy(int.MaxValue);…etc. I wish they would’ve at least used a different keyword to help programmers coming from other languages avoid this misconception.

  • http://davybrion.com Davy Brion

    @Xr280xr

    i don’t wanna get into the whole ‘to var or not to var’ debate but i really think you’ve got a very closed minded view on it

    if you don’t want to use var that’s fine, but there’s no real reason why it should be avoided, and certainly not for dogmatic language reasons