Asynchronous HttpWebRequest

Since there was just a posting on HttpWebRequest and since it just so happens that I've been having fun with that very class today, I though I'd add my two cents regarding this wicked cool type.

I have a collection of URLs, a couple of hundred for example, and need to query each URL to determine if the site still exists and, if so, when it was last modified. Doing this synchronously could take a couple of minutes depending on bandwidth and traffic.

However, by scanning the URLs asynchronously, you can build a much more responsive and user-friendly application.

There are three important points that need to be integrated to completely solve this:

  1. Scan the list in a new thread so your UI stays responsive.
  2. Use HttpWebRequest.BeginGetResponse() to initiate an asynchronous request.
  3. Use ThreadPool.RegisterWaitForSingleObject() to register a timeout delegate for unresponsive Web requests.

Yo! Hands off the Thread, Man!

Starting up a new thread is very simple using the .NET Framework System.Threading namespace. We create a new thread, mark it to run in the background and kick it off.

Thread t = new Thread(new ThreadStart(ScanSites));
t.IsBackground = true;
t.Start();

As you might guess, the ScanSites method (a custom method shown below) will run under a thread separate from the Windows.Forms UI. The user will be able to interact with the application without noticing the background process chugging along (hopefully).

Reach Out and Touch Someone

A Web request begins its life fairly mundane. You first need to create a new request. The concrete HttpWebRequest.Create() method returns an abstract WebRequest object. You can modify its properties before calling BeginGetResponse() .

The BeginGetResponse() method is typical of many other asychronous kick-off routines: it requires a pointer to a callback routine and a user-defined argument.

  private void ScanSites ()
  {
    // for each URL in the collection...
    WebRequest request = HttpWebRequest.Create(uri);
    request.Method = "HEAD";
 
    // RequestState is a custom class to pass info
    RequestState state = new RequestState(request,data);
 
    IAsyncResult result = request.BeginGetResponse(
      new AsyncCallback(UpdateItem),state);
 
    // PLACEHOLDER: See below...
  }
 
  private void UpdateItem (IAsyncResult result)
  {
    // grab the custom state object
    RequestState state = (RequestState)result.AsyncState;
    WebRequest request = (WebRequest)state.request;
    // get the Response
    HttpWebResponse response =
      (HttpWebResponse )request.EndGetResponse(result);
    // process the response...
  }

So far, so good. But what's up with the PLACEHOLDER comment? Read on my friend. Read on...

Careful with that Axe Eugene!

Although the WebRequest class has a Timeout property, it is ignored when using asynchronous requests. So we need to set up our own timer to keep an eye on lengthy HTTP calls. If a call takes too long, we should jump in and abort it, probably marking the URL as suspicious (for example, the site is down or no longer exists).

Here's an example. Replace the PLACEHOLDER comment above with the following call. Then add the ScanTimeoutCallback routine somewhere in your class.

  ThreadPool.RegisterWaitForSingleObject(
    result.AsyncWaitHandle,
    new WaitOrTimerCallback(ScanTimeoutCallback),
    state,
    (30* 1000),  // 30 second timeout
    true
    );
 
  private static void ScanTimeoutCallback (
    object state, bool timedOut)
  {
    if (timedOut)
    {
      RequestState reqState = (RequestState)state;
      if (reqState != null)
        reqState.request.Abort();
    }
  }

Not too hard, ay? We've covered all three points with pretty straighforward code. Multi-threaded programming in Windows.Form is trivial, but required for a good user experience. Sending asynchronous HTTP requests is equally trivial; just remember to construct a custom state object containing all the relevant information you may need within the AsyncCallback. And, finally, remember to abort requests that refuse to complete. A little code goes a long way.

You might also like...

Comments

Steven Cohn

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Before software should be reusable, it should be usable.” - Ralph Johnson