From 4d572d8173881206890400f37a9d825d3a6dd198 Mon Sep 17 00:00:00 2001 From: David Montesinos Date: Mon, 21 Apr 2025 10:48:48 +0200 Subject: [PATCH 1/6] Allow earlier return in files server --- Oqtane.Server/Pages/Files.cshtml.cs | 58 ++++++++++++++--------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/Oqtane.Server/Pages/Files.cshtml.cs b/Oqtane.Server/Pages/Files.cshtml.cs index b241388e..97fc2c57 100644 --- a/Oqtane.Server/Pages/Files.cshtml.cs +++ b/Oqtane.Server/Pages/Files.cshtml.cs @@ -112,7 +112,7 @@ namespace Oqtane.Pages url += Request.QueryString.Value.Substring(1); } - + return RedirectPermanent(url); } @@ -137,6 +137,34 @@ namespace Oqtane.Pages string downloadName = file.Name; string filepath = _files.GetFilePath(file); + if (Request.QueryString.HasValue) + { + etag = Utilities.GenerateSimpleHash(Request.QueryString.Value); + } + else + { + etag = Convert.ToString(file.ModifiedOn.Ticks ^ file.Size, 16); + } + + var header = ""; + if (HttpContext.Request.Headers.TryGetValue(HeaderNames.IfNoneMatch, out var ifNoneMatch)) + { + header = ifNoneMatch.ToString(); + } + + if (header.Equals(etag)) + { + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotModified; + return Content(String.Empty); + } + + if (!System.IO.File.Exists(filepath)) + { + _logger.Log(LogLevel.Error, this, LogFunction.Read, "File Does Not Exist {FilePath}", filepath); + HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; + return BrokenFile(); + } + // evaluate any querystring parameters bool isRequestingImageManipulation = false; @@ -165,34 +193,6 @@ namespace Oqtane.Pages isRequestingImageManipulation = true; } - if (isRequestingImageManipulation) - { - etag = Utilities.GenerateSimpleHash(Request.QueryString.Value); - } - else - { - etag = Convert.ToString(file.ModifiedOn.Ticks ^ file.Size, 16); - } - - var header = ""; - if (HttpContext.Request.Headers.TryGetValue(HeaderNames.IfNoneMatch, out var ifNoneMatch)) - { - header = ifNoneMatch.ToString(); - } - - if (header.Equals(etag)) - { - HttpContext.Response.StatusCode = (int)HttpStatusCode.NotModified; - return Content(String.Empty); - } - - if (!System.IO.File.Exists(filepath)) - { - _logger.Log(LogLevel.Error, this, LogFunction.Read, "File Does Not Exist {FilePath}", filepath); - HttpContext.Response.StatusCode = (int)HttpStatusCode.NotFound; - return BrokenFile(); - } - if (isRequestingImageManipulation) { var _ImageFiles = _settingRepository.GetSetting(EntityNames.Site, _alias.SiteId, "ImageFiles")?.SettingValue; From 1b00fa74bcbabdf4e854a660fd92788496b08143 Mon Sep 17 00:00:00 2001 From: David Montesinos Date: Mon, 21 Apr 2025 11:14:24 +0200 Subject: [PATCH 2/6] Compute file server etag with MD5 and always include ModifiedOn --- Oqtane.Server/Pages/Files.cshtml.cs | 10 +++++----- Oqtane.Shared/Shared/Utilities.cs | 8 ++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Oqtane.Server/Pages/Files.cshtml.cs b/Oqtane.Server/Pages/Files.cshtml.cs index 97fc2c57..7be27e89 100644 --- a/Oqtane.Server/Pages/Files.cshtml.cs +++ b/Oqtane.Server/Pages/Files.cshtml.cs @@ -137,15 +137,15 @@ namespace Oqtane.Pages string downloadName = file.Name; string filepath = _files.GetFilePath(file); + var etagInput = $"{file.ModifiedOn.Ticks}:{file.Size}"; + if (Request.QueryString.HasValue) { - etag = Utilities.GenerateSimpleHash(Request.QueryString.Value); - } - else - { - etag = Convert.ToString(file.ModifiedOn.Ticks ^ file.Size, 16); + etagInput += $":{Request.QueryString.Value}"; } + etag = Utilities.GenerateHashMD5(etagInput); + var header = ""; if (HttpContext.Request.Headers.TryGetValue(HeaderNames.IfNoneMatch, out var ifNoneMatch)) { diff --git a/Oqtane.Shared/Shared/Utilities.cs b/Oqtane.Shared/Shared/Utilities.cs index 826fac70..d38fe490 100644 --- a/Oqtane.Shared/Shared/Utilities.cs +++ b/Oqtane.Shared/Shared/Utilities.cs @@ -5,6 +5,7 @@ using System.Globalization; using System.IO; using System.Linq; using System.Net; +using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using File = Oqtane.Models.File; @@ -619,6 +620,13 @@ namespace Oqtane.Shared } } + public static string GenerateHashMD5(string input) + { + var bytes = Encoding.UTF8.GetBytes(input); + var hashBytes = MD5.HashData(bytes); + return Convert.ToHexString(hashBytes); + } + [Obsolete("ContentUrl(Alias alias, int fileId) is deprecated. Use FileUrl(Alias alias, int fileId) instead.", false)] public static string ContentUrl(Alias alias, int fileId) { From e7acd14faa9a3021ca3099f6979835503e098a0f Mon Sep 17 00:00:00 2001 From: David Montesinos Date: Mon, 21 Apr 2025 15:51:25 +0200 Subject: [PATCH 3/6] Replace MD5 hash with a longer simple hash --- Oqtane.Server/Pages/Files.cshtml.cs | 2 +- Oqtane.Shared/Shared/Utilities.cs | 14 ++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Oqtane.Server/Pages/Files.cshtml.cs b/Oqtane.Server/Pages/Files.cshtml.cs index 7be27e89..551be05f 100644 --- a/Oqtane.Server/Pages/Files.cshtml.cs +++ b/Oqtane.Server/Pages/Files.cshtml.cs @@ -144,7 +144,7 @@ namespace Oqtane.Pages etagInput += $":{Request.QueryString.Value}"; } - etag = Utilities.GenerateHashMD5(etagInput); + etag = Utilities.GenerateSimpleHash16(etagInput); var header = ""; if (HttpContext.Request.Headers.TryGetValue(HeaderNames.IfNoneMatch, out var ifNoneMatch)) diff --git a/Oqtane.Shared/Shared/Utilities.cs b/Oqtane.Shared/Shared/Utilities.cs index d38fe490..30e25bb2 100644 --- a/Oqtane.Shared/Shared/Utilities.cs +++ b/Oqtane.Shared/Shared/Utilities.cs @@ -620,11 +620,17 @@ namespace Oqtane.Shared } } - public static string GenerateHashMD5(string input) + public static string GenerateSimpleHash16(string text) { - var bytes = Encoding.UTF8.GetBytes(input); - var hashBytes = MD5.HashData(bytes); - return Convert.ToHexString(hashBytes); + unchecked // prevent overflow exception + { + long hash = 23; + foreach (char c in text) + { + hash = hash * 31 + c; + } + return hash.ToString("X16"); + } } [Obsolete("ContentUrl(Alias alias, int fileId) is deprecated. Use FileUrl(Alias alias, int fileId) instead.", false)] From 430f83e8e9d034b7edee27da3749342bec31b462 Mon Sep 17 00:00:00 2001 From: David Montesinos Date: Mon, 21 Apr 2025 16:46:55 +0200 Subject: [PATCH 4/6] Only compute hash when file has query string --- Oqtane.Server/Pages/Files.cshtml.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Oqtane.Server/Pages/Files.cshtml.cs b/Oqtane.Server/Pages/Files.cshtml.cs index 551be05f..4105262e 100644 --- a/Oqtane.Server/Pages/Files.cshtml.cs +++ b/Oqtane.Server/Pages/Files.cshtml.cs @@ -137,14 +137,14 @@ namespace Oqtane.Pages string downloadName = file.Name; string filepath = _files.GetFilePath(file); - var etagInput = $"{file.ModifiedOn.Ticks}:{file.Size}"; - if (Request.QueryString.HasValue) { - etagInput += $":{Request.QueryString.Value}"; + etag = Utilities.GenerateSimpleHash16($"{file.ModifiedOn.Ticks}:{file.Size}:{Request.QueryString.Value}"); + } + else + { + etag = Convert.ToString(file.ModifiedOn.Ticks ^ file.Size, 16); } - - etag = Utilities.GenerateSimpleHash16(etagInput); var header = ""; if (HttpContext.Request.Headers.TryGetValue(HeaderNames.IfNoneMatch, out var ifNoneMatch)) From d888d83a986185e1109fb27c74f20086b6308ea6 Mon Sep 17 00:00:00 2001 From: David Montesinos Date: Mon, 21 Apr 2025 18:44:16 +0200 Subject: [PATCH 5/6] Simplify files etag calculation --- Oqtane.Server/Pages/Files.cshtml.cs | 11 +---------- Oqtane.Shared/Shared/Utilities.cs | 13 ------------- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/Oqtane.Server/Pages/Files.cshtml.cs b/Oqtane.Server/Pages/Files.cshtml.cs index 4105262e..f01cd0b7 100644 --- a/Oqtane.Server/Pages/Files.cshtml.cs +++ b/Oqtane.Server/Pages/Files.cshtml.cs @@ -133,19 +133,10 @@ namespace Oqtane.Pages } } - string etag; + string etag = Convert.ToString(file.ModifiedOn.Ticks ^ file.Size, 16); string downloadName = file.Name; string filepath = _files.GetFilePath(file); - if (Request.QueryString.HasValue) - { - etag = Utilities.GenerateSimpleHash16($"{file.ModifiedOn.Ticks}:{file.Size}:{Request.QueryString.Value}"); - } - else - { - etag = Convert.ToString(file.ModifiedOn.Ticks ^ file.Size, 16); - } - var header = ""; if (HttpContext.Request.Headers.TryGetValue(HeaderNames.IfNoneMatch, out var ifNoneMatch)) { diff --git a/Oqtane.Shared/Shared/Utilities.cs b/Oqtane.Shared/Shared/Utilities.cs index 30e25bb2..ee4586b0 100644 --- a/Oqtane.Shared/Shared/Utilities.cs +++ b/Oqtane.Shared/Shared/Utilities.cs @@ -620,19 +620,6 @@ namespace Oqtane.Shared } } - public static string GenerateSimpleHash16(string text) - { - unchecked // prevent overflow exception - { - long hash = 23; - foreach (char c in text) - { - hash = hash * 31 + c; - } - return hash.ToString("X16"); - } - } - [Obsolete("ContentUrl(Alias alias, int fileId) is deprecated. Use FileUrl(Alias alias, int fileId) instead.", false)] public static string ContentUrl(Alias alias, int fileId) { From da7b0460921354ca5835615eef9a1c87d943cb46 Mon Sep 17 00:00:00 2001 From: David Montesinos Date: Mon, 21 Apr 2025 18:47:34 +0200 Subject: [PATCH 6/6] Remove extra using --- Oqtane.Shared/Shared/Utilities.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Oqtane.Shared/Shared/Utilities.cs b/Oqtane.Shared/Shared/Utilities.cs index ee4586b0..826fac70 100644 --- a/Oqtane.Shared/Shared/Utilities.cs +++ b/Oqtane.Shared/Shared/Utilities.cs @@ -5,7 +5,6 @@ using System.Globalization; using System.IO; using System.Linq; using System.Net; -using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using File = Oqtane.Models.File;