aboutsummaryrefslogtreecommitdiff
path: root/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
diff options
context:
space:
mode:
authorstefan <stefan@hegedues.at>2018-09-12 19:26:21 +0200
committerstefan <stefan@hegedues.at>2018-09-12 19:26:21 +0200
commit48facb797ed912e4ea6b04b17d1ff190ac2daac4 (patch)
tree8dae77a31670a888d733484cb17dd4077d5444e8 /Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
parentc32d8656382a0eacb301692e0084377fc433ae9b (diff)
Update to 3.5.2 and .net core 2.1
Diffstat (limited to 'Emby.Server.Implementations/HttpServer/HttpResultFactory.cs')
-rw-r--r--Emby.Server.Implementations/HttpServer/HttpResultFactory.cs452
1 files changed, 273 insertions, 179 deletions
diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
index 86deccee1..df493b4c3 100644
--- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
+++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs
@@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
+using System.IO.Compression;
using System.Net;
using System.Runtime.Serialization;
using System.Text;
@@ -30,16 +31,17 @@ namespace Emby.Server.Implementations.HttpServer
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem;
private readonly IJsonSerializer _jsonSerializer;
- private readonly IMemoryStreamFactory _memoryStreamFactory;
+
+ private IBrotliCompressor _brotliCompressor;
/// <summary>
/// Initializes a new instance of the <see cref="HttpResultFactory" /> class.
/// </summary>
- public HttpResultFactory(ILogManager logManager, IFileSystem fileSystem, IJsonSerializer jsonSerializer, IMemoryStreamFactory memoryStreamFactory)
+ public HttpResultFactory(ILogManager logManager, IFileSystem fileSystem, IJsonSerializer jsonSerializer, IBrotliCompressor brotliCompressor)
{
_fileSystem = fileSystem;
_jsonSerializer = jsonSerializer;
- _memoryStreamFactory = memoryStreamFactory;
+ _brotliCompressor = brotliCompressor;
_logger = logManager.GetLogger("HttpResultFactory");
}
@@ -50,9 +52,24 @@ namespace Emby.Server.Implementations.HttpServer
/// <param name="contentType">Type of the content.</param>
/// <param name="responseHeaders">The response headers.</param>
/// <returns>System.Object.</returns>
- public object GetResult(object content, string contentType, IDictionary<string, string> responseHeaders = null)
+ public object GetResult(IRequest requestContext, byte[] content, string contentType, IDictionary<string, string> responseHeaders = null)
+ {
+ return GetHttpResult(requestContext, content, contentType, true, responseHeaders);
+ }
+
+ public object GetResult(string content, string contentType, IDictionary<string, string> responseHeaders = null)
{
- return GetHttpResult(content, contentType, true, responseHeaders);
+ return GetHttpResult(null, content, contentType, true, responseHeaders);
+ }
+
+ public object GetResult(IRequest requestContext, Stream content, string contentType, IDictionary<string, string> responseHeaders = null)
+ {
+ return GetHttpResult(requestContext, content, contentType, true, responseHeaders);
+ }
+
+ public object GetResult(IRequest requestContext, string content, string contentType, IDictionary<string, string> responseHeaders = null)
+ {
+ return GetHttpResult(requestContext, content, contentType, true, responseHeaders);
}
public object GetRedirectResult(string url)
@@ -60,7 +77,7 @@ namespace Emby.Server.Implementations.HttpServer
var responseHeaders = new Dictionary<string, string>();
responseHeaders["Location"] = url;
- var result = new HttpResult(new byte[] { }, "text/plain", HttpStatusCode.Redirect);
+ var result = new HttpResult(Array.Empty<byte>(), "text/plain", HttpStatusCode.Redirect);
AddResponseHeaders(result, responseHeaders);
@@ -70,39 +87,98 @@ namespace Emby.Server.Implementations.HttpServer
/// <summary>
/// Gets the HTTP result.
/// </summary>
- private IHasHeaders GetHttpResult(object content, string contentType, bool addCachePrevention, IDictionary<string, string> responseHeaders = null)
+ private IHasHeaders GetHttpResult(IRequest requestContext, Stream content, string contentType, bool addCachePrevention, IDictionary<string, string> responseHeaders = null)
{
- IHasHeaders result;
+ var result = new StreamWriter(content, contentType, _logger);
- var stream = content as Stream;
+ if (responseHeaders == null)
+ {
+ responseHeaders = new Dictionary<string, string>();
+ }
- if (stream != null)
+ string expires;
+ if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out expires))
{
- result = new StreamWriter(stream, contentType, _logger);
+ responseHeaders["Expires"] = "-1";
}
- else
+ AddResponseHeaders(result, responseHeaders);
+
+ return result;
+ }
+
+ /// <summary>
+ /// Gets the HTTP result.
+ /// </summary>
+ private IHasHeaders GetHttpResult(IRequest requestContext, byte[] content, string contentType, bool addCachePrevention, IDictionary<string, string> responseHeaders = null)
+ {
+ IHasHeaders result;
+
+ var compressionType = requestContext == null ? null : GetCompressionType(requestContext, content, contentType);
+
+ var isHeadRequest = string.Equals(requestContext.Verb, "head", StringComparison.OrdinalIgnoreCase);
+
+ if (string.IsNullOrEmpty(compressionType))
{
- var bytes = content as byte[];
+ var contentLength = content.Length;
- if (bytes != null)
+ if (isHeadRequest)
{
- result = new StreamWriter(bytes, contentType, _logger);
+ content = Array.Empty<byte>();
}
- else
- {
- var text = content as string;
- if (text != null)
- {
- result = new StreamWriter(Encoding.UTF8.GetBytes(text), contentType, _logger);
- }
- else
- {
- result = new HttpResult(content, contentType, HttpStatusCode.OK);
- }
+ result = new StreamWriter(content, contentType, contentLength, _logger);
+ }
+ else
+ {
+ result = GetCompressedResult(content, compressionType, responseHeaders, isHeadRequest, contentType);
+ }
+
+ if (responseHeaders == null)
+ {
+ responseHeaders = new Dictionary<string, string>();
+ }
+
+ string expires;
+ if (addCachePrevention && !responseHeaders.TryGetValue("Expires", out expires))
+ {
+ responseHeaders["Expires"] = "-1";
+ }
+
+ AddResponseHeaders(result, responseHeaders);
+
+ return result;
+ }
+
+ /// <summary>
+ /// Gets the HTTP result.
+ /// </summary>
+ private IHasHeaders GetHttpResult(IRequest requestContext, string content, string contentType, bool addCachePrevention, IDictionary<string, string> responseHeaders = null)
+ {
+ IHasHeaders result;
+
+ var bytes = Encoding.UTF8.GetBytes(content);
+
+ var compressionType = requestContext == null ? null : GetCompressionType(requestContext, bytes, contentType);
+
+ var isHeadRequest = requestContext == null ? false : string.Equals(requestContext.Verb, "head", StringComparison.OrdinalIgnoreCase);
+
+ if (string.IsNullOrEmpty(compressionType))
+ {
+ var contentLength = bytes.Length;
+
+ if (isHeadRequest)
+ {
+ bytes = Array.Empty<byte>();
}
+
+ result = new StreamWriter(bytes, contentType, contentLength, _logger);
}
+ else
+ {
+ result = GetCompressedResult(bytes, compressionType, responseHeaders, isHeadRequest, contentType);
+ }
+
if (responseHeaders == null)
{
responseHeaders = new Dictionary<string, string>();
@@ -123,20 +199,9 @@ namespace Emby.Server.Implementations.HttpServer
/// Gets the optimized result.
/// </summary>
/// <typeparam name="T"></typeparam>
- /// <param name="requestContext">The request context.</param>
- /// <param name="result">The result.</param>
- /// <param name="responseHeaders">The response headers.</param>
- /// <returns>System.Object.</returns>
- /// <exception cref="System.ArgumentNullException">result</exception>
- public object GetOptimizedResult<T>(IRequest requestContext, T result, IDictionary<string, string> responseHeaders = null)
+ public object GetResult<T>(IRequest requestContext, T result, IDictionary<string, string> responseHeaders = null)
where T : class
{
- return GetOptimizedResultInternal<T>(requestContext, result, true, responseHeaders);
- }
-
- private object GetOptimizedResultInternal<T>(IRequest requestContext, T result, bool addCachePrevention, IDictionary<string, string> responseHeaders = null)
- where T : class
- {
if (result == null)
{
throw new ArgumentNullException("result");
@@ -147,24 +212,49 @@ namespace Emby.Server.Implementations.HttpServer
responseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
- if (addCachePrevention)
+ responseHeaders["Expires"] = "-1";
+
+ return ToOptimizedResultInternal(requestContext, result, responseHeaders);
+ }
+
+ private string GetCompressionType(IRequest request, byte[] content, string responseContentType)
+ {
+ if (responseContentType == null)
{
- responseHeaders["Expires"] = "-1";
+ return null;
}
- return ToOptimizedResultInternal(requestContext, result, responseHeaders);
+ // Per apple docs, hls manifests must be compressed
+ if (!responseContentType.StartsWith("text/", StringComparison.OrdinalIgnoreCase) &&
+ responseContentType.IndexOf("json", StringComparison.OrdinalIgnoreCase) == -1 &&
+ responseContentType.IndexOf("javascript", StringComparison.OrdinalIgnoreCase) == -1 &&
+ responseContentType.IndexOf("xml", StringComparison.OrdinalIgnoreCase) == -1 &&
+ responseContentType.IndexOf("application/x-mpegURL", StringComparison.OrdinalIgnoreCase) == -1)
+ {
+ return null;
+ }
+
+ if (content.Length < 1024)
+ {
+ return null;
+ }
+
+ return GetCompressionType(request);
}
- public static string GetCompressionType(IRequest request)
+ private string GetCompressionType(IRequest request)
{
var acceptEncoding = request.Headers["Accept-Encoding"];
- if (!string.IsNullOrWhiteSpace(acceptEncoding))
+ if (acceptEncoding != null)
{
- if (acceptEncoding.Contains("deflate"))
+ //if (_brotliCompressor != null && acceptEncoding.IndexOf("br", StringComparison.OrdinalIgnoreCase) != -1)
+ // return "br";
+
+ if (acceptEncoding.IndexOf("deflate", StringComparison.OrdinalIgnoreCase) != -1)
return "deflate";
- if (acceptEncoding.Contains("gzip"))
+ if (acceptEncoding.IndexOf("gzip", StringComparison.OrdinalIgnoreCase) != -1)
return "gzip";
}
@@ -180,7 +270,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <returns></returns>
public object ToOptimizedResult<T>(IRequest request, T dto)
{
- return ToOptimizedResultInternal(request, dto, null);
+ return ToOptimizedResultInternal(request, dto);
}
private object ToOptimizedResultInternal<T>(IRequest request, T dto, IDictionary<string, string> responseHeaders = null)
@@ -192,153 +282,137 @@ namespace Emby.Server.Implementations.HttpServer
case "application/xml":
case "text/xml":
case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml
- return GetHttpResult(SerializeToXmlString(dto), contentType, false, responseHeaders);
+ return GetHttpResult(request, SerializeToXmlString(dto), contentType, false, responseHeaders);
case "application/json":
case "text/json":
- return GetHttpResult(_jsonSerializer.SerializeToString(dto), contentType, false, responseHeaders);
+ return GetHttpResult(request, _jsonSerializer.SerializeToString(dto), contentType, false, responseHeaders);
default:
- {
- var ms = new MemoryStream();
- var writerFn = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType);
-
- writerFn(dto, ms);
+ break;
+ }
- ms.Position = 0;
+ var isHeadRequest = string.Equals(request.Verb, "head", StringComparison.OrdinalIgnoreCase);
- if (string.Equals(request.Verb, "head", StringComparison.OrdinalIgnoreCase))
- {
- return GetHttpResult(new byte[] { }, contentType, true, responseHeaders);
- }
+ var ms = new MemoryStream();
+ var writerFn = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType);
- return GetHttpResult(ms, contentType, true, responseHeaders);
- }
- }
- }
+ writerFn(dto, ms);
- public static string GetRealContentType(string contentType)
- {
- return contentType == null
- ? null
- : contentType.Split(';')[0].ToLower().Trim();
- }
+ ms.Position = 0;
- private string SerializeToXmlString(object from)
- {
- using (var ms = new MemoryStream())
+ if (isHeadRequest)
{
- var xwSettings = new XmlWriterSettings();
- xwSettings.Encoding = new UTF8Encoding(false);
- xwSettings.OmitXmlDeclaration = false;
-
- using (var xw = XmlWriter.Create(ms, xwSettings))
+ using (ms)
{
- var serializer = new DataContractSerializer(from.GetType());
- serializer.WriteObject(xw, from);
- xw.Flush();
- ms.Seek(0, SeekOrigin.Begin);
- var reader = new StreamReader(ms);
- return reader.ReadToEnd();
+ return GetHttpResult(request, Array.Empty<byte>(), contentType, true, responseHeaders);
}
}
+
+ return GetHttpResult(request, ms, contentType, true, responseHeaders);
}
- /// <summary>
- /// Gets the optimized result using cache.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="requestContext">The request context.</param>
- /// <param name="cacheKey">The cache key.</param>
- /// <param name="lastDateModified">The last date modified.</param>
- /// <param name="cacheDuration">Duration of the cache.</param>
- /// <param name="factoryFn">The factory fn.</param>
- /// <param name="responseHeaders">The response headers.</param>
- /// <returns>System.Object.</returns>
- /// <exception cref="System.ArgumentNullException">cacheKey
- /// or
- /// factoryFn</exception>
- public object GetOptimizedResultUsingCache<T>(IRequest requestContext, Guid cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration, Func<T> factoryFn, IDictionary<string, string> responseHeaders = null)
- where T : class
+ private IHasHeaders GetCompressedResult(byte[] content,
+ string requestedCompressionType,
+ IDictionary<string, string> responseHeaders,
+ bool isHeadRequest,
+ string contentType)
{
- if (cacheKey == Guid.Empty)
- {
- throw new ArgumentNullException("cacheKey");
- }
- if (factoryFn == null)
- {
- throw new ArgumentNullException("factoryFn");
- }
-
- var key = cacheKey.ToString("N");
-
if (responseHeaders == null)
{
responseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
- // See if the result is already cached in the browser
- var result = GetCachedResult(requestContext, responseHeaders, cacheKey, key, lastDateModified, cacheDuration, null);
+ content = Compress(content, requestedCompressionType);
+ responseHeaders["Content-Encoding"] = requestedCompressionType;
- if (result != null)
- {
- return result;
- }
+ responseHeaders["Vary"] = "Accept-Encoding";
- return GetOptimizedResultInternal(requestContext, factoryFn(), false, responseHeaders);
- }
+ var contentLength = content.Length;
- /// <summary>
- /// To the cached result.
- /// </summary>
- /// <typeparam name="T"></typeparam>
- /// <param name="requestContext">The request context.</param>
- /// <param name="cacheKey">The cache key.</param>
- /// <param name="lastDateModified">The last date modified.</param>
- /// <param name="cacheDuration">Duration of the cache.</param>
- /// <param name="factoryFn">The factory fn.</param>
- /// <param name="contentType">Type of the content.</param>
- /// <param name="responseHeaders">The response headers.</param>
- /// <returns>System.Object.</returns>
- /// <exception cref="System.ArgumentNullException">cacheKey</exception>
- public object GetCachedResult<T>(IRequest requestContext, Guid cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration, Func<T> factoryFn, string contentType, IDictionary<string, string> responseHeaders = null)
- where T : class
- {
- if (cacheKey == Guid.Empty)
+ if (isHeadRequest)
{
- throw new ArgumentNullException("cacheKey");
+ var result = new StreamWriter(Array.Empty<byte>(), contentType, contentLength, _logger);
+ AddResponseHeaders(result, responseHeaders);
+ return result;
}
- if (factoryFn == null)
+ else
{
- throw new ArgumentNullException("factoryFn");
+ var result = new StreamWriter(content, contentType, contentLength, _logger);
+ AddResponseHeaders(result, responseHeaders);
+ return result;
}
+ }
- var key = cacheKey.ToString("N");
+ private byte[] Compress(byte[] bytes, string compressionType)
+ {
+ if (string.Equals(compressionType, "br", StringComparison.OrdinalIgnoreCase))
+ return CompressBrotli(bytes);
- if (responseHeaders == null)
- {
- responseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
- }
+ if (string.Equals(compressionType, "deflate", StringComparison.OrdinalIgnoreCase))
+ return Deflate(bytes);
+
+ if (string.Equals(compressionType, "gzip", StringComparison.OrdinalIgnoreCase))
+ return GZip(bytes);
- // See if the result is already cached in the browser
- var result = GetCachedResult(requestContext, responseHeaders, cacheKey, key, lastDateModified, cacheDuration, contentType);
+ throw new NotSupportedException(compressionType);
+ }
+
+ private byte[] CompressBrotli(byte[] bytes)
+ {
+ return _brotliCompressor.Compress(bytes);
+ }
- if (result != null)
+ private byte[] Deflate(byte[] bytes)
+ {
+ // In .NET FX incompat-ville, you can't access compressed bytes without closing DeflateStream
+ // Which means we must use MemoryStream since you have to use ToArray() on a closed Stream
+ using (var ms = new MemoryStream())
+ using (var zipStream = new DeflateStream(ms, CompressionMode.Compress))
{
- return result;
+ zipStream.Write(bytes, 0, bytes.Length);
+ zipStream.Dispose();
+
+ return ms.ToArray();
}
+ }
- result = factoryFn();
+ private byte[] GZip(byte[] buffer)
+ {
+ using (var ms = new MemoryStream())
+ using (var zipStream = new GZipStream(ms, CompressionMode.Compress))
+ {
+ zipStream.Write(buffer, 0, buffer.Length);
+ zipStream.Dispose();
- // Apply caching headers
- var hasHeaders = result as IHasHeaders;
+ return ms.ToArray();
+ }
+ }
- if (hasHeaders != null)
+ public static string GetRealContentType(string contentType)
+ {
+ return contentType == null
+ ? null
+ : contentType.Split(';')[0].ToLower().Trim();
+ }
+
+ private string SerializeToXmlString(object from)
+ {
+ using (var ms = new MemoryStream())
{
- AddResponseHeaders(hasHeaders, responseHeaders);
- return hasHeaders;
- }
+ var xwSettings = new XmlWriterSettings();
+ xwSettings.Encoding = new UTF8Encoding(false);
+ xwSettings.OmitXmlDeclaration = false;
- return GetHttpResult(result, contentType, false, responseHeaders);
+ using (var xw = XmlWriter.Create(ms, xwSettings))
+ {
+ var serializer = new DataContractSerializer(from.GetType());
+ serializer.WriteObject(xw, from);
+ xw.Flush();
+ ms.Seek(0, SeekOrigin.Begin);
+ var reader = new StreamReader(ms);
+ return reader.ReadToEnd();
+ }
+ }
}
/// <summary>
@@ -357,7 +431,7 @@ namespace Emby.Server.Implementations.HttpServer
AddAgeHeader(responseHeaders, lastDateModified);
AddExpiresHeader(responseHeaders, cacheKeyString, cacheDuration);
- var result = new HttpResult(new byte[] { }, contentType ?? "text/html", HttpStatusCode.NotModified);
+ var result = new HttpResult(Array.Empty<byte>(), contentType ?? "text/html", HttpStatusCode.NotModified);
AddResponseHeaders(result, responseHeaders);
@@ -402,7 +476,7 @@ namespace Emby.Server.Implementations.HttpServer
throw new ArgumentException("FileShare must be either Read or ReadWrite");
}
- if (string.IsNullOrWhiteSpace(options.ContentType))
+ if (string.IsNullOrEmpty(options.ContentType))
{
options.ContentType = MimeTypes.GetMimeType(path);
}
@@ -460,19 +534,17 @@ namespace Emby.Server.Implementations.HttpServer
options.ResponseHeaders = options.ResponseHeaders ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
var contentType = options.ContentType;
- if (cacheKey == Guid.Empty)
+ if (!cacheKey.Equals(Guid.Empty))
{
- throw new ArgumentNullException("cacheKey");
- }
+ var key = cacheKey.ToString("N");
- var key = cacheKey.ToString("N");
+ // See if the result is already cached in the browser
+ var result = GetCachedResult(requestContext, options.ResponseHeaders, cacheKey, key, options.DateLastModified, options.CacheDuration, contentType);
- // See if the result is already cached in the browser
- var result = GetCachedResult(requestContext, options.ResponseHeaders, cacheKey, key, options.DateLastModified, options.CacheDuration, contentType);
-
- if (result != null)
- {
- return result;
+ if (result != null)
+ {
+ return result;
+ }
}
// TODO: We don't really need the option value
@@ -484,7 +556,7 @@ namespace Emby.Server.Implementations.HttpServer
var rangeHeader = requestContext.Headers.Get("Range");
- if (!isHeadRequest && !string.IsNullOrWhiteSpace(options.Path))
+ if (!isHeadRequest && !string.IsNullOrEmpty(options.Path))
{
var hasHeaders = new FileWriter(options.Path, contentType, rangeHeader, _logger, _fileSystem)
{
@@ -497,11 +569,24 @@ namespace Emby.Server.Implementations.HttpServer
return hasHeaders;
}
- if (!string.IsNullOrWhiteSpace(rangeHeader))
+ var stream = await factoryFn().ConfigureAwait(false);
+
+ var totalContentLength = options.ContentLength;
+ if (!totalContentLength.HasValue)
{
- var stream = await factoryFn().ConfigureAwait(false);
+ try
+ {
+ totalContentLength = stream.Length;
+ }
+ catch (NotSupportedException)
+ {
- var hasHeaders = new RangeRequestWriter(rangeHeader, stream, contentType, isHeadRequest, _logger)
+ }
+ }
+
+ if (!string.IsNullOrWhiteSpace(rangeHeader) && totalContentLength.HasValue)
+ {
+ var hasHeaders = new RangeRequestWriter(rangeHeader, totalContentLength.Value, stream, contentType, isHeadRequest, _logger)
{
OnComplete = options.OnComplete
};
@@ -511,15 +596,17 @@ namespace Emby.Server.Implementations.HttpServer
}
else
{
- var stream = await factoryFn().ConfigureAwait(false);
-
- responseHeaders["Content-Length"] = stream.Length.ToString(UsCulture);
+ if (totalContentLength.HasValue)
+ {
+ responseHeaders["Content-Length"] = totalContentLength.Value.ToString(UsCulture);
+ }
if (isHeadRequest)
{
- stream.Dispose();
-
- return GetHttpResult(new byte[] { }, contentType, true, responseHeaders);
+ using (stream)
+ {
+ return GetHttpResult(requestContext, Array.Empty<byte>(), contentType, true, responseHeaders);
+ }
}
var hasHeaders = new StreamWriter(stream, contentType, _logger)
@@ -603,7 +690,7 @@ namespace Emby.Server.Implementations.HttpServer
/// <param name="lastDateModified">The last date modified.</param>
/// <param name="cacheDuration">Duration of the cache.</param>
/// <returns><c>true</c> if [is not modified] [the specified cache key]; otherwise, <c>false</c>.</returns>
- private bool IsNotModified(IRequest requestContext, Guid? cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration)
+ private bool IsNotModified(IRequest requestContext, Guid cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration)
{
//var isNotModified = true;
@@ -624,8 +711,10 @@ namespace Emby.Server.Implementations.HttpServer
var ifNoneMatchHeader = requestContext.Headers.Get("If-None-Match");
+ var hasCacheKey = !cacheKey.Equals(Guid.Empty);
+
// Validate If-None-Match
- if ((cacheKey.HasValue || !string.IsNullOrEmpty(ifNoneMatchHeader)))
+ if ((hasCacheKey || !string.IsNullOrEmpty(ifNoneMatchHeader)))
{
Guid ifNoneMatch;
@@ -633,7 +722,7 @@ namespace Emby.Server.Implementations.HttpServer
if (Guid.TryParse(ifNoneMatchHeader, out ifNoneMatch))
{
- if (cacheKey.HasValue && cacheKey.Value == ifNoneMatch)
+ if (hasCacheKey && cacheKey.Equals(ifNoneMatch))
{
return true;
}
@@ -697,4 +786,9 @@ namespace Emby.Server.Implementations.HttpServer
}
}
}
+
+ public interface IBrotliCompressor
+ {
+ byte[] Compress(byte[] content);
+ }
} \ No newline at end of file