Home > Silverlight > A GetResponse Extension for Synchronized Web Requests in Silverlight

A GetResponse Extension for Synchronized Web Requests in Silverlight

February 8th, 2010

Unlike its CLR counter part, Silverlight’s HttpWebRequest does not provide a synchronous GetResponse method that allows to receive an HTTP response in a blocking operation. As a result, you are forced to use the asynchronous BeginGetResponse method, and handle the results on a callback method.

This makes sense if the response is requested on the UI thread (otherwise, it would freeze the UI), but it might be a problem in certain scenarios (e.g. if you want to submit multiple requests in an orderly fashion).

However, you can get around the issue by using wait handles, which allow you to easily block a running operation. I encapsulated the required functionality in a simple extension method that brings GetResponse method back into Silverlight.

The usage is simple – you just invoke the GetResponse() extension method with an optional timeout. Here’s a sample that submits five synchronous HTTP requests:

private void RunRequests()
{
  for (int i = 0; i < 5; i++)
  {
    Uri uri = new Uri("http://localhost/users?user=" + i);
    HttpWebRequest request = 
      (HttpWebRequest)WebRequestCreator.ClientHttp.Create(uri);

    //get response as blocking operation which times out after 10 secs
    HttpWebResponse response = request.GetResponse(10000);
  }
}

 

You will run into timeout issues if you run execute this method on the UI thread because internally, the requested response accesses the UI thread (for whatever reasons). Only invoke this extension method on background threads!

 

Here’s the implementation:

public static class RequestHelper
{
  /// <summary>
  /// A blocking operation that does not continue until a response has been
  /// received for a given <see cref="HttpWebRequest"/>, or the request
  /// timed out.
  /// </summary>
  /// <param name="request">The request to be sent.</param>
  /// <param name="timeout">An optional timeout.</param>
  /// <returns>The response that was received for the request.</returns>
  /// <exception cref="TimeoutException">If the <paramref name="timeout"/>
  /// parameter was set, and no response was received within the specified
  /// time.</exception>
  public static HttpWebResponse GetResponse(this HttpWebRequest request,
                                            int? timeout)
  {
    if (request == null) throw new ArgumentNullException("request");

    if (System.Windows.Deployment.Current.Dispatcher.CheckAccess())
    {
      const string msg = "Invoking this method on the UI thread is forbidden.";
      throw new InvalidOperationException(msg);
    }

    AutoResetEvent waitHandle = new AutoResetEvent(false);
    HttpWebResponse response = null;
    Exception exception = null;

    AsyncCallback callback = ar =>
       {
         try
         {
           //get the response
           response = (HttpWebResponse)request.EndGetResponse(ar);
         }
         catch(Exception e)
         {
           exception = e;
         }
         finally
         {
           //setting the handle unblocks the loop below
           waitHandle.Set(); 
         }
       };


    //request response async
    var asyncResult = request.BeginGetResponse(callback, null);
    if (asyncResult.CompletedSynchronously) return response;

    bool hasSignal = waitHandle.WaitOne(timeout ?? Timeout.Infinite);
    if (!hasSignal)
    {
      throw new TimeoutException("No response received in time.");
    }

    //bubble exception that occurred on worker thread
    if (exception != null) throw exception;

    return response;
  }
}

Author: Categories: Silverlight Tags:
  1. February 9th, 2010 at 01:39 | #1

    Thanks for the write up.

    Might be useful for some future project.

  2. February 10th, 2010 at 17:30 | #2

    I’d like to use this in RestSharp (http://github.com/johnsheehan/RestSharp). Can you tell me what the license is for this code?

  3. February 10th, 2010 at 17:36 | #3

    I’ve taken the snippet from VFS, which is MS-PL but feel free to use it under RestSharp’s Apache license. Btw – the SL REST client library contains the full class, which also has a GetRequestStream extension for HttpRequest.

  4. February 10th, 2010 at 17:42 | #4

    Great, thanks!

  5. February 11th, 2010 at 18:41 | #5

    Would it not be better to use a ManualResetEvent? If the request completes between the “if (asyncResult.CompletedSynchronously)” test and the “waitHandle.WaitOne” call, the AutoResetEvent will have been signalled and reset, and the WaitOne call will time out.

  6. February 11th, 2010 at 18:56 | #6

    @Richard
    The handle would have been signalled, but only reset with WaitOne passing. Which means that WaitOne would pass immediately, wouldn’t it?

    Here’s a little snippet to illustrate the behavior:

       
    public void TestHandle() { AutoResetEvent wh = new AutoResetEvent(false); ThreadPool.QueueUserWorkItem((s) => { Console.Out.WriteLine("setting wait handle..."); wh.Set(); }); Thread.Sleep(1000); Console.Out.WriteLine("entering wait"); wh.WaitOne(9000); //will exit immediately Console.Out.WriteLine("Finished"); }
  7. February 11th, 2010 at 19:40 | #7

    OK, I didn’t realize that an AutoResetEvent isn’t reset until something waits for it. I need to pay closer attention to the documentation!

    “When signaled, the EventWaitHandle resets automatically after releasing a single thread. If no threads are waiting, the EventWaitHandle remains signaled until a thread blocks, and resets after releasing the thread.”

    I still think a ManualResetEvent would be a marginally better choice, since the event never needs to be reset.

  8. February 11th, 2010 at 19:48 | #8

    Richard,

    This was an intentional choice. I was considering using ManualResetEvents, but decided to keep the (slightly redundant) AutoResetEvent anyway.

    Although a reset is not needed, it doesn’t hurt, and feels like a good choice from an architectural point of view: If there *was* subsequent requests, we we wouldn’t want them to pass, but lock the handle as soon as we processed a response (which is something I did before I wrote that extension). Accordingly, the automatic reset might save people quite some headache if they decide to refactor the method and reuse the WaitHandle.

  9. Fallon Massey
    February 19th, 2010 at 21:50 | #9

    This is GREAT stuff, and I have only one question.

    Is it possible to use this technique to POST data to the server?

    I can’t seem to make that work.

    Thanks.

  10. February 20th, 2010 at 01:20 | #10

    @Fallon Massey
    Fallon,
    You can easily POST from Silverlight with my Silverlight port of the REST start kit:

    http://www.hardcodet.net/2010/02/wcf-rest-starter-kit-for-silverlight

  11. Fallon Massey
    February 20th, 2010 at 02:24 | #11

    Duh… I found this article first(on the web), and didn’t see the starter kit.

    Thanks!

  12. Andreas
    March 21st, 2010 at 14:01 | #12

    Thank you, this is very useful.
    I would like to use it in my project, which will be published under the New BSD-License. Is it okay to use it for that?

  13. March 21st, 2010 at 14:47 | #13

    Andreas,
    Of course, this is just a snippet. Use it in whatever way you like 🙂

  14. Alec
    February 28th, 2011 at 15:37 | #14

    Very useful in my case — I am running a bunch of helper threads that each need to process some web requests in a very specific order. Thank you.

  1. No trackbacks yet.