fix: add HTTP Range header forwarding for iOS clients

This commit is contained in:
V1ck3s
2026-01-12 19:35:29 +01:00
committed by Branson
parent 8a66db915d
commit d31a380176
3 changed files with 145 additions and 48 deletions

View File

@@ -10,16 +10,16 @@ public class SubsonicProxyService
{
private readonly HttpClient _httpClient;
private readonly SubsonicSettings _subsonicSettings;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IHttpContextAccessor _httpContextAccessor;
public SubsonicProxyService(
IHttpClientFactory httpClientFactory,
Microsoft.Extensions.Options.IOptions<SubsonicSettings> subsonicSettings,
IHttpContextAccessor httpContextAccessor)
IHttpContextAccessor httpContextAccessor)
{
_httpClient = httpClientFactory.CreateClient();
_subsonicSettings = subsonicSettings.Value;
_httpContextAccessor = httpContextAccessor;
_httpContextAccessor = httpContextAccessor;
}
/// <summary>
@@ -69,33 +69,36 @@ public class SubsonicProxyService
{
try
{
// Get HTTP context for request/response forwarding
var httpContext = _httpContextAccessor.HttpContext;
if (httpContext == null)
{
return new StatusCodeResult(500);
}
var incomingRequest = httpContext.Request;
var outgoingResponse = httpContext.Response;
// Get HTTP context for request/response forwarding
var httpContext = _httpContextAccessor.HttpContext;
if (httpContext == null)
{
return new ObjectResult(new { error = "HTTP context not available" })
{
StatusCode = 500
};
}
var incomingRequest = httpContext.Request;
var outgoingResponse = httpContext.Response;
var query = string.Join("&", parameters.Select(kv =>
$"{Uri.EscapeDataString(kv.Key)}={Uri.EscapeDataString(kv.Value)}"));
var url = $"{_subsonicSettings.Url}/rest/stream?{query}";
using var request = new HttpRequestMessage(HttpMethod.Get, url);
// Forward Range headers (fix for iOS client)
if (incomingRequest.Headers.TryGetValue("Range", out var range))
{
request.Headers.TryAddWithoutValidation("Range", range.ToString());
}
if (incomingRequest.Headers.TryGetValue("If-Range", out var ifRange))
{
request.Headers.TryAddWithoutValidation("If-Range", ifRange.ToString());
}
// Forward Range headers for progressive streaming support (iOS clients)
if (incomingRequest.Headers.TryGetValue("Range", out var range))
{
request.Headers.TryAddWithoutValidation("Range", range.ToArray());
}
if (incomingRequest.Headers.TryGetValue("If-Range", out var ifRange))
{
request.Headers.TryAddWithoutValidation("If-Range", ifRange.ToArray());
}
var response = await _httpClient.SendAsync(
request,
HttpCompletionOption.ResponseHeadersRead,
@@ -106,22 +109,25 @@ public class SubsonicProxyService
return new StatusCodeResult((int)response.StatusCode);
}
// Iterate over and forward streaming-required headers
foreach (var header in new[]
{
"Accept-Ranges",
"Content-Range",
"Content-Length",
"ETag",
"Last-Modified"
})
{
if (response.Headers.TryGetValues(header, out var values) ||
response.Content.Headers.TryGetValues(header, out values))
{
outgoingResponse.Headers[header] = values.ToArray();
}
}
// Forward HTTP status code (e.g., 206 Partial Content for range requests)
outgoingResponse.StatusCode = (int)response.StatusCode;
// Forward streaming-required headers from upstream response
foreach (var header in new[]
{
"Accept-Ranges",
"Content-Range",
"Content-Length",
"ETag",
"Last-Modified"
})
{
if (response.Headers.TryGetValues(header, out var values) ||
response.Content.Headers.TryGetValues(header, out values))
{
outgoingResponse.Headers[header] = values.ToArray();
}
}
var stream = await response.Content.ReadAsStreamAsync(cancellationToken);
var contentType = response.Content.Headers.ContentType?.ToString() ?? "audio/mpeg";