* Re-add content length, semi revert of changes in #1010
This commit is contained in:
parent
696a36b4a5
commit
28c2ac528d
|
@ -67,6 +67,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(rangeHeader))
|
if (string.IsNullOrWhiteSpace(rangeHeader))
|
||||||
{
|
{
|
||||||
|
Headers[HeaderNames.ContentLength] = TotalContentLength.ToString(CultureInfo.InvariantCulture);
|
||||||
StatusCode = HttpStatusCode.OK;
|
StatusCode = HttpStatusCode.OK;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -99,10 +100,13 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
RangeStart = requestedRange.Key;
|
RangeStart = requestedRange.Key;
|
||||||
RangeLength = 1 + RangeEnd - RangeStart;
|
RangeLength = 1 + RangeEnd - RangeStart;
|
||||||
|
|
||||||
|
// Content-Length is the length of what we're serving, not the original content
|
||||||
|
var lengthString = RangeLength.ToString(CultureInfo.InvariantCulture);
|
||||||
|
Headers[HeaderNames.ContentLength] = lengthString;
|
||||||
var rangeString = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}";
|
var rangeString = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}";
|
||||||
Headers[HeaderNames.ContentRange] = rangeString;
|
Headers[HeaderNames.ContentRange] = rangeString;
|
||||||
|
|
||||||
Logger.LogInformation("Setting range response values for {0}. RangeRequest: {1} Content-Range: {2}", Path, RangeHeader, rangeString);
|
Logger.LogInformation("Setting range response values for {0}. RangeRequest: {1} Content-Length: {2}, Content-Range: {3}", Path, RangeHeader, lengthString, rangeString);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -638,6 +638,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
private static Task Write(IResponse response, string text)
|
private static Task Write(IResponse response, string text)
|
||||||
{
|
{
|
||||||
var bOutput = Encoding.UTF8.GetBytes(text);
|
var bOutput = Encoding.UTF8.GetBytes(text);
|
||||||
|
response.OriginalResponse.ContentLength = bOutput.Length;
|
||||||
return response.OutputStream.WriteAsync(bOutput, 0, bOutput.Length);
|
return response.OutputStream.WriteAsync(bOutput, 0, bOutput.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -132,7 +132,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
content = Array.Empty<byte>();
|
content = Array.Empty<byte>();
|
||||||
}
|
}
|
||||||
|
|
||||||
result = new StreamWriter(content, contentType);
|
result = new StreamWriter(content, contentType, contentLength);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -176,7 +176,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
bytes = Array.Empty<byte>();
|
bytes = Array.Empty<byte>();
|
||||||
}
|
}
|
||||||
|
|
||||||
result = new StreamWriter(bytes, contentType);
|
result = new StreamWriter(bytes, contentType, contentLength);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -335,13 +335,13 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
|
|
||||||
if (isHeadRequest)
|
if (isHeadRequest)
|
||||||
{
|
{
|
||||||
var result = new StreamWriter(Array.Empty<byte>(), contentType);
|
var result = new StreamWriter(Array.Empty<byte>(), contentType, contentLength);
|
||||||
AddResponseHeaders(result, responseHeaders);
|
AddResponseHeaders(result, responseHeaders);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var result = new StreamWriter(content, contentType);
|
var result = new StreamWriter(content, contentType, contentLength);
|
||||||
AddResponseHeaders(result, responseHeaders);
|
AddResponseHeaders(result, responseHeaders);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -581,6 +581,11 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (totalContentLength.HasValue)
|
||||||
|
{
|
||||||
|
responseHeaders["Content-Length"] = totalContentLength.Value.ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
if (isHeadRequest)
|
if (isHeadRequest)
|
||||||
{
|
{
|
||||||
using (stream)
|
using (stream)
|
||||||
|
|
|
@ -96,6 +96,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
RangeStart = requestedRange.Key;
|
RangeStart = requestedRange.Key;
|
||||||
RangeLength = 1 + RangeEnd - RangeStart;
|
RangeLength = 1 + RangeEnd - RangeStart;
|
||||||
|
|
||||||
|
Headers[HeaderNames.ContentLength] = RangeLength.ToString(CultureInfo.InvariantCulture);
|
||||||
Headers[HeaderNames.ContentRange] = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}";
|
Headers[HeaderNames.ContentRange] = $"bytes {RangeStart}-{RangeEnd}/{TotalContentLength}";
|
||||||
|
|
||||||
if (RangeStart > 0 && SourceStream.CanSeek)
|
if (RangeStart > 0 && SourceStream.CanSeek)
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
public void FilterResponse(IRequest req, IResponse res, object dto)
|
public void FilterResponse(IRequest req, IResponse res, object dto)
|
||||||
{
|
{
|
||||||
// Try to prevent compatibility view
|
// Try to prevent compatibility view
|
||||||
res.AddHeader("Access-Control-Allow-Headers", "Accept, Accept-Language, Authorization, Cache-Control, Content-Disposition, Content-Encoding, Content-Language, Content-MD5, Content-Range, Content-Type, Date, Host, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, Origin, OriginToken, Pragma, Range, Slug, Transfer-Encoding, Want-Digest, X-MediaBrowser-Token, X-Emby-Authorization");
|
res.AddHeader("Access-Control-Allow-Headers", "Accept, Accept-Language, Authorization, Cache-Control, Content-Disposition, Content-Encoding, Content-Language, Content-Length, Content-MD5, Content-Range, Content-Type, Date, Host, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, Origin, OriginToken, Pragma, Range, Slug, Transfer-Encoding, Want-Digest, X-MediaBrowser-Token, X-Emby-Authorization");
|
||||||
res.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
|
res.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
|
||||||
res.AddHeader("Access-Control-Allow-Origin", "*");
|
res.AddHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
|
||||||
|
@ -58,6 +58,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
|
|
||||||
if (length > 0)
|
if (length > 0)
|
||||||
{
|
{
|
||||||
|
res.OriginalResponse.ContentLength = length;
|
||||||
res.SendChunked = false;
|
res.SendChunked = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
@ -49,6 +50,13 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
|
|
||||||
SourceStream = source;
|
SourceStream = source;
|
||||||
|
|
||||||
|
Headers["Content-Type"] = contentType;
|
||||||
|
|
||||||
|
if (source.CanSeek)
|
||||||
|
{
|
||||||
|
Headers[HeaderNames.ContentLength] = source.Length.ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
Headers[HeaderNames.ContentType] = contentType;
|
Headers[HeaderNames.ContentType] = contentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +65,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="source">The source.</param>
|
/// <param name="source">The source.</param>
|
||||||
/// <param name="contentType">Type of the content.</param>
|
/// <param name="contentType">Type of the content.</param>
|
||||||
public StreamWriter(byte[] source, string contentType)
|
public StreamWriter(byte[] source, string contentType, int contentLength)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(contentType))
|
if (string.IsNullOrEmpty(contentType))
|
||||||
{
|
{
|
||||||
|
@ -66,6 +74,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
|
|
||||||
SourceBytes = source;
|
SourceBytes = source;
|
||||||
|
|
||||||
|
Headers[HeaderNames.ContentLength] = contentLength.ToString(CultureInfo.InvariantCulture);
|
||||||
Headers[HeaderNames.ContentType] = contentType;
|
Headers[HeaderNames.ContentType] = contentType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,11 @@ namespace Emby.Server.Implementations.Services
|
||||||
{
|
{
|
||||||
var contentLength = bytesResponse.Length;
|
var contentLength = bytesResponse.Length;
|
||||||
|
|
||||||
|
if (response != null)
|
||||||
|
{
|
||||||
|
response.OriginalResponse.ContentLength = contentLength;
|
||||||
|
}
|
||||||
|
|
||||||
if (contentLength > 0)
|
if (contentLength > 0)
|
||||||
{
|
{
|
||||||
await responseStream.WriteAsync(bytesResponse, 0, contentLength, cancellationToken).ConfigureAwait(false);
|
await responseStream.WriteAsync(bytesResponse, 0, contentLength, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
@ -20,6 +21,8 @@ namespace Emby.Server.Implementations.Services
|
||||||
{
|
{
|
||||||
response.StatusCode = (int)HttpStatusCode.NoContent;
|
response.StatusCode = (int)HttpStatusCode.NoContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
response.OriginalResponse.ContentLength = 0;
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,11 +42,6 @@ namespace Emby.Server.Implementations.Services
|
||||||
|
|
||||||
response.StatusCode = httpResult.Status;
|
response.StatusCode = httpResult.Status;
|
||||||
response.StatusDescription = httpResult.StatusCode.ToString();
|
response.StatusDescription = httpResult.StatusCode.ToString();
|
||||||
//if (string.IsNullOrEmpty(httpResult.ContentType))
|
|
||||||
//{
|
|
||||||
// httpResult.ContentType = defaultContentType;
|
|
||||||
//}
|
|
||||||
//response.ContentType = httpResult.ContentType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var responseOptions = result as IHasHeaders;
|
var responseOptions = result as IHasHeaders;
|
||||||
|
@ -53,6 +51,7 @@ namespace Emby.Server.Implementations.Services
|
||||||
{
|
{
|
||||||
if (string.Equals(responseHeaders.Key, "Content-Length", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(responseHeaders.Key, "Content-Length", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
response.OriginalResponse.ContentLength = long.Parse(responseHeaders.Value, CultureInfo.InvariantCulture);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,51 +71,36 @@ namespace Emby.Server.Implementations.Services
|
||||||
response.ContentType += "; charset=utf-8";
|
response.ContentType += "; charset=utf-8";
|
||||||
}
|
}
|
||||||
|
|
||||||
var asyncStreamWriter = result as IAsyncStreamWriter;
|
switch (result)
|
||||||
if (asyncStreamWriter != null)
|
|
||||||
{
|
{
|
||||||
|
case IAsyncStreamWriter asyncStreamWriter:
|
||||||
return asyncStreamWriter.WriteToAsync(response.OutputStream, cancellationToken);
|
return asyncStreamWriter.WriteToAsync(response.OutputStream, cancellationToken);
|
||||||
}
|
case IStreamWriter streamWriter:
|
||||||
|
|
||||||
var streamWriter = result as IStreamWriter;
|
|
||||||
if (streamWriter != null)
|
|
||||||
{
|
|
||||||
streamWriter.WriteTo(response.OutputStream);
|
streamWriter.WriteTo(response.OutputStream);
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
case FileWriter fileWriter:
|
||||||
|
|
||||||
var fileWriter = result as FileWriter;
|
|
||||||
if (fileWriter != null)
|
|
||||||
{
|
|
||||||
return fileWriter.WriteToAsync(response, cancellationToken);
|
return fileWriter.WriteToAsync(response, cancellationToken);
|
||||||
}
|
case Stream stream:
|
||||||
|
|
||||||
var stream = result as Stream;
|
|
||||||
if (stream != null)
|
|
||||||
{
|
|
||||||
return CopyStream(stream, response.OutputStream);
|
return CopyStream(stream, response.OutputStream);
|
||||||
}
|
case byte[] bytes:
|
||||||
|
|
||||||
var bytes = result as byte[];
|
|
||||||
if (bytes != null)
|
|
||||||
{
|
|
||||||
response.ContentType = "application/octet-stream";
|
response.ContentType = "application/octet-stream";
|
||||||
|
response.OriginalResponse.ContentLength = bytes.Length;
|
||||||
|
|
||||||
if (bytes.Length > 0)
|
if (bytes.Length > 0)
|
||||||
{
|
{
|
||||||
return response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
|
return response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
|
case string responseText:
|
||||||
|
var responseTextAsBytes = Encoding.UTF8.GetBytes(responseText);
|
||||||
|
response.OriginalResponse.ContentLength = responseTextAsBytes.Length;
|
||||||
|
|
||||||
|
if (responseTextAsBytes.Length > 0)
|
||||||
|
{
|
||||||
|
return response.OutputStream.WriteAsync(responseTextAsBytes, 0, responseTextAsBytes.Length, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
var responseText = result as string;
|
|
||||||
if (responseText != null)
|
|
||||||
{
|
|
||||||
bytes = Encoding.UTF8.GetBytes(responseText);
|
|
||||||
if (bytes.Length > 0)
|
|
||||||
{
|
|
||||||
return response.OutputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken);
|
|
||||||
}
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,14 +127,13 @@ namespace Emby.Server.Implementations.Services
|
||||||
ms.Position = 0;
|
ms.Position = 0;
|
||||||
|
|
||||||
var contentLength = ms.Length;
|
var contentLength = ms.Length;
|
||||||
|
response.OriginalResponse.ContentLength = contentLength;
|
||||||
|
|
||||||
if (contentLength > 0)
|
if (contentLength > 0)
|
||||||
{
|
{
|
||||||
await ms.CopyToAsync(response.OutputStream).ConfigureAwait(false);
|
await ms.CopyToAsync(response.OutputStream).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//serializer(result, outputStream);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
@ -13,6 +14,7 @@ using MediaBrowser.Controller.Net;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.MediaInfo;
|
using MediaBrowser.Model.MediaInfo;
|
||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Model.Serialization;
|
||||||
|
using MediaBrowser.Model.Services;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
namespace MediaBrowser.Api.Playback.Progressive
|
namespace MediaBrowser.Api.Playback.Progressive
|
||||||
|
@ -279,10 +281,9 @@ namespace MediaBrowser.Api.Playback.Progressive
|
||||||
/// <returns>Task{System.Object}.</returns>
|
/// <returns>Task{System.Object}.</returns>
|
||||||
private async Task<object> GetStaticRemoteStreamResult(StreamState state, Dictionary<string, string> responseHeaders, bool isHeadRequest, CancellationTokenSource cancellationTokenSource)
|
private async Task<object> GetStaticRemoteStreamResult(StreamState state, Dictionary<string, string> responseHeaders, bool isHeadRequest, CancellationTokenSource cancellationTokenSource)
|
||||||
{
|
{
|
||||||
string useragent = null;
|
state.RemoteHttpHeaders.TryGetValue(HeaderNames.UserAgent, out var useragent);
|
||||||
state.RemoteHttpHeaders.TryGetValue("User-Agent", out useragent);
|
|
||||||
|
|
||||||
var trySupportSeek = false;
|
const bool trySupportSeek = false;
|
||||||
|
|
||||||
var options = new HttpRequestOptions
|
var options = new HttpRequestOptions
|
||||||
{
|
{
|
||||||
|
@ -317,6 +318,12 @@ namespace MediaBrowser.Api.Playback.Progressive
|
||||||
responseHeaders[HeaderNames.AcceptRanges] = "none";
|
responseHeaders[HeaderNames.AcceptRanges] = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Seeing cases of -1 here
|
||||||
|
if (response.ContentLength.HasValue && response.ContentLength.Value >= 0)
|
||||||
|
{
|
||||||
|
responseHeaders[HeaderNames.ContentLength] = response.ContentLength.Value.ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
if (isHeadRequest)
|
if (isHeadRequest)
|
||||||
{
|
{
|
||||||
using (response)
|
using (response)
|
||||||
|
@ -356,10 +363,31 @@ namespace MediaBrowser.Api.Playback.Progressive
|
||||||
var contentType = state.GetMimeType(outputPath);
|
var contentType = state.GetMimeType(outputPath);
|
||||||
|
|
||||||
// TODO: The isHeadRequest is only here because ServiceStack will add Content-Length=0 to the response
|
// TODO: The isHeadRequest is only here because ServiceStack will add Content-Length=0 to the response
|
||||||
|
var contentLength = state.EstimateContentLength || isHeadRequest ? GetEstimatedContentLength(state) : null;
|
||||||
|
|
||||||
|
if (contentLength.HasValue)
|
||||||
|
{
|
||||||
|
responseHeaders[HeaderNames.ContentLength] = contentLength.Value.ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
// Headers only
|
// Headers only
|
||||||
if (isHeadRequest)
|
if (isHeadRequest)
|
||||||
{
|
{
|
||||||
return ResultFactory.GetResult(null, Array.Empty<byte>(), contentType, responseHeaders);
|
var streamResult = ResultFactory.GetResult(null, Array.Empty<byte>(), contentType, responseHeaders);
|
||||||
|
|
||||||
|
if (streamResult is IHasHeaders hasHeaders)
|
||||||
|
{
|
||||||
|
if (contentLength.HasValue)
|
||||||
|
{
|
||||||
|
hasHeaders.Headers[HeaderNames.ContentLength] = contentLength.Value.ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hasHeaders.Headers.Remove(HeaderNames.ContentLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return streamResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(outputPath);
|
var transcodingLock = ApiEntryPoint.Instance.GetTranscodingLock(outputPath);
|
||||||
|
@ -397,5 +425,22 @@ namespace MediaBrowser.Api.Playback.Progressive
|
||||||
transcodingLock.Release();
|
transcodingLock.Release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the length of the estimated content.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="state">The state.</param>
|
||||||
|
/// <returns>System.Nullable{System.Int64}.</returns>
|
||||||
|
private long? GetEstimatedContentLength(StreamState state)
|
||||||
|
{
|
||||||
|
var totalBitrate = state.TotalOutputBitrate ?? 0;
|
||||||
|
|
||||||
|
if (totalBitrate > 0 && state.RunTimeTicks.HasValue)
|
||||||
|
{
|
||||||
|
return Convert.ToInt64(totalBitrate * TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalSeconds / 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user