Skip Navigation LinksHome > View Post

Smuggling Headers with WCF

Yesterday we looked at how we might add a little context so we can tie seemingly disparate events together again.

Today we'll look at passing this context across a WCF service boundary by authoring a behavior extension. If tracing isn't your thing then this post should also interest anybody who would want to send a header with every request to a WCF service.

Here, we'll check for an ActivityId (that isn't the default GUID value {000...000}) and sneak it into all messages leaving our client in a Soap Header. Then we'll have quick look for this soap header on the server and change the server threads ActivityId if we find one.

If you wanted to fiddle with the SOAP crossing the wire in the .asmx world you'd implement a SoapExtension cover your self in SOAP and play with bubbles. In WCF, however, things are a little different and, in my opinion, better.

Fiddling with messages

In WCF, if you want to fiddle with messages closer to the WCF stack you have to create an implementation of either IClientMessageInspector or IDispatchMessageInspector for the client and server respectively.
Remember, these roles can easily be reversed in WCF thanks to Duplex Contracts and the like.
Here's how we'll add our soap header to outgoing messages from the client

private const string HEADER_NAME = "suggestedActivityId";
private const string HEADER_NS = "urn:thejoyofcode-com:services:activity-header:2006-11";
private static Guid DEFAULT_GUID = new Guid();

public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel)
{
    // don't send this if it's {000...000}
    if (System.Diagnostics.Trace.CorrelationManager.ActivityId != DEFAULT_GUID)
    {
        // before we send the request, we need to add our suggested context header
        MessageHeader<Guid> mh = new MessageHeader<Guid>();
        mh.Content = System.Diagnostics.Trace.CorrelationManager.ActivityId;
        mh.Actor = string.Empty;
        mh.MustUnderstand = false;
        // add the new header to our request
        request.Headers.Add(mh.GetUntypedHeader(HEADER_NAME, HEADER_NS));
    }
    return null;
}

and here's how we'll grab them on the server

public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
{
    // Now we need to mine out our suggested activityId
    int index = request.Headers.FindHeader(HEADER_NAME, HEADER_NS);
    if (index > -1)
    {
        Guid activityId = request.Headers.GetHeader<Guid>(HEADER_NAME, HEADER_NS);
    
        if (activityId != DEFAULT_GUID)
        {
            System.Diagnostics.Trace.CorrelationManager.ActivityId = activityId;
        }
    }
    
    return null;
}

That was easy! Sadly, we're not quite there yet. To plug the inspectors into WCF we need to implement IEndpointBehavior and BehaviorExtensionElement. The latter of which is necessary to support configuration via your Xml config file.

Fortunately, this part is pretty easy so rather than bore you with it here download the code and check it out for yourself.

Configuring a behavior extension is pretty easy

<behaviors>
    <serviceBehaviors>
        <behavior name="messageServiceBehavior">
            <serviceMetadata httpGetEnabled="true"/>
        </behavior>
    </serviceBehaviors>
    <endpointBehaviors>
        <behavior name="withActivityInspectors">
            <activityInspectors />
        </behavior>
    </endpointBehaviors>
</behaviors>
<extensions>
    <behaviorExtensions>
        <add name="activityInspectors" type="TheJoyOfCode.ServiceHeaderSample.ActivityHeaderBehavior, TheJoyOfCode.ServiceHeaderSample, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
    </behaviorExtensions>
</extensions>

Now we just have to specify a "withActivityInspectors" behaviourConfiguration attribute on the applicable endpoints (server and client).

And here's what our soap header looks like over the wire (using a BasicHttpBinding anyway):

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Header>
        <suggestedActivityId xmlns="urn:thejoyofcode-com:services:activity-header:2006-11">6f9f9fd1-09ab-4fdd-ba30-ea7d7759a77f</suggestedActivityId>

The source code also contains a little application to show the inspectors in action. The application acts as both server and client so it's perhaps not the most compelling example for a distributed technology but it does at least pass the ActivityId with the service call.

Note that I implemented both message inspector interfaces in the same class, which is private and belongs to the class that implements IEndpointBehavior.

UPDATE: fixed the bad link to the source code. Apologies.

Tags: WCF

 
Josh Post By Josh Twist
4:52 AM
29 Nov 2006

» Next Post: Prevalence
« Previous Post: Brighten up your traces with a splash of colour

Comments are closed for this post.

Posted by Josh @ 07 Mar 2007 4:58 AM
Minor point but I just realised that I could have used the static Guid.Empty property instead of creating my own empty guid (DEFAULT_GUID).

© 2005 - 2014 Josh Twist - All Rights Reserved.