Friday, February 15, 2013

WCF Behaviors. Wildcard name or empty string name.

Few days ago I found a WCF behavior configuration section similar to this:

<behaviors>
    <endpointBehaviors>
        <behavior name="*">
     <webHttp/>
        </behavior>
    </endpointBehaviors>
</behaviors>

Does name="*" make any sense? .net 4 comes with great impovements in default configurations. And if one wants to specify a behavior for all endpoints then behavior with an empty name (or even without the name attribute at all) is used. So what about wildcards? Usually an asterics is used for exactly the same purpose. What is the difference then? This question marked as answered actually doesn't contain the answer.

First of all we can use an asterics as an argument during channel factory creation.

var factory = new ChannelFactory<IMyService>("*");

In this case first endpoint configuration is taken. The feature is described in more details here.
It has nothing to do with behaviors.

In the configuration above the "*" symbol acts as a regular behaviorConfiguration name. You can reference it in any of our endpoints.

<endpoint 
    address="http://localhost:9001/http/" 
    contract="Shared.IMyService" 
    binding="basicHttpBinding" 
    behaviorConfiguration="*"/>

But if you want apply a behavior to all of your enpoints just use an empty name for it.

<behavior name="">
    <webHttp/>
</behavior>

Or even

<behavior>
    <webHttp/>
</behavior>

You can read more about WCF configuration defaults in this article.

Thursday, February 14, 2013

WCF. Fighting your way through a proxy.

Recently I discovered that my desktop tool for memorizing English words doesn't work when a client is behind a proxy.

The problem was pretty common and I quickly found this awesome answer on stackoverflow. But still there were a few things to deal with:
1. Move all those hardcoded strings to config files.
2. Define proxy usage per binding.
3. Test the application.

First might be easily accomplished using ConfigurationManager class. Here is the code I got:

using System;
using System.Configuration;
using System.Net;
using log4net;

namespace VX.Desktop.Infrastructure
{
    public class CustomProxy : IWebProxy
    {
        private const string CustomProxyAddressKey = "CustomProxyAddress";
        private const string CustomProxyUserKey = "CustomProxyUser";
        private const string CustomProxyPassword = "CustomProxyPassword";

        private readonly ILog logger = LogManager.GetLogger(typeof (CustomProxy));
        
        public Uri GetProxy(Uri destination)
        {
            var proxyAddress = ConfigurationManager.AppSettings[CustomProxyAddressKey];
            if (string.IsNullOrEmpty(proxyAddress))
            {
                logger.Error("Error retrieving CustomProxyAddress from configuration file. Make sure you have a corresponding key in appSettings section of application config file.");
                return null;
            }
            
            logger.InfoFormat("Proxy address: {0}", proxyAddress);
            return new Uri(proxyAddress);
        }

        public bool IsBypassed(Uri host)
        {
            logger.InfoFormat("IsBypassed for host: {0} is false", host);
            return false;
        }

        public ICredentials Credentials
        {
            get
            {
                logger.InfoFormat("Getting proxy credentials");
                string userName = ConfigurationManager.AppSettings[CustomProxyUserKey];
                string password = ConfigurationManager.AppSettings[CustomProxyPassword];
                logger.InfoFormat("Done. {0}", userName);
                if (string.IsNullOrEmpty(userName) || string.IsNullOrEmpty(password))
                {
                    logger.Error(
                        "Error retrieving proxy credentials from configuration file. Make sure you have corresponding keys in appSettings section of application config file.");
                }

                return new NetworkCredential(userName, password);
            }
            set { }
        }
    }
}

Nice ways of addressing the second and the third issues are described here.
So we should use something like:

<bindings>
    <basicHttpBinding>
        <binding name="myBindingWithProxy" useDefaultWebProxy="true" />
    </basicHttpBinding>
</bindings>
instead of
<defaultProxy enabled="true" useDefaultCredentials="false">
  <module type = "SomeNameSpace.MyProxy, SomeAssembly" />
</defaultProxy>

Now we can use other bindings without any proxies. And to test all this stuff we can use a great tool - Fiddler. Just check the Rules -> Require Proxy Authentication option and you're done.