// --------------------------------------------------------------------------------------------------------------------
//
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
//
//
// Processes any image requests within the web application.
//
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Web.HttpModules
{
#region Using
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using System.Security.Principal;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
using System.Web.Hosting;
using System.Web.Security;
using ImageProcessor.Web.Caching;
using ImageProcessor.Web.Configuration;
using ImageProcessor.Web.Extensions;
using ImageProcessor.Web.Helpers;
#endregion
///
/// Processes any image requests within the web application.
///
public sealed class ImageProcessingModule : IHttpModule
{
#region Fields
///
/// The key for storing the response type of the current image.
///
private const string CachedResponseTypeKey = "CACHED_IMAGE_RESPONSE_TYPE_054F217C-11CF-49FF-8D2F-698E8E6EB58F";
///
/// The key for storing the cached path of the current image.
///
private const string CachedPathKey = "CACHED_IMAGE_PATH_TYPE_E0741478-C17B-433D-96A8-6CDA797644E9";
///
/// The key for storing the file dependency of the current image.
///
private const string CachedResponseFileDependency = "CACHED_IMAGE_DEPENDENCY_054F217C-11CF-49FF-8D2F-698E8E6EB58F";
///
/// The regular expression to search strings for.
///
private static readonly Regex PresetRegex = new Regex(@"preset=[^&]+", RegexOptions.Compiled);
///
/// The assembly version.
///
private static readonly string AssemblyVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
///
/// The locker for preventing duplicate requests.
///
private static readonly AsyncDuplicateLock Locker = new AsyncDuplicateLock();
///
/// The value to prefix any remote image requests with to ensure they get captured.
///
private static string remotePrefix;
///
/// Whether to preserve exif meta data.
///
private static bool? preserveExifMetaData;
///
/// A value indicating whether this instance of the given entity has been disposed.
///
/// if this instance has been disposed; otherwise, .
///
/// If the entity is disposed, it must not be disposed a second
/// time. The isDisposed field is set the first time the entity
/// is disposed. If the isDisposed field is true, then the Dispose()
/// method will not dispose again. This help not to prolong the entity's
/// life in the Garbage Collector.
///
private bool isDisposed;
#endregion
#region Destructors
///
/// Finalizes an instance of the class.
///
///
/// Use C# destructor syntax for finalization code.
/// This destructor will run only if the Dispose method
/// does not get called.
/// It gives your base class the opportunity to finalize.
/// Do not provide destructors in types derived from this class.
///
~ImageProcessingModule()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
this.Dispose(false);
}
#endregion
///
/// The event that is called when a new image is processed.
///
public static event EventHandler OnPostProcessing;
#region IHttpModule Members
///
/// Initializes a module and prepares it to handle requests.
///
///
/// An that provides
/// access to the methods, properties, and events common to all
/// application objects within an ASP.NET application
///
public void Init(HttpApplication context)
{
if (remotePrefix == null)
{
remotePrefix = ImageProcessorConfiguration.Instance.RemotePrefix;
}
if (preserveExifMetaData == null)
{
preserveExifMetaData = ImageProcessorConfiguration.Instance.PreserveExifMetaData;
}
EventHandlerTaskAsyncHelper postAuthorizeHelper = new EventHandlerTaskAsyncHelper(this.PostAuthorizeRequest);
context.AddOnPostAuthorizeRequestAsync(postAuthorizeHelper.BeginEventHandler, postAuthorizeHelper.EndEventHandler);
EventHandlerTaskAsyncHelper postProcessHelper = new EventHandlerTaskAsyncHelper(this.PostProcessImage);
context.AddOnPostRequestHandlerExecuteAsync(postProcessHelper.BeginEventHandler, postProcessHelper.EndEventHandler);
context.PreSendRequestHeaders += this.ContextPreSendRequestHeaders;
}
///
/// Disposes of the resources (other than memory) used by the module that implements .
///
public void Dispose()
{
this.Dispose(true);
// This object will be cleaned up by the Dispose method.
// Therefore, you should call GC.SupressFinalize to
// take this object off the finalization queue
// and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
///
/// Disposes the object and frees resources for the Garbage Collector.
///
/// If true, the object gets disposed.
private void Dispose(bool disposing)
{
if (this.isDisposed)
{
return;
}
if (disposing)
{
// Dispose of any managed resources here.
}
// Call the appropriate methods to clean up
// unmanaged resources here.
// Note disposing is done.
this.isDisposed = true;
}
#endregion
///
/// Occurs when the user for the current request has been authorized.
///
///
/// The source of the event.
///
///
/// An EventArgs that contains the event data.
///
///
/// The .
///
private Task PostAuthorizeRequest(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
return this.ProcessImageAsync(context);
}
///
/// Occurs when the ASP.NET event handler finishes execution.
///
///
/// The source of the event.
///
///
/// An EventArgs that contains the event data.
///
///
/// The .
///
private Task PostProcessImage(object sender, EventArgs e)
{
HttpContext context = ((HttpApplication)sender).Context;
object cachedPathObject = context.Items[CachedPathKey];
if (cachedPathObject != null)
{
string cachedPath = cachedPathObject.ToString();
// Trim the cache.
DiskCache.TrimCachedFolders(cachedPath);
// Fire the post processing event.
EventHandler handler = OnPostProcessing;
if (handler != null)
{
context.Items[CachedPathKey] = null;
return Task.Run(() => handler(this, new PostProcessingEventArgs { CachedImagePath = cachedPath }));
}
}
return Task.FromResult