TheDeveloperBlog.com

Home | Contact Us

CSharp | Java | Python | Swift | GO | WPF | Ruby | Scala | F# | JavaScript

ASP.NET HttpWorkerRequest Example

This ASP.NET article covers the HttpWorkerRequest type in depth. It has C# example code.

HttpWorkerRequest helps with performance. It is not commonly used, but provides big advantages in some cases to more common classes.

You can use the powerful HttpWorkerRequest in a reliable way. It can benefit your ASP.NET program.

Example. The HttpWorkerRequest class is used internally by ASP.NET, and provides a lower-level way of accessing ASP.NET internals. In this code, we use the HttpContext to call GetService and get the current worker.

Then: You can use the same code that the intrinsic objects use, but with no overhead.

Casts: We use explicit casts. We cast from the IServiceProvider interface, from the object return returned by GetService.

Casts

Based on:

.NET 4

Example that uses HttpWorkerRequest: C#

using System;
using System.Web;

public class Handler1 : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
	IServiceProvider provider = (IServiceProvider)context;
	HttpWorkerRequest worker = (HttpWorkerRequest)provider.GetService(
	    typeof(HttpWorkerRequest));
	//
	// Get the Referer with HttpWorkerRequest.
	//
	string referer = worker.GetKnownRequestHeader(
	    HttpWorkerRequest.HeaderReferer);
	//
	// Get the Accept-Encoding with HttpWorkerReqest
	//
	string acceptEncoding = worker.GetKnownRequestHeader(
	    HttpWorkerRequest.HeaderAcceptEncoding);
	//
	// Display the values.
	//
	HttpResponse response = context.Response;
	response.Write("Referer: ");
	response.Write(referer != null);
	response.Write(" Accept-Encoding: ");
	response.Write(acceptEncoding);
    }

    public bool IsReusable
    {
	get
	{
	    return false;
	}
    }
}

This example is a generic handler you can run in the code-behind file of a HTTP handler. You can use the contents of the ProcessRequest method. The first lines with IServiceProvider simply use an interface to get the HttpWorkerRequest.

Next: It shows how to get the referer of the page and also the Accept-Encoding headers.

And: You can do this with the Request object, but it is slower and more prone to errors.

Set headers. Here we set HTTP headers with the HttpWorkerRequest. This allows you to bypass the ASP.NET AddHeader method, which has a fair amount of overhead. We specify that the handler should be cached for 2 hours here.

Note: The SendKnownResponseHeader method is not the same as AddHeader, but sometimes you can use it instead.

Example that uses SendKnownResponseHeader: C#

public void ProcessRequest(HttpContext context)
{
    IServiceProvider provider = (IServiceProvider)context;
    HttpWorkerRequest worker = (HttpWorkerRequest)provider.GetService(
	typeof(HttpWorkerRequest));
    //
    // Set Cache-Control with HttpWorkerRequest.
    //
    worker.SendKnownResponseHeader(HttpWorkerRequest.HeaderCacheControl,
	"private, max-age=7200");
}

Harder. The HttpWorkerRequest is not commonly used in simple ASP.NET projects, and it is much harder to use. For example, its settings can interact in different ways with your Web.config. Setting the Content-Length is tricky to get right.

And: Due to the complexity of the class, these are things you will have to hack.

Performance. Here we see a simple benchmark that compares setting two HTTP headers on a response. The first method uses the Response object, and the second method uses the HttpWorkerRequest object.

And: Internally, the first version will call into the same methods as the second version.

HTTP header method versions benchmarked: C#

public static void Set1(HttpContext context, string contentEncoding,
    string cacheControl)
{
    context.Response.AddHeader("Content-Encoding", contentEncoding);
    context.Response.AddHeader("Cache-Control", cacheControl);
}

public static void Set2(HttpContext context, string contentEncoding,
    string cacheControl)
{
    IServiceProvider provider = (IServiceProvider)context;
    HttpWorkerRequest worker = (HttpWorkerRequest)provider.GetService(
	typeof(HttpWorkerRequest));
    worker.SendKnownResponseHeader(HttpWorkerRequest.HeaderContentEncoding,
	contentEncoding);
    worker.SendKnownResponseHeader(HttpWorkerRequest.HeaderCacheControl,
	cacheControl);
}

Calling code, 1 million iterations: C#

Set1(context, "gzip", "private, max-age=7200");
Response.ClearHeaders();

Set2(context, "gzip", "private, max-age=7200");
Response.ClearHeaders();

Benchmark results

Set1 Response:          895 ms
     Note:              Uses AddHeader
Set2 HttpWorkerRequest: 488 ms
     Note:              Uses SendKnownResponseHeader

Intrinsic objects. You can accomplish almost everything that HttpWorkerRequest lets you do with the Context, Request and Response intrinsic objects. However, when you use Request and Response, they execute complicated and slow logic.

And: Eventually these objects then use HttpWorkerRequest themselves. We can optimize this process.

When you call the UrlReferer property on Request, the property does several string comparisons and then creates a new Uri object. This causes a heap allocation. If you check the UrlReferer on every request, this overhead can add up.

Uri Class

AppendHeader method. When you open the AppendHeader method in ASP.NET, you will find a lot of complex logic and error checking. Often you do not need all this overhead. Internally, the method also calls into the HttpWorkerRequest.

Get HttpWorkerRequest. Next, we look at a public static class that contains one method named Get. This method accepts a reference to the current HttpContext and then returns the reference to the HttpWorkerRequest accessed through that reference.

Unfortunately: The HttpWorkerRequest is a lower-level type and is used mainly for internal ASP.NET work, so this logic is required.

Class that accesses HttpWorkerRequest: C#

using System;
using System.Web;

/// <summary>
/// Contains logic for the worker request.
/// </summary>
public static class WorkerTool
{
    /// <summary>
    /// Cached type of worker request.
    /// </summary>
    static Type _workerType = typeof(HttpWorkerRequest);

    /// <summary>
    /// Get the worker request for this context in ASP.NET.
    /// This is encapsulated here because it is very implementation-specific.
    /// </summary>
    /// <param name="context">The context for the current request.</param>
    /// <returns>The worker request, useful for low-level ASP.NET processing.</returns>
    public static HttpWorkerRequest Get(HttpContext context)
    {
	IServiceProvider provider = context; // Cast to interface
	var worker = provider.GetService(_workerType) as HttpWorkerRequest; // Get worker
	return worker;
    }
}

Calling code snippet

var context = HttpContext.Current; // Get context
var worker = WorkerTool.Get(context); // Get worker
string url = worker.GetRawUrl(); // Use worker

The WorkerTool static class contains two members. One is a static Type pointer to the type of the HttpWorkerRequest. The second is the casting logic and GetService call required for getting the worker.

Type: The Type pointer is stored in static memory to avoid having to use reflection more than necessary during request processing.

Note: The as-cast is used for slightly better performance than a regular cast. I recommend it.

As: Cast Examples

Discussion. HttpWorkerRequest has features that are not available through the Response and Request objects, and also performance. There are precise ways to get the Accept-Encoding headers with the HttpWorkerRequest that are not available elsewhere.

Also: Using HttpWorkerRequest avoids some overhead with collections that are otherwise required for headers.

MSDN states that usually "your code will not deal with HttpWorkerRequest directly." However, it adds that this may be necessary to implement if you are implementing your own hosting environment.

Summary. We used the secret HttpWorkerRequest to develop C# web applications that are faster and have clearer code in some respects. This is considered a lower-level interface to ASP.NET, and it should be used with care and testing.