using System.Net;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
using MediaBrowser.Model.Logging;
using ServiceStack.Common;
using ServiceStack.Common.Web;
using ServiceStack.ServiceHost;
using ServiceStack.ServiceInterface;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using MimeTypes = MediaBrowser.Common.Net.MimeTypes;
namespace MediaBrowser.Server.Implementations.HttpServer
{
///
/// Class BaseRestService
///
public class BaseRestService : Service, IRestfulService
{
///
/// Gets or sets the logger.
///
/// The logger.
public ILogger Logger { get; set; }
///
/// Gets a value indicating whether this instance is range request.
///
/// true if this instance is range request; otherwise, false.
protected bool IsRangeRequest
{
get
{
return Request.Headers.AllKeys.Contains("Range");
}
}
///
/// To the optimized result.
///
///
/// The result.
/// System.Object.
/// result
protected object ToOptimizedResult(T result)
where T : class
{
if (result == null)
{
throw new ArgumentNullException("result");
}
Response.AddHeader("Vary", "Accept-Encoding");
return RequestContext.ToOptimizedResult(result);
}
///
/// To the optimized result using cache.
///
///
/// The cache key.
/// The last date modified.
/// Duration of the cache.
/// The factory fn.
/// System.Object.
/// cacheKey
protected object ToOptimizedResultUsingCache(Guid cacheKey, DateTime lastDateModified, TimeSpan? cacheDuration, Func factoryFn)
where T : class
{
if (cacheKey == Guid.Empty)
{
throw new ArgumentNullException("cacheKey");
}
if (factoryFn == null)
{
throw new ArgumentNullException("factoryFn");
}
var key = cacheKey.ToString("N");
var result = PreProcessCachedResult(cacheKey, key, lastDateModified, cacheDuration);
if (result != null)
{
// Return null so that service stack won't do anything
return null;
}
return ToOptimizedResult(factoryFn());
}
///
/// To the cached result.
///
///
/// The cache key.
/// The last date modified.
/// Duration of the cache.
/// The factory fn.
/// Type of the content.
/// System.Object.
/// cacheKey
protected object ToCachedResult(Guid cacheKey, DateTime lastDateModified, TimeSpan? cacheDuration, Func factoryFn, string contentType)
where T : class
{
if (cacheKey == Guid.Empty)
{
throw new ArgumentNullException("cacheKey");
}
if (factoryFn == null)
{
throw new ArgumentNullException("factoryFn");
}
Response.ContentType = contentType;
var key = cacheKey.ToString("N");
var result = PreProcessCachedResult(cacheKey, key, lastDateModified, cacheDuration);
if (result != null)
{
// Return null so that service stack won't do anything
return null;
}
return factoryFn();
}
///
/// To the static file result.
///
/// The path.
/// if set to true [headers only].
/// System.Object.
/// path
protected object ToStaticFileResult(string path, bool headersOnly = false)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException("path");
}
var dateModified = File.GetLastWriteTimeUtc(path);
var cacheKey = path + dateModified.Ticks;
return ToStaticResult(cacheKey.GetMD5(), dateModified, null, MimeTypes.GetMimeType(path), () => Task.FromResult(GetFileStream(path)), headersOnly);
}
///
/// Gets the file stream.
///
/// The path.
/// Stream.
private Stream GetFileStream(string path)
{
return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous);
}
///
/// To the static result.
///
/// The cache key.
/// The last date modified.
/// Duration of the cache.
/// Type of the content.
/// The factory fn.
/// if set to true [headers only].
/// System.Object.
/// cacheKey
protected object ToStaticResult(Guid cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration, string contentType, Func> factoryFn, bool headersOnly = false)
{
if (cacheKey == Guid.Empty)
{
throw new ArgumentNullException("cacheKey");
}
if (factoryFn == null)
{
throw new ArgumentNullException("factoryFn");
}
var key = cacheKey.ToString("N");
Response.ContentType = contentType;
var result = PreProcessCachedResult(cacheKey, key, lastDateModified, cacheDuration);
if (result != null)
{
// Return null so that service stack won't do anything
return null;
}
var compress = ShouldCompressResponse(contentType);
if (compress)
{
Response.AddHeader("Vary", "Accept-Encoding");
}
return ToStaticResult(contentType, factoryFn, compress, headersOnly).Result;
}
///
/// Shoulds the compress response.
///
/// Type of the content.
/// true if XXXX, false otherwise
private bool ShouldCompressResponse(string contentType)
{
// It will take some work to support compression with byte range requests
if (IsRangeRequest)
{
return false;
}
// Don't compress media
if (contentType.StartsWith("audio/", StringComparison.OrdinalIgnoreCase) || contentType.StartsWith("video/", StringComparison.OrdinalIgnoreCase))
{
return false;
}
// Don't compress images
if (contentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
{
return false;
}
if (contentType.StartsWith("font/", StringComparison.OrdinalIgnoreCase))
{
return false;
}
if (contentType.StartsWith("application/", StringComparison.OrdinalIgnoreCase))
{
return false;
}
return true;
}
///
/// To the static result.
///
/// Type of the content.
/// The factory fn.
/// if set to true [compress].
/// if set to true [headers only].
/// System.Object.
private async Task