public static Action DownloadAsync(string url, string auth, string referer, string connectionGroupName, DateTime?cacheLastModifiedTime, Action <HttpWebResponse> onResponse, Action <byte[], int> onDownloadChunk, Action onComplete, Action <Exception> onException) { const int readBufferSize = 8192; const int requestTimeoutMS = 60000; const int readTimeoutMS = 60000; object sync = new object(); bool aborting = false; HttpWebRequest request = null; HttpWebResponse response = null; Stream responseStream = null; Action cleanup = () => { if (request != null) { request.Abort(); request = null; } if (responseStream != null) { try { responseStream.Close(); } catch { } responseStream = null; } if (response != null) { try { response.Close(); } catch { } response = null; } }; Action <Exception> abortDownloadInternal = (ex) => { lock (sync) { if (aborting) { return; } aborting = true; cleanup(); onException(ex); } }; Action abortDownload = () => { ThreadPool.QueueUserWorkItem((s) => { abortDownloadInternal(new Exception("Download has been aborted.")); }); }; lock (sync) { try { request = BuildWebRequest(url: url, auth: auth, connectionGroupName: connectionGroupName, cacheLastModifiedTime: cacheLastModifiedTime, referer: referer); // Unfortunately BeginGetResponse blocks until the DNS lookup has finished IAsyncResult requestResult = request.BeginGetResponse((requestResultParam) => { lock (sync) { try { if (aborting) { return; } response = (HttpWebResponse)request.EndGetResponse(requestResultParam); if (GetMIMETypeFromContentType(response.ContentType) == "text/html") { var memoryStream = new MemoryStream(); CopyStream(new ThrottledStream(response.GetResponseStream(), Settings.MaximumBytesPerSecond ?? ThrottledStream.Infinite), memoryStream); memoryStream.Position = 0; byte[] redirectPageBytes = memoryStream.ToArray(); Encoding pageEncoding = DetectHTMLEncoding(redirectPageBytes, response.ContentType); string metaRedirectHtml = pageEncoding.GetString(redirectPageBytes); memoryStream.Position = 0; responseStream = memoryStream; string redirectUrl = GetRedirectUrl(metaRedirectHtml, response.ResponseUri.AbsoluteUri); if (!string.IsNullOrEmpty(redirectUrl)) { HttpWebRequest redirectionRequest = BuildWebRequest(url: redirectUrl, auth: auth, connectionGroupName: connectionGroupName, cacheLastModifiedTime: cacheLastModifiedTime); response = (HttpWebResponse)redirectionRequest.GetResponse(); responseStream = new ThrottledStream(response.GetResponseStream(), Settings.MaximumBytesPerSecond ?? ThrottledStream.Infinite); } } else { responseStream = new ThrottledStream(response.GetResponseStream(), Settings.MaximumBytesPerSecond ?? ThrottledStream.Infinite); } onResponse(response); byte[] buff = new byte[readBufferSize]; AsyncCallback readCallback = null; readCallback = (readResultParam) => { lock (sync) { try { if (aborting) { return; } if (readResultParam != null) { int bytesRead = responseStream.EndRead(readResultParam); if (bytesRead == 0) { request = null; onComplete(); aborting = true; cleanup(); return; } onDownloadChunk(buff, bytesRead); } IAsyncResult readResult = responseStream.BeginRead(buff, 0, buff.Length, readCallback, null); ThreadPool.RegisterWaitForSingleObject(readResult.AsyncWaitHandle, (state, timedOut) => { if (!timedOut) { return; } abortDownloadInternal(new Exception("Timed out while reading response.")); }, null, readTimeoutMS, true); } catch (Exception ex) { abortDownloadInternal(ex); } } }; readCallback(null); } catch (Exception ex) { if (ex is WebException) { WebException webEx = (WebException)ex; if (webEx.Status == WebExceptionStatus.ProtocolError) { HttpStatusCode code = ((HttpWebResponse)webEx.Response).StatusCode; if (code == HttpStatusCode.NotFound) { ex = new HTTP404Exception(); } else if (code == HttpStatusCode.NotModified) { ex = new HTTP304Exception(); } } } abortDownloadInternal(ex); } } }, null); ThreadPool.RegisterWaitForSingleObject(requestResult.AsyncWaitHandle, (state, timedOut) => { if (!timedOut) { return; } abortDownloadInternal(new Exception("Timed out while waiting for response.")); }, null, requestTimeoutMS, true); } catch (Exception ex) { abortDownloadInternal(ex); } } return(abortDownload); }
public static Action DownloadAsync(string url, string auth, string referer, string connectionGroupName, DateTime?cacheLastModifiedTime, Action <HttpWebResponse> onResponse, Action <byte[], int> onDownloadChunk, Action onComplete, Action <Exception> onException) { const int readBufferSize = 8192; const int requestTimeoutMS = 60000; const int readTimeoutMS = 60000; object sync = new object(); bool aborting = false; HttpWebRequest request = null; HttpWebResponse response = null; Stream responseStream = null; Action cleanup = () => { if (request != null) { request.Abort(); request = null; } if (responseStream != null) { try { responseStream.Close(); } catch { } responseStream = null; } if (response != null) { try { response.Close(); } catch { } response = null; } }; Action <Exception> abortDownloadInternal = (ex) => { lock (sync) { if (aborting) { return; } aborting = true; cleanup(); onException(ex); } }; Action abortDownload = () => { ThreadPool.QueueUserWorkItem((s) => { abortDownloadInternal(new Exception("Download has been aborted.")); }); }; lock (sync) { try { request = (HttpWebRequest)WebRequest.Create(url); if (connectionGroupName != null) { request.ConnectionGroupName = connectionGroupName; } request.UserAgent = (Settings.UseCustomUserAgent == true) ? Settings.CustomUserAgent : ("Chan Thread Watch " + Version); request.Referer = referer; if (cacheLastModifiedTime != null) { request.IfModifiedSince = cacheLastModifiedTime.Value; } if (!String.IsNullOrEmpty(auth)) { Encoding encoding = Encoding.GetEncoding("iso-8859-1"); request.Headers.Add("Authorization", "Basic " + Convert.ToBase64String(encoding.GetBytes(auth))); } // Unfortunately BeginGetResponse blocks until the DNS lookup has finished IAsyncResult requestResult = request.BeginGetResponse((requestResultParam) => { lock (sync) { try { if (aborting) { return; } response = (HttpWebResponse)request.EndGetResponse(requestResultParam); responseStream = new ThrottledStream(response.GetResponseStream(), Settings.MaximumBytesPerSecond ?? ThrottledStream.Infinite); onResponse(response); byte[] buff = new byte[readBufferSize]; AsyncCallback readCallback = null; readCallback = (readResultParam) => { lock (sync) { try { if (aborting) { return; } if (readResultParam != null) { int bytesRead = responseStream.EndRead(readResultParam); if (bytesRead == 0) { request = null; onComplete(); aborting = true; cleanup(); return; } onDownloadChunk(buff, bytesRead); } IAsyncResult readResult = responseStream.BeginRead(buff, 0, buff.Length, readCallback, null); ThreadPool.RegisterWaitForSingleObject(readResult.AsyncWaitHandle, (state, timedOut) => { if (!timedOut) { return; } abortDownloadInternal(new Exception("Timed out while reading response.")); }, null, readTimeoutMS, true); } catch (Exception ex) { abortDownloadInternal(ex); } } }; readCallback(null); } catch (Exception ex) { if (ex is WebException) { WebException webEx = (WebException)ex; if (webEx.Status == WebExceptionStatus.ProtocolError) { HttpStatusCode code = ((HttpWebResponse)webEx.Response).StatusCode; if (code == HttpStatusCode.NotFound) { ex = new HTTP404Exception(); } else if (code == HttpStatusCode.NotModified) { ex = new HTTP304Exception(); } } } abortDownloadInternal(ex); } } }, null); ThreadPool.RegisterWaitForSingleObject(requestResult.AsyncWaitHandle, (state, timedOut) => { if (!timedOut) { return; } abortDownloadInternal(new Exception("Timed out while waiting for response.")); }, null, requestTimeoutMS, true); } catch (Exception ex) { abortDownloadInternal(ex); } } return(abortDownload); }