DCSIMG
April 2008 - Posts - Manu Cohen-Yashar's Blog

Manu Cohen-Yashar's Blog

April 2008 - Posts

Slides and demos from TechEd Eilat

I heard the slides and demos are not on the official TechEd site.

Unfortulately it is too big to put here so we should wait for the TechEd site

Sorry about that

manu 

 

The Story of WCF invocation on IIS

 

The story starts just after the request has been dispatched to the worker process, but before pretty much anything else has run. We now join the following thought process already in progress:

 

...We have a piece of infrastructure (an HttpModule for the HTTP case, a new thing called a ProtocolHandler for the non-HTTP case in WAS) that receives Indigo requests and stuffs them into a holding area while the transport stack comes up. The magic API is ServiceHostingEnvironment.EnsureServiceAvailable( string virtualPath ), which is what eventually calls ServiceHost.Open(). The logic for ESA does the following:

 

1) First, look up the virtual path in a cache to see if we already have an active ServiceHost for it. If so, great -- the service is active, so we're done.

 

2) If we don't find an entry in the cache, it means the service hasn't been activated yet. So the first thing we do is call ASP.NET's static BuildProvider.GetCompiledCustomString() method.

 

3) The ASP.NET compilation system ends up calling our build provider, because we mapped the *.svc extension to our build provider in the root web.config. Our build provider parses contents of the .svc file (including the @ServiceHost directive), dynamically compiles any inline code present therein, and then returns several bits of interesting data back to the caller. This interesting data contains things like the value of the Service/Factory attributes from the @ServiceHost directive and a list of referenced assemblies we used during compilation. Our build provider returns more than just a type, which is why we call BuildProvider.GetCompiledCustomString() instead of the more conventional BuildProvider.GetCompiledType().

 

4) Popping a few stack frames back to ESA(), the next thing we do is cons up an instance of IServiceHostFactory (ServiceHostFactoryBase in recent bits). If the user specified a specific factory by providing a CLR type name in the .svc file's Factory attribute, we'll Activator.CreateInstance() on that type. Otherwise, we new up the default factory (System.ServiceModel.Activation.ServiceHostFactory).

 

5) Now that we have host factory, we take the string contents of the Service attribute and pass it to ServiceHostFactoryBase.CreateServiceHost( string constructorString, Uri[] baseAddresses). The base addresses came from the IIS/WAS configuration store, and the constructor string just came from the Service="blah" attribute in the .svc. ESA() makes no assumptions about the format or contents of the string; how that string gets interpreted is entirely up to the factory/host.

 

6) We have a default implementation of ServiceHostFactoryBase that returns instances of ServiceHost. In this world, the contents of the constructor string are interpreted as the CLR type name of your service implementation type. So ServiceHostFactory does the moral equivalent of Type.GetType and passes that down to its own virtual method CreateServiceHost( Type t, Uri[] baseAddresses ). By default this implementation just does "return new ServiceHost(t, baseAddresses)". If you have your own derivative of ServiceHost, you can derive from ServiceHostFactory and override this method to return a new instance of your custom host and we'll do the work of turning that constructor string into a System.Type for you. If you're deriving directly from ServiceHostBase instead, you can derive from ServiceHostFactoryBase and get access to the unadulterated, unparsed value the user put in Service="blah" back in the .svc file. What you do with it then is entirely up to you.

 

7) Now that we have the ServiceHost back from the factory, we can stick it in the cache so we skip all this next time around. Then we call ServiceHost.Open(), which kicks off the normal process of building a description, creating a channel stack, and all the other stuff we normally do at Open() time.

 

8) Once that channel stack is fully up, it starts an accept loop. This percolates all the way down to the transport...which, for the hosted case, causes the thread to block until a message arrives in very same holding area that I mentioned at the beginning of this mail. Since the transport put one in there prior to calling ESA(), this call returns immediately and the message then flows up the channel stack where it gets processed in exactly the same way it would have in self-host.

 

Manu

Extend ServiceHost on IIS Hosting Scenarios.

Extend ServiceHost on IIS Hosting Scenarios.

 

The motivation for API’s that I’m going to talk about in this post stems from the fact that the standard API for hosting services in WCF (System.ServiceModel.ServiceHost) is a first-class extensibility point in the WCF architecture. We expect that lots of folks will derive their own host classes from ServiceHost, usually with the intent of overriding ServiceHost.OnOpening in order to do interesting things with the ServiceDescription (e.g. adding default endpoints imperatively, or twiddling knobs on Behaviors) prior to  opening the service.

 

In the self-host world, you can get away without creating a custom ServiceHost because you write the code that instantiates the host and subsequently calls Open on it. In between those two steps you can do whatever you want, e.g.:

 

public static void Main()

{

   ServiceHost host = new ServiceHost( typeof( MyService ) );

   host.Description.Behaviors.Add( new MyServiceBehavior() );

   host.Open();

    ...

}

 

However, this solution has the downside of not being particularly reusable. The code that manipulates the description is baked into the host program (in this case, the Main() function) so if you want to reuse that logic in other contexts you’ve got a little bit of a problem. A slight refactoring will solve that problem; all we have to do is move the code that adds the ServiceBehavior out of Main and into the OnOpening method of a custom derivative of ServiceHost:

 

public class DerivedHost : ServiceHost

{

   public DerivedHost( Type t, params Uri baseAddresses ) :

      base( t, baseAddresses ) {}

 

   public override void OnOpening()

   {

         this.Description.Behaviors.Add( new MyServiceBehavior() );

   }

}

 

Then, inside of Main() you just say

 

public static void Main()

{

   ServiceHost host = new DerivedHost( typeof( MyService ) );

   host.Open();

   ...

}

 

And you’ve now encapsulated that custom logic up into a nice, clean abstraction that can be easily reused across lots of different host executables.

 

What’s not immediately obvious at this point is how to use this custom ServiceHost inside of IIS or WAS. Those environments are different than the self-host world, because the hosting environment is the one instantiating the ServiceHost on behalf of the application .The WAS hosting infrastructure doesn’t know anything about your custom ServiceHost derivative, and it seems like there’s no good place to write that new DerivedHost( typeof( MyService ) ); line when running in IIS/WAS. What’s a poor customer of the WCF extensibility model to do in this case?

 

This is exactly the problem that the ServiceHostFactory API was designed to solve.

 

Because the ServiceHost used to host the service is a potentially polymorphic type, the hosting environment never instantiates it directly. Instead, we use a factory pattern to provide a layer of indirection between the hosting environment and the concrete type of the service. Unless you tell us otherwise, we’ll use a  default implementation (System.ServiceModel.Activation.ServiceHostFactory) that simply returns an instance of System.ServiceModel.ServiceHost. You can provide your own factory (one that presumably returns your derived host) by specifying the CLR type name of your factory implementation in the @ServiceHost directive.

 

The intent is that for simple cases, implementing your own factory should be a trivial exercise. For example, here’s a ServiceHostFactory that returns the DerivedHost from previous examples in this post:

 

public class DerivedFactory : ServiceHostFactory

{

   public override ServiceHost CreateServiceHost(Type t,
                                                 Uri[] baseAddresses )

   {

      return new DerivedHost( t, baseAddresses )

   }

}

 

To use this factory instead of the default factory, just provide the type name in the @ServiceHost directive as follows:

 

<% @ServiceHost Factory=”DerivedFactory” Service=”MyService” %>

 

 

While there’s not technical prohibition on doing pretty much whatever you want to the ServiceHost you return from ServiceHostFactory.CreateServiceHost(), our guidance here is to keep your factory implementations as light as possible. If you have lots of custom logic, you’re better off putting that inside your host instead of inside the factory for the same reusability reasons that you don’t want to put a lot of host-related code inside of Main().

 

manu

Creating X.509 Certificates using makecert.exe

 

Creating x.509  certificates is a very common task. Unfortunately the knowledge how to do it is quite rare. If you want a certificate that the whole world would trust you need to buy one, but if you need it for your own use you can create it using a tool called MakeCert.exe

After downloading the tool you have to perform the following procedure:

Creating a Root Certificate Authority

makecert.exe -n "CN=My Root CA,O=Organization,OU=Org Unit,L=San Diego,S=CA,C=US" -pe -ss my -sr LocalMachine -sky exchange -m 96 -a sha1 -len 2048 -r My_Root_CA.cer

Import Root Certificate Authority Certificate into Trusted Root Store

certutil.exe -f -addstore Root My_Root_CA.cer

Create Backup (Export) PFX file of Root Certificate Authority Certificate

certutil.exe -privatekey -exportpfx "My Root CA" My_Root_CA.pfx

Create a Server Certificate issued from the previously created Certificate Authority

makecert.exe -n "CN=Server" -pe -ss my -sr LocalMachine -sky exchange -m 96 -in "My Root CA" -is my -ir LocalMachine -a sha1 -eku 1.3.6.1.5.5.7.3.1,1.3.6.1.5.5.7.3.2 Server.cer

Create Backup (Export) PFX file of Server Certificate

certutil.exe -privatekey -exportpfx "Server" Server.pfx

Create a Computer certificate issued from the previously created Certificate Authority

makecert.exe -n "CN=Computer" -pe -ss my -sr Localmachine -sky exchange -m 96 -in "My Root CA" -is my -ir LocalMachine -a sha1 -eku 1.3.6.1.5.5.7.3.2 Computer.cer

Create Backup (Export) PFX file of Computer Certificate

certutil.exe -privatekey -exportpfx "Computer" Computer.pfx

OID Reference

Encrypting File System (1.3.6.1.4.1.311.10.3.4)

Secure Email (1.3.6.1.5.5.7.3.4)

Smart Card Logon (1.3.6.1.4.1.311.20.2.2)

Client Authentication (1.3.6.1.5.5.7.3.2)

Server Authentication (1.3.6.1.5.5.7.3.1)

IP security IKE intermediate (1.3.6.1.5.5.8.2.2)

 

manu