/// <summary> /// Asynchronous download method implementation. /// </summary> /// <param name="task">The DownloadTask to process.</param> public void BeginDownload(DownloadTask task) { currentTask = task; // If we resume way too often, just return if (CheckForResumeAndProceed(currentTask)) { return; } Uri reqUri = new Uri(task.DownloadItem.Enclosure.Url); int priority = 10; RequestParameter reqParam = RequestParameter.Create(reqUri, FeedSource.UserAgentString(String.Empty), task.DownloadItem.Proxy, task.DownloadItem.Credentials, DateTime.MinValue, null); // global cookie handling: reqParam.SetCookies = FeedSource.SetCookies; state = BackgroundDownloadManager.AsyncWebRequest.QueueRequest(reqParam, OnRequestStart, OnRequestComplete, OnRequestException, OnRequestProgress, priority); }
/// <summary> /// WebResponse processing. /// </summary> private void ProcessResponse(RequestState state) { try { HttpWebResponse httpResponse = state.Response as HttpWebResponse; FileWebResponse fileResponse = state.Response as FileWebResponse; NntpWebResponse nntpResponse = state.Response as NntpWebResponse; if (httpResponse != null) { if (httpResponse.ResponseUri != state.RequestUri) { Log.Debug( String.Format("httpResponse.ResponseUri != state.RequestUri: \r\n'{0}'\r\n'{1}'", httpResponse.ResponseUri, state.RequestUri)); } if (HttpStatusCode.OK == httpResponse.StatusCode || HttpExtendedStatusCode.IMUsed == (HttpExtendedStatusCode)httpResponse.StatusCode) { HttpCookieManager.GetCookies(httpResponse); // provide last request Uri and ETag: state.RequestParams.ETag = httpResponse.Headers.Get("ETag"); try { state.RequestParams.LastModified = httpResponse.LastModified; } catch (Exception lmEx) { Log.Debug("httpResponse.LastModified() parse failure: " + lmEx.Message); // Build in header parser failed on provided date format // Try our own parser (last chance) try { state.RequestParams.LastModified = DateTimeExt.ParseRfc2822DateTime(httpResponse.Headers.Get("Last-Modified")); } catch (FormatException) { /* ignore */ } } state.ResponseStream = httpResponse.GetResponseStream(); state.ResponseStream.BeginRead(state.ReadBuffer, 0, RequestState.BUFFER_SIZE, ReadCallback, state); // async read started, so we are done here: Log.Debug("ProcessResponse() web response OK: " + state.RequestUri); return; } if (httpResponse.StatusCode == HttpStatusCode.NotModified) { HttpCookieManager.GetCookies(httpResponse); string eTag = httpResponse.Headers.Get("ETag"); // also if it was not modified, we receive a httpResponse.LastModified with current date! // so we did not store it (is is just the same as last-retrived) // provide last request Uri and ETag: state.OnRequestCompleted(state.InitialRequestUri, state.RequestParams.RequestUri, eTag, MinValue, RequestResult.NotModified); // cleanup: FinalizeWebRequest(state); } else if ((httpResponse.StatusCode == HttpStatusCode.MovedPermanently) || (httpResponse.StatusCode == HttpStatusCode.Moved)) { state.RetryCount++; if (state.RetryCount > RequestState.MAX_RETRIES) { // there is no WebExceptionStatus.UnknownError in .NET 1.0 !!! throw new WebException("Repeated HTTP httpResponse: " + httpResponse.StatusCode, null, WebExceptionStatus.RequestCanceled, httpResponse); } string url2 = httpResponse.Headers["Location"]; //Check for any cookies HttpCookieManager.GetCookies(httpResponse); state.MovedPermanently = true; //Remove Url from queue _queuedRequests.Remove(state.InitialRequestUri.CanonicalizedUri()); Log.Debug("ProcessResponse() Moved: '" + state.InitialRequestUri + " to " + url2); // Enqueue the request with the new Url. // We raise the queue priority a bit to get the retry request closer to the just // finished one. So the user get better feedback, because the whole processing // of one request (including the redirection/moved/... ) is visualized as one update // action. Uri req; //Try absolute first if (!Uri.TryCreate(url2, UriKind.Absolute, out req)) { // Try relative if (!Uri.TryCreate(httpResponse.ResponseUri, url2, out req)) { throw new WebException( string.Format( "Original resource temporary redirected. Request new resource at '{0}{1}' failed: ", httpResponse.ResponseUri, url2)); } } RequestParameter rqp = RequestParameter.Create(req, state.RequestParams); QueueRequestAgain(rqp, state.Priority + 1, state); } else if (IsRedirect(httpResponse.StatusCode)) { state.RetryCount++; if (state.RetryCount > RequestState.MAX_RETRIES) { // there is no WebExceptionStatus.UnknownError in .NET 1.0 !!! throw new WebException("Repeated HTTP httpResponse: " + httpResponse.StatusCode, null, WebExceptionStatus.RequestCanceled, httpResponse); } string url2 = httpResponse.Headers["Location"]; //Check for any cookies HttpCookieManager.GetCookies(httpResponse); //Remove Url from queue _queuedRequests.Remove(state.InitialRequestUri.CanonicalizedUri()); Log.Debug("ProcessResponse() Redirect: '" + state.InitialRequestUri + " to " + url2); // Enqueue the request with the new Url. // We raise the queue priority a bit to get the retry request closer to the just // finished one. So the user get better feedback, because the whole processing // of one request (including the redirection/moved/... ) is visualized as one update // action. Uri req; //Try absolute first if (!Uri.TryCreate(url2, UriKind.Absolute, out req)) { // Try relative if (!Uri.TryCreate(httpResponse.ResponseUri, url2, out req)) { throw new WebException( string.Format( "Original resource temporary redirected. Request new resource at '{0}{1}' failed: ", httpResponse.ResponseUri, url2)); } } RequestParameter rqp = RequestParameter.Create(req, RebuildCredentials(state.RequestParams.Credentials, url2), state.RequestParams); QueueRequestAgain(rqp, state.Priority + 1, state); } else if (IsUnauthorized(httpResponse.StatusCode)) { if (state.RequestParams.Credentials == null) { // no initial credentials, try with default credentials state.RetryCount++; //Remove Url from queue _queuedRequests.Remove(state.InitialRequestUri.CanonicalizedUri()); // Enqueue the request with the new Url. // We raise the queue priority a bit to get the retry request closer to the just // finished one. So the user get better feedback, because the whole processing // of one request (including the redirection/moved/... ) is visualized as one update // action. RequestParameter rqp = RequestParameter.Create(CredentialCache.DefaultCredentials, state.RequestParams); QueueRequestAgain(rqp, state.Priority + 1, state); } else { // failed with provided credentials if (state.RequestParams.SetCookies) { // one more request without cookies state.RetryCount++; //Remove Url from queue _queuedRequests.Remove(state.InitialRequestUri.CanonicalizedUri()); // Enqueue the request with the new Url. // We raise the queue priority a bit to get the retry request closer to the just // finished one. So the user get better feedback, because the whole processing // of one request (including the redirection/moved/... ) is visualized as one update // action. RequestParameter rqp = RequestParameter.Create(false, state.RequestParams); QueueRequestAgain(rqp, state.Priority + 1, state); } else { throw new ResourceAuthorizationException(); } } } else if (IsAccessForbidden(httpResponse.StatusCode) && state.InitialRequestUri.Scheme == "https") { throw new ClientCertificateRequiredException(); } else if (httpResponse.StatusCode == HttpStatusCode.Gone) { throw new ResourceGoneException(); } else { string statusDescription = httpResponse.StatusDescription; if (String.IsNullOrEmpty(statusDescription)) { statusDescription = httpResponse.StatusCode.ToString(); } string htmlStatusMessage = null; try { htmlStatusMessage = new StreamReader(httpResponse.GetResponseStream()).ReadToEnd(); } catch { } if (String.IsNullOrEmpty(htmlStatusMessage)) { throw new WebException("Unexpected HTTP Response: " + statusDescription); } if (htmlStatusMessage.Contains("<")) { throw new WebException(htmlStatusMessage); } throw new WebException( "<html><head><title>Unexpected HTTP Response</title></head><body><h2>Unexpected HTTP Response: " + statusDescription + "</h2><p>" + htmlStatusMessage + "</p></html>"); } } else if (fileResponse != null) { string reqFile = fileResponse.ResponseUri.LocalPath; if (File.Exists(reqFile)) { DateTime lwt = File.GetLastWriteTime(reqFile); state.RequestParams.ETag = lwt.ToString(); state.RequestParams.LastModified = lwt; } state.ResponseStream = fileResponse.GetResponseStream(); state.ResponseStream.BeginRead(state.ReadBuffer, 0, RequestState.BUFFER_SIZE, ReadCallback, state); // async read started, so we are done here: Log.Debug("ProcessResponse() file response OK: " + state.RequestUri); return; } else if (nntpResponse != null) { state.RequestParams.LastModified = DateTime.Now; state.ResponseStream = nntpResponse.GetResponseStream(); state.ResponseStream.BeginRead(state.ReadBuffer, 0, RequestState.BUFFER_SIZE, ReadCallback, state); // async read started, so we are done here: Log.Debug("ProcessResponse() nntp response OK: " + state.RequestUri); return; } else { Debug.Assert(false, "ProcessResponse(): unhandled WebResponse type: " + state.Response.GetType()); FinalizeWebRequest(state); } } catch (ThreadAbortException) { FinalizeWebRequest(state); // ignore, just return } catch (Exception ex) { Log.Debug("ProcessResponse() exception: " + state.RequestUri + " :" + ex.Message); state.OnRequestException(state.InitialRequestUri, ex); FinalizeWebRequest(state); } }