A GetResponse Extension for Synchronized Web Requests in Silverlight
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; } }
Thanks for the write up.
Might be useful for some future project.
I’d like to use this in RestSharp (http://github.com/johnsheehan/RestSharp). Can you tell me what the license is for this code?
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.
Great, thanks!
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.
@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:
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.
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.
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.
@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
Duh… I found this article first(on the web), and didn’t see the starter kit.
Thanks!
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?
Andreas,
Of course, this is just a snippet. Use it in whatever way you like 🙂
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.