Monday, 13 June 2011

WCF ProtectionLevel performance test

Normally you should always have your security settings turned on even within an intranet environment.

There are times though when performance is critical and you may want to tweek the relevant WCF security settings.

I thought that I would have a look and see what the effect is of the ProtectionLevel security setting which can be set imperatively or declaratively... so I wrote the following performance test to evaluate the three different possible settings.


using System;
using System.Linq;
using System.ServiceModel;
using System.Net.Security;
using System.Diagnostics;

namespace ProtectionLevel1
{
    //This is the minimum protection level that the binding must comply with.
    [ServiceContract(ProtectionLevel = ProtectionLevel.None)]
    public interface IConvertString
    {
        [OperationContract]
        string ConvertString(string input);
    }

    // Simple service which reverses a string and returns the result
    public class ConvertStringService : IConvertString
    {
        #region IConvertString Members

        public string ConvertString(string input)
        {
            char[] chars = input.ToCharArray();
            chars.Reverse<char>();
            return chars.ToString();
        }

        #endregion
    }

    class Program
    {
        // Setup the Service host
        private static ServiceHost StartServer<T>(string Uri, NetTcpBinding binding)
        {
            Uri uri = new Uri(Uri);

            ServiceHost host = new ServiceHost(typeof(ConvertStringService));
            host.AddServiceEndpoint(typeof(T), binding, uri);
            host.Open();

            Console.WriteLine("Service opened using NetTcpBinding with {0} protection level.", Enum.GetName(typeof(ProtectionLevel), binding.Security.Transport.ProtectionLevel));

            return host;
        }

        // Create channel using NetTcp binding with Uri and execute performance test.
        private static double RunPerformanceTestOnNetTcpBinding(NetTcpBinding binding, string endpointUri)
        {
            const int iterations = 300;
            const string stringToConvert = "12";

            binding.Security.Mode = SecurityMode.Transport;

            Stopwatch watch = new Stopwatch();
            watch.Start();
            for (Int32 i = 0; i < iterations; i++)
            {
                ChannelFactory<IConvertString>.CreateChannel(binding,
                    new EndpointAddress(endpointUri))
                    .ConvertString(stringToConvert);
            }
            watch.Stop();

            Console.WriteLine("ConvertString with ProtectionLevel {2} : {0} times; Time taken : {1} ",
                iterations.ToString(),
                watch.Elapsed.TotalSeconds.ToString(),
                Enum.GetName(typeof(ProtectionLevel), binding.Security.Transport.ProtectionLevel));

            return watch.Elapsed.TotalSeconds;

        }

        static void Main(string[] args)
        {

            string uri = "net.tcp://localhost:7000/IConvertString";

            NetTcpBinding bindingNoEncryption = new NetTcpBinding();
            bindingNoEncryption.Security.Transport.ProtectionLevel = ProtectionLevel.None;

            NetTcpBinding bindingWithSign = new NetTcpBinding();
            bindingWithSign.Security.Transport.ProtectionLevel = ProtectionLevel.Sign;

            NetTcpBinding bindingWithEncryption = new NetTcpBinding();
            bindingWithEncryption.Security.Transport.ProtectionLevel = ProtectionLevel.EncryptAndSign;

            Console.WriteLine("Run performance test or press q to Quit...");
            while (Console.ReadLine() != "q")
            {

                double noEncryptionTime;
                double signTime;
                double encryptionTime;

                using (ServiceHost host = StartServer<IConvertString>(uri, bindingNoEncryption))
                {

                    noEncryptionTime = RunPerformanceTestOnNetTcpBinding(bindingNoEncryption, uri);
                    host.Close();
                }

                using (ServiceHost host = StartServer<IConvertString>(uri, bindingWithSign))
                {
                    signTime = RunPerformanceTestOnNetTcpBinding(bindingWithSign, uri);
                    host.Close();
                }

                using (ServiceHost host = StartServer<IConvertString>(uri, bindingWithEncryption))
                {
                    encryptionTime = RunPerformanceTestOnNetTcpBinding(bindingWithEncryption, uri);
                    host.Close();
                }

                Console.WriteLine("Run performance test again or press 'q' to Quit...");

            }

        }

    }

}


The results from my performance test are below:



As you can see from the above tests the when using Transport security with the NetTcpBinding ProtectionLevel Sign performance is not much different to EncryptAndSign, although there is a definite performance improvement when using Protection Level None.

Additional resources:
Understanding Protection Level