diff --git a/Jellyfin.Api/Controllers/SystemController.cs b/Jellyfin.Api/Controllers/SystemController.cs index bbbe5fb8da..5c64d731b6 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 b18ba70512..44b30baac0 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 0000000000..ce5ac11ea5 --- /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()); + } + } +}