From 81d675990f87586061bdce5d585dad28f7e181fa Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 5 May 2021 22:52:39 +0100 Subject: Enable automatic url decoding --- .../Middleware/UrlDecodeQueryFeature.cs | 75 ++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs (limited to 'Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs') diff --git a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs new file mode 100644 index 000000000..0232b89ce --- /dev/null +++ b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs @@ -0,0 +1,75 @@ +using System.Collections.Generic; +using System.Linq; +using System.Web; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.Primitives; + +namespace Jellyfin.Server.Middleware +{ + /// + /// Defines the . + /// + public class UrlDecodeQueryFeature : IQueryFeature + { + private IQueryCollection? _store; + + /// + /// Initializes a new instance of the class. + /// + /// The instance. + public UrlDecodeQueryFeature(IQueryFeature feature) + { + Query = feature.Query; + } + + /// + /// Gets or sets a value indicating the url decoded . + /// + public IQueryCollection Query + { + get + { + return _store ?? QueryCollection.Empty; + } + + set + { + // Only interested in where the querystring is encoded which shows up as one key with everything else in the value. + if (value.Count != 1) + { + _store = value; + return; + } + + // Encoded querystrings have no value, so don't process anything if a values is present. + var kvp = value.First(); + if (!string.IsNullOrEmpty(kvp.Value)) + { + _store = value; + return; + } + + // Unencode and re-parse querystring. + var unencodedKey = HttpUtility.UrlDecode(kvp.Key); + + if (string.Equals(unencodedKey, kvp.Key, System.StringComparison.Ordinal)) + { + _store = value; + return; + } + + var pairs = new Dictionary(); + var queryString = unencodedKey.Split('&', System.StringSplitOptions.RemoveEmptyEntries); + + foreach (var pair in queryString) + { + var item = pair.Split('=', System.StringSplitOptions.RemoveEmptyEntries); + pairs.Add(item[0], new StringValues(item.Length == 2 ? item[1] : string.Empty)); + } + + _store = new QueryCollection(pairs); + } + } + } +} -- cgit v1.2.3 From dabeabc553afc4551356566f9184e6659f604cee Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 5 May 2021 23:14:05 +0100 Subject: corrected comments --- Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs') diff --git a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs index 0232b89ce..b18ba7051 100644 --- a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs +++ b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs @@ -35,14 +35,14 @@ namespace Jellyfin.Server.Middleware set { - // Only interested in where the querystring is encoded which shows up as one key with everything else in the value. + // Only interested in where the querystring is encoded which shows up as one key with nothing in the value. if (value.Count != 1) { _store = value; return; } - // Encoded querystrings have no value, so don't process anything if a values is present. + // Encoded querystrings have no value, so don't process anything if a value is present. var kvp = value.First(); if (!string.IsNullOrEmpty(kvp.Value)) { @@ -55,6 +55,7 @@ namespace Jellyfin.Server.Middleware if (string.Equals(unencodedKey, kvp.Key, System.StringComparison.Ordinal)) { + // Don't do anything if it's not encoded. _store = value; return; } -- cgit v1.2.3 From 4f5c9e95041a39ae549bfa3f36bbeda054bce3ca Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Fri, 7 May 2021 14:02:42 +0100 Subject: tests and small fix. --- Jellyfin.Api/Controllers/SystemController.cs | 8 +++- .../Middleware/UrlDecodeQueryFeature.cs | 9 ++++- .../Controllers/EncodedQueryStringTest.cs | 47 ++++++++++++++++++++++ 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 tests/Jellyfin.Api.Tests/Controllers/EncodedQueryStringTest.cs (limited to 'Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs') diff --git a/Jellyfin.Api/Controllers/SystemController.cs b/Jellyfin.Api/Controllers/SystemController.cs index bbbe5fb8d..5c64d731b 100644 --- a/Jellyfin.Api/Controllers/SystemController.cs +++ b/Jellyfin.Api/Controllers/SystemController.cs @@ -84,13 +84,19 @@ namespace Jellyfin.Api.Controllers /// /// Pings the system. /// + /// Optional: Parameters to echo back in the response. /// Information retrieved. /// The server name. [HttpGet("Ping", Name = "GetPingSystem")] [HttpPost("Ping", Name = "PostPingSystem")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult PingSystem() + public ActionResult PingSystem([FromQuery]Dictionary? @params = null) { + if (@params != null && @params.Count > 0) + { + Response.Headers.Add("querystring", string.Join("&", @params.Select(x => x.Key + "=" + x.Value))); + } + return _appHost.Name; } diff --git a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs index b18ba7051..44b30baac 100644 --- a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs +++ b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs @@ -66,7 +66,14 @@ namespace Jellyfin.Server.Middleware foreach (var pair in queryString) { var item = pair.Split('=', System.StringSplitOptions.RemoveEmptyEntries); - pairs.Add(item[0], new StringValues(item.Length == 2 ? item[1] : string.Empty)); + if (item.Length > 0) + { + pairs.Add(item[0], new StringValues(item.Length == 2 ? item[1] : string.Empty)); + } + else + { + pairs.Add(pair, string.Empty); + } } _store = new QueryCollection(pairs); diff --git a/tests/Jellyfin.Api.Tests/Controllers/EncodedQueryStringTest.cs b/tests/Jellyfin.Api.Tests/Controllers/EncodedQueryStringTest.cs new file mode 100644 index 000000000..ce5ac11ea --- /dev/null +++ b/tests/Jellyfin.Api.Tests/Controllers/EncodedQueryStringTest.cs @@ -0,0 +1,47 @@ +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Xunit; + +namespace Jellyfin.Api.Tests.Controllers +{ + /// + /// Defines the test for encoded querystrings in the url. + /// + public class EncodedQueryStringTest : IClassFixture + { + private readonly JellyfinApplicationFactory _factory; + + public EncodedQueryStringTest(JellyfinApplicationFactory factory) + { + _factory = factory; + } + + [Fact] + public async Task Ensure_Ping_Working() + { + var client = _factory.CreateClient(); + + var response = await client.GetAsync("system/ping").ConfigureAwait(false); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } + + [Theory] + [InlineData("a=1&b=2&c=3", "a=1&b=2&c=3")] // won't be processed as there is more than 1. + [InlineData("a=1", "a=1")] // won't be processed as it has a value + [InlineData("%3D", "==")] // will decode with an empty string value '=' = ''. + [InlineData("a%3D1%26b%3D2%26c%3D3", "a=1&b=2&c=3")] // will be processed. + + public async Task Ensure_Decoding_Of_Urls_Is_Working(string sourceUrl, string unencodedUrl) + { + var client = _factory.CreateClient(); + + var response = await client.GetAsync("system/ping?" + sourceUrl).ConfigureAwait(false); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal(unencodedUrl, response.Headers.GetValues("querystring").First()); + } + } +} -- cgit v1.2.3 From bd71de131c384765b9240b7a54649e2c9258b133 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sat, 8 May 2021 12:52:25 +0100 Subject: Changed to use span --- .../Middleware/UrlDecodeQueryFeature.cs | 21 +++++++----- .../Controllers/EncodedQueryStringTest.cs | 39 --------------------- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 1 + .../Jellyfin.Server.Implementations.Tests.csproj | 1 + .../Middleware/EncodedQueryStringTest.cs | 40 ++++++++++++++++++++++ 5 files changed, 55 insertions(+), 47 deletions(-) delete mode 100644 tests/Jellyfin.Api.Tests/Controllers/EncodedQueryStringTest.cs create mode 100644 tests/Jellyfin.Server.Implementations.Tests/Middleware/EncodedQueryStringTest.cs (limited to 'Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs') diff --git a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs index 44b30baac..c89a318e1 100644 --- a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs +++ b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Web; +using MediaBrowser.Common.Extensions; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.Extensions.Primitives; @@ -61,19 +62,23 @@ namespace Jellyfin.Server.Middleware } var pairs = new Dictionary(); - var queryString = unencodedKey.Split('&', System.StringSplitOptions.RemoveEmptyEntries); + var queryString = unencodedKey.SpanSplit('&'); foreach (var pair in queryString) { - var item = pair.Split('=', System.StringSplitOptions.RemoveEmptyEntries); - if (item.Length > 0) - { - pairs.Add(item[0], new StringValues(item.Length == 2 ? item[1] : string.Empty)); - } - else + var item = pair.Split('='); + item.MoveNext(); + + var key = item.Current; + var val = item.MoveNext() ? item.Current : string.Empty; + if (key.Length == 0 && val.Length == 0) { - pairs.Add(pair, string.Empty); + // encoded is an equals. + pairs.Add(pair.ToString(), new StringValues(string.Empty)); + continue; } + + pairs.Add(key.ToString(), new StringValues(val.ToString())); } _store = new QueryCollection(pairs); diff --git a/tests/Jellyfin.Api.Tests/Controllers/EncodedQueryStringTest.cs b/tests/Jellyfin.Api.Tests/Controllers/EncodedQueryStringTest.cs deleted file mode 100644 index d6a423dcd..000000000 --- a/tests/Jellyfin.Api.Tests/Controllers/EncodedQueryStringTest.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Text; -using System.Threading.Tasks; -using Xunit; - -namespace Jellyfin.Api.Tests.Controllers -{ - /// - /// Defines the test for encoded querystrings in the url. - /// - public class EncodedQueryStringTest : IClassFixture - { - private readonly JellyfinApplicationFactory _factory; - - public EncodedQueryStringTest(JellyfinApplicationFactory factory) - { - _factory = factory; - } - - [Theory] - [InlineData("a=1&b=2&c=3", "a=1&b=2&c=3")] // won't be processed as there is more than 1. - [InlineData("a=1", "a=1")] // won't be processed as it has a value - [InlineData("%3D", "==")] // will decode with an empty string value '=' = ''. - [InlineData("a%3D1%26b%3D2%26c%3D3", "a=1&b=2&c=3")] // will be processed. - public async Task Ensure_Decoding_Of_Urls_Is_Working(string sourceUrl, string unencodedUrl) - { - var client = _factory.CreateClient(); - - var response = await client.GetAsync("Tests/UrlDecode?" + sourceUrl).ConfigureAwait(false); - Assert.Equal(HttpStatusCode.OK, response.StatusCode); - string reply = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - Assert.Equal(unencodedUrl, reply); - } - } -} diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 397b863b7..e3577caee 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -37,6 +37,7 @@ + diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index 27713d58a..ccc18d686 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -42,6 +42,7 @@ + diff --git a/tests/Jellyfin.Server.Implementations.Tests/Middleware/EncodedQueryStringTest.cs b/tests/Jellyfin.Server.Implementations.Tests/Middleware/EncodedQueryStringTest.cs new file mode 100644 index 000000000..e5865fb5a --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/Middleware/EncodedQueryStringTest.cs @@ -0,0 +1,40 @@ +using System; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Jellyfin.Server.Integration.Tests; +using Xunit; + +namespace Jellyfin.Server.Implementations.Tests.Middleware +{ + /// + /// Defines the test for encoded querystrings in the url. + /// + public class EncodedQueryStringTest : IClassFixture + { + private readonly JellyfinApplicationFactory _factory; + + public EncodedQueryStringTest(JellyfinApplicationFactory factory) + { + _factory = factory; + } + + [Theory] + [InlineData("a=1&b=2&c=3", "a=1&b=2&c=3")] // won't be processed as there is more than 1. + [InlineData("a=1", "a=1")] // won't be processed as it has a value + [InlineData("%3D", "==")] // will decode with an empty string value '=' = ''. + [InlineData("a%3D1%26b%3D2%26c%3D3", "a=1&b=2&c=3")] // will be processed. + public async Task Ensure_Decoding_Of_Urls_Is_Working(string sourceUrl, string unencodedUrl) + { + var client = _factory.CreateClient(); + + var response = await client.GetAsync("Tests/UrlDecode?" + sourceUrl).ConfigureAwait(false); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + string reply = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + Assert.Equal(unencodedUrl, reply); + } + } +} -- cgit v1.2.3 From 903bf2a086c49266c010f947180bd660b2c58931 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Sat, 8 May 2021 16:00:41 +0100 Subject: changed to use index --- Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs') diff --git a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs index c89a318e1..dd05f7bc5 100644 --- a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs +++ b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs @@ -66,19 +66,17 @@ namespace Jellyfin.Server.Middleware foreach (var pair in queryString) { - var item = pair.Split('='); - item.MoveNext(); + var section = pair.ToString(); + var i = section.IndexOf('=', System.StringComparison.Ordinal); - var key = item.Current; - var val = item.MoveNext() ? item.Current : string.Empty; - if (key.Length == 0 && val.Length == 0) + if (i == -1) { // encoded is an equals. - pairs.Add(pair.ToString(), new StringValues(string.Empty)); + pairs.Add(section, new StringValues(string.Empty)); continue; } - pairs.Add(key.ToString(), new StringValues(val.ToString())); + pairs.Add(section[0..i], new StringValues(section[(i + 1)..])); } _store = new QueryCollection(pairs); -- cgit v1.2.3 From d0bfb56d2ef0607b10d83706f7ec76fc16bb4cf9 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Wed, 12 May 2021 16:19:08 +0100 Subject: changed to slice. --- Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs') diff --git a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs index dd05f7bc5..7d2c30b9d 100644 --- a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs +++ b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using System.Web; @@ -66,17 +67,16 @@ namespace Jellyfin.Server.Middleware foreach (var pair in queryString) { - var section = pair.ToString(); - var i = section.IndexOf('=', System.StringComparison.Ordinal); + var i = pair.IndexOf('='); if (i == -1) { // encoded is an equals. - pairs.Add(section, new StringValues(string.Empty)); + pairs.Add(pair[0..i].ToString(), new StringValues(string.Empty)); continue; } - pairs.Add(section[0..i], new StringValues(section[(i + 1)..])); + pairs.Add(pair[0..i].ToString(), new StringValues(pair[(i + 1)..].ToString())); } _store = new QueryCollection(pairs); -- cgit v1.2.3 From e1a0b5d2a14d901f277d9179b23efbea3eba0f3a Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 7 Jun 2021 16:21:15 +0100 Subject: Update Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs Co-authored-by: Claus Vium --- Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs') diff --git a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs index 7d2c30b9d..987c66da7 100644 --- a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs +++ b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs @@ -72,7 +72,7 @@ namespace Jellyfin.Server.Middleware if (i == -1) { // encoded is an equals. - pairs.Add(pair[0..i].ToString(), new StringValues(string.Empty)); + pairs.Add(pair[..i].ToString(), StringValues.Empty); continue; } -- cgit v1.2.3 From 371f8629b1be0f54e8703470b34a29ab2fb36d8c Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 7 Jun 2021 16:21:47 +0100 Subject: Update Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs Co-authored-by: Claus Vium --- Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs') diff --git a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs index 987c66da7..bcc79a5f2 100644 --- a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs +++ b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs @@ -76,7 +76,7 @@ namespace Jellyfin.Server.Middleware continue; } - pairs.Add(pair[0..i].ToString(), new StringValues(pair[(i + 1)..].ToString())); + pairs.Add(pair[..i].ToString(), new StringValues(pair[(i + 1)..].ToString())); } _store = new QueryCollection(pairs); -- cgit v1.2.3 From c1fa7cbbf824796e0aa2b143edba88da77235828 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 7 Jun 2021 16:22:11 +0100 Subject: Update Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs Co-authored-by: Claus Vium --- Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs') diff --git a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs index bcc79a5f2..890947ba0 100644 --- a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs +++ b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs @@ -45,7 +45,7 @@ namespace Jellyfin.Server.Middleware } // Encoded querystrings have no value, so don't process anything if a value is present. - var kvp = value.First(); + var (key, stringValues) = value.First(); if (!string.IsNullOrEmpty(kvp.Value)) { _store = value; -- cgit v1.2.3 From ada052fcb1f37c0253dcc3fa0edbb81c630169c5 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 7 Jun 2021 16:22:17 +0100 Subject: Update Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs Co-authored-by: Claus Vium --- Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs') diff --git a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs index 890947ba0..91d2ce124 100644 --- a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs +++ b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs @@ -53,7 +53,7 @@ namespace Jellyfin.Server.Middleware } // Unencode and re-parse querystring. - var unencodedKey = HttpUtility.UrlDecode(kvp.Key); + var unencodedKey = HttpUtility.UrlDecode(key); if (string.Equals(unencodedKey, kvp.Key, System.StringComparison.Ordinal)) { -- cgit v1.2.3 From 37326a80993346c66e2f562677bf9ccd43d581b5 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 7 Jun 2021 16:22:28 +0100 Subject: Update Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs Co-authored-by: Claus Vium --- Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs') diff --git a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs index 91d2ce124..b3b3b391e 100644 --- a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs +++ b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs @@ -55,7 +55,7 @@ namespace Jellyfin.Server.Middleware // Unencode and re-parse querystring. var unencodedKey = HttpUtility.UrlDecode(key); - if (string.Equals(unencodedKey, kvp.Key, System.StringComparison.Ordinal)) + if (string.Equals(unencodedKey, key, System.StringComparison.Ordinal)) { // Don't do anything if it's not encoded. _store = value; -- cgit v1.2.3 From 147612f59bb5870f04197087e3d5fcd954061471 Mon Sep 17 00:00:00 2001 From: BaronGreenback Date: Mon, 7 Jun 2021 16:22:37 +0100 Subject: Update Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs Co-authored-by: Claus Vium --- Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs') diff --git a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs index b3b3b391e..804e58b5a 100644 --- a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs +++ b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs @@ -46,7 +46,7 @@ namespace Jellyfin.Server.Middleware // Encoded querystrings have no value, so don't process anything if a value is present. var (key, stringValues) = value.First(); - if (!string.IsNullOrEmpty(kvp.Value)) + if (!string.IsNullOrEmpty(stringValues)) { _store = value; return; -- cgit v1.2.3 From 2fc14375f8b935b2530c82a23621e3f001b6c05c Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 8 Jun 2021 15:35:49 +0200 Subject: Fix index out of range and add reg tests --- .../Middleware/UrlDecodeQueryFeature.cs | 4 +-- .../UrlDecodeQueryFeatureTests.cs | 30 ++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 tests/Jellyfin.Server.Tests/UrlDecodeQueryFeatureTests.cs (limited to 'Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs') diff --git a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs index 804e58b5a..f1b47ce33 100644 --- a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs +++ b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs @@ -55,7 +55,7 @@ namespace Jellyfin.Server.Middleware // Unencode and re-parse querystring. var unencodedKey = HttpUtility.UrlDecode(key); - if (string.Equals(unencodedKey, key, System.StringComparison.Ordinal)) + if (string.Equals(unencodedKey, key, StringComparison.Ordinal)) { // Don't do anything if it's not encoded. _store = value; @@ -72,7 +72,7 @@ namespace Jellyfin.Server.Middleware if (i == -1) { // encoded is an equals. - pairs.Add(pair[..i].ToString(), StringValues.Empty); + pairs.Add(pair.ToString(), StringValues.Empty); continue; } diff --git a/tests/Jellyfin.Server.Tests/UrlDecodeQueryFeatureTests.cs b/tests/Jellyfin.Server.Tests/UrlDecodeQueryFeatureTests.cs new file mode 100644 index 000000000..d0eac138a --- /dev/null +++ b/tests/Jellyfin.Server.Tests/UrlDecodeQueryFeatureTests.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using System.Linq; +using Jellyfin.Server.Middleware; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.Primitives; +using Xunit; + +namespace Jellyfin.Server.Tests +{ + public static class UrlDecodeQueryFeatureTests + { + [Theory] + [InlineData("e0a72cb2a2c7", "e0a72cb2a2c7")] // isn't encoded + [InlineData("random+test", "random test")] // encoded + [InlineData("random%20test", "random test")] // encoded + public static void EmptyValueTest(string query, string key) + { + var dict = new Dictionary + { + { query, StringValues.Empty } + }; + var test = new UrlDecodeQueryFeature(new QueryFeature(new QueryCollection(dict))); + Assert.Single(test.Query); + var (k, v) = test.Query.First(); + Assert.Equal(k, key); + Assert.Empty(v); + } + } +} -- cgit v1.2.3 From 06401ffa0d8ae98fa42c750847f97c3c291b06ae Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 8 Jun 2021 22:26:59 +0200 Subject: Fix duplicate keys causing an exception --- Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs | 11 ++++++++--- .../Controllers/EncoderController.cs | 20 ++++++++++++++++++++ .../EncodedQueryStringTest.cs | 15 +++++++++++++++ .../UrlDecodeQueryFeatureTests.cs | 3 ++- 4 files changed, 45 insertions(+), 4 deletions(-) (limited to 'Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs') diff --git a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs index f1b47ce33..310a3d31a 100644 --- a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs +++ b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs @@ -68,15 +68,20 @@ namespace Jellyfin.Server.Middleware foreach (var pair in queryString) { var i = pair.IndexOf('='); - if (i == -1) { // encoded is an equals. - pairs.Add(pair.ToString(), StringValues.Empty); + // We use TryAdd so duplicate keys get ignored + pairs.TryAdd(pair.ToString(), StringValues.Empty); continue; } - pairs.Add(pair[..i].ToString(), new StringValues(pair[(i + 1)..].ToString())); + var k = pair[..i].ToString(); + var v = pair[(i + 1)..].ToString(); + if (!pairs.TryAdd(k, new StringValues(v))) + { + pairs[k] = StringValues.Concat(pairs[k], v); + } } _store = new QueryCollection(pairs); diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/EncoderController.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/EncoderController.cs index 14f92f0d8..c8ce58047 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Controllers/EncoderController.cs +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/EncoderController.cs @@ -29,5 +29,25 @@ namespace Jellyfin.Api.Controllers StatusCode = 200 }; } + + /// + /// Tests the url decoding. + /// + /// Parameters to echo back in the response. + /// An . + /// Information retrieved. + [HttpGet("UrlArrayDecode")] + [ProducesResponseType(StatusCodes.Status200OK)] + public ContentResult TestUrlArrayDecoding([FromQuery] Dictionary? @params = null) + { + return new ContentResult() + { + Content = (@params != null && @params.Count > 0) + ? string.Join("&", @params.Select(x => x.Key + "=" + string.Join(',', x.Value))) + : string.Empty, + ContentType = "text/plain; charset=utf-8", + StatusCode = 200 + }; + } } } diff --git a/tests/Jellyfin.Server.Integration.Tests/EncodedQueryStringTest.cs b/tests/Jellyfin.Server.Integration.Tests/EncodedQueryStringTest.cs index 29d3fe33d..732b4f050 100644 --- a/tests/Jellyfin.Server.Integration.Tests/EncodedQueryStringTest.cs +++ b/tests/Jellyfin.Server.Integration.Tests/EncodedQueryStringTest.cs @@ -20,6 +20,8 @@ namespace Jellyfin.Server.Integration.Tests [InlineData("a=1&b=2&c=3", "a=1&b=2&c=3")] // won't be processed as there is more than 1. [InlineData("a=1", "a=1")] // won't be processed as it has a value [InlineData("a%3D1%26b%3D2%26c%3D3", "a=1&b=2&c=3")] // will be processed. + [InlineData("a=b&a=c", "a=b")] + [InlineData("a%3Db%26a%3Dc", "a=b")] public async Task Ensure_Decoding_Of_Urls_Is_Working(string sourceUrl, string unencodedUrl) { var client = _factory.CreateClient(); @@ -29,5 +31,18 @@ namespace Jellyfin.Server.Integration.Tests string reply = await response.Content.ReadAsStringAsync().ConfigureAwait(false); Assert.Equal(unencodedUrl, reply); } + + [Theory] + [InlineData("a=b&a=c", "a=b,c")] + [InlineData("a%3Db%26a%3Dc", "a=b,c")] + public async Task Ensure_Array_Decoding_Of_Urls_Is_Working(string sourceUrl, string unencodedUrl) + { + var client = _factory.CreateClient(); + + var response = await client.GetAsync("Encoder/UrlArrayDecode?" + sourceUrl).ConfigureAwait(false); + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + string reply = await response.Content.ReadAsStringAsync().ConfigureAwait(false); + Assert.Equal(unencodedUrl, reply); + } } } diff --git a/tests/Jellyfin.Server.Tests/UrlDecodeQueryFeatureTests.cs b/tests/Jellyfin.Server.Tests/UrlDecodeQueryFeatureTests.cs index d0eac138a..419afb2dc 100644 --- a/tests/Jellyfin.Server.Tests/UrlDecodeQueryFeatureTests.cs +++ b/tests/Jellyfin.Server.Tests/UrlDecodeQueryFeatureTests.cs @@ -14,6 +14,7 @@ namespace Jellyfin.Server.Tests [InlineData("e0a72cb2a2c7", "e0a72cb2a2c7")] // isn't encoded [InlineData("random+test", "random test")] // encoded [InlineData("random%20test", "random test")] // encoded + [InlineData("++", " ")] // encoded public static void EmptyValueTest(string query, string key) { var dict = new Dictionary @@ -23,7 +24,7 @@ namespace Jellyfin.Server.Tests var test = new UrlDecodeQueryFeature(new QueryFeature(new QueryCollection(dict))); Assert.Single(test.Query); var (k, v) = test.Query.First(); - Assert.Equal(k, key); + Assert.Equal(key, k); Assert.Empty(v); } } -- cgit v1.2.3