public static HttpCacheResult InitializeHttpCacheResult(
            string httpCacheDirectory,
            Uri sourceUri,
            string cacheKey,
            HttpSourceCacheContext context)
        {
            // When the MaxAge is TimeSpan.Zero, this means the caller is passing is using a temporary directory for
            // cache files, instead of the global HTTP cache used by default. Additionally, the cleaning up of this
            // directory is the responsibility of the caller.
            if (context.MaxAge > TimeSpan.Zero)
            {
                var baseFolderName = CachingUtility.RemoveInvalidFileNameChars(CachingUtility.ComputeHash(sourceUri.OriginalString));
                var baseFileName   = CachingUtility.RemoveInvalidFileNameChars(cacheKey) + ".dat";
                var cacheFolder    = Path.Combine(httpCacheDirectory, baseFolderName);
                var cacheFile      = Path.Combine(cacheFolder, baseFileName);
                var newCacheFile   = cacheFile + "-new";

                return(new HttpCacheResult(
                           context.MaxAge,
                           newCacheFile,
                           cacheFile));
            }
            else
            {
                var temporaryFile    = Path.Combine(context.RootTempFolder, Path.GetRandomFileName());
                var newTemporaryFile = Path.Combine(context.RootTempFolder, Path.GetRandomFileName());

                return(new HttpCacheResult(
                           context.MaxAge,
                           newTemporaryFile,
                           temporaryFile));
            }
        }
示例#2
0
 protected virtual Stream TryReadCacheFile(string uri, TimeSpan maxAge, string cacheFile)
 {
     // Do not need the uri here
     return(CachingUtility.ReadCacheFile(maxAge, cacheFile));
 }
        public static async Task CreateCacheFileAsync(
            HttpCacheResult result,
            HttpResponseMessage response,
            Action <Stream> ensureValidContents,
            CancellationToken cancellationToken)
        {
            // Get the cache file directories, so we can make sure they are created before writing
            // files to them.
            var newCacheFileDirectory = Path.GetDirectoryName(result.NewFile);
            var cacheFileDirectory    = Path.GetDirectoryName(result.CacheFile);

            // Make sure the new cache file directory is created before writing a file to it.
            DirectoryUtility.CreateSharedDirectory(newCacheFileDirectory);

            // The update of a cached file is divided into two steps:
            // 1) Delete the old file.
            // 2) Create a new file with the same name.
            using (var fileStream = new FileStream(
                       result.NewFile,
                       FileMode.Create,
                       FileAccess.ReadWrite,
                       FileShare.None,
                       BufferSize))
            {
                using (var networkStream = await response.Content.ReadAsStreamAsync())
                {
                    await networkStream.CopyToAsync(fileStream, BufferSize, cancellationToken);
                }

                // Validate the content before putting it into the cache.
                if (ensureValidContents != null)
                {
                    fileStream.Seek(0, SeekOrigin.Begin);
                    ensureValidContents.Invoke(fileStream);
                }
            }

            if (File.Exists(result.CacheFile))
            {
                // Process B can perform deletion on an opened file if the file is opened by process A
                // with FileShare.Delete flag. However, the file won't be actually deleted until A close it.
                // This special feature can cause race condition, so we never delete an opened file.
                if (!CachingUtility.IsFileAlreadyOpen(result.CacheFile))
                {
                    File.Delete(result.CacheFile);
                }
            }

            // Make sure the cache file directory is created before moving or writing a file to it.
            if (cacheFileDirectory != newCacheFileDirectory)
            {
                DirectoryUtility.CreateSharedDirectory(cacheFileDirectory);
            }

            // If the destination file doesn't exist, we can safely perform moving operation.
            // Otherwise, moving operation will fail.
            if (!File.Exists(result.CacheFile))
            {
                File.Move(
                    result.NewFile,
                    result.CacheFile);
            }

            // Even the file deletion operation above succeeds but the file is not actually deleted,
            // we can still safely read it because it means that some other process just updated it
            // and we don't need to update it with the same content again.
            result.Stream = new FileStream(
                result.CacheFile,
                FileMode.Open,
                FileAccess.Read,
                FileShare.Read | FileShare.Delete,
                BufferSize);
        }
示例#4
0
        public static async Task CreateCacheFileAsync(
            HttpCacheResult result,
            HttpResponseMessage response,
            Action <Stream> ensureValidContents,
            CancellationToken cancellationToken)
        {
            // Get the cache file directories, so we can make sure they are created before writing
            // files to them.
            var newCacheFileDirectory = Path.GetDirectoryName(result.NewFile);
            var cacheFileDirectory    = Path.GetDirectoryName(result.CacheFile);

            // Make sure the new cache file directory is created before writing a file to it.
            DirectoryUtility.CreateSharedDirectory(newCacheFileDirectory);

            // The update of a cached file is divided into two steps:
            // 1) Delete the old file.
            // 2) Create a new file with the same name.

            // Some FileStream operations on Windows are synchronous even though it may not seem so.
            // The HTTP stack rewrite in .NET Core 2.1 introduced circumstances whereby these
            // synchronous FileStream calls will keep an IO completion thread busy and block other
            // HTTP requests from completing.  The immediate solution is to perform write and read
            // operations on separate streams, but only on .NET Core where the problem exists.
            // See https://github.com/dotnet/corefx/issues/31914 for details.
            const int writeBufferSize =
#if IS_CORECLR
                1;  // This disables write buffering.
#else
                BufferSize;
#endif

            using (var fileStream = new FileStream(
                       result.NewFile,
                       FileMode.Create,
                       FileAccess.Write,
                       FileShare.None,
                       writeBufferSize,
                       useAsync: true))
            {
                using (var networkStream = await response.Content.ReadAsStreamAsync())
                {
                    await networkStream.CopyToAsync(fileStream, BufferSize, cancellationToken);
                }
            }

            using (var fileStream = new FileStream(
                       result.NewFile,
                       FileMode.Open,
                       FileAccess.Read,
                       FileShare.None,
                       BufferSize,
                       useAsync: true))
            {
                // Validate the content before putting it into the cache.
                ensureValidContents?.Invoke(fileStream);
            }

            if (File.Exists(result.CacheFile))
            {
                // Process B can perform deletion on an opened file if the file is opened by process A
                // with FileShare.Delete flag. However, the file won't be actually deleted until A close it.
                // This special feature can cause race condition, so we never delete an opened file.
                if (!CachingUtility.IsFileAlreadyOpen(result.CacheFile))
                {
                    File.Delete(result.CacheFile);
                }
            }

            // Make sure the cache file directory is created before moving or writing a file to it.
            if (cacheFileDirectory != newCacheFileDirectory)
            {
                DirectoryUtility.CreateSharedDirectory(cacheFileDirectory);
            }

            // If the destination file doesn't exist, we can safely perform moving operation.
            // Otherwise, moving operation will fail.
            if (!File.Exists(result.CacheFile))
            {
                File.Move(
                    result.NewFile,
                    result.CacheFile);
            }

            // Even the file deletion operation above succeeds but the file is not actually deleted,
            // we can still safely read it because it means that some other process just updated it
            // and we don't need to update it with the same content again.
            result.Stream = new FileStream(
                result.CacheFile,
                FileMode.Open,
                FileAccess.Read,
                FileShare.Read | FileShare.Delete,
                BufferSize,
                useAsync: true);
        }