diff --git a/NzbDrone.Api/Extensions/CacheHeaderPipeline.cs b/NzbDrone.Api/Extensions/CacheHeaderPipeline.cs deleted file mode 100644 index 69a605d68..000000000 --- a/NzbDrone.Api/Extensions/CacheHeaderPipeline.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Nancy; - -namespace NzbDrone.Api.Extensions -{ - public static class CacheHeaderPipeline - { - public static void Handle(NancyContext context) - { - if (context.Response.ContentType.Contains("json")) - { - context.Response.Headers.DisableCache(); - - } - } - } -} \ No newline at end of file diff --git a/NzbDrone.Api/Extensions/Pipelines/CacheHeaderPipeline.cs b/NzbDrone.Api/Extensions/Pipelines/CacheHeaderPipeline.cs new file mode 100644 index 000000000..70f7fc0bf --- /dev/null +++ b/NzbDrone.Api/Extensions/Pipelines/CacheHeaderPipeline.cs @@ -0,0 +1,34 @@ +using System; +using Nancy; +using Nancy.Bootstrapper; +using NzbDrone.Api.Frontend; + +namespace NzbDrone.Api.Extensions.Pipelines +{ + public class CacheHeaderPipeline : IRegisterNancyPipeline + { + private readonly ICacheableSpecification _cacheableSpecification; + + public CacheHeaderPipeline(ICacheableSpecification cacheableSpecification) + { + _cacheableSpecification = cacheableSpecification; + } + + public void Register(IPipelines pipelines) + { + pipelines.AfterRequest.AddItemToStartOfPipeline(Handle); + } + + private void Handle(NancyContext context) + { + if (_cacheableSpecification.IsCacheable(context)) + { + context.Response.Headers.EnableCache(); + } + else + { + context.Response.Headers.DisableCache(); + } + } + } +} \ No newline at end of file diff --git a/NzbDrone.Api/Extensions/GZipPipeline.cs b/NzbDrone.Api/Extensions/Pipelines/GZipPipeline.cs similarity index 76% rename from NzbDrone.Api/Extensions/GZipPipeline.cs rename to NzbDrone.Api/Extensions/Pipelines/GZipPipeline.cs index 0447c65d6..6a2e93e84 100644 --- a/NzbDrone.Api/Extensions/GZipPipeline.cs +++ b/NzbDrone.Api/Extensions/Pipelines/GZipPipeline.cs @@ -2,17 +2,18 @@ using System.IO.Compression; using System.Linq; using Nancy; +using Nancy.Bootstrapper; -namespace NzbDrone.Api.Extensions +namespace NzbDrone.Api.Extensions.Pipelines { - public static class GzipCompressionPipeline + public class GzipCompressionPipeline : IRegisterNancyPipeline { - public static void Handle(NancyContext context) + public void Register(IPipelines pipelines) { - context.Response.CompressResponse(context.Request); + pipelines.AfterRequest.AddItemToEndOfPipeline(c => CompressResponse(c.Request, c.Response)); } - public static Response CompressResponse(this Response response, Request request) + private Response CompressResponse(Request request, Response response) { if (!response.ContentType.Contains("image") && request.Headers.AcceptEncoding.Any(x => x.Contains("gzip")) diff --git a/NzbDrone.Api/Extensions/Pipelines/IRegisterNancyPipeline.cs b/NzbDrone.Api/Extensions/Pipelines/IRegisterNancyPipeline.cs new file mode 100644 index 000000000..a738cbe5c --- /dev/null +++ b/NzbDrone.Api/Extensions/Pipelines/IRegisterNancyPipeline.cs @@ -0,0 +1,9 @@ +using Nancy.Bootstrapper; + +namespace NzbDrone.Api.Extensions.Pipelines +{ + public interface IRegisterNancyPipeline + { + void Register(IPipelines pipelines); + } +} \ No newline at end of file diff --git a/NzbDrone.Api/Extensions/Pipelines/IfModifiedPipeline.cs b/NzbDrone.Api/Extensions/Pipelines/IfModifiedPipeline.cs new file mode 100644 index 000000000..964f45f88 --- /dev/null +++ b/NzbDrone.Api/Extensions/Pipelines/IfModifiedPipeline.cs @@ -0,0 +1,33 @@ +using Nancy; +using Nancy.Bootstrapper; +using NzbDrone.Api.Frontend; + +namespace NzbDrone.Api.Extensions.Pipelines +{ + public class IfModifiedPipeline : IRegisterNancyPipeline + { + private readonly ICacheableSpecification _cacheableSpecification; + + public IfModifiedPipeline(ICacheableSpecification cacheableSpecification) + { + _cacheableSpecification = cacheableSpecification; + } + + public void Register(IPipelines pipelines) + { + pipelines.BeforeRequest.AddItemToStartOfPipeline(Handle); + } + + private Response Handle(NancyContext context) + { + if (_cacheableSpecification.IsCacheable(context) && context.Request.Headers.IfModifiedSince.HasValue) + { + var response = new Response { ContentType = MimeTypes.GetMimeType(context.Request.Path), StatusCode = HttpStatusCode.NotModified }; + response.Headers.EnableCache(); + return response; + } + + return null; + } + } +} \ No newline at end of file diff --git a/NzbDrone.Api/Frontend/IsCacheableSpecification.cs b/NzbDrone.Api/Frontend/IsCacheableSpecification.cs index 30775159a..f8d8b20b7 100644 --- a/NzbDrone.Api/Frontend/IsCacheableSpecification.cs +++ b/NzbDrone.Api/Frontend/IsCacheableSpecification.cs @@ -1,16 +1,28 @@ using System; using Nancy; +using NzbDrone.Common.EnvironmentInfo; namespace NzbDrone.Api.Frontend { - public class IsCacheableSpecification + public interface ICacheableSpecification { - public bool IsCacheable(Request request) - { - if (request.Path.Contains(".")) return false; - if (request.Path.StartsWith("/api", StringComparison.CurrentCultureIgnoreCase)) return false; - if (request.Path.StartsWith("/signalr", StringComparison.CurrentCultureIgnoreCase)) return false; + bool IsCacheable(NancyContext context); + } + public class CacheableSpecification : ICacheableSpecification + { + public bool IsCacheable(NancyContext context) + { + if (context.Request.Query.v == BuildInfo.Version) return true; + + if (context.Request.Path.StartsWith("/api", StringComparison.CurrentCultureIgnoreCase)) return false; + if (context.Request.Path.StartsWith("/signalr", StringComparison.CurrentCultureIgnoreCase)) return false; + if (context.Request.Path.EndsWith("app.js")) return false; + + if (context.Response != null) + { + if (context.Response.ContentType.Contains("text/html")) return false; + } return true; } diff --git a/NzbDrone.Api/Frontend/StaticResourceModule.cs b/NzbDrone.Api/Frontend/StaticResourceModule.cs index 42ab38302..5a160fe3b 100644 --- a/NzbDrone.Api/Frontend/StaticResourceModule.cs +++ b/NzbDrone.Api/Frontend/StaticResourceModule.cs @@ -61,8 +61,6 @@ private Response Index() if (_diskProvider.FileExists(filePath, _caseSensitive)) { var response = new StreamResponse(() => File.OpenRead(filePath), MimeTypes.GetMimeType(filePath)); - //_addCacheHeaders.ToResponse(context.Request, response); - return response; } diff --git a/NzbDrone.Api/NancyBootstrapper.cs b/NzbDrone.Api/NancyBootstrapper.cs index 63b2ba1c3..5f65c80e2 100644 --- a/NzbDrone.Api/NancyBootstrapper.cs +++ b/NzbDrone.Api/NancyBootstrapper.cs @@ -1,9 +1,11 @@ -using NLog; +using System; +using NLog; using Nancy.Bootstrapper; using Nancy.Diagnostics; using NzbDrone.Api.Authentication; using NzbDrone.Api.ErrorManagement; using NzbDrone.Api.Extensions; +using NzbDrone.Api.Extensions.Pipelines; using NzbDrone.Common.Messaging; using NzbDrone.Core.Instrumentation; using NzbDrone.Core.Lifecycle; @@ -26,23 +28,34 @@ protected override void ApplicationStartup(TinyIoCContainer container, IPipeline { _logger.Info("Starting NzbDrone API"); + + RegisterPipelines(pipelines); + container.Resolve().Register(); container.Resolve().Register(pipelines); container.Resolve().PublishEvent(new ApplicationStartedEvent()); - pipelines.AfterRequest.AddItemToStartOfPipeline(GzipCompressionPipeline.Handle); - pipelines.AfterRequest.AddItemToEndOfPipeline(CacheHeaderPipeline.Handle); ApplicationPipelines.OnError.AddItemToEndOfPipeline(container.Resolve().HandleException); } + private void RegisterPipelines(IPipelines pipelines) + { + var pipelineRegistrars = _tinyIoCContainer.ResolveAll(); + + foreach (var registerNancyPipeline in pipelineRegistrars) + { + registerNancyPipeline.Register(pipelines); + } + + } + protected override TinyIoCContainer GetApplicationContainer() { return _tinyIoCContainer; } - protected override DiagnosticsConfiguration DiagnosticsConfiguration { get { return new DiagnosticsConfiguration { Password = @"password" }; } diff --git a/NzbDrone.Api/NzbDrone.Api.csproj b/NzbDrone.Api/NzbDrone.Api.csproj index f634d7afb..cc87521aa 100644 --- a/NzbDrone.Api/NzbDrone.Api.csproj +++ b/NzbDrone.Api/NzbDrone.Api.csproj @@ -91,8 +91,10 @@ - - + + + + diff --git a/NzbDrone.Common/HttpProvider.cs b/NzbDrone.Common/HttpProvider.cs index 61e311574..4e3ea8817 100644 --- a/NzbDrone.Common/HttpProvider.cs +++ b/NzbDrone.Common/HttpProvider.cs @@ -13,7 +13,6 @@ public interface IHttpProvider { string DownloadString(string url); string DownloadString(string url, string username, string password); - string DownloadString(string url, ICredentials identity); Dictionary GetHeader(string url); Stream DownloadStream(string url, NetworkCredential credential = null); @@ -44,7 +43,7 @@ public string DownloadString(string url, string username, string password) return DownloadString(url, new NetworkCredential(username, password)); } - public string DownloadString(string url, ICredentials identity) + private string DownloadString(string url, ICredentials identity) { try {