Logo Search packages:      
Sourcecode: gdata-sharp version File versions  Download package

request.cs

/* Copyright (c) 2006 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
*/
#region Using directives

#define USE_TRACING

using System;
using System.IO;
using System.Net;
using System.Collections.Generic;
using System.Globalization;
using System.ComponentModel;

#endregion

/////////////////////////////////////////////////////////////////////
// <summary>contains GDataRequest our thin wrapper class for request/response
//  </summary>
////////////////////////////////////////////////////////////////////
namespace Google.GData.Client
{

    /// <summary>
    /// the class holds username and password to replace networkcredentials
    /// </summary>
00038     public class GDataCredentials 
    {
        private string passWord;
        private string userName;
        private string clientToken;
  
        /// <summary>
        /// default constructor
        /// </summary>
        /// <param name="username">the username to use</param>
        /// <param name="password">the password to use</param>
00049         public GDataCredentials(string username, string password)
        {
            this.userName = username;
            this.passWord = password;
        }

        /// <summary>
        /// default constructor
        /// </summary>
        /// <param name="clientToken">the client login token to use</param>
00059         public GDataCredentials(string clientToken)
        {
            this.clientToken = clientToken;
        }


        //////////////////////////////////////////////////////////////////////
        /// <summary>accessor method public string Username</summary> 
        /// <returns> </returns>
        //////////////////////////////////////////////////////////////////////
        public string Username
00070         {
            get {return this.userName;}
            set {this.userName = value;}
        }
        // end of accessor public string Username

        //////////////////////////////////////////////////////////////////////
        /// <summary>accessor method Password</summary> 
        /// <returns> </returns>
        //////////////////////////////////////////////////////////////////////
        public string Password
00081         {
            set {this.passWord = value;}
        }


        internal string getPassword()
        {
            return this.passWord;
        }
        // end of accessor Password

        /// <summary>
        /// returns the stored clienttoken
        /// </summary>
        /// <returns></returns>
        public string ClientToken
00097         {
            get
            {
                return this.clientToken;
            }
        }

        /// <summary>
        /// returns a windows conforming NetworkCredential 
        /// </summary>
        public ICredentials NetworkCredential 
00108         {
            get 
            {
                return new NetworkCredential(this.userName, this.passWord);
            }
        }
     }

    
    //////////////////////////////////////////////////////////////////////
    /// <summary>base GDataRequestFactory implementation</summary> 
    //////////////////////////////////////////////////////////////////////
00120     public class GDataRequestFactory : IGDataRequestFactory  
    {
        /// <summary>this factory's agent</summary> 
00123         public const string GDataAgent = "SA";
        
        /// <summary>
        /// the default content type for the atom envelope
        /// </summary>
00128         public const string DefaultContentType = "application/atom+xml; charset=UTF-8";
        /// <summary>holds the user-agent</summary> 
00130         private string userAgent;
        private List<string> customHeaders;     // holds any custom headers to set
        private IWebProxy webProxy;                  // holds an iwebproxy interface
        private bool keepAlive;                     // indicates wether or not to keep the connection alive
        private bool useGZip;
        private string contentType = DefaultContentType;
        private string slugHeader;
        // set to default by default
        private int timeOut = -1;
   


        /// <summary>Cookie setting header, returned from server</summary>
00143         public const string SetCookieHeader = "Set-Cookie"; 
        /// <summary>Cookie client header</summary>
00145         public const string CookieHeader = "Cookie"; 
        /// <summary>Slug client header</summary>
00147         public const string SlugHeader = "Slug";

        /// <summary>
        /// constant for the Etag header
        /// </summary>
00152         public const string EtagHeader = "Etag"; 

        /// <summary>
        /// constant for the If-Match header
        /// </summary>
00157         public const string IfMatch = "If-Match"; 

        /// <summary>
        /// constant for the if-None-match header
        /// </summary>
00162         public const string IfNoneMatch = "If-None-Match"; 
        



        //////////////////////////////////////////////////////////////////////
        /// <summary>default constructor</summary> 
        //////////////////////////////////////////////////////////////////////
00170         public GDataRequestFactory(string userAgent)
        {
            this.userAgent = Utilities.ConstructUserAgent(userAgent, this.GetType().Name);
            this.keepAlive = true;
        }
        /////////////////////////////////////////////////////////////////////////////

        //////////////////////////////////////////////////////////////////////
        /// <summary>default constructor</summary> 
        //////////////////////////////////////////////////////////////////////
00180         public virtual IGDataRequest CreateRequest(GDataRequestType type, Uri uriTarget)
        {
            return new GDataRequest(type, uriTarget, this);
        }
        /////////////////////////////////////////////////////////////////////////////


        //////////////////////////////////////////////////////////////////////
        /// <summary>set wether or not new request should use GZip</summary>
        //////////////////////////////////////////////////////////////////////
        public bool UseGZip
00191         {
            get { return (this.useGZip); }
            set { this.useGZip = value; }
        }
        //////////////////////////////////////////////////////////////////////

#if WindowsCE || PocketPC
#else
        private CookieContainer cookies;
        //////////////////////////////////////////////////////////////////////
        /// <summary>The cookie container that is used for requests. Note 
        /// that this is not available on the compact framework</summary> 
        /// <returns> </returns>
        //////////////////////////////////////////////////////////////////////
        public CookieContainer Cookies
00206         {
            get 
            {
                if (this.cookies == null)
                    this.cookies = new CookieContainer();

                return this.cookies; }
            set { this.cookies = value; }
        }
        //////////////////////////////////////////////////////////////////////
#endif

        //////////////////////////////////////////////////////////////////////
        /// <summary>set's and get's the content Type, used for binary transfers</summary> 
        /// <returns> </returns>
        //////////////////////////////////////////////////////////////////////
        public string ContentType
00223         {
            get {return this.contentType;}
            set {this.contentType = value;}
        }
        /////////////////////////////////////////////////////////////////////////////

        //////////////////////////////////////////////////////////////////////
        /// <summary>set's and get's the slug header, used for binary transfers
        /// note that the data will be converted to ASCII and URLencoded on setting it
        /// </summary> 
        /// <returns> </returns>
        //////////////////////////////////////////////////////////////////////
        public string Slug
00236         {
            get {return this.slugHeader;}
            set 
            {
                this.slugHeader = Utilities.EncodeSlugHeader(value);
            }
        }
        /////////////////////////////////////////////////////////////////////////////


        //////////////////////////////////////////////////////////////////////
        /// <summary>accessor method public string UserAgent</summary> 
        /// <returns> </returns>
        //////////////////////////////////////////////////////////////////////
        public virtual string UserAgent
00251         {
            get { return this.userAgent; }
            set {this.userAgent = value;}
        }
        /////////////////////////////////////////////////////////////////////////////

        //////////////////////////////////////////////////////////////////////
        /// <summary>accessor method to the webproxy object to use</summary> 
        /// <returns> </returns>
        //////////////////////////////////////////////////////////////////////
        public IWebProxy Proxy
00262         {
            get {return this.webProxy;}
            set {this.webProxy = value;}
        }
        /////////////////////////////////////////////////////////////////////////////
    
        /////////////////////////////////////////////////////////////////////
        /// <summary>indicates if the connection should be kept alive, default
        /// is true</summary> 
        /// <returns> </returns>
        //////////////////////////////////////////////////////////////////////
        public bool KeepAlive
00274         {
          get {return this.keepAlive;}
          set {this.keepAlive = value;}
        }
        /////////////////////////////////////////////////////////////////////////////

        //////////////////////////////////////////////////////////////////////
        /// <summary>get's and set's the Timeout property used for the created
        /// HTTPRequestObject in milliseconds. if you set it to -1 it will stick 
        /// with the default of the HTPPRequestObject. From MSDN:
        /// The number of milliseconds to wait before the request times out. 
        /// The default is 100,000 milliseconds (100 seconds).</summary> 
        /// <returns> </returns>
        //////////////////////////////////////////////////////////////////////
        public int Timeout
00289         {
            get {return this.timeOut;}
            set {this.timeOut = value;}
        }
        // end of accessor public int Timeout


        internal bool hasCustomHeaders
        {
            get {
                return this.customHeaders != null; 
            }
        }

        //////////////////////////////////////////////////////////////////////
        /// <summary>accessor method public StringArray CustomHeaders</summary> 
        /// <returns> </returns>
        //////////////////////////////////////////////////////////////////////
        public List<string> CustomHeaders
00308         {
            get {
                if (this.customHeaders == null) 
                {
                    this.customHeaders = new List<string>(); 
                }
                return this.customHeaders;
                }
        }
        // end of accessor public StringArray CustomHeaders
    }
    /////////////////////////////////////////////////////////////////////////////


    //////////////////////////////////////////////////////////////////////
    /// <summary>base GDataRequest implmentation</summary> 
    //////////////////////////////////////////////////////////////////////
00325     public class GDataRequest : IGDataRequest, IDisposable, ISupportsEtag
    {
        /// <summary>holds the webRequest object</summary> 
00328         private WebRequest webRequest; 
        /// <summary>holds the webresponse object</summary> 
00330         private WebResponse webResponse;
        /// <summary>holds the target Uri</summary> 
00332         private Uri targetUri;
        /// <summary>holds request type</summary> 
00334         private GDataRequestType type;
        /// <summary>holds the credential information</summary> 
00336         private GDataCredentials credentials; 
        /// <summary>holds the request if a stream is open</summary> 
00338         private Stream requestStream;
        private GDataRequestFactory factory; // holds the factory to use
        /// <summary>holds if we are disposed</summary> 
00341         protected bool disposed; 
        /// <summary>set wheter or not this request should use GZip</summary>
00343         private bool useGZip;
        /// <summary>holds the timestamp for conditional GET</summary>
00345         private DateTime ifModifiedSince = DateTime.MinValue;
        /// <summary>stream from the response</summary>
00347         private Stream  responseStream;
        /// <summary>holds the contenttype to use if overridden</summary>
00349         private string contentType;
        /// <summary>holds the slugheader to use if overridden</summary>
00351         private string slugHeader;
        // holds the returned contentlength
        private long contentLength;
        private string eTag;




        //////////////////////////////////////////////////////////////////////
        /// <summary>default constructor</summary> 
        //////////////////////////////////////////////////////////////////////
00362         internal GDataRequest(GDataRequestType type, Uri uriTarget, GDataRequestFactory factory)
        {
            this.type = type;
            this.targetUri = uriTarget;
            this.factory = factory; 
            this.useGZip = this.factory.UseGZip; // use gzip setting from factory
        }
        /////////////////////////////////////////////////////////////////////////////


        //////////////////////////////////////////////////////////////////////
        /// <summary>implements the disposable interface</summary> 
        //////////////////////////////////////////////////////////////////////
00375         public void Dispose()
        {
            if (this.responseStream != null)
                this.responseStream.Close();
            Dispose(true);
            GC.SuppressFinalize(this);
        }


        /// <summary>
        /// exposing the private targetUri so that subclasses can override
        /// the value for redirect handling
        /// </summary>
        protected Uri TargetUri 
00389         {
            get 
            {
                return this.targetUri;
            }
            set 
            {
                this.targetUri = value;
            }
        }
        //////////////////////////////////////////////////////////////////////

        //////////////////////////////////////////////////////////////////////
        /// <summary>set wether or not this request should use GZip</summary>
        //////////////////////////////////////////////////////////////////////
        public bool UseGZip
00405         {
            get { return (this.useGZip); }
            set { this.useGZip = value; }
        }
        //////////////////////////////////////////////////////////////////////

        //////////////////////////////////////////////////////////////////////
        /// <summary>set a timestamp for conditional GET</summary>
        //////////////////////////////////////////////////////////////////////
        public DateTime IfModifiedSince
00415         {
            get { return (this.ifModifiedSince); }
            set { this.ifModifiedSince = value; }
        }
        //////////////////////////////////////////////////////////////////////


        //////////////////////////////////////////////////////////////////////
        /// <summary>does the real disposition</summary> 
        /// <param name="disposing">indicates if dispose called it or finalize</param>
        //////////////////////////////////////////////////////////////////////
00426         protected virtual void Dispose(bool disposing)
        {
            if (this.disposed == true)
            {
                return;
            }
            if (disposing == true)
            {
                Tracing.TraceMsg("disposing of request"); 
                Reset(); 
                this.disposed = true;
            }
        }



        //////////////////////////////////////////////////////////////////////
        /// <summary>accessor method for the GDataCredentials used</summary> 
        /// <returns> </returns>
        //////////////////////////////////////////////////////////////////////
        public GDataCredentials Credentials
00447         {
            get {return this.credentials;}
            set {this.credentials = value;}
        }
        /////////////////////////////////////////////////////////////////////////////


       //////////////////////////////////////////////////////////////////////
        /// <summary>set's and get's the content Type, used for binary transfers</summary> 
        /// <returns> </returns>
        //////////////////////////////////////////////////////////////////////
        public string ContentType
00459         {
            get {return this.contentType == null ? this.factory.ContentType : this.contentType;}
            set {this.contentType = value;}
        }
        /////////////////////////////////////////////////////////////////////////////

        //////////////////////////////////////////////////////////////////////
        /// <summary>set's and get's the etag header value, used for concurrency</summary> 
        /// <returns> </returns>
        //////////////////////////////////////////////////////////////////////
        public string Etag
00470         {
            get { return this.eTag; }
            set { this.eTag = value; }
        }
        /////////////////////////////////////////////////////////////////////////////


        //////////////////////////////////////////////////////////////////////
        /// <summary>set's and get's the slugHeader, used for binary transfers
        /// will encode to ascii and urlencode the string on setting it. 
        /// </summary> 
        /// 
        /// <returns> </returns>
        //////////////////////////////////////////////////////////////////////
        public string Slug
00485         {
            get {return this.slugHeader == null ? this.factory.Slug : this.slugHeader;}
            set 
            {
                this.slugHeader = Utilities.EncodeSlugHeader(value);
            }
        }
        /////////////////////////////////////////////////////////////////////////////


        /// <summary>
        /// returnes the contentlength of the response, -1 if none was given
        /// </summary>
        /// <returns></returns>
        public long ContentLength
00500         {
            get
            {
                return this.contentLength;
            }
        }


        //////////////////////////////////////////////////////////////////////
        /// <summary>accessor method protected WebRequest Request</summary> 
        /// <returns> </returns>
        //////////////////////////////////////////////////////////////////////
        protected WebRequest Request
00513         {
            get {return this.webRequest;}
            set {this.webRequest = value;}
        }
        /////////////////////////////////////////////////////////////////////////////

        //////////////////////////////////////////////////////////////////////
        /// <summary>accessor method protected WebResponse Response</summary> 
        /// <returns> </returns>
        //////////////////////////////////////////////////////////////////////
        protected WebResponse Response
00524         {
            get {return this.webResponse;}
            set {this.webResponse = value;}
        }
        /////////////////////////////////////////////////////////////////////////////

        //////////////////////////////////////////////////////////////////////
        /// <summary>resets the object's state</summary> 
        //////////////////////////////////////////////////////////////////////
00533         protected virtual void Reset()
        {
            this.requestStream = null;
            this.webRequest = null;
            if (this.webResponse != null)
            {
                this.webResponse.Close();
            }
            this.webResponse = null; 
        }
        /////////////////////////////////////////////////////////////////////////////


        //////////////////////////////////////////////////////////////////////
        /// <summary>returns the writable request stream</summary> 
        /// <returns> the stream to write into</returns>
        //////////////////////////////////////////////////////////////////////
00550         public virtual Stream GetRequestStream()
        {
            EnsureWebRequest();
            this.requestStream = this.webRequest.GetRequestStream();
            return this.requestStream; 
        }
        /////////////////////////////////////////////////////////////////////////////

        //////////////////////////////////////////////////////////////////////
        /// <summary>ensures that the correct HTTP verb is set on the stream</summary> 
        //////////////////////////////////////////////////////////////////////
00561         protected virtual void EnsureWebRequest()
        {

            if (this.webRequest == null && this.targetUri != null)
            {
                this.webRequest = WebRequest.Create(this.targetUri);

                this.webResponse = null; 
                if (this.webRequest == null)
                {
                    throw new OutOfMemoryException("Could not create a new Webrequest"); 
                }
                HttpWebRequest web = this.webRequest as HttpWebRequest;

                if (web != null)
                {
                    switch (this.type) 
                    {
                        case GDataRequestType.Delete:
                            web.Method = HttpMethods.Delete;
                            break;
                        case GDataRequestType.Update:
                            web.Method = HttpMethods.Put;
                            break;
                        case GDataRequestType.Batch:
                        case GDataRequestType.Insert:
                            web.Method = HttpMethods.Post;
                            break;
                        case GDataRequestType.Query:
                            web.Method = HttpMethods.Get;
                            break;
    
                    }
                    if (this.useGZip == true)
                        web.Headers.Add("Accept-Encoding", "gzip");

                    if (this.Etag != null)
                    {
                            web.Headers.Add(GDataRequestFactory.EtagHeader, this.Etag);
                            switch (this.type) 
                            {
                                case GDataRequestType.Update:
                                case GDataRequestType.Delete:
                                    if (Utilities.IsWeakETag(this) == false)
                                    {
                                        web.Headers.Add(GDataRequestFactory.IfMatch, this.Etag);
                                    }
                                    break;
                                case GDataRequestType.Query:
                                    web.Headers.Add(GDataRequestFactory.IfNoneMatch, this.Etag);
                                    break;
                            }
                    }

                    if (this.IfModifiedSince != DateTime.MinValue)
                        web.IfModifiedSince = this.IfModifiedSince;

                    web.ContentType = this.ContentType;
                    web.UserAgent = this.factory.UserAgent;
                    web.KeepAlive = this.factory.KeepAlive;
#if WindowsCE || PocketPC
#else
                    web.CookieContainer = this.factory.Cookies;
#endif
                    // add all custom headers
                    if (this.factory.hasCustomHeaders == true)
                    {
                        foreach (string s in this.factory.CustomHeaders)
                        {
                            this.Request.Headers.Add(s); 
                        }
                    }
                    if (this.factory.Timeout != -1)
                    {
                        web.Timeout = this.factory.Timeout;
                    }

                    if (this.Slug != null)
                    {
                        this.Request.Headers.Add(GDataRequestFactory.SlugHeader + ": " + this.Slug);
                    }
                    if (this.factory.Proxy != null)
                    {
                        this.Request.Proxy = this.factory.Proxy; 
                    }
                }
                
                EnsureCredentials(); 
            }
        }
        /////////////////////////////////////////////////////////////////////////////



        //////////////////////////////////////////////////////////////////////
        /// <summary>sets up the correct credentials for this call, pending 
        /// security scheme</summary> 
        //////////////////////////////////////////////////////////////////////
00659         protected virtual void EnsureCredentials()
        {
            if (this.Credentials != null)
            {
                this.webRequest.Credentials = this.Credentials.NetworkCredential; 
            }
        }
        /////////////////////////////////////////////////////////////////////////////



        
        //////////////////////////////////////////////////////////////////////
        /// <summary>Log's the request object if overridden in subclass</summary>
        /// <param name="request">the request to log</param> 
        //////////////////////////////////////////////////////////////////////
00675         protected virtual void LogRequest(WebRequest request) 
        {
        }

    
        //////////////////////////////////////////////////////////////////////
        /// <summary>Log's the response object if overridden in subclass</summary>
        /// <param name="response">the response to log</param> 
        //////////////////////////////////////////////////////////////////////
00684         protected virtual void LogResponse(WebResponse response) 
        {
        }

        //////////////////////////////////////////////////////////////////////
        /// <summary>Executes the request and prepares the response stream. Also 
        /// does error checking</summary> 
        //////////////////////////////////////////////////////////////////////
00692         public virtual void Execute()
        {
            try 
            {
                EnsureWebRequest();
                // if we ever handed out a stream, we want to close it before doing the real excecution
                if (this.requestStream != null)
                {
                    this.requestStream.Close(); 
                }
                Tracing.TraceCall("calling the real execution over the webresponse");
                LogRequest(this.webRequest);
                this.webResponse = this.webRequest.GetResponse(); 
            }
            catch (WebException e)
            {
                Tracing.TraceCall("GDataRequest::Execute failed: " + this.targetUri.ToString()); 
                GDataRequestException gde = new GDataRequestException("Execution of request failed: " + this.targetUri.ToString(), e);
                throw gde;
            }
            if (this.webResponse != null)
                this.responseStream = this.webResponse.GetResponseStream();

            LogResponse(this.webResponse);
            if (this.webResponse is HttpWebResponse)
            {
                HttpWebResponse response = this.webResponse as HttpWebResponse;
                HttpWebRequest request = this.webRequest as HttpWebRequest;

                this.useGZip = (string.Compare(response.ContentEncoding, "gzip", true, CultureInfo.InvariantCulture) == 0);
                if (this.useGZip == true)
                    this.responseStream = new GZipStream(this.responseStream, CompressionMode.Decompress);

                Tracing.Assert(response != null, "The response should not be NULL"); 
                Tracing.Assert(request != null, "The request should not be NULL"); 

                int code = (int)response.StatusCode;

                Tracing.TraceMsg("Returned contenttype is: " + (response.ContentType == null ? "None" : response.ContentType) + " from URI : " + request.RequestUri.ToString()); ; 
                Tracing.TraceMsg("Returned statuscode is: " + response.StatusCode + code); 

                if (response.StatusCode == HttpStatusCode.Forbidden)
                {
                    // that could imply that we need to reauthenticate
                    Tracing.TraceMsg("need to reauthenticate");
                    throw new GDataForbiddenException("Execution of request returned HttpStatusCode.Forbidden: " + 
                                                    this.targetUri.ToString() + response.StatusCode.ToString(), this.webResponse); 
                }

                if (response.StatusCode == HttpStatusCode.Conflict)
                {
                    // a put went bad due to a version conflict
                    throw new GDataVersionConflictException("Execution of request returned HttpStatusCode.Conflict: " + 
                                                    this.targetUri.ToString() + response.StatusCode.ToString(), this.webResponse); 
                }

                if ((this.IfModifiedSince != DateTime.MinValue 
                     || this.Etag != null) && response.StatusCode == HttpStatusCode.NotModified)
                {
                    // Throw an exception for conditional GET
                    throw new GDataNotModifiedException("Content not modified: " + this.targetUri.ToString(), this.webResponse);
                }
            
                if (response.StatusCode == HttpStatusCode.Redirect ||
                    response.StatusCode == HttpStatusCode.Found ||
                    response.StatusCode == HttpStatusCode.RedirectKeepVerb)
                {
                    Tracing.TraceMsg("throwing for redirect");
                    throw new GDataRedirectException("Execution resulted in a redirect from " + this.targetUri.ToString(), this.webResponse);
                }

                if (code > 299)
                {
                    // treat everything else over 300 as errors
                    throw new GDataRequestException("Execution of request returned unexpected result: " + this.targetUri.ToString() + 
                                                    response.StatusCode.ToString(), this.webResponse); 
                }

                this.contentLength = response.ContentLength;

                // if we got an etag back, remember it


                this.eTag = response.Headers[GDataRequestFactory.EtagHeader];

                response = null;
                request = null; 
            }
        }
        /////////////////////////////////////////////////////////////////////////////


        //////////////////////////////////////////////////////////////////////
        /// <summary>gets the readable response stream</summary> 
        /// <returns> the response stream</returns>
        //////////////////////////////////////////////////////////////////////
00788         public virtual Stream GetResponseStream()
        {
            return (this.responseStream);
        }
        /////////////////////////////////////////////////////////////////////////////

    }
    /////////////////////////////////////////////////////////////////////////////
} 
/////////////////////////////////////////////////////////////////////////////

Generated by  Doxygen 1.6.0   Back to index