Monday, February 9, 2009

AJAX WCF Services and HTTP/HTTPS multiple domains on single IP Address

It seems simple, hosting  an AJAX WCF .svc in IIS under multiple domains with both http and https protocol. Unfortunately this turned out to be a little complex under the binding model used by WCF. You may recieve messages such as "Could not find a base address that matches scheme http for the endpoint with binding WebHttpBinding. Registered base address schemes are [https]".After some exploration I came up with the following solution:
Configuration:
<system.serviceModel> 
 <services>
  <service name="MyNamespace.MyService" behaviorConfiguration="DefaultAspNet">
   <endpoint address="" behaviorConfiguration="ServiceAspNetAjaxBehavior" binding="webHttpBinding" contract="MyNamespace.MyService"/>
   <endpoint address="" behaviorConfiguration="ServiceAspNetAjaxBehavior" bindingConfiguration="Secure" binding="webHttpBinding" contract="MyNamespace.MyService"/>
  </service>
 </services>
 <behaviors>
  <endpointBehaviors>
   <behavior name="ServiceAspNetAjaxBehavior">
    <enableWebScript />
   </behavior>
  </endpointBehaviors>
  <serviceBehaviors>
   <behavior name="DefaultAspNet">
    <serviceAuthorization impersonateCallerForAllOperations="false" />
    <serviceMetadata httpGetEnabled="true" httpGetUrl="" />
    <serviceDebug httpHelpPageEnabled="true" includeExceptionDetailInFaults="true" />
   </behavior>
  </serviceBehaviors>
 </behaviors>
 <bindings>
  <webHttpBinding>
  <binding name="Secure">
   <security mode="Transport"/>
  </binding>
  </webHttpBinding>
 </bindings>
 <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
</system.serviceModel>
Factory used to allow multiple host service usage:
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ServiceModel.Activation;
using System.ServiceModel;
//Your Namespace Here
 public class IisServiceHostFactory : ServiceHostFactory
 {
  protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
  {
   ServiceHost result = null; if (baseAddresses != null && baseAddresses.Length > 0 && HttpContext.Current != null && HttpContext.Current.Request != null)
   {
    Uri baseAddress = baseAddresses.First();
    UriBuilder httpUriBuilder = new UriBuilder(baseAddress);
    httpUriBuilder.Scheme = Uri.UriSchemeHttp; httpUriBuilder.Port = 80;
    httpUriBuilder.Host = HttpContext.Current.Request.Url.Host;
    UriBuilder httpsUriBuilder = new UriBuilder(baseAddress);
    httpsUriBuilder.Scheme = Uri.UriSchemeHttps; httpsUriBuilder.Port = 443;
    httpsUriBuilder.Host = HttpContext.Current.Request.Url.Host; result = new ServiceHost(serviceType, httpUriBuilder.Uri, httpsUriBuilder.Uri);
   }
   else
   {
    result = new ServiceHost(serviceType, baseAddresses);
   }
   return result;
  }
 }
//Your Namespace Here