/// <summary> /// Handles post configuration setup /// </summary> public override void AfterConfigure() { base.AfterConfigure(); if (!Directory.Exists(SourceFolder)) { Directory.CreateDirectory(SourceFolder); } m_filecache = new LRUCache <DateTime>( sizelimit: MaxMirrorCacheSize, countlimit: MaxMirrorCacheCount, expirationHandler: TryDeleteAsync, sizeHandler: TryFileSizeAsync ); foreach (var f in Directory.EnumerateFiles(SourceFolder, "*", SearchOption.AllDirectories)) { m_filecache.AddOrReplaceAsync(f, DateTime.Now + StartupMirrorCacheAgeSeconds).Wait(); } m_404cache = new LRUCache <DateTime>(countlimit: Max404CacheCount); }
/// <summary> /// Downloads the file from the remote URL, signalling the progress task along the way /// </summary> /// <returns>An awaitable task.</returns> /// <param name="context">The request context.</param> /// <param name="localpath">The path to store the file at.</param> private async Task DownloadRemoteFileAsync(IHttpContext context, string localpath, TaskCompletionSource <long> signal) { try { using (var resp = await GetRemoteStream(context)) { lock (m_statuslock) { // Signal all others that we now know the full source length m_activeTransferSizes[localpath] = resp.Size; // And ensure the target folder exists if (!Directory.Exists(Path.GetDirectoryName(localpath))) { Directory.CreateDirectory(Path.GetDirectoryName(localpath)); } } var pg = 0L; using (var remote = resp.Stream) using (var local = new FileStream(localpath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite)) { // Note: Since we write to the actual file, // we may interfere with active downloads of // the file... int r; var buffer = new byte[8 * 1024]; // Read some data while ((r = await remote.ReadAsync(buffer, 0, buffer.Length)) > 0) { // Write it to the local file await local.WriteAsync(buffer, 0, r); await local.FlushAsync(); pg += r; // Swap around and notify of the progress var oldsignal = signal; lock (m_statuslock) m_activeTransfers[localpath] = (signal = new TaskCompletionSource <long>()).Task; oldsignal.TrySetResult(pg); } } // Update the file timestamp if possible if (resp.LastModified.Ticks != 0) { try { File.SetLastWriteTime(localpath, resp.LastModified); } catch { } } // Register that the file is now available as a normal file await m_filecache.AddOrReplaceAsync(localpath, DateTime.Now); // Now the cache returns the results, so we can unregister our callback lock (m_statuslock) { m_activeTransferSizes.Remove(localpath); m_activeTransfers.Remove(localpath); } // Prevent hanging followers signal.TrySetResult(pg); } } catch (Exception ex) { if (ex is HttpException he && he.StatusCode == HttpStatusCode.NotFound) { await m_404cache.AddOrReplaceAsync(localpath, DateTime.Now); } lock (m_statuslock) { m_activeTransferSizes.Remove(localpath); m_activeTransfers.Remove(localpath); } signal.TrySetException(ex); } }