Ready-made usual flavour HttpWebRequest for Windows Phone 7

Posted by & filed under Sources.

Si vous effectuez des portages d’applications .NET vers Silverlight ou Windows Phone 7, cette classe pourrait bien vous être utile.
En effet, dans Silverlight, les WebRequests sont asynchrones (c’est très bien !), seulement ce changement d’API peut causer de nombreuses modifications de code. C’est dans cette optique que j’ai crée cette classe C# : HttpWebRequestSync qui propose une interface au plus proche de ce qui existait avant.

Attention, pour le coup vous perdrez l’avantage des requêtes asynchrones et votre thread appelant sera bloqué pendant le déroulement de la requête, comme au bon vieux temps !
Tout retour de bug ou message de remerciement est le bienvenu, ce code est disponible sous licence LGPL.

If you’re looking forward to port .NET applications to Silverlight/Windows Phone 7, the following might interest you.
In Silverlight, the WebRequests are asynchronous (and that’s a good thing), but this change in the API can cause numerous changes in your code. That’s what lead me to write this C# class : HttpWebRequestSync featuring an interface as close as possible as what was there before.

Note that you’ll lose the advantage of asynchronous requests and that you’re thread will be locked waiting for the request to complete, ol’school style.
If you find any bug or just want to leave a ‘thank you’ not you’re welcome. This code is available under the LGPL license.

/**********************************************************
 * HttpWebRequestSync.cs
 * Provides an implementation close to the .NET CF (3.5)
 * synchronous HttpWebRequest with Windows Phone 7's
 * asynchronous implementation.
 * TL;DR : Abstract the dev from all the async callback.
 *
 * Written by : Pierre BELIN <pierre.belin@inbox.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the Lesser GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the Lesser GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 **********************************************************/
using System;
using System.Net;
using System.Threading;
using System.IO;

namespace Ree7.Utils.Net
{
 public class HttpWebRequestSync
 {
 private ManualResetEvent getRequestDone = new ManualResetEvent(false);
 private ManualResetEvent getResponseDone = new ManualResetEvent(false);
 private HttpWebRequest request;

 private HttpWebResponse response;
 private Stream requestStream;

 /// <summary>
 /// Private ctor, to get an HttpWebRequestSync you shall use Create()
 /// </summary>
 private HttpWebRequestSync()
 {

 }

 /// <summary>
 /// Initializes a new HttpWebRequestSync for the specified URI scheme
 /// </summary>
 public static HttpWebRequestSync Create(string url)
 {
 return Create(new Uri(url));
 }

 /// <summary>
 /// Initializes a new HttpWebRequestSync for the specified URI scheme
 /// </summary>
 public static HttpWebRequestSync Create(Uri uri)
 {
 HttpWebRequestSync wrs = new HttpWebRequestSync();
 wrs.request = (HttpWebRequest)WebRequest.Create(uri);

 return wrs;
 }

 #region HttpWebRequest Interface
 /// <summary>
 /// Summary:
 ///     Gets or sets the value of the Accept HTTP header.
 ///
 /// Returns:
 ///     The value of the Accept HTTP header. The default value is null.
 /// </summary>
 public string Accept
 {
 get
 {
 return request.Accept;
 }
 set
 {
 request.Accept = value;
 }
 }
 /// <summary>
 /// Not Implemented Yet
 /// </summary>
 public int ContentLength
 {
 get
 {
 return 0;
 }
 set
 {
 return;
 }
 }
 /// <summary>
 /// Summary:
 ///     Gets or sets the value of the Content-type HTTP header.
 ///
 /// Returns:
 ///     The value of the Content-type HTTP header. The default value is null.
 /// </summary>
 public string ContentType
 {
 get
 {
 return request.ContentType;
 }
 set
 {
 request.ContentType = value;
 }
 }
 /// <summary>
 /// Summary:
 ///     Specifies the collection of System.Net.CookieCollection objects associated
 ///     with the HTTP request.
 ///
 /// Returns:
 ///     A System.Net.CookieContainer that contains a collection of System.Net.CookieCollection
 ///     objects associated with the HTTP request.
 ///
 /// Exceptions:
 ///   System.NotImplementedException:
 ///     This property is not implemented.
 /// </summary>
 public CookieContainer CookieContainer
 {
 get
 {
 return request.CookieContainer;
 }
 set
 {
 request.CookieContainer = value;
 }
 }
 /// <summary>
 /// Summary:
 ///     Gets a value that indicates whether a response has been received from an
 ///     Internet resource.
 ///
 /// Returns:
 ///     true if a response has been received; otherwise, false.
 ///
 /// Exceptions:
 ///   System.NotImplementedException:
 ///     This property is not implemented.
 /// </summary>
 public bool HaveResponse
 {
 get
 {
 return request.HaveResponse;
 }
 }
 /// <summary>
 /// Returns the underlying HttpWebRequest (handle it with care)
 /// </summary>
 public HttpWebRequest HttpWebRequest
 {
 get
 {
 return request;
 }
 }
 /// <summary>
 /// Summary:
 ///     Specifies a collection of the name/value pairs that make up the HTTP headers.
 ///
 /// Returns:
 ///     A System.Net.WebHeaderCollection that contains the name/value pairs that
 ///     make up the headers for the HTTP request.
 ///
 /// Exceptions:
 ///   System.InvalidOperationException:
 ///     The request has been started by calling the System.Net.HttpWebRequest.BeginGetRequestStream

(System.AsyncCallback,System.Object)
 ///     or System.Net.HttpWebRequest.BeginGetResponse(System.AsyncCallback,System.Object)
 ///     method.
 /// </summary>
 public WebHeaderCollection Headers
 {
 get
 {
 return request.Headers;
 }
 set
 {
 request.Headers = value;
 }
 }
 /// <summary>
 /// Summary:
 ///     Gets or sets the method for the request.
 ///
 /// Returns:
 ///     The request method to use to contact the Internet resource. The default value
 ///     is GET.
 ///
 /// Exceptions:
 ///   System.ArgumentException:
 ///     No method is supplied.-or- The method string contains invalid characters.
 ///
 ///   System.NotImplementedException:
 ///     This property is not implemented.
 ///
 ///   System.NotSupportedException:
 ///     The System.Net.HttpWebRequest.Method property is not GET or POST.
 /// </summary>
 public string Method
 {
 get
 {
 return request.Method;
 }
 set
 {
 request.Method = value;
 }
 }
 /// <summary>
 /// Summary:
 ///     Gets the original Uniform Resource Identifier (URI) of the request.
 ///
 /// Returns:
 ///     A System.Uri that contains the URI of the Internet resource passed to the
 ///     System.Net.WebRequest.Create(System.Uri) method.
 ///
 /// Exceptions:
 ///   System.NotImplementedException:
 ///     This property is not implemented.
 /// </summary>
 public Uri RequestUri
 {
 get
 {
 return request.RequestUri;
 }
 }
 #endregion

 public Stream GetRequestStream()
 {
 // start the asynchronous operation
 request.AllowReadStreamBuffering = false;
 request.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), request);

 // Keep the main thread from continuing while the asynchronous
 // operation completes. A real world application
 // could do something useful such as updating its user interface.
 getRequestDone.WaitOne();

 Stream result = requestStream;

 // Avoid keeping an unnecessary ref on the stream in this object
 requestStream = null;

 return result;
 }

 private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
 {
 request = (HttpWebRequest)asynchronousResult.AsyncState;
 requestStream = request.EndGetRequestStream(asynchronousResult);

 getRequestDone.Set();
 }

 public HttpWebResponse GetResponse()
 {
 // Start the asynchronous operation to get the response
 request.BeginGetResponse(new AsyncCallback(GetResponseCallback), request);

 getResponseDone.WaitOne();

 HttpWebResponse result = response;

 // Avoid keeping an unnecessary ref on the response in this object
 response = null;

 return result;
 }

 private void GetResponseCallback(IAsyncResult asynchronousResult)
 {
 request = (HttpWebRequest)asynchronousResult.AsyncState;

 // End the operation
 response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);

 getResponseDone.Set();
 }

 }
}

3 Responses to “Ready-made usual flavour HttpWebRequest for Windows Phone 7”

  1. greve

    I tried your code, and it works fine untill getResponseDone.WaitOne(); in public HttpWebResponse GetResponse(). From there it just hangs… any idea why?

  2. Pierre Belin

    Hi greve,

    I guess you’re running the code from the UI thread.
    The completed Callback of HttpWebRequest runs on the UI thread too, and as it was locked by WaitOne()… the completed callback cannot be executed.

    I did not precise it in the post but this class was intended to be used on another thread than the UI thread, by construction, as it implements a time-consuming synchronous operation, using it in UI thread, even if it worked in it would hang the UI of your application during the whole file transfer, which is I guess not what you want to do 😉