In the Visualise Azure Table Storage with Excel and Fiddler I described how to use Fiddler as a handy proxy that can be used to inject the protocol differences between standard oData and the non-standard oData supported by Azure Table storage. While Fiddler is a handy desktop tool to illustrate the solution, it is not really appropriate as a part of a system architecture. A simple request to host the same Azure Table Storage to oData functionality as a hosted service lead me down a long and windy road in the world of WCF, but one worth documenting.

What Fiddler really provides is a local proxy through which requests route before leaving the client and responses route through before being returned to the browser. A hosted solution requires something slightly different, a router with the ability to take requests from one URL and serve them from another URL. Additionally we need the router to do content inspection and URL rewriting so that the content returned to the client directs requests back through the router.

So take a look at a couple of design options and building blocks to make sure there’s nothing obvious that already does it:

IIS, URLRewrite and ARR

I covered briefly the use of Application Request Routing as a router in the Publish Content to the Azure CDN with UrRewrite blog. URLRewrite and ARR working together makes a great router. It’s a powerful and high performance way to direct traffic intended for one location to another location while modifying the request and potentially the response along the way. While the URLRewite part is powerful, it is not possible within the constraints of the regexp based rules and simple plugin model, to implement the kind of rich functionality required to create the Azure security key we need.

Fail!

URLRewrite, ARR and ASP.NET HttpModule

An HttpModule very neatly plugs into the IIS pipeline and gives us the opportunity to write powerful .NET code to affect the request on the way through, and potentially (when the requesting Url is changed), hand off to ARR to forward the request “off-box” and route the response back through. There are a few tricks to getting this to work.

Firstly working in the IIS pipeline can be a tricky business. It’s important to remember you are working with streams of data, not strings or collections. The act of reading the stream can have an impact on those that come after you in the processing stack. And when operating between IIS native pipeline (where ARR operates) and .NET integrated pipeline (where HttpModule operates), it’s not obvious when the stream is being read. A simple statement such as this:

HttpContext context = HttpContext.Current;
HttpRequest request = context.Request;
string value = request.Params[“Name”];

Actually permanently affects the incoming stream in a way that makes it unable to be processed back at the native pipeline level. Certain .NET statements lift the stream out of IIS at certain points and surfaces it in a .NET interface, which prohibits further stream processing at the native level. To address this issue, .NET 4.0 now has the request.InsertEntityBody(). This method call will put a copy of the request back into the native pipeline in IIS so both APIs can operate on the streams. That means that after any change to the request a call to request.InsertEntityBody() needs to be made to pass that change back to IIS.

There are further complications when working with stream. To implement the addition of the oData paging <link> requires the module to look in the response stream for the headers and render a new link in the response body using those values. That means buffering the response reading the just the headers and then performing a replace before Flushing the stream. It can be done (I think) but the question is, do we need all the complexity of IIS and URLRewrite, ARR and HttpModule?

Maybe

WCF

WCF abstracts the notion of communication away from the hosting platform, message protocol and formats. Sure you can still use IIS to host a WCF service, but you don’t have to. WCF can be hosted in its own service listening on a certain ports and acting on messages arriving on them. WCF can be used as a transport and message agnostic router which provides everything that IIS, URLRewrite, ARR and HttpModule do together. So WCF looks promising and is worth a closer look.

WCF 4.0 Routing Service

First stop on the WCF trail is the new and very promising sounding WCF 4.0 Routing Service! This looks like it’s exactly what we need, but before going much further there is a statement in the documentation that says “The Routing Service does not currently support routing of WCF REST services. To route REST calls, consider using System.Web.Routing or Application Request Routing”. Huh! Back to ARR? Why WCF would single out REST to not be supported. In the interests of not fighting the available technology, rather than look much further into getting around that limitation, I decided to believe the doco, bin it and move on.

Fail

WCF Router

What about building our own router using WCF.

Michelle covered the basics in her 2 part blog “Building a WCF Router” which has all the basic building blocks and concepts which we can build upon. With a start like that, surely this can’t be too complicated? Next, Bing and Google madly to see if anyone has done it already which reveals these valid attempts:

  • There’s a different approach here but it ties the router solution to the underlying data structure so isn’t as generic as we need, but affirms that others are trying to solve the same problem.
  • This is as close as it gets and but it doesn’t rewrite the responses so they point back to the router.

We’ll use this basic construct and add Routing, Rewriting and WCF Behaviours to plug in the additional features (including oData/Azure Table Storage authentication functionality later). But before implementing any further let’s pause for a moment and look at how each of these WCF components comes together to solve the problem.

In designing the Router I wanted to store no state at the Router. That is everything requires to act on a message has to be available in the message or available message context. That means there are certain points where we need to save Urls and set properties on messages to carry additional information.

  • A request comes in from a client to the Router, but before reaching the Router Rewrite Behaviour (on the service side) picks up the RouterUrl and saves it for later
  • The Router decodes the Url and works out the destination service to send to, but before reaching the service the Rewrite Behaviour (on the client side) saves the Service Url for later
  • The destination service processes the request and responds with some data, but before arriving at the router the Rewrite Behaviour (on the client side) tunnels the saved Service Url into the message properties
  • The Router takes the response and replies to the Client, but before arriving at the Client the message contents are replaced so any Service Urls are replaced with matching Router Urls.

Router

So it’s time to start coding starting with the simplest possible router which has a single method that accepts any Message and responds with a Message.

[ServiceContract]
public interface IRouter
{
  [OperationContract(Action = “*”, ReplyAction = “*”, Name=“*”)]
  [WebGet()]
  Message ProcessMessage(Message requestMessage);
}

RouteTo

First question we need to resolve is how to tell the router where to route to. This is often done with Http headers or similar, but since we are hoping to support vanilla REST and oData clients (like Excel) there is no opportunity to insert headers so I’ve chosen to use the URL. Specifically the first Uri path segment after the domain is mapped to the service URL.

For instance: http://router.cloudapp.net/netflix/v2/Catalog => https://odata.netflix.com/v2/Catalog

Registration

Next we come up with a strategy for registering and unregistering routes. Ideally I’d like to support registration at runtime (using REST) so take control of a couple of special Router URLs.
http://router.cloudapp.net/register?name=netflix&routeTo=odata.netflix.com&type=Route
Will enable the previous route; and similarly for unregister
http://router.cloudapp.net/unregister?name=abc
(The Type parameter is used to describe the type of routing and will be described later.)

Routing

The routing part is very simple in WCF. Before passing a message onto the WCF runtime with ProcessMessage, simply rewrite the “To” Header by replacing the routerUrl with the destination service Url and WCF will do the rest (no Application Request Routing complications here).

if (serviceUri != null)
{
//map the Uri arriving at the Router to the corresponding Uri at the service 
UrlRewriter urlRewriter = new UrlRewriter(serviceUri, request.Headers.To);
request.Headers.To = new Uri(urlRewriter.ReplaceWithService(request.Headers.To.ToString()));
}
//now the Router makes the Service request
Message reply = router.ProcessMessage(request);
return reply;

Rewrite Behaviour

So we can successfully forward messages onto configured endpoints and get responses. The problem is the responses (as is the case for web pages and REST/oData) will contain Uri’s for the originating service so that subsequent requests will go direct to the service and not via the router. What we need to do is look for any service Uri’s in the returned content and replace with the Router address. This can be done with a WCF behaviour using IDispatchMessageInspector and IClientMessageInspector to pick up the Urls from the message and meet up in BeforeSendReply to modify the reply just before sending it back to the client.

//rewrite all the urls to point to the proxy url
    reply = Common.ReplaceInMessage(reply, serviceRequestPath, proxyRequestPath);

With any WCF solution the devil is in the config. To make the behaviour magic happen we use configuration to add the service behaviour before the Router

<service name=WcfRouter.Router>
<endpoint name=routerEndpoint” behaviorConfiguration=RouteAndRewrite” binding=webHttpBinding” contract=WcfRouter.IRouter/>
</service>

And add the client behaviour after the Router

<client>
<endpoint name=Route behaviorConfiguration=Route binding=webHttpBinding contract=WcfRouter.IRouter/>
</client>

Routing Types

To support the different possible routing behaviours we use the “Type” parameter on registration. The value of Type is used to select the name of the client endpoint, which in turn chooses the set of behaviours to be applied on the service side of the router.

<client>
<endpoint name=Route behaviorConfiguration=Route binding=webHttpBinding contract=WcfRouter.IRouter/>
 <endpoint name=RouteAndRewrite behaviorConfiguration=RouteAndRewritebinding=webHttpBindingcontract=WcfRouter.IRouter/>
</client>

Where Type=”Route” selects an empty behaviour set

<endpointBehaviors>
<behavior name=Route></behavior>

And Type=”RouteAndRewrite” selects the RewriteBehaviourExtension

<behavior name=RouteAndRewrite>
<RewriteBehaviorExtension/>
</behaviour>
</endpointBehaviors>

Now we’ve built a generic rewriting router with a registration model. But the real goal here is the ability to register and plugin additional WCF behaviour extensions, so…

Azure Table Storage Auth

Back to the original problem which is Azure Table Storage requires authentication keys to be sent with the request. That means the client endpoint we use to request data from table storage needs an additional behaviour too to modify the request outgoing from the router to table storage.

This is a slightly different behaviour as it is the router acting as a service client and so is and modifying the request using an implementation of IClientMessageInspector.BeforeSendRequest
and adds headers onto the outgoing webrequest.

GenerateAzureAuthHeaders(storageCredentials, request.Headers.To , out dateHeader, out authorizationHeader);
httpRequest.Headers.Add(DateHeader, dateHeader);
httpRequest.Headers.Add(AuthorizationHeader, authorizationHeader);

oData Link Element

One last piece of the puzzle is the oData <link> element. This is an additional element that needs to be added to reply from table storage to implement paging. This replaces the header based implementation used on table storage replies with the standard oData method of a link Uri to the next page. Again a custom behaviour can help here on the router client side, but this time implementing IClientMessageInspector.AfterReceiveReply. This method gives us the opportunity to replace the message contents returned from table storage with a new oData compliant one including link elements (which will be rewritten to be routed through the proxy by our earlier rewrite behaviour).

linkElement = “<link rel=\”next\” href=\”” + WebUtility.HtmlEncode(newUri) + “\”/>”;
//Do the replace and recreate the message
reply = ReplaceInMessage(reply, FeedEndElement, linkElement + “\n” + FeedEndElement);

Transfer Encoding

While all that looks dead simple, one very important note when implementing this extra Azure behaviour (that took a lot of debugging). The responses coming back from Azure table storage have a header that can be seen in Fiddler.

Transfer-Encoding: chunked

When using WCF to change Messages, it is important to create a new copy of the Message and copy across all of the Properties and Headers from the old message. It turns out that the act of copying the message means it is no longer “chunked” so that header must be removed before passing the Message back through WCF. If you don’t remove the header, no Message body is returned and no error is either.

httpResponse.Headers.Remove(TransferEncodingHeader);

Registration

To properly handle the new AzureAuth routing ability, we need one more piece of information, the StorageKey to be associated with a given route like so.

http://router.cloudapp.net/register?name=backtester&routeTo=backtester.table.core.windows.net&type=AzureAuth&connectionstring=DefaultEndpointsProtocol=http;AccountName=backtester;AccountKey=blah

Hosting

The power of WCF is it can be hosted pretty much anywhere. For the most flexibility it is best hosted on its own where it can take over the entire Url space so any Url can be served. However IIS is also an option but requires the use of an annoying .svc extension in your router Urls. I’ve done both and the Router service is happy either way. It can be deployed into Azure as a WebRole, a WorkerRole(with some additional effort) or for purposes of this example hosted in Azure Web Sites for anyone to try.

Just browse to http://tablerouter.azurewebsites.net

Try out the Netflix router with Urls like http://tablerouter.azurewebsites.net/Router.svc/netflix/Catalog/Languages(). Here we are browsing the oData of the Netflix catalog but using our Router to serve the content.

Now try the pre-registered quote route http://tablerouter.azurewebsites.net/Router.svc/quotes/Quotes()

Then finally get an Azure Table Storage Url and Connection String and register your own route.

Browsing

This brings us back to the original request. How to view Azure Table Storage using Excel. As per the original Fiddler based blog, follow the same steps.

  • Use Excel 2013 or Excel 2010 with PowerPivot
  • Data=>From Other Data Sources=>oData Feed
  • Follow the wizard with your registered route
  • And view the data!

A note on Security

A note on security; what we have done with the oData proxy is to remove the need for vanilla oData clients (like Excel) to know and understand the nuances of the Azure Table Storage security keys. But to do that we’ve had to give the Router the ability to impersonate a secure client by given it your security key information which is kept in memory on the Router. You now have a publicly accessible Url to your view.

Code

The code is available here with some additional bits and pieces for hosting in WebRoles and WorkerRoles

Category:
Application Development and Integration, Azure Platform, WCF

Join the conversation! 11 Comments

  1. Hello,
    Nice sample,
    I am a bit of a newbie to consume data from Azure Table Storage and need to get out the data directly in Excel, so your example is right on ..
    I have a question though.
    Does your solution require you to have SSL certificat on the server running the Router or Table storage

    I register
    http://127.0.0.1:81/Router.svc/register?name=kafta&routeTo=kafta.table.core.windows.net&type=AzureAuth&connectionstring=DefaultEndpointsProtocol=https;AccountName=kafta;AccountKey=

    and the Router service says its registered, sofar so good. I can follow the hole process in VS.

    The Problem:
    I keep getting the same results and it is
    “No valid combination of account information found.”

    When I put the following question to a table called Equipment List contained in Table storage in the account Kafta

    http://127.0.0.1:81/Router.svc/Kafta/EquipmentList?Filter=PartitionKey = ‘2013-06 ‘

    I have also tested to register the DefaultEndpointProtocol=http; with no luck.

    Any thougts on this matter?

    “kafta is a fictitious name on the account for you who wonder”

    Best Regards
    Patrik

    Reply
  2. My earlier reply was deleted but..
    it breaks in line
    StorageCredentials storageCredentials = CloudStorageAccount.Parse(connection).Credentials;
    in the Befor sendRequest in AzureAuthInspektor.cs

    Error routing , No valid combination of account information found

    any reply would be much appreciated

    Reply
  3. Thanks peter for nice blog. It helps allot. I am planning to write Routing service which can route both SOAP and OData requests. I took your solution and tried to debug. I am getting empty string while reading message before sending to client.

    using (XmlDictionaryReader originalBodyReader = originalMessage.GetReaderAtBodyContents())
    {
    messageBodyString = originalBodyReader.ReadOuterXml();
    //This messageBodyString is empty. But originalMessage has data.
    }

    Reply
    • Yeah I had the same problem which is why I called it a RESTful Router. The problem I had is that I could not get WCF to be totally agnostic of the message format. You’ll see I started playing with something called a RawMapper as per this article http://blogs.msdn.com/b/carlosfigueira/archive/2008/04/17/wcf-raw-programming-model-receiving-arbitrary-data.aspx but didn’t persist with it as I didn’t really need SOAP routing

      Reply
    • I achieved replacing service Uri with router uri for OData responses by below code

      public Message ChangeString(Message oldMessage)
      {
      MemoryStream ms = new MemoryStream();
      XmlWriter xw = XmlWriter.Create(ms);
      oldMessage.WriteMessage(xw);
      xw.Flush();
      string body = Encoding.UTF8.GetString(ms.ToArray());
      xw.Close();

      body = body.Replace(_servicePrefix, _routerPrefix);

      ms = new MemoryStream(Encoding.UTF8.GetBytes(body));
      XmlDictionaryReader xdr = XmlDictionaryReader.CreateTextReader(ms, new XmlDictionaryReaderQuotas());
      Message newMessage = Message.CreateMessage(xdr, int.MaxValue, oldMessage.Version);
      newMessage.Properties.CopyProperties(oldMessage.Properties);
      return newMessage;
      }

      Reply
  4. I tried to route the SOAP requests via this router. It worked well when i change address filter at service end.

    [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
    public class FirstService : IFirstService

    But I observed that router is receiving incoming message four times as opposed to one time. However, WCF SOAP call is executed successfully and returned response.

    Dont know why it is sending message 4 times to router.

    Reply
  5. Hi, thanks for sharing this information. I’ve one question though, I would be very grateful if you answer this. Can I route HTTP requests to an ASP.NET Web API service through WCF RESTful router.

    Client -> WebHttpBinding -> ASP.NET Web API

    Is this possible?

    Reply
  6. Yes, absolutely, however this sample has now been updated.
    Firstly you can browse TableStorage directly by the new Excel PowerQuery
    Also the code has been rewritten to use WebAPI instead of WCF in a version called the “Busker Proxy”
    https://blog.kloud.com.au/2014/02/23/do-it-yourself-fiddler-service/

    Also for generic routing of HTTP requests, always have a look at the IIS Application Request Routing module like in this searies of blogs
    https://blog.kloud.com.au/2014/06/06/do-it-yourself-cloud-accelerator/

    Reply
  7. […] I’m not interested to create any additional WCF router for […]

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: