Skip to content

Commit

Permalink
Fixing issue where static files module would not dispose of the buffe…
Browse files Browse the repository at this point in the history
…r and release the file. I think this is the root cause of old issue #47
  • Loading branch information
mariodivece committed Aug 20, 2017
1 parent 819e2f6 commit c67be8d
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 117 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,4 @@ TestResult.xml
/_site
/_api
/tools
/.vs/Unosquare.Labs.EmbedIO/v15/sqlite3/storage.ide
232 changes: 115 additions & 117 deletions src/Unosquare.Labs.EmbedIO/Modules/StaticFilesModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ private class RamCacheEntry
/// Initializes a new instance of the <see cref="StaticFilesModule"/> class.
/// </summary>
/// <param name="paths">The paths.</param>
public StaticFilesModule(Dictionary<string, string> paths)
public StaticFilesModule(Dictionary<string, string> paths)
: this(paths.First().Value, null, paths)
{
}
Expand All @@ -159,7 +159,7 @@ public StaticFilesModule(Dictionary<string, string> paths)
/// <param name="additionalPaths">The additional paths.</param>
/// <exception cref="System.ArgumentException">Path ' + fileSystemPath + ' does not exist.</exception>
public StaticFilesModule(
string fileSystemPath,
string fileSystemPath,
Dictionary<string, string> headers = null,
Dictionary<string, string> additionalPaths = null)
{
Expand Down Expand Up @@ -223,161 +223,159 @@ private async Task<bool> HandleGet(HttpListenerContext context, CancellationToke
// Check if the requested local path is part of the root File System Path
if (IsPartOfPath(requestFullLocalPath, baseLocalPath) == false)
{
context.Response.StatusCode = (int) System.Net.HttpStatusCode.Forbidden;
context.Response.StatusCode = (int)System.Net.HttpStatusCode.Forbidden;
return true;
}

var eTagValid = false;
Stream buffer = null;
var partialHeader = context.RequestHeader(Headers.Range);
var usingPartial = string.IsNullOrWhiteSpace(partialHeader) == false && partialHeader.StartsWith("bytes=");

if (ExistsLocalPath(requestLocalPath, ref requestFullLocalPath) == false)
try
{
return false;
}

var fileDate = File.GetLastWriteTime(requestFullLocalPath);
var eTagValid = false;
var partialHeader = context.RequestHeader(Headers.Range);
var usingPartial = string.IsNullOrWhiteSpace(partialHeader) == false && partialHeader.StartsWith("bytes=");

var requestHash = context.RequestHeader(Headers.IfNotMatch);
if (ExistsLocalPath(requestLocalPath, ref requestFullLocalPath) == false)
{
return false;
}

if (RamCache.ContainsKey(requestFullLocalPath) && RamCache[requestFullLocalPath].LastModified == fileDate)
{
$"RAM Cache: {requestFullLocalPath}".Debug();
var fileDate = File.GetLastWriteTime(requestFullLocalPath);

var currentHash = RamCache[requestFullLocalPath].Buffer.ComputeMD5().ToUpperHex() + '-' + fileDate.Ticks;
var requestHash = context.RequestHeader(Headers.IfNotMatch);

if (string.IsNullOrWhiteSpace(requestHash) || requestHash != currentHash)
if (RamCache.ContainsKey(requestFullLocalPath) && RamCache[requestFullLocalPath].LastModified == fileDate)
{
buffer = new MemoryStream(RamCache[requestFullLocalPath].Buffer);
context.Response.AddHeader(Headers.ETag, currentHash);
$"RAM Cache: {requestFullLocalPath}".Debug();

var currentHash = RamCache[requestFullLocalPath].Buffer.ComputeMD5().ToUpperHex() + '-' + fileDate.Ticks;

if (string.IsNullOrWhiteSpace(requestHash) || requestHash != currentHash)
{
buffer = new MemoryStream(RamCache[requestFullLocalPath].Buffer);
context.Response.AddHeader(Headers.ETag, currentHash);
}
else
{
eTagValid = true;
}
}
else
{
eTagValid = true;
}
}
else
{
$"File System: {requestFullLocalPath}".Debug();

if (sendBuffer)
{
buffer = new FileStream(requestFullLocalPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
$"File System: {requestFullLocalPath}".Debug();

if (usingPartial == false)
if (sendBuffer)
{
eTagValid = UpdateFileCache(context, buffer, fileDate, requestHash, requestFullLocalPath);
buffer = new FileStream(requestFullLocalPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);

if (usingPartial == false)
{
eTagValid = UpdateFileCache(context, buffer, fileDate, requestHash, requestFullLocalPath);
}
}
}
}

// check to see if the file was modified or e-tag is the same
var utcFileDateString = fileDate.ToUniversalTime()
.ToString(Strings.BrowserTimeFormat, Strings.StandardCultureInfo);
// check to see if the file was modified or e-tag is the same
var utcFileDateString = fileDate.ToUniversalTime()
.ToString(Strings.BrowserTimeFormat, Strings.StandardCultureInfo);

if (usingPartial == false &&
(eTagValid || context.RequestHeader(Headers.IfModifiedSince).Equals(utcFileDateString)))
{
SetStatusCode304(context);
return true;
}
if (usingPartial == false &&
(eTagValid || context.RequestHeader(Headers.IfModifiedSince).Equals(utcFileDateString)))
{
SetStatusCode304(context);
return true;
}

SetHeaders(context, requestFullLocalPath, utcFileDateString);
SetHeaders(context, requestFullLocalPath, utcFileDateString);

var fileSize = new FileInfo(requestFullLocalPath).Length;
var fileSize = new FileInfo(requestFullLocalPath).Length;

if (sendBuffer == false)
{
context.Response.ContentLength64 = buffer?.Length ?? fileSize;
return true;
}
if (sendBuffer == false)
{
context.Response.ContentLength64 = buffer?.Length ?? fileSize;
return true;
}

// If buffer is null something is really wrong
if (buffer == null)
{
return false;
}
// If buffer is null something is really wrong
if (buffer == null) { return false; }

var lowerByteIndex = 0;
var upperByteIndex = 0;
long byteLength;
var isPartial = usingPartial &&
CalculateRange(partialHeader, fileSize, out lowerByteIndex, out upperByteIndex);
var lowerByteIndex = 0;
var upperByteIndex = 0;
long byteLength;
var isPartial = usingPartial &&
CalculateRange(partialHeader, fileSize, out lowerByteIndex, out upperByteIndex);

if (isPartial)
{
if (upperByteIndex > fileSize)
if (isPartial)
{
context.Response.StatusCode = 416;
context.Response.AddHeader(Headers.ContentRanges,
$"bytes */{fileSize}");
if (upperByteIndex > fileSize)
{
context.Response.StatusCode = 416;
context.Response.AddHeader(Headers.ContentRanges,
$"bytes */{fileSize}");

return true;
}
return true;
}

if (upperByteIndex == fileSize)
{
byteLength = buffer.Length;
if (upperByteIndex == fileSize)
{
byteLength = buffer.Length;
}
else
{
byteLength = upperByteIndex - lowerByteIndex + 1;

context.Response.AddHeader(Headers.ContentRanges,
$"bytes {lowerByteIndex}-{upperByteIndex}/{fileSize}");

context.Response.StatusCode = 206;

$"Opening stream {requestFullLocalPath} bytes {lowerByteIndex}-{upperByteIndex} size {byteLength}"
.Debug();
}
}
else
{
byteLength = upperByteIndex - lowerByteIndex + 1;
if (UseGzip &&
context.RequestHeader(Headers.AcceptEncoding).Contains(Headers.CompressionGzip) &&
buffer.Length < MaxGzipInputLength &&

// Ignore audio/video from compression
context.Response.ContentType?.StartsWith("audio") == false &&
context.Response.ContentType?.StartsWith("video") == false)
{
// Perform compression if available
buffer = buffer.Compress();
context.Response.AddHeader(Headers.ContentEncoding, Headers.CompressionGzip);
lowerByteIndex = 0;
}

context.Response.AddHeader(Headers.ContentRanges,
$"bytes {lowerByteIndex}-{upperByteIndex}/{fileSize}");
byteLength = buffer.Length;
}

context.Response.StatusCode = 206;
context.Response.ContentLength64 = byteLength;

$"Opening stream {requestFullLocalPath} bytes {lowerByteIndex}-{upperByteIndex} size {byteLength}"
.Debug();
try
{
await WriteToOutputStream(context, byteLength, buffer, lowerByteIndex, ct);
}
}
else
{
if (UseGzip &&
context.RequestHeader(Headers.AcceptEncoding).Contains(Headers.CompressionGzip) &&
buffer.Length < MaxGzipInputLength &&

// Ignore audio/video from compression
context.Response.ContentType?.StartsWith("audio") == false &&
context.Response.ContentType?.StartsWith("video") == false)
catch (HttpListenerException)
{
// Perform compression if available
buffer = buffer.Compress();
context.Response.AddHeader(Headers.ContentEncoding, Headers.CompressionGzip);
lowerByteIndex = 0;
// Connection error, nothing else to do
}

byteLength = buffer.Length;
}

context.Response.ContentLength64 = byteLength;

try
{
await WriteToOutputStream(context, byteLength, buffer, lowerByteIndex, ct);
}
catch (HttpListenerException)
{
// Connection error, nothing else to do
}
finally
{
#if NETFX
buffer.Close();
#endif
buffer.Dispose();
buffer?.Dispose();
}

return true;
}

private static async Task WriteToOutputStream(
HttpListenerContext context,
long byteLength,
HttpListenerContext context,
long byteLength,
Stream buffer,
int lowerByteIndex,
int lowerByteIndex,
CancellationToken ct)
{
var streamBuffer = new byte[ChuckSize];
Expand Down Expand Up @@ -425,9 +423,9 @@ private void SetHeaders(HttpListenerContext context, string localPath, string ut
}

private bool UpdateFileCache(
HttpListenerContext context,
Stream buffer,
DateTime fileDate,
HttpListenerContext context,
Stream buffer,
DateTime fileDate,
string requestHash,
string localPath)
{
Expand Down Expand Up @@ -459,8 +457,8 @@ private bool UpdateFileCache(
}

private static bool CalculateRange(
string partialHeader,
long fileSize,
string partialHeader,
long fileSize,
out int lowerByteIndex,
out int upperByteIndex)
{
Expand All @@ -479,15 +477,15 @@ private static bool CalculateRange(
string.IsNullOrWhiteSpace(range[1])) ||
(range.Length == 1 && int.TryParse(range[0], out lowerByteIndex)))
{
upperByteIndex = (int) fileSize;
upperByteIndex = (int)fileSize;
return true;
}

if (range.Length == 2 && string.IsNullOrWhiteSpace(range[0]) &&
int.TryParse(range[1], out upperByteIndex))
{
lowerByteIndex = (int) fileSize - upperByteIndex;
upperByteIndex = (int) fileSize;
lowerByteIndex = (int)fileSize - upperByteIndex;
upperByteIndex = (int)fileSize;
return true;
}

Expand Down

0 comments on commit c67be8d

Please sign in to comment.