Skip navigation

There aren’t lots of batching scenario samples out there. The one stands out among them is Brad Wilson’s Batching Handler for ASP.NET Web API. The sample is concise and elegant by leveraging route-specific endpoint handler as well as await async programming model in .net 4.5.

However there is one drawback when you try to customize you own scenario base on this sample, that is if a MessageHandler is added to the configuration, then all the embed request will return a 500 Internal Server Error.

Analysis

Exception is thrown from HttpClientFactory.CreatePipeline, when it detects that the first MessageHandler (actually all of them) already has InnerHandler property set. What cause the failure is because the HttpConfiguration is reused in an in-memory HttpServer created by BatchHandler. When the in-memory HttpServer is first initialized it tries to re-create the pipeline.

Please note that this issue is tracked by Issue 260 as a known bug. Once it is fixed then you don’t need following mitigation any more.

Mitigation

Web-host

Here’s my amended sample: https://github.com/troydai/webapi.sample.batching/tree/master/webapi.sample.batching

The core solution is to rewrite the Initialize method by deriving a new class from HttpServer.

    public class BatchServer : HttpServer
    {
        private readonly HttpConfiguration _config;

        public BatchServer(HttpConfiguration configuration)
            : base(configuration)
        {
            _config = configuration;
        }

        protected override void Initialize()
        {
            var firstInPipeline = _config.MessageHandlers.FirstOrDefault();
            if (firstInPipeline != null && firstInPipeline.InnerHandler != null)
            {
                InnerHandler = firstInPipeline;
            }
            else
            {
                base.Initialize();
            }
        }
    }

When the BatchingServer is initialized, it detects if the pipeline already exists by looking for first handler and it’s inner handler. Otherwise it call to the base implementation. This log avoid the re-creation issue. The BatchingServer replace  the HttpServer in BatchHandler

Self-host

In the same project, I created another self-host sample. The initial intention is to use it as a test bed to rapidly verify ideas, however it turns out that under self-host the solution could be very different.

    public class BatchHandler : DelegatingHandler
    {
        private HttpMessageInvoker _server;

        public void SetHandler(HttpMessageHandler handler)
        {
            _server = new HttpMessageInvoker(handler);
        }

        // remaining are the sample implementation
    }

Rather than a configuration, the BatchHandler accepts a MessageHandler. The actual anticipation is an HttpServer.

var config = new HttpSelfHostConfiguration(baseAddress);
config.MessageHandlers.Add(new MarkingMessageHandler());

var batchHandler = new BatchHandler();

config.Routes.MapHttpRoute(
    "batch",
    "api/batch",
    null,
    null,
    batchHandle);

config.Routes.MapHttpRoute(
    "default",
    "api/{controller}/{id}",
    new { id = RouteParameter.Optional });

this.Server = new HttpSelfHostServer(config);

batchHandler.SetHandler(this.Server);

Since user can access to the HttpServer instance, which enables a much simpler solution that is resubmit the request to the HttpServer.

Alternative Mitigation

There is other mitigations which doesn’t involve changes on HttpConfiguration.

  • If submitting the embedded request to the original pipeline is not required, then HttpServer is not required. What you can do is push the embedded request to the separated handler pipeline, which is specifically for embedded requests, and ultimately to the dispatcher. In this solution one particularly important thing is you have to manually add route information to every embedded requests, because these request are created from multi-part content and haven’t gone through HttpServer. They’re so “innocent” that if you don’t so, they will “lost their way” and return 404.
  • Or you can clone the HttpConfiguration, and feed it to a new created HttpServer. To do so, you shall clone all the MessageHandler as well, make sure the InnerHandler are cleaned. This mitigation does not work well if the MessageHandler is not clonable, for example a Singleton-pattern MessageHandler.

Summary

  • The root cause of the issue is that HttpServer tries to re-create pipeline based on a reused HttpConfiguration.
  • This issue is known and tracked by Issue 260.
  • Sampling two mitigation solutions under web-host and self-host.

Links

About these ads

One Trackback/Pingback

  1. […] Sample | detailed description | VS 2012 […]

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

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: