moved logic for reading static file to mappers

This commit is contained in:
kay.one 2013-08-19 23:23:36 -07:00
parent 290e072f2e
commit 9f0ae763ff
20 changed files with 144 additions and 209 deletions

View File

@ -1,32 +0,0 @@
using System;
using Nancy;
using NzbDrone.Api.Extensions;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Api.Frontend
{
public interface IAddCacheHeaders
{
void ToResponse(Request request, Response response);
}
public class AddCacheHeaders : IAddCacheHeaders
{
public void ToResponse(Request request, Response response)
{
if (!RuntimeInfo.IsProduction)
{
response.Headers.DisableCache();
return;
}
if (request.Url.Path.EndsWith("app.js", StringComparison.CurrentCultureIgnoreCase))
{
response.Headers.DisableCache();
return;
}
response.Headers.EnableCache();
}
}
}

View File

@ -1,9 +1,11 @@

using Nancy;
namespace NzbDrone.Api.Frontend.Mappers
{
public interface IMapHttpRequestsToDisk
{
string Map(string resourceUrl);
bool CanHandle(string resourceUrl);
Response GetResponse(string resourceUrl);
}
}

View File

@ -1,25 +1,47 @@
using System.IO;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Api.Frontend.Mappers
{
public class IndexHtmlMapper : IMapHttpRequestsToDisk
public class IndexHtmlMapper : StaticResourceMapperBase
{
private readonly IDiskProvider _diskProvider;
private readonly string _indexPath;
public IndexHtmlMapper(IAppFolderInfo appFolderInfo)
public IndexHtmlMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, Logger logger)
: base(diskProvider, logger)
{
_diskProvider = diskProvider;
_indexPath = Path.Combine(appFolderInfo.StartUpFolder, "UI", "index.html");
}
public string Map(string resourceUrl)
protected override string Map(string resourceUrl)
{
return _indexPath;
}
public bool CanHandle(string resourceUrl)
public override bool CanHandle(string resourceUrl)
{
return !resourceUrl.Contains(".");
}
protected override Stream GetContentStream(string filePath)
{
return StringToStream(GetIndexText());
}
private string GetIndexText()
{
var text = _diskProvider.ReadAllText(_indexPath);
text = text.Replace(".css", ".css?v=" + BuildInfo.Version);
text = text.Replace(".js", ".js?v=" + BuildInfo.Version);
return text;
}
}
}

View File

@ -1,19 +1,21 @@
using System.IO;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Api.Frontend.Mappers
{
public class LogFileMapper : IMapHttpRequestsToDisk
public class LogFileMapper : StaticResourceMapperBase
{
private readonly IAppFolderInfo _appFolderInfo;
public LogFileMapper(IAppFolderInfo appFolderInfo)
public LogFileMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, Logger logger)
: base(diskProvider, logger)
{
_appFolderInfo = appFolderInfo;
}
public string Map(string resourceUrl)
protected override string Map(string resourceUrl)
{
var path = resourceUrl.Replace('/', Path.DirectorySeparatorChar);
path = Path.GetFileName(path);
@ -21,7 +23,7 @@ namespace NzbDrone.Api.Frontend.Mappers
return Path.Combine(_appFolderInfo.GetLogFolder(), path);
}
public bool CanHandle(string resourceUrl)
public override bool CanHandle(string resourceUrl)
{
return resourceUrl.StartsWith("/log") && resourceUrl.EndsWith(".txt");
}

View File

@ -1,19 +1,21 @@
using System.IO;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Api.Frontend.Mappers
{
public class MediaCoverMapper : IMapHttpRequestsToDisk
public class MediaCoverMapper : StaticResourceMapperBase
{
private readonly IAppFolderInfo _appFolderInfo;
public MediaCoverMapper(IAppFolderInfo appFolderInfo)
public MediaCoverMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, Logger logger)
: base(diskProvider, logger)
{
_appFolderInfo = appFolderInfo;
}
public string Map(string resourceUrl)
protected override string Map(string resourceUrl)
{
var path = resourceUrl.Replace('/', Path.DirectorySeparatorChar);
path = path.Trim(Path.DirectorySeparatorChar);
@ -21,7 +23,7 @@ namespace NzbDrone.Api.Frontend.Mappers
return Path.Combine(_appFolderInfo.GetAppDataPath(), path);
}
public bool CanHandle(string resourceUrl)
public override bool CanHandle(string resourceUrl)
{
return resourceUrl.StartsWith("/MediaCover");
}

View File

@ -1,18 +1,21 @@
using System.IO;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Api.Frontend.Mappers
{
public class StaticResourceMapper : IMapHttpRequestsToDisk
public class StaticResourceMapper : StaticResourceMapperBase
{
private readonly IAppFolderInfo _appFolderInfo;
public StaticResourceMapper(IAppFolderInfo appFolderInfo)
public StaticResourceMapper(IAppFolderInfo appFolderInfo, IDiskProvider diskProvider, Logger logger)
: base(diskProvider, logger)
{
_appFolderInfo = appFolderInfo;
}
public string Map(string resourceUrl)
protected override string Map(string resourceUrl)
{
var path = resourceUrl.Replace('/', Path.DirectorySeparatorChar);
path = path.Trim(Path.DirectorySeparatorChar);
@ -20,7 +23,7 @@ namespace NzbDrone.Api.Frontend.Mappers
return Path.Combine(_appFolderInfo.StartUpFolder, "UI", path);
}
public bool CanHandle(string resourceUrl)
public override bool CanHandle(string resourceUrl)
{
return resourceUrl.StartsWith("/Content") || resourceUrl.EndsWith(".js") || resourceUrl.EndsWith(".css");
}

View File

@ -0,0 +1,66 @@
using System.IO;
using NLog;
using Nancy;
using Nancy.Responses;
using NzbDrone.Common;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Api.Frontend.Mappers
{
public abstract class StaticResourceMapperBase : IMapHttpRequestsToDisk
{
private readonly IDiskProvider _diskProvider;
private readonly Logger _logger;
private readonly bool _caseSensitive;
private static readonly NotFoundResponse NotFoundResponse = new NotFoundResponse();
protected StaticResourceMapperBase(IDiskProvider diskProvider, Logger logger)
{
_diskProvider = diskProvider;
_logger = logger;
if (!RuntimeInfo.IsProduction)
{
_caseSensitive = true;
}
}
protected abstract string Map(string resourceUrl);
public abstract bool CanHandle(string resourceUrl);
public Response GetResponse(string resourceUrl)
{
var filePath = Map(resourceUrl);
if (_diskProvider.FileExists(filePath, _caseSensitive))
{
var response = new StreamResponse(() => GetContentStream(filePath), MimeTypes.GetMimeType(filePath));
return response;
}
_logger.Warn("File {0} not found", filePath);
return NotFoundResponse;
}
protected virtual Stream GetContentStream(string filePath)
{
return File.OpenRead(filePath);
}
protected static Stream StringToStream(string text)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.Write(text);
writer.Flush();
stream.Position = 0;
return stream;
}
}
}

View File

@ -1,41 +1,25 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NLog;
using Nancy;
using Nancy.Responses;
using NzbDrone.Api.Frontend.Mappers;
using NzbDrone.Common;
using NzbDrone.Common.Cache;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Api.Frontend
{
public class StaticResourceModule : NancyModule
{
private readonly IDiskProvider _diskProvider;
private readonly IEnumerable<IMapHttpRequestsToDisk> _requestMappers;
private readonly Logger _logger;
private readonly ICached<string> _indexCache;
private readonly bool _caseSensitive;
public StaticResourceModule(IDiskProvider diskProvider, ICacheManger cacheManger, IEnumerable<IMapHttpRequestsToDisk> requestMappers, Logger logger)
public StaticResourceModule(IEnumerable<IMapHttpRequestsToDisk> requestMappers, Logger logger)
{
_diskProvider = diskProvider;
_requestMappers = requestMappers;
_logger = logger;
_indexCache = cacheManger.GetCache<string>(typeof(StaticResourceModule));
Get["/{resource*}"] = x => Index();
Get["/"] = x => Index();
if (!RuntimeInfo.IsProduction)
{
_caseSensitive = true;
}
}
private Response Index()
@ -50,52 +34,17 @@ namespace NzbDrone.Api.Frontend
return null;
}
var mapper = _requestMappers.SingleOrDefault(m => m.CanHandle(path));
if (mapper != null)
{
var filePath = mapper.Map(path);
if (_diskProvider.FileExists(filePath, _caseSensitive))
{
var response = new StreamResponse(() => File.OpenRead(filePath), MimeTypes.GetMimeType(filePath));
return response;
}
_logger.Warn("File {0} not found", filePath);
}
else
{
_logger.Warn("Couldn't find handler for {0}", path);
return mapper.GetResponse(path);
}
return new NotFoundResponse();
_logger.Warn("Couldn't find handler for {0}", path);
/* htmlResponse.Contents = stream =>
{
var lastWrite = _diskProvider.GetLastFileWrite(_indexPath);
var text = _indexCache.Get(lastWrite.Ticks.ToString(), GetIndexText);
var streamWriter = new StreamWriter(stream);
streamWriter.Write(text);
streamWriter.Flush();
};*/
//htmlResponse.Headers.DisableCache();
return null;
}
/* private string GetIndexText()
{
var text = _diskProvider.ReadAllText(_indexPath);
text = text.Replace(".css", ".css?v=" + BuildInfo.Version);
text = text.Replace(".js", ".js?v=" + BuildInfo.Version);
return text;
}*/
}
}

View File

@ -1,79 +0,0 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using NLog;
using Nancy;
using Nancy.Responses;
using NzbDrone.Api.Frontend.Mappers;
using NzbDrone.Common;
using NzbDrone.Common.EnvironmentInfo;
namespace NzbDrone.Api.Frontend
{
public interface IProcessStaticResource
{
Response ProcessStaticResourceRequest(NancyContext context, string workingFolder);
}
public class StaticResourceProvider : IProcessStaticResource
{
private readonly IDiskProvider _diskProvider;
private readonly IEnumerable<IMapHttpRequestsToDisk> _requestMappers;
private readonly IAddCacheHeaders _addCacheHeaders;
private readonly Logger _logger;
private readonly bool _caseSensitive;
public StaticResourceProvider(IDiskProvider diskProvider,
IEnumerable<IMapHttpRequestsToDisk> requestMappers,
IAddCacheHeaders addCacheHeaders,
Logger logger)
{
_diskProvider = diskProvider;
_requestMappers = requestMappers;
_addCacheHeaders = addCacheHeaders;
_logger = logger;
if (!RuntimeInfo.IsProduction)
{
_caseSensitive = true;
}
}
public Response ProcessStaticResourceRequest(NancyContext context, string workingFolder)
{
var path = context.Request.Url.Path;
if (string.IsNullOrWhiteSpace(path))
{
return null;
}
if (context.Request.Headers.IfModifiedSince.HasValue)
{
var response = new Response { ContentType = MimeTypes.GetMimeType(path), StatusCode = HttpStatusCode.NotModified };
_addCacheHeaders.ToResponse(context.Request, response);
return response;
}
var mapper = _requestMappers.SingleOrDefault(m => m.CanHandle(path));
if (mapper != null)
{
var filePath = mapper.Map(path);
if (_diskProvider.FileExists(filePath, _caseSensitive))
{
var response = new StreamResponse(() => File.OpenRead(filePath), MimeTypes.GetMimeType(filePath));
_addCacheHeaders.ToResponse(context.Request, response);
return response;
}
_logger.Warn("File {0} not found", filePath);
}
return null;
}
}
}

View File

@ -96,15 +96,14 @@
<Compile Include="Extensions\Pipelines\IfModifiedPipeline.cs" />
<Compile Include="Extensions\Pipelines\IRegisterNancyPipeline.cs" />
<Compile Include="Extensions\NancyJsonSerializer.cs" />
<Compile Include="Frontend\IAddCacheHeaders.cs" />
<Compile Include="Frontend\IsCacheableSpecification.cs" />
<Compile Include="Frontend\Mappers\IndexHtmlMapper.cs" />
<Compile Include="Frontend\Mappers\LogFileMapper.cs" />
<Compile Include="Frontend\Mappers\MediaCoverMapper.cs" />
<Compile Include="Frontend\Mappers\StaticResourceMapper.cs" />
<Compile Include="Frontend\Mappers\IMapHttpRequestsToDisk.cs" />
<Compile Include="Frontend\Mappers\StaticResourceMapperBase.cs" />
<Compile Include="Frontend\StaticResourceModule.cs" />
<Compile Include="Frontend\StaticResourceProvider.cs" />
<Compile Include="History\HistoryResource.cs" />
<Compile Include="History\HistoryModule.cs" />
<Compile Include="Indexers\IndexerSchemaModule.cs" />

View File

@ -76,7 +76,9 @@ namespace NzbDrone.Common.Test.EventingTests
public class CommandA : ICommand
{
// ReSharper disable UnusedParameter.Local
public CommandA(int id = 0)
// ReSharper restore UnusedParameter.Local
{
}

View File

@ -22,14 +22,15 @@ namespace NzbDrone.Common
public class HttpProvider : IHttpProvider
{
private readonly Logger _logger;
public const string ContentLenghtHeader = "Content-Length";
public const string CONTENT_LENGHT_HEADER = "Content-Length";
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
private readonly string _userAgent;
public HttpProvider()
public HttpProvider(Logger logger)
{
_logger = logger;
_userAgent = String.Format("NzbDrone {0}", BuildInfo.Version);
}
@ -53,12 +54,12 @@ namespace NzbDrone.Common
}
catch (WebException e)
{
logger.Warn("Failed to get response from: {0} {1}", url, e.Message);
_logger.Warn("Failed to get response from: {0} {1}", url, e.Message);
throw;
}
catch (Exception e)
{
logger.WarnException("Failed to get response from: " + url, e);
_logger.WarnException("Failed to get response from: " + url, e);
throw;
}
}
@ -101,23 +102,23 @@ namespace NzbDrone.Common
fileInfo.Directory.Create();
}
logger.Trace("Downloading [{0}] to [{1}]", url, fileName);
_logger.Trace("Downloading [{0}] to [{1}]", url, fileName);
var stopWatch = Stopwatch.StartNew();
var webClient = new WebClient();
webClient.Headers.Add(HttpRequestHeader.UserAgent, _userAgent);
webClient.DownloadFile(url, fileName);
stopWatch.Stop();
logger.Trace("Downloading Completed. took {0:0}s", stopWatch.Elapsed.Seconds);
_logger.Trace("Downloading Completed. took {0:0}s", stopWatch.Elapsed.Seconds);
}
catch (WebException e)
{
logger.Warn("Failed to get response from: {0} {1}", url, e.Message);
_logger.Warn("Failed to get response from: {0} {1}", url, e.Message);
throw;
}
catch (Exception e)
{
logger.WarnException("Failed to get response from: " + url, e);
_logger.WarnException("Failed to get response from: " + url, e);
throw;
}
}
@ -126,7 +127,7 @@ namespace NzbDrone.Common
{
address = String.Format("http://{0}/jsonrpc", address);
logger.Trace("Posting command: {0}, to {1}", command, address);
_logger.Trace("Posting command: {0}, to {1}", command, address);
byte[] byteArray = Encoding.ASCII.GetBytes(command);

View File

@ -7,8 +7,6 @@ namespace NzbDrone.Core.Test.Framework
{
public abstract class CoreTest : TestBase
{
protected FileStream OpenRead(params string[] path)
{
return File.OpenRead(Path.Combine(path));
@ -21,7 +19,7 @@ namespace NzbDrone.Core.Test.Framework
protected void UseRealHttp()
{
Mocker.SetConstant<IHttpProvider>(new HttpProvider());
Mocker.SetConstant<IHttpProvider>(new HttpProvider(TestLogger));
}
protected void UseRealDisk()

View File

@ -48,7 +48,7 @@ namespace NzbDrone.Core.Test.MediaCoverTests
public void should_return_false_if_file_exists_but_diffrent_size()
{
GivenExistingFileSize(100);
_headers.Add(HttpProvider.ContentLenghtHeader, "200");
_headers.Add(HttpProvider.CONTENT_LENGHT_HEADER, "200");
Subject.AlreadyExists("http://url", "c:\\file.exe").Should().BeFalse();
}
@ -58,7 +58,7 @@ namespace NzbDrone.Core.Test.MediaCoverTests
public void should_return_ture_if_file_exists_and_same_size()
{
GivenExistingFileSize(100);
_headers.Add(HttpProvider.ContentLenghtHeader, "100");
_headers.Add(HttpProvider.CONTENT_LENGHT_HEADER, "100");
Subject.AlreadyExists("http://url", "c:\\file.exe").Should().BeTrue();
}

View File

@ -27,7 +27,7 @@ namespace NzbDrone.Core.Test.NotificationTests
{
_notifications = new List<INotification>();
_notifications.Add(new Notifications.Xbmc.Xbmc(null, null));
_notifications.Add(new Notifications.Xbmc.Xbmc(null));
_notifications.Add(new PlexClient(null));
_notifications.Add(new PlexServer(null));
_notifications.Add(new Email(null));
@ -52,7 +52,7 @@ namespace NzbDrone.Core.Test.NotificationTests
Mocker.SetConstant<INotificationRepository>(Mocker.Resolve<NotificationRepository>());
Mocker.GetMock<IContainer>().Setup(s => s.Resolve(typeof(Notifications.Xbmc.Xbmc)))
.Returns(new Notifications.Xbmc.Xbmc(null, null));
.Returns(new Notifications.Xbmc.Xbmc(null));
Mocker.GetMock<IContainer>().Setup(s => s.Resolve(typeof(PlexClient)))
.Returns(new PlexClient(null));

View File

@ -10,7 +10,7 @@ namespace NzbDrone.Core.DataAugmentation.Scene
{
public interface ISceneMappingService
{
string GetSceneName(int tvdbId, int seasonNumber = -1);
string GetSceneName(int tvdbId);
Nullable<int> GetTvDbId(string cleanName);
}
@ -35,7 +35,7 @@ namespace NzbDrone.Core.DataAugmentation.Scene
_logger = logger;
}
public string GetSceneName(int tvdbId, int seasonNumber = -1)
public string GetSceneName(int tvdbId)
{
var mapping = _getSceneNameCache.Find(tvdbId.ToString());

View File

@ -116,7 +116,7 @@ namespace NzbDrone.Core.IndexerSearch
spec.SeriesId = series.Id;
spec.SeriesTvRageId = series.TvRageId;
spec.SceneTitle = _sceneMapping.GetSceneName(series.TvdbId, seasonNumber);
spec.SceneTitle = _sceneMapping.GetSceneName(series.TvdbId);
if (string.IsNullOrWhiteSpace(spec.SceneTitle))
{

View File

@ -33,7 +33,7 @@ namespace NzbDrone.Core.MediaCover
string sizeString;
if (headers.TryGetValue(HttpProvider.ContentLenghtHeader, out sizeString))
if (headers.TryGetValue(HttpProvider.CONTENT_LENGHT_HEADER, out sizeString))
{
int size;
int.TryParse(sizeString, out size);

View File

@ -1,5 +1,4 @@
using NLog;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Notifications.Xbmc
{
@ -7,7 +6,7 @@ namespace NzbDrone.Core.Notifications.Xbmc
{
private readonly IXbmcService _xbmcProvider;
public Xbmc(IXbmcService xbmcProvider, Logger logger)
public Xbmc(IXbmcService xbmcProvider)
{
_xbmcProvider = xbmcProvider;
}

View File

@ -9,6 +9,7 @@
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=NUnit_002ENonPublicMethodWithTestAttribute/@EntryIndexedValue">ERROR</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ReturnTypeCanBeEnumerable_002EGlobal/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=StringLiteralTypo/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnusedParameter_002ELocal/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseObjectOrCollectionInitializer/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=NzbDrone/@EntryIndexedValue">&lt;?xml version="1.0" encoding="utf-16"?&gt;&lt;Profile name="NzbDrone"&gt;&lt;CSArrangeThisQualifier&gt;True&lt;/CSArrangeThisQualifier&gt;&lt;RemoveCodeRedundancies&gt;True&lt;/RemoveCodeRedundancies&gt;&lt;CSUseAutoProperty&gt;True&lt;/CSUseAutoProperty&gt;&lt;CSMakeFieldReadonly&gt;True&lt;/CSMakeFieldReadonly&gt;&lt;CSUseVar&gt;&lt;BehavourStyle&gt;CAN_CHANGE_TO_IMPLICIT&lt;/BehavourStyle&gt;&lt;LocalVariableStyle&gt;IMPLICIT_EXCEPT_SIMPLE_TYPES&lt;/LocalVariableStyle&gt;&lt;ForeachVariableStyle&gt;ALWAYS_IMPLICIT&lt;/ForeachVariableStyle&gt;&lt;/CSUseVar&gt;&lt;CSUpdateFileHeader&gt;True&lt;/CSUpdateFileHeader&gt;&lt;CSOptimizeUsings&gt;&lt;OptimizeUsings&gt;True&lt;/OptimizeUsings&gt;&lt;EmbraceInRegion&gt;False&lt;/EmbraceInRegion&gt;&lt;RegionName&gt;&lt;/RegionName&gt;&lt;/CSOptimizeUsings&gt;&lt;CSShortenReferences&gt;True&lt;/CSShortenReferences&gt;&lt;CSReorderTypeMembers&gt;True&lt;/CSReorderTypeMembers&gt;&lt;XAMLCollapseEmptyTags&gt;False&lt;/XAMLCollapseEmptyTags&gt;&lt;/Profile&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/CodeCleanup/SilentCleanupProfile/@EntryValue">NzbDrone</s:String>