Category: REST


While working on a project using the WCF webHttpRelayBinding binding with SAS based authentication over transport security, I found that my services were taking a very long time to spin up (30-60 seconds) and that my runtime performance was a bit less than optimal in terms of latency (and I had proven that the latency was not a result of my backend service).  To give you an idea what I was working with, my web.config file had contents similar to the below in the system.serviceModel element.

      <endpointBehaviors>
        <behavior name="sharedSecretClientCredentials">
          <transportClientEndpointBehavior>
            <tokenProvider type="SharedAccessSignature">
              <sharedAccessSignature keyName="keyname" key="key" />
            </tokenProvider>
          </transportClientEndpointBehavior>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <services>
      <service name="Microsoft.BizTalk.Adapter.Wcf.Runtime.BizTalkServiceInstance" behaviorConfiguration="ServiceBehaviorConfiguration">
        <endpoint name="RelayEndpoint" address="https://sbnamespace.servicebus.windows.net/service" binding="webHttpRelayBinding" bindingNamespace="http://tempuri.org/" bindingConfiguration="RelayEndpointConfig" behaviorConfiguration="sharedSecretClientCredentials" contract="Microsoft.BizTalk.Adapter.Wcf.Runtime.ITwoWayAsync" />
      </service>
    </services>
    <bindings>
      <webHttpRelayBinding>
        <binding name="RelayEndpointConfig">
          <security relayClientAuthenticationType="RelayAccessToken" mode="Transport" />
        </binding>
      </webHttpRelayBinding>     
    </bindings>

I didn’t observe such problems on my development VM (which I was running with pretty much no firewalls behind it), but did observe this on my client’s UAT environment. This was in spite of following Microsoft’s guidelines that suggest that you should have outbound TCP ports 9350-9354 open on your firewall to enable Service Bus connectivity.  I went through an exercise using the PortQuiz website to prove that these ports were indeed accessible from the UAT server so the performance issues were puzzling.

Next up, I spun up a fiddler capture.  To start with I applied the below filter into Fiddler to get rid of the extra noise.

tcp.port eq 9350 || tcp.port eq 9351 || tcp.port eq 9352 || tcp.port eq 9353 || tcp.port eq 9354

I then initialized some of my services (I shut them down forcibly and then spun them up again) to observe which ports were in use.  I saw that the conversation with Service Bus was being initialized on port 9350 as expected, however that appeared to be the end of the story.  I wasn’t seeing any comms on ports 9351-9354.  I then right clicked one of the displayed records in WireShark and chose “Conversation Filter -> IP” which updates the filter such that it displays anything with a source or destination IP address matching those on the selected record.

This suddenly resulted in a whole lot more records being displayed and helped me get to the root of the issue.  What I was observing was that after Service Bus made the initial connection on port 9350, it attempted to continue the conversation on port 5671 (AMQPS or AMQP over SSL) which hadn’t been opened on the firewall.  This connection attempt was obviously failing, and the observed behavior was that some retries were attempted with fairly large gaps in between until Service Bus finally decided to fall back to port 443 (HTTPS) instead.  Pay particular attention to the lines in the following screenshot with the numbers 1681, 2105, and 2905 in the first column.

2.6 Capture

This explained why my service was taking a long time to start up (because Service Bus was failing to connect via AMQPS and was going through retry cycles before falling back to HTTPS) and also explained why my runtime performance was lower than my expectation (because HTTPS is known to be slower than TCP).  However this didn’t explain why Service Bus was attempting to use port 5671 rather than 9351-9354 as per documentation.

Repeating the same test on my own VM showed that Service Bus was continuing the connection on ports 9351-9354 as expected… So why the difference? On the suggestion of my colleague Mahindra, I compared the assembly versions of the Microsoft.ServiceBus assembly across the two machines. You can do this by running “gacutil -l Microsoft.ServiceBus” in a Visual Studio command prompt, or by manually checking the GAC directory which is typically “C:\Windows\Microsoft.NET\assembly\GAC_MSIL” for .NET 4+ assemblies.

Voila. I found that I was running version 2.1.0.0 on the machine that was behaving correctly, and version 2.6.0.0 on the machine that was misbehaving. It appears that the protocol choosing behavior for Service Bus relays has changed sometime in between these two assembly versions. I have not pinpointed exactly which version this change occurred in, and I don’t yet know whether this change was by design or accidental. Either way, Microsoft have not yet updated their documentation, which means that others will be as confused as I was.

So what are your choices?

  1. You can downgrade to an older version of the assembly.  2.1.0.0 will definitely work for you, but you might be able to get away with a slightly higher version which is less than 2.6.0.0, but it will be up to you to establish which versions are acceptable since I haven’t managed to do this.  You’ll need to update the webHttpRelayBinding binding registration in your machine.config files (or wherever you’ve chosen to register it if you’ve gone for a more granular approach) to point to the older assembly as well.
  2. You can choose to stick with the latest version of the assembly and open up outbound TCP connections on port 5671 on your firewall.

I chose to stick with option #1 because I’m not sure at this stage whether this change in behavior is intentional or incidental, and also because my impression is that raw TCP over ports 9351-9354 would be faster than the AMQPS protocol.  You will find that option #2 is also functional.

With the older version of the assembly in play I could not see traffic on ports 9351-9354 as expected, my services were spinning up in less than a second, and latency was much more in line with my expectations.
2.1 Capture

In a recent post I mentioned how you could dynamically set HTTP headers on a WCF-WebHttp send port.  This post will take this a bit further and discuss how you can dynamically set the target endpoint address as well as the HTTP method.

First things first, just as I mentioned in the post about HTTP headers, you must ensure that the BTS.IsDynamicSend context property is set to true to enable the methods I am about to describe.

Overriding the endpoint address is a piece of cake.  Just set the BTS.OutboundTransportLocation context property to the target URL.

    public Microsoft.BizTalk.Message.Interop.IBaseMessage Execute(IPipelineContext pContext, Microsoft.BizTalk.Message.Interop.IBaseMessage pInMsg)
    {
        // Some non important logic here

        pInMsg.Context.Write("OutboundTransportLocation", "http://schemas.microsoft.com/BizTalk/2003/system-properties", "http://target.service.com/parameters/go/here");
        pInMsg.Context.Write("IsDynamicSend", "http://schemas.microsoft.com/BizTalk/2003/system-properties", true);
        return pInMsg;
    }

Overriding the HTTP method took a bit more trial and error.  In order to achieve this you must set the WCF.HttpMethodAndUrl to the desired HTTP method.

    public Microsoft.BizTalk.Message.Interop.IBaseMessage Execute(IPipelineContext pContext, Microsoft.BizTalk.Message.Interop.IBaseMessage pInMsg)
    {
        // Some non important logic here

        pInMsg.Context.Write("HttpMethodAndUrl", "http://schemas.microsoft.com/BizTalk/2006/01/Adapters/WCF-properties", "POST");
        pInMsg.Context.Write("IsDynamicSend", "http://schemas.microsoft.com/BizTalk/2003/system-properties", true);
        return pInMsg;
    }

So what exactly does this context property represent and why is it named Method and URL?  This context property represents the value that would normally be contained in the HTTP method and URL mapping section of a WCF-WebHttp send port’s configuration.
MethodAndUrlMapping

If you inspect the help text underneath the textbox in the above screenshot you’ll notice that there are ways to set the HTTP method and URL mapping in this configuration.

  • By hardcoding the HTTP method in the text box such that every message being processed by the send port will have the same HTTP method.
  • By dynamically setting it (and the URL parameters as well) based on the XML BtsHttpUrlMapping configuration based on the value of the BTS.Operation context property on the message being processed.

Whatever value you have in the HTTP method and URL configuration of your send port will be put into the WCF.HttpMethodAndUrl context property on your message before it gets handled by the WCF adapter stack, which will apply the correct HTTP method and outbound URL.

So back to overriding the method, by setting a value to the WCF.HttpMethodAndUrl context property and ensuring that the HTTP method and URL configuration on the send port is blanked out, we are able to dynamically set the HTTP method on the outbound message.

I would be negligent if I didn’t mention that setting these two context properties (and BTS.IsDynamicSend) is a piece of cake when using the BRE Pipeline Framework which has out of the box capabilities to set these context properties. Being rules based, you can inspect the messages that are being processed and dynamically decide what the outbound URL and HTTP method should be based on message content and/or context.
BRE

Theoretically we could even dynamically set the WCF.HttpMethodAndUrl context property value to contain a BtsHttpUrlMapping XML instance so that we could dynamically set the HTTP method and URL based on the BTS.Operation context property value.  Given that it is a lot easier to just set the BTS.OutboundTransportLocation and WCF.HttpMethodAndUrl context properties to set the HTTP method and URL dynamically, this method is just too complicated to have its usage justified in most scenarios.

I have just got through a rather difficult project that involved BizTalk calling on a mixture of SOAP/REST services, and also exposing multiple RESTFul services as well.  The project had a very short time frame and we had many technical hurdles to cross.  One of the toughest challenges was communicating appropriate details when we were having problems calling one of our target services from BizTalk, or getting appropriate details from the consumers of services exposed by BizTalk when they were having trouble calling said services.

With relatively simple services this is typically no big deal… You just ask the service consumer to share the message body with you so you can see whether it is properly formed or not.  If the service in question is being hosted by BizTalk and the message has got to the receive pipeline (i.e. it hasn’t failed on the adapter layer) then you could even take advantage of tracked message events and view the message body and associated context (which would also include custom SOAP/HTTP header values found in the tracked context properties).  You could always look at using tools like Fiddler to inspect HTTP requests as well.

On our project we decided to use RequestBin because the REST services we were exposing from BizTalk required a fair few custom HTTP headers, and were in fact exposed using Azure Service Bus Relays thus required an Authorization header containing a SAS signature as well (note that Service Bus would strip off this header before it gets to BizTalk so you will never see it in tracked message events).  Moreover, we were calling on the TripIt API which is a RESTFul web service that makes use of OAUTH for authentication/authorization.  Once again the outbound HTTP headers had to be just right or we would encounter errors.  In cases of HTTP Posts, the XML request also had to be URL encoded and made into a request body parameter, further complicating matters.  In some tricky scenarios when we encountered problems we needed to contact TripIt for help and provide them with the appropriate details.

RequestBin helped solve a lot of problems for us. At its core RequestBin is a service that tracks your entire HTTP request, including the HTTP headers and body. RequestBin will always reply to your requests with an HTTP 200 and a message body saying “ok”.

You start by browsing to the RequestBin website and choosing to create a RequestBin.
Create

You will then be presented with a bin URL.
Bin URL
This is the URL the service consumer should be directing their requests to instead of the actual target service when they want their requests to be tracked. If you append a ?inspect to the end of the URL and browse to this amalgamated URL in your web browser then you can view a history of all requests made to the RequestBin.
BinResult
Having all parties that were consuming services get accustomed to providing RequestBin captures to the service providers made communication between different parties much more streamlined. This was especially important given that some of the parties were in different countries and time zones, and every time a query resulted in more questions rather than an answer meant another day wasted for the project. I don’t think I will ever work on a project involving external web services providers or consumers without using RequestBin again.

Of note is that your RequestBin will only be valid for 48 hours and will only keep track of your last 20 requests. If you want a more durable service then you might want to look into the RunScope Traffic Inspector, however note that this is a paid product (albeit with a free trial). This product goes well beyond the scope of RequestBin and appears to be comparable (at least in some sense) to Azure API Management or Sentinet. I would think that if your requirements are similar to mine then RequestBin is the ideal solution given how lightweight and easy it is to use, and chances are the limits are not going to bother you.

A final suggestion I’ll leave you with is to use RequestBin to inspect HTTP requests for SOAP services based on different bindings (obviously this is limited to the HTTP based bindings). Doing so will give you a much deeper appreciation of how the request formats differ based on bindings. A very simple example of this is how the SOAP Action is contained in HTTP headers for the basicHttpBinding but in SOAP headers for the wsHttpBinding. You can of course use WCF tracing for this as well, but RequestBin is so easy to use that it’s a pretty good alternative.

Ricardo Marques from Codit wrote a fantastic blog post a few years ago about dynamically setting HTTP headers for the WCF-WebHttp adapter using dynamic send ports in BizTalk Server based on the WCF.HttpHeaders context property. At the end of the article he stated that static send ports would not respect this context property, and this statement is the topic of this blog post.

Let me start by saying that I personally dislike dynamic send ports. It’s not that I don’t like the idea behind them, it’s more that I dislike the implementation (I won’t go into my reasons today). Wherever possible I will seek a way to enforce dynamic behavior on a static send port instead, and I have found a way to do this for dynamic HTTP headers.

The trick is super easy. All you need to do is set the BTS.IsDynamicSend context property value to true on the message before it gets processed by your WCF-WebHttp send adapter in order for it to respect the values in your WCF.HttpHeaders context property without losing access to all the other configuration you have set up on the static send port. I’ve extended the example provided in Ricardo’s original post below to illustrate how you set the context properties.

    public Microsoft.BizTalk.Message.Interop.IBaseMessage Execute(IPipelineContext pContext, Microsoft.BizTalk.Message.Interop.IBaseMessage pInMsg)
    {
        // Some non important logic here

        pInMsg.Context.Write("HttpHeaders", "http://schemas.microsoft.com/BizTalk/2006/01/Adapters/WCF-properties", "content-type: application/atom+xml");
        pInMsg.Context.Write("IsDynamicSend", "http://schemas.microsoft.com/BizTalk/2003/system-properties", true);
        return pInMsg;
    }

The inspiration to try this context property came from my colleague James’ blog post. His post was written regarding BizTalk 2009 so it’s good to see that Microsoft have been consistent about the usage of this context property even with the latest adapters. Thanks to both Ricardo and James for your good write-ups 🙂

This blog details problems that you will definitely encounter if you use the BizTalk WCF-WebHttp Receive Adapter in tandem with Azure Service Bus Relays (at least in BizTalk 2013 I haven’t confirmed if the problem has been fixed since but I doubt it). The problem is that if the service consumer tries to append URL parameters to the Service Bus Relay URL then they will be met with the dreaded AddressFilterMismatch exception. The traditional means of working around AddressFilterMismatch exceptions also leads to more problems which I will describe in this post. I have come up with a generic solution that ensures that you will not encounter such problems and that using the WCF-WebHttp BizTalk adapter in Receive Locations in tandem with Service Bus Relays is viable.

In this blog post I’ve detailed the problem, explained why it occurs, and explained how the solution works as well. If all you’re interested in is using the solution then you can download the installer here (or here if your machine is 32-bit), or the source code here.

The installer will GAC the relevant dll, and add a Service Behavior called BTWebHTTPSBRelayCompatibility to your machine.config files (if you prefer not to have your machine.config file updated then please don’t use the installer, download the source code and use the behavior as you wish). You will need to use this Service Behavior on your BizTalk WCF-WebHttp receive location to fix the problem detailed in this post. Please ensure you test the installer and the behavior before you attempt to use it in a real life environment, and let me know if you have any feedback.

 

Preamble about WCF-WebHttp Adapter and Service Bus Relays

Adding a relay endpoint to your WCF-WebHttp receive location is a piece of cake.  When running through the WCF Service Publishing Wizard, you just need to ensure you tick the checkbox in the below screenshot and you will be presented with additional screens at the final stages of the wizard that allow you to choose a service bus namespace and specify the ACS credentials that BizTalk uses to connect to the relay (I’ve found a way to get this to work with SAS, but more on this in another blog post).

Wizard

The wizard doesn’t actually create the relay for you.  This is done at the time the service is started up based on entries that get placed in the service’s web.config file as a result of the wizard (you could even manually add the web.config sections if you missed the relevant wizard options).  Note in the below screenshot that an additional endpoint that uses the webHttpRelayBinding binding has been added to the web.config file.  The beauty of this is that you can change the relay endpoint URL anytime by just updating the web.config.

Web.config

Now when the service spins up you’ll find that the relay has been enlisted automatically for you.  If you tear the service down, for example by taking down the application pool or stopping IIS, you’ll notice that the relay is no longer listed in the Azure Portal.

Portal

 

The Problem

What I found was that when I tried to send requests (regardless of the HTTP method) to the base URL of the relay (which in the above example is https://jctest.servicebus.windows.net/JCTestService/Service1.svc) there were no problems.  However the second I tried to add URL parameters to the end of the URL I would get an AddressFilterMismatch error.  I also encountered similar problems if I tried to change my relay address URL such that it differs from the URL format of my receive location (for example if I specify the relay URL in my web.config to be a value of https://jctest.servicebus.windows.net/API/V1).

AddressFilterMismatch

 

The obvious workaround and why it isn’t good enough

The aforementioned error message shouldn’t be new to anyone who has used the WCF-WSHttp receive location, as similar errors are encountered in scenarios where the URL that service consumers post to doesn’t match the server name (due to a DNS alias/load balancer etc…). The most common solution I’ve seen touted around to get around this problem is to use a WCF Service Behavior that changes the Dispatcher AddressFilter to a MatchAllMessageFilter which basically means that URL matching will not occur. This would be done in the ApplyDispatchBehavior method of the Service Behavior as below.

MatchAllBehavior

The nice thing about Service Behaviors is that they apply to all endpoints within our service, including our relay endpoint. Adding this behavior to the behaviors tab of our receive location will actually get around the two aforementioned problems which resulted in AddressFilterMismatch errors, however they introduce a new, very BizTalk specific problem.
You’ll now find that regardless of your URL/Method/Operation mapping on your BizTalk Receive location, the BTS.Operation context property will never be promoted on your inbound message. This is very limiting, because the BTS.Operation context property is a fantastic way to route your message to relevant orchestrations/send ports without having to have their filters based on the URL of the message. Any URL parameters that you tokenized in the mapping will also not be written to the context.
HTTPURLMethodMapping

 

Why the Problem Occurs
Getting to the bottom of the problem required a fair amount of usage of RedGate’s fantastic Reflector tool, I don’t think anyone could possibly figure out the problem without a reflection tool or having access to the source code.
I reflected the assemblies that are used within the BizTalk receive location and found that the way Microsoft implement the URL/Method to context property mapping is via a private Endpoint Behavior which is aptly named InternalEndpointBehavior. Microsoft actually adds this behavior to the default endpoint of the receive location programmatically within the adapter code, and the behavior does not get added to the relay endpoint which is why the relay endpoint behaves differently from the default endpoint.
The InternalEndpointBehavior sets the AddressFilter on the default endpoint to an instance of the WebHttpReceiveLocationEndpointAddressFilter class. This AddressFilter is what does all the magic of promoting the BTS.Operation and other context properties based on the inbound URL/Method. Unfortunately this class is marked as internal as well, so we can’t instantiate an instance of it from a custom behavior.
Since our first solution attempt was to use the MatchAllMessageFilter on our endpoint we would not see this context property promotion occur.
 
The Solution

The solution I’ve put in place is to create a custom service behavior that performs the below logic in it’s ApplyDispatchBehavior method.  You can download all the source code here.

First the behavior needs to figure out which ChannelDispatcher is related to the relay (since we’ll have the relay as well as the default ChannelDispatcher to contend with).

IdentifyRelayDispatcher
Next the behavior needs to loop through all endpoints, saving the base endpoint addresses for the relay endpoint as well as the default endpoint to local variables (the reason for this will be apparent soon). It also identifies the default endpoint and copies a reference to the contained InternalEndpointBehavior (which as I’ve mentioned applies the WebHttpReceiveLocationEndpointAddressFilter AddressFilter to the endpoint).

GetInternalBehaviorAndURLs

Next we’re going to apply the InternalEndpointBehavior to the relay endpoint which will apply the WebHttpReceiveLocationEndpointAddressFilter to the relay endpoint when it is executed.
ApplyInternalBehavior

This actually isn’t the end. This solution is good enough if the URL of the Service Bus relay mirrors the on-premise URL (except for the host name of course), however the second we want our relay URL to differ in structure (perhaps because we want to remove the .SVC extension, or provide a more friendly URL format) we will start to get an AddressFilterMismatch exception. The reason for this is that the URL format that the filter is searching for is based on the default endpoint’s format.  And because the filter is an internal class we have no ability to override the expected URL format.
So if your relay endpoint URL is https://jctest.servicebus.windows.net/test/service1.svc and the default endpoint URL is https://servername.onpremises.com/test/service1.svc then you won’t have a problem. But if the relay endpoint URL was https://jctest.servicebus.windows.net/API/V1 then you’d get the dreaded AddressFilterMismatch exception. For the default non-relay endpoint, the typical solution to this would be to use IIS’ URL Rewrite feature as described here, but I have found that this didn’t work for me with the Relay Endpoint.

So to get around this, we need to replace the Service Bus URL with the default endpoint URL in the To WCF Addressing header before the message gets to the AddressFilter.  I haven’t found an appropriate component that can change the To SOAP header value before the AdressFilter stage, so the solution I’ve implemented is to delay the execution of the AddressFilter such that it runs in a custom Message Inspector which will first override the To WCF Addressing header value such that it matches the AddressFilter’s accepted format, execute the AddressFilter, and then change the To WCF Addressing header back to it’s original value.

I’ll show you how I’ve achieved the aforementioned solution.  We now have the WebHttpReceiveLocationEndpointAddressFilter filter applied to our relay endpoint but this won’t work for us because we can’t run the Message Inspector prior to the filter.  So what we’ll do instead is copy a reference to the WebHttpReceiveLocationEndpointAddressFilter filter to a local variable, and change the filter on our relay endpoint to be a MatchAllMessageFilter, which will let pretty much any URL through (don’t worry, we’ll run our WebHttpReceiveLocationEndpointAddressFilter later).

ExtractFilter

The final step in our behavior is to now instantiate our custom Message Inspector, passing in the base address of the default endpoint, the base address of the relay endpoint and the reference to the WebHttpReceiveLocationEndpointAddressFilter filter object.  We then add this Message Inspector to our relay endpoint dispatcher.

AddMessageInspector

The custom Message Inspector is very simple. All it needs to do is override the To WCF Addressing Header from the Relay Endpoint URL format to the default URL format, execute the AddressFilter, and then set the To WCF Addressing Header back to the Relay Endpoint URL format.
MessageInspector

The beauty of this solution is that no change is introduced to the default endpoint at all, so you are only changing the behavior of the relay endpoint such that it matches the behavior of the default endpoint. Also, the code in the Service Behavior class will only execute when your service is first spun up. From then on the only code that will execute as each request is received will be the custom message inspector which is pretty lightweight and does very little additional work that isn’t encountered on the default endpoint anyways.  You can now set your Service Bus Relay endpoint URL to be whatever you want, and messages will get successfully relayed to your Receive Location with the BTS.Operation and the context properties based on tokenized URL parameters promoted/written to context as expected.

Whats possible

Of course, what would be even better would be if this behavior wasn’t necessary at all and that the BizTalk WCF-WebHttp receive location automatically set the appropriate filter on the relay endpoint for you…maybe Microsoft will adress this in BizTalk Server vNext? 🙂  Until then hopefully this will do the trick for you. If you have any better solutions then please do share them in the comments section.

%d bloggers like this: