From 15687ab80a87378a1d465d1f8fdabadf4577ce1a Mon Sep 17 00:00:00 2001 From: Ryan Nowak Date: Thu, 27 Aug 2015 21:48:13 -0700 Subject: [PATCH] Fix #343 - Avoid going to disk when reading the form This change tries to avoid looking up the TEMP directory for most reads of the form data. We'll now only hit the disk when necessary. --- src/Microsoft.AspNet.Http/BufferingHelper.cs | 27 ++++++++++++------- .../FileBufferingReadStream.cs | 22 ++++++++++++++- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.AspNet.Http/BufferingHelper.cs b/src/Microsoft.AspNet.Http/BufferingHelper.cs index 3335203e29..47b08915c4 100644 --- a/src/Microsoft.AspNet.Http/BufferingHelper.cs +++ b/src/Microsoft.AspNet.Http/BufferingHelper.cs @@ -12,21 +12,30 @@ namespace Microsoft.AspNet.Http.Internal { internal const int DefaultBufferThreshold = 1024 * 30; + private readonly static Func _getTempDirectory = () => TempDirectory; + + private static string _tempDirectory; + public static string TempDirectory { get { - // Look for folders in the following order. - var temp = Environment.GetEnvironmentVariable("ASPNET_TEMP") ?? // ASPNET_TEMP - User set temporary location. - Path.GetTempPath(); // Fall back. - - if (!Directory.Exists(temp)) + if (_tempDirectory == null) { - // TODO: ??? - throw new DirectoryNotFoundException(temp); + // Look for folders in the following order. + var temp = Environment.GetEnvironmentVariable("ASPNET_TEMP") ?? // ASPNET_TEMP - User set temporary location. + Path.GetTempPath(); // Fall back. + + if (!Directory.Exists(temp)) + { + // TODO: ??? + throw new DirectoryNotFoundException(temp); + } + + _tempDirectory = temp; } - return temp; + return _tempDirectory; } } @@ -37,7 +46,7 @@ namespace Microsoft.AspNet.Http.Internal { // TODO: Register this buffer for disposal at the end of the request to ensure the temp file is deleted. // Otherwise it won't get deleted until GC closes the stream. - request.Body = new FileBufferingReadStream(body, bufferThreshold, TempDirectory); + request.Body = new FileBufferingReadStream(body, bufferThreshold, _getTempDirectory); } return request; } diff --git a/src/Microsoft.AspNet.WebUtilities/FileBufferingReadStream.cs b/src/Microsoft.AspNet.WebUtilities/FileBufferingReadStream.cs index b6df361dc4..9bed427578 100644 --- a/src/Microsoft.AspNet.WebUtilities/FileBufferingReadStream.cs +++ b/src/Microsoft.AspNet.WebUtilities/FileBufferingReadStream.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Diagnostics; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -18,7 +19,8 @@ namespace Microsoft.AspNet.WebUtilities { private readonly Stream _inner; private readonly int _memoryThreshold; - private readonly string _tempFileDirectory; + private string _tempFileDirectory; + private readonly Func _tempFileDirectoryAccessor; private Stream _buffer = new MemoryStream(); // TODO: We could have a more efficiently expanding buffer stream. private bool _inMemory = true; @@ -26,6 +28,17 @@ namespace Microsoft.AspNet.WebUtilities private bool _disposed; + // TODO: allow for an optional buffer size limit to prevent filling hard disks. 1gb? + public FileBufferingReadStream( + [NotNull] Stream inner, + int memoryThreshold, + [NotNull] Func tempFileDirectoryAccessor) + { + _inner = inner; + _memoryThreshold = memoryThreshold; + _tempFileDirectoryAccessor = tempFileDirectoryAccessor; + } + // TODO: allow for an optional buffer size limit to prevent filling hard disks. 1gb? public FileBufferingReadStream([NotNull] Stream inner, int memoryThreshold, [NotNull] string tempFileDirectory) { @@ -88,6 +101,13 @@ namespace Microsoft.AspNet.WebUtilities private Stream CreateTempFile() { + if (_tempFileDirectory == null) + { + Debug.Assert(_tempFileDirectoryAccessor != null); + _tempFileDirectory = _tempFileDirectoryAccessor(); + Debug.Assert(_tempFileDirectory != null); + } + var fileName = Path.Combine(_tempFileDirectory, "ASPNET_" + Guid.NewGuid().ToString() + ".tmp"); return new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.Delete, 1024 * 16, FileOptions.Asynchronous | FileOptions.DeleteOnClose | FileOptions.SequentialScan);