Exposing WCF service without .svc extension

I know, this is a weird requirement, but sometimes they appear in your backlog. The story is: as company XXX I want to expose a service based on WCF in IIS without having the .svc suffix in the address. I’m actually using Castle Windsor WCF Integration to resolve my service class with castle, and it turns out that exposing a service without using standard .svc files it is just a matter of configure routing. This line of code is configuring the route of a specific name to a wcf service

RouteTable.Routes.Add(
  new ServiceRoute("UiService", 
    new DefaultServiceHostFactory(IoC.GetContainer().Kernel), typeof(IUiService)));

This indicates to Asp.Net engine that the UiService address route actually maps to the DefaultServiceHostFactory of Castle and it will be solved by the implementation of a IUiService. Remember that you need to enable aspNetCompatibilityEnabled in your servicemodel section of web config and all the service should have the attribute

    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]

If you do not remember how to enable aspNetCompatibility it is just a setting in your web.config inside the ServiceModel section

    <serviceHostingEnvironment aspNetCompatibilityEnabled="True">

    </serviceHostingEnvironment>

Now your service is exposed as a simple url, ex: http://www.mysite.org/services/UiService

Gian Maria.

Avoid WCF to test validity of HTTPS certificates

In a previous article I deal on How To test ssl based wcf service and part of the solution is to create a self issued certificate and make it valid inserting generated certificate in Trusted Root Certification Authority.

This operation makes that CA trusted and is an operation that is not so good if you really care on the security of your machine. There is also situation where you want to temporarily disable the check of the validity of the certificate, ES: you need to revoke certificate and issue another one with a new certification trusted authority , or basically you want your application to use a self issued https certificate in your intranet but you do not want to install that CA in everyone computer Trusted Root Certification Authority. Whatever is your need, sometimes there is the need to use WCF over https and ignore completely any certification error (untrusted CA, revoked certificate, etc)

A possible solution is disabling the check for certificate validity for the specific application, and it can be done in a real simple way. First of all handle the event ServerCertificateValidationCallback

ServicePointManager.ServerCertificateValidationCallback += 
  new RemoteCertificateValidationCallback(ValidateCertificate);

You can now simply assure that the certificate is always valid.

 public static bool ValidateCertificate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors sslPolicyErrors)
 {
       return true;
 }

This routine basically ignore every certification error, but you can inspect the variable sslPolicyError to understand the exact type of the error in certificate validation, if any. With these simple lines of code you are able to use WCF over HTTPS binding with a certificate that is revokes or that comes from an untrusted CA.

Gian Maria

The username is not provided. Specify username in ClientCredentials when use CreateChannel at Runtime

I’ve encountered a strange problem with WCF today, I have a custom Castle facility that is able to automatically create Wcf proxies with code, and it worked perfectly until I need to add a simple functionality: I want to be able to change at runtime some of the options of bindings that are configured in app.config. The solution was really simple, just write a method to retrieve the BindingConfiguration from the configuration file and the game is almost done.

public static Binding ResolveBinding(string name)
{
    System.Configuration.Configuration config =
        System.Configuration.ConfigurationManager.OpenExeConfiguration(Assembly.GetEntryAssembly().Location);

    var serviceModel = ServiceModelSectionGroup.GetSectionGroup(config);

    BindingsSection section = serviceModel.Bindings;

    foreach (var bindingCollection in section.BindingCollections)
    {
        var bindingElement = bindingCollection.ConfiguredBindings.SingleOrDefault(e => e.Name == name);
        if (bindingElement != null)
        {
            var binding = (Binding)Activator.CreateInstance(bindingCollection.BindingType);
            binding.Name = bindingElement.Name;
            bindingElement.ApplyConfiguration(binding);

            return binding;
        }
    }

    return null;
}

This code is really simple, it just opens the configuration file related to the entry assembly, get the the ServiceModelSectionGroup and then iterate through all BindingCollections (in Bindings section), looking for a Binding configuration with specified name. Once you find the configuration you can simply create an instance of the BindingType and use the ApplyConfiguration of the BindingElement to apply the configuration present in configuration file. Once you have the instance of the binding configured as for the config file you can simply modify all the option you need. My specific need is the ability to modify the useDefaultHttpWebProxy property, to be able to use or not use configured proxy as needed.

var binding = ResolveBinding(element.BindingConfiguration);
if (binding is BasicHttpBinding)
{
    BasicHttpBinding bhb = (BasicHttpBinding)binding;
    bhb.UseDefaultWebProxy = true;
}
else if (binding is WSHttpBinding)
{
    WSHttpBinding wshb = (WSHttpBinding)binding;
        wshb.UseDefaultWebProxy = true;

}
else if (binding is CustomBinding)
{
    CustomBinding cshb = (CustomBinding)binding;
    cshb.Elements
        .OfType<HttpsTransportBindingElement>()
        .Single()
        .UseDefaultWebProxy = true;
}

This code is really simple, I needs only to cast the binding to the correct type (I’m using BasicHttpBinding, WsHttpBinding and CustomBinding in this software) to set UseDefaultWebProxy value and the game is done. Now I only need to modify the piece of code that actually creates the proxy

MethodInfo createchannel = instance.GetType().GetMethod("CreateChannel", new[] { typeof(EndpointAddress) });
proxy = createchannel.Invoke(instance, new[] { endpointAddress });

the Instance variable contains a reference to ChannelFactory<T> and I create the proxy simply calling the CreateChannel (with reflection) passing the EndpointAddress. After a brief check on MSDN I found a version of CreateChannel that accepts a binding and not only an Endpoint element. In this page (http://msdn.microsoft.com/en-us/library/aa344556(v=vs.85).aspx) you can verify that a CreateChannel exists that accepts a binding and an endpoint address so I change the above code to invoke this version of the method. Everything works as expected, except that I got The username is not provided. Specify username in ClientCredentials error for all authenticated service; reverting the code to call the old Createchannel solved the problem.

I’ve lost half an hour wondering why credential are not passed to the server if I create the channel with the CreateChannel(Binding, EndpointAddress) but if I create the channel with CreateChannel(EndpointAddress) everything works as expected, until I realize my mistake. The CreateChannel(Binding, EndpointAddress) is a static method of the ChannelFactory, and when you create proxy dynamically with code, credentials are set directly inside the ChannelFactory, I’ve found my mistake.

When you create the proxy with instance method CreateChannel(EndpointAddress), it maintains a connection with the original factory, so it was able to grab credentials when needed from the factory, if you create the channel with the static CreateChannel(Binding, EndpointAddress) you are actually calling this code.

image

Figure 1: Reflected code of CreateChannel static method

As you can verify this method actually creates a new ChannelFactory passing the binding, then uses this new factory to create the channel, and credentials set into the original factory are lost, because the channel is actually created from another ChannelFactory instance. The solution is really obvious; use the CreateChannel(EndpointAddress) method, but changing the call to the constructor of the ChannelFactory to pass modified binding object. Everything now works perfectly, here is the code to create channel factory passing the binding.

instance = Activator.CreateInstance(channelFactoryBaseType, binding);

Gian Maria.

Wcf over HTTPS, compression, binary binding and some measurement

I’ve an application that is composed by WPF clients deployed over the internet and a central WCF service exposed over Https for security reason, everything works fine, but as the number of clients grows, performance starts to decrease a little. Since I have a great number of clients that operates behind a single standard ADSL 8 MBit (used for everything, mail, browsing, etc..), I measured and found that bandwidth is becoming a bottleneck, so I need to reduce amount of traffic between clients and server.

It turns out that the easiest way to gain bandwidth is enabling IIS dynamic compression, as you can read on this interesting article by Scott Hanselman that explain in great details how you can enable compression in IIS. Since my server has plenty of CPU available, compression is a real interesting way to reduce bandwidth.

This is not the only option, you can also enable binary serialization over Http because binary serialization usually is smaller than standard serialization you obtain with DataContractSerializer, the only drawback is that such an endpoint is accessible only with WCF client; but if this is an issue it is really simple to expose the same service with multiple endpoints, each one with a different binding. I decided to add another endpoint with customBinding to use the BinaryMessageEncoding (because I want to be interoperable with non .NET clients), so I added this binding configuration

      <customBinding>
        <binding name="BinaryHttpBindingWithNoAuth">
          <binaryMessageEncoding>
            <readerQuotas maxDepth="32" maxStringContentLength="2100000000" maxArrayLength="16384" maxBytesPerRead="2147483647" maxNameTableCharCount="16384" />
          </binaryMessageEncoding>
          <httpsTransport />
        </binding>

        <binding name="BinaryHttpBinding">
          <binaryMessageEncoding>
            <readerQuotas maxDepth="32" maxStringContentLength="2100000000" maxArrayLength="16384" maxBytesPerRead="2147483647" maxNameTableCharCount="16384" />
          </binaryMessageEncoding>
          <security authenticationMode="UserNameOverTransport">
  
          </security>
          <httpsTransport />
        </binding>
      </customBinding>

Then I added another endpoint of this service that use this binding (actually I have more than a single wcf service, some of them without authentication, some other that needs to be authenticated, this is the reason why I need two configuration for binding).

Then I modified my client program to be able to use standard or binary endpoint with a user settings and I started to measure bandwidth usage in various situation. I’ve started with a real scenario, startup of the application with production real data, because during startup the application issues seventeen requests to the service to initialize everything, it start requesting customer information, then try to login the user and finally loads all data the user needs to start working. I can improve performances reducing number of requests, but actually I’m interested only in download bandwidth.

Here is the sum of bytes downloaded for all the requests, first column is SOAP uncompressed, second one SOAP compressed, third column BinaryMessageEncoding uncompressed, and finally fourth column BinaryMessageEncoding compressed.

image

Figure 1: Measurement over the amount of data downloaded at application startup

With SOAP binding and no compression the startup download ~262Kb of data and since data is mostly textual, simply enabling Http Dynamic compression (second column) saved ~80% of the bandwidth, because total amount of data is now ~52Kb. Now it is interesting to compare these numbers with BinaryMessageEncoding (third column) because the total size is ~155Kb, a ~40% saving respect SOAP uncompressed data. This is interesting because if for some reason you have small CPU power to enable compression, with Binary Encoding you can still save a lot of bandwidth without resorting to compression. The fourth column represents BinaryEncoding with Dynamic compression, since the content is mostly textual I can gain ~76% of the bandwidth (36Kb instead of 155Kb) enabling compression even for the Binary Encoding. It is a ~30% save respect to compressed SOAP and ~86% gain respect to SOAP uncompressed.

These numbers can vary a lot in your situation, because they are highly dependent of format of data (I need to send large portion of textual data so it my data is highly compressible), but it worth takes some measurement to understand if you can gain similar amount of bandwidth with your data. The important stuff is that everything is done simply changing WCF configuration and you do not need to change a single line of code.

Gian Maria.

Troubleshoot WCF The underlying connection was closed: A connection that was expected to be kept alive was closed by the server

I’ve already blogged in the past on how to easily troubleshoot WCF Exception and that suggestion is valid for every exception you encounter in WCF. Today I have a function that gave the error

System.ServiceModel.CommunicationException: The underlying connection was closed: A connection that was expected to be kept alive was closed by the server. —> System.Net.WebException: The underlying connection was closed: A connection that was expected to be kept alive was closed by the server. —> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. —> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host

This problem is usually caused by some error in the communication channel, but even in such a situation, enabling the trace listener immediately gave to you an idea of the problem.

 

SNAGHTML20daa9b

Figure 1: How to identify the problem thanks to WCF Trace Listener

In my situation the error is ClientProxy …. is not expected and this is due to the fact that I’m returning from WCF a couple of classes that are mapped with NHIbernate, the entity returned has a property of type Customer that is a lazy property, thus at runtime it is represented by a dynamic proxy that is not known to WCF. The solution is trivial, just issue a Fetch(obj => obj.Client) in the LINQ query to fetch Client and avoiding NH to create a proxy, but the key point is that with WCF trace listener, finding problems in WCF is matter of few seconds.

Gian MAria.