1) { int delay = retryDelaysSeconds[attempt - 2]; Log.LogWarning("Downloading package \"" + package + "\" failed (" + reason + "), attempt " + (attempt - 1) + " of " + attempts + "; retrying in " + delay + " s."); if (TaskCanceled.WaitOne(delay * 1000)) { Log.LogMessage(MessageImportance.High, "Downloading canceled for package \"" + package + "\"."); return false; } } reason = DownloadOnce(client, uri, output, urifilename); if (reason == null) { return true; } if (File.Exists(output)) { Log.LogMessage(MessageImportance.High, "Deleting incomplete file " + output + " for package \"" + package + "\"."); try { File.Delete(output); } catch (Exception e) { // A scanner may still hold the file; the next attempt // overwrites it anyway, so do not let this end the retries. Log.LogWarning("Could not delete " + output + ": " + e.Message); } } if (TaskCanceled.WaitOne(0)) { Log.LogMessage(MessageImportance.High, "Downloading canceled for package \"" + package + "\"."); return false; } } Log.LogError("Downloading package \"" + package + "\" failed after " + attempts + " attempts: " + reason + "."); return false; } // One attempt: null when the file arrived, otherwise why it did not. private string DownloadOnce(WebClient client, Uri uri, string output, string urifilename) { var syncObject = new State { filename = urifilename, progress = -1, lastActivityTicks = DateTime.UtcNow.Ticks }; lock (syncObject) { client.DownloadFileAsync(uri, output, syncObject); // Wait on the completed flag, not on Monitor.Wait's result: a pulse // that lands while a timed-out Wait is reacquiring the lock is lost. while (!syncObject.completed) { if (Monitor.Wait(syncObject, 1000) || syncObject.completed) { continue; } if (TaskCanceled.WaitOne(0)) { client.CancelAsync(); WaitCompleted(syncObject); return "canceled"; } // DownloadFileAsync ignores WebRequest timeouts, so a connection // that stops delivering would otherwise wait forever. long idle = DateTime.UtcNow.Ticks - Interlocked.Read(ref syncObject.lastActivityTicks); if (idle > TimeSpan.FromSeconds(stallTimeoutSeconds).Ticks) { client.CancelAsync(); WaitCompleted(syncObject); return "no data for " + stallTimeoutSeconds + " s"; } } } if (syncObject.error != null) { return Describe(syncObject.error); } if (syncObject.cancelled) { return "canceled"; } if (!File.Exists(output)) { return "no file written"; } if (new FileInfo(output).Length == 0) { return "0 bytes"; } return null; } // Caller holds the lock on state. private static void WaitCompleted(State state) { while (!state.completed) { Monitor.Wait(state); } } private static string Describe(Exception error) { var web = error as WebException; if (web != null) { var http = web.Response as HttpWebResponse; if (http != null) { return "HTTP " + (int)http.StatusCode + " " + http.StatusDescription; } return web.Status + ": " + web.Message; } return error.GetType().Name + ": " + error.Message; } private void Extract(string filename, string extracttofolder) { // Use the caller-supplied extraction tool when set (the 7za bootstrap // passes 7zr.exe to unpack the full 7za.exe out of the "extra" package); // otherwise fall back to that bootstrapped 7za.exe for every other package. string tool = (arctool != null && arctool.Trim() != "") ? arctool : Path.Combine(new string[] { basedir, "libs", "win32", "7za", "7za.exe" }); string args = " x \"" + filename + "\" -y -o\"" + extracttofolder + "\""; Log.LogMessage(MessageImportance.High, "arctool : " + tool); Log.LogMessage(MessageImportance.High, "args : " + args); Log.LogMessage(MessageImportance.High, "WorkingDirectory : " + Path.GetDirectoryName(tool)); var proc = new Process { StartInfo = new ProcessStartInfo { FileName = tool, Arguments = args, UseShellExecute = false, RedirectStandardOutput = true, CreateNoWindow = true, WorkingDirectory = Path.GetDirectoryName(tool) } }; proc.Start(); while (!proc.StandardOutput.EndOfStream) { string line = proc.StandardOutput.ReadLine(); Log.LogMessage(MessageImportance.High, Path.GetFileName(filename) + " : " + line); if (TaskCanceled.WaitOne(0)) { proc.Kill(); break; } } proc.WaitForExit(); } private void DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) { var state = (State)e.UserState; lock (state) { state.error = e.Error; state.cancelled = e.Cancelled; state.completed = true; //releases blocked thread Monitor.Pulse(state); } } private string humanSize(double len) { string[] sizes = { "B", "KB", "MB", "GB", "TB" }; int order = 0; while (len >= 1024 && order < sizes.Length - 1) { order++; len = len / 1024; } return String.Format("{0:0.##} {1}", len, sizes[order]); } private void DownloadProgressCallback(object sender, DownloadProgressChangedEventArgs e) { Interlocked.Exchange(ref ((State)e.UserState).lastActivityTicks, DateTime.UtcNow.Ticks); if (((State)e.UserState).progress < e.ProgressPercentage) { ((State)e.UserState).progress = e.ProgressPercentage; // Displays the transfer progress. Log.LogMessage(MessageImportance.High, ((State)e.UserState).filename + " : downloaded " + humanSize(e.BytesReceived) + " of " + humanSize(e.TotalBytesToReceive) + " " + e.ProgressPercentage + " % complete..."); } } } ]]> 26.02 https://github.com/ip7z/7zip/releases/download/$(SevenZipVersion) 7z$(SevenZipVersion.Replace('.',''))-extra true