예제 #1
0
        // Define other methods and classes here
        private void ResponseCallback(IAsyncResult result)
        {
            // Get and fill the RequestState
            RequestState state = (RequestState)result.AsyncState;

            try
            {
                bool           downloadFile = false;
                HttpWebRequest request      = state.Request;

                // End the Asynchronous response and get the actual response object
                state.Response = (HttpWebResponse)request.EndGetResponse(result);

                state.StatusCode = state.Response.StatusCode;

                switch (state.StatusCode)
                {
                case HttpStatusCode.OK:
                case HttpStatusCode.Created:
                case HttpStatusCode.Accepted:
                    state.Downloaded = DateTime.UtcNow;
                    break;

                case HttpStatusCode.NoContent:
                    Device.Log.Info("Empty payload returned in CacheFetcher: Result {0} for {1}", state.StatusCode, request.RequestUri);
                    state.Expiration       = DateTime.UtcNow;
                    state.AttemptToRefresh = DateTime.UtcNow;
                    state.Downloaded       = DateTime.UtcNow;
                    OnDownloadComplete(state);
                    return;

                default:
                    state.ErrorMessage = String.Format("Get failed. Received HTTP {0} for {1}", state.StatusCode, request.RequestUri);
                    Device.Log.Error(state.ErrorMessage);
                    state.Expiration       = DateTime.UtcNow;
                    state.AttemptToRefresh = DateTime.UtcNow;
                    state.Downloaded       = DateTime.UtcNow;
                    OnDownloadComplete(state);

                    return;
                }

                #region Determine whether new version of file needs to be downloaded.

                // create storage directory if it doesn't exist.
                Device.File.EnsureDirectoryExistsForFile(state.CacheFileName);

                string tempCacheFile = state.CacheFileName + "_" + DateTime.Now.Ticks.ToString() + ".tmp";

                bool itemInCache = Device.File.Exists(state.CacheFileName);

                Device.Log.Debug("CacheFetcher.ResponseCallback Uri: {0}  IsExpired: {1} ", state.AbsoluteUri, state.CacheIndexItem.IsExpired);
                Device.Log.Debug("CacheFetcher.ResponseCallback Uri: {0}  IsStale: {1} ", state.AbsoluteUri, state.CacheIndexItem.IsStale);
                Device.Log.Debug("CacheFetcher.ResponseCallback Uri: {0}  Downloaded: {1} ", state.AbsoluteUri, state.CacheIndexItem.Downloaded);
                Device.Log.Debug("CacheFetcher.ResponseCallback Uri: {0}  Header Last-Modified: {1} ", state.AbsoluteUri, state.Response.Headers["Last-Modified"].TryParseDateTimeUtc());
                Device.Log.Debug("CacheFetcher.ResponseCallback Uri: {0}  Header Etag: {1}  NRL: Etag: {2}", state.AbsoluteUri, state.Response.Headers["Etag"], state.CacheIndexItem.ETag);

                if (!itemInCache)
                {
                    downloadFile = true;
                }
                else if (state.CacheIndexItem.IsExpired || state.CacheIndexItem.IsStale)
                {
                    // At this point, since the cached item is "old", assume the item needs to be downloaded...
                    downloadFile = true;

                    // ... but check headers for actual content expiration.  If the file in cache hasn't changed on
                    // the web server, we won't waste any time downloading the same thing we've already got.
                    if (state.Response.Headers["Last-Modified"].TryParseDateTimeUtc() < state.CacheIndexItem.Downloaded && state.CacheIndexItem.Downloaded > DateTime.MinValue.ToUniversalTime())
                    {
                        Device.Log.Debug("CacheFetcher.ResponseCallback.Download Check: Unchanged since Last-Downloaded value");

                        downloadFile = false;
                    }
                    else if (state.Response.Headers["ETag"] != null && state.CacheIndexItem.ETag != null && state.CacheIndexItem.ETag == state.Response.Headers["ETag"])
                    {
                        Device.Log.Debug("CacheFetcher.ResponseCallback.Download Check: Etag matched");

                        downloadFile = false;
                    }
                }

                #endregion

                #region Download file if necessary

                DateTime dtMetric;

                if (downloadFile)
                {
                    //To-Do: refactor streamreaders/writers conversion to strings to a more efficient streaming mechanism
                    Stream httpStream = null;
                    try
                    {
                        httpStream = state.Response.GetResponseStream();

                        dtMetric = DateTime.UtcNow;


                        using (var ms = new MemoryStream())
                        {
                            httpStream.CopyTo(ms);
                            state.ResponseBytes  = ms.ToArray();
                            state.ResponseString = Encoding.UTF8.GetString(state.ResponseBytes, 0, state.ResponseBytes.Length);
                        }

                        // pre download work.
                        // if the item is in cache and expired then delete it
                        if (itemInCache)
                        {
                            Device.File.Delete(state.CacheFileName);
                            itemInCache = false;
                        }
                        Device.File.Save(tempCacheFile, state.ResponseBytes);

                        Device.Log.Metric(string.Format("CacheFetcher save stream to temp file: Name: {0} Time: {1:F0} milliseconds ", tempCacheFile, DateTime.UtcNow.Subtract(dtMetric).TotalMilliseconds));

                        // Set the CacheIndexItem properties upon successful download.
                        state.CacheIndexItem.Downloaded = state.Downloaded;
                        state.CacheIndexItem.Expiration = SetExpirationTime(state);

                        state.CacheIndexItem.AttemptToRefresh = state.Response.Headers["iFactr-Attempt-Refresh"].TryParseDateTimeUtc();
                        state.CacheIndexItem.ETag             = state.Response.Headers["ETag"];
                        state.CacheIndexItem.ContentType      = state.Response.Headers["Content-Type"];

                        state.AttemptToRefresh = state.CacheIndexItem.AttemptToRefresh;
                        state.Expiration       = state.CacheIndexItem.Expiration;
                    }
                    finally
                    {
                        if (httpStream != null)
                        {
                            httpStream.Close();
                            httpStream.Dispose();
                        }

                        if (state.Response != null)
                        {
                            state.Response.Dispose();
                        }
                        state.Response.Close();
                    }

                    // move downloaded tmp file to cache file location.
                    if (Device.File.Length(tempCacheFile) == 0 && !(state.Response != null && state.Response.StatusCode == HttpStatusCode.OK))
                    {
                        Device.File.Delete(tempCacheFile);
                        string error = "File Download returned an empty file.  Validate the URI is correct. uri=" + state.Request.RequestUri;
                        //throw new NetworkResourceLibraryException( error );
                        Device.Log.Error(error);
                    }
                    else
                    {
                        dtMetric = DateTime.UtcNow;

                        if (Device.File.Exists(state.CacheFileName))
                        {
                            Device.File.Delete(state.CacheFileName);
                        }

                        Device.File.Move(tempCacheFile, state.CacheFileName);
                        Device.Log.Metric(string.Format("CacheFetcher move temp file to cache file: Name: {0}  Length: {1}  Time: {2:0} milliseconds ", state.CacheFileName, Device.File.Length(state.CacheFileName), DateTime.UtcNow.Subtract(dtMetric).TotalMilliseconds));
                    }
                }
                else  // downloadFile == false
                {
                    Device.Log.Debug("CacheFetcher.ResponseCallback.Download Check: No need to download, file is current");

                    // update cache index item with current values
                    state.CacheIndexItem.Downloaded = state.Response.Headers["Date"].TryParseDateTimeUtc();
                    state.CacheIndexItem.Expiration = SetExpirationTime(state);

                    state.CacheIndexItem.AttemptToRefresh = state.Response.Headers["iFactr-Attempt-Refresh"].TryParseDateTimeUtc();
                    state.CacheIndexItem.ETag             = state.Response.Headers["ETag"];
                    state.CacheIndexItem.ContentType      = state.Response.Headers["Content-Type"];
                }

                #endregion

                OnDownloadComplete(state);
            }
            catch (WebException ex)
            {
                string StatusDescription = string.Empty;
                ex.Data.Add("Uri", state.Request.RequestUri);
                ex.Data.Add("Verb", state.Request.Method);
                if (ex.Response != null)
                {
                    state.StatusCode  = ((HttpWebResponse)ex.Response).StatusCode;
                    StatusDescription = ((HttpWebResponse)ex.Response).StatusDescription;
                }
                else if (ex.Message.ToLower().Contains("request was aborted"))
                {
                    state.StatusCode  = HttpStatusCode.RequestTimeout;
                    StatusDescription = "Request cancelled by client because the server did not respond within timeout";
                }
                else
                {
                    state.StatusCode = (HttpStatusCode)(-2);
                }
                state.WebExceptionStatusCode = ex.Status;
                ex.Data.Add("StatusCode", state.StatusCode);
                ex.Data.Add("WebException.Status", ex.Status);
                ex.Data.Add("StatusDescription", StatusDescription);
                state.ErrorMessage     = string.Format("Call to {0} had a WebException. {1}   Status: {2}   Desc: {3}", state.Request.RequestUri, ex.Message, ex.Status, StatusDescription);
                state.Exception        = ex;
                state.Expiration       = DateTime.UtcNow;
                state.AttemptToRefresh = DateTime.UtcNow;
                state.Downloaded       = DateTime.UtcNow;

                OnError(state);
            }
            catch (Exception ex)
            {
                ex.Data.Add("Uri", state.Request.RequestUri);
                ex.Data.Add("Verb", state.Request.Method);
                state.ErrorMessage     = string.Format("Call to {0} had an Exception. {1}", state.Request.RequestUri, ex.Message);
                state.Exception        = ex;
                state.StatusCode       = (HttpStatusCode)(-1);
                state.Expiration       = DateTime.UtcNow;
                state.AttemptToRefresh = DateTime.UtcNow;
                state.Downloaded       = DateTime.UtcNow;

                OnError(state);
            }
            finally
            {
                if (state.Response != null)
                {
                    state.Response.Dispose();
                    state.Response.Close();
                }
                state.Request = null;

                _allDone.Set();
            }
        }
예제 #2
0
        /// <summary>
        /// Performs an asynchronous fetch from the cache index.
        /// </summary>
        /// <param name="parameters">The parameters.</param>
        /// <param name="timeoutMilliseconds">The timeout milliseconds.</param>
        public void FetchAsynch(Object parameters, int timeoutMilliseconds)
        {
            var fetchParameters = (FetchParameters)parameters;

            var request = (HttpWebRequest)WebRequest.Create(fetchParameters.CacheIndex.GetAbsouteUri(fetchParameters.CacheIndexItem));

            request.Method = "GET";
            //request.Proxy = null;

            request.AutomaticDecompression = DecompressionMethods.GZip;

            if (fetchParameters.Headers != null && fetchParameters.Headers.Any())
            {
                foreach (string key in fetchParameters.Headers.Keys)
                {
                    if (key.ToLower() == "accept")
                    {
                        request.Accept = fetchParameters.Headers[key];
                    }
                    else if (key.ToLower() == "content-type")
                    {
                        request.ContentType = fetchParameters.Headers[key];
                    }
                    else if (key.ToLower() == "host")
                    {
                        //TODO: add the URL explaining PCL incompatibility
                        Exception ex = new ArgumentException("Host header value cannot be set in PCL libraries.");
                        Device.Log.Error(ex);
                        throw ex;
                    }
                    else
                    {
                        request.Headers[key] = fetchParameters.Headers[key];
                    }
                }
            }

            RequestState state = new RequestState()
            {
                Request        = request,
                CacheFileName  = fetchParameters.CacheIndex.GetCachePath(fetchParameters.CacheIndexItem),
                CacheIndex     = fetchParameters.CacheIndex,
                CacheIndexItem = fetchParameters.CacheIndexItem,
                RelativeUri    = fetchParameters.CacheIndexItem.RelativeUri,
                BaseUri        = fetchParameters.CacheIndex.BaseUri,
                Expiration     = DateTime.UtcNow.Add(fetchParameters.DefaultExpiration),
            };

            try
            {
                // Start the asynchronous request.
                IAsyncResult result = request.BeginGetResponse(ResponseCallback, state);
                if (!_allDone.WaitOne(timeoutMilliseconds))
                {
                    try { request.Abort(); } catch (Exception) { } // .Abort() always throws exception
                    return;
                }
            }
            catch (Exception exc)
            {
                Device.Log.Error("CacheFetcher.FetchAsynch encountered exception", exc);
                _autoEvent.Set();
            }
        }