/BlogEngine/DotNetSlave.BusinessLogic/Web/HttpHandlers/JavaScriptHandler.cs
C# | 269 lines | 153 code | 45 blank | 71 comment | 16 complexity | 46103e0d600cdc88386e99030001c628 MD5 | raw file
Possible License(s): LGPL-2.1, Apache-2.0, BSD-3-Clause
- namespace BlogEngine.Core.Web.HttpHandlers
- {
- using System;
- using System.Collections.Generic;
- using System.Configuration;
- using System.IO;
- using System.Linq;
- using System.Net;
- using System.Net.Sockets;
- using System.Security;
- using System.Text;
- using System.Text.RegularExpressions;
- using System.Web;
- using System.Web.Caching;
-
- using BlogEngine.Core.Web.HttpModules;
-
- /// <summary>
- /// Removes whitespace in all stylesheets added to the
- /// header of the HTML document in site.master.
- /// </summary>
- /// <remarks>
- ///
- /// This handler uses an external library to perform minification of scripts.
- /// See the BlogEngine.Core.JavascriptMinifier class for more details.
- ///
- /// </remarks>
- public class JavaScriptHandler : IHttpHandler
- {
- #region Properties
-
- /// <summary>
- /// Gets a value indicating whether another request can use the <see cref = "T:System.Web.IHttpHandler"></see> instance.
- /// </summary>
- /// <value></value>
- /// <returns>true if the <see cref = "T:System.Web.IHttpHandler"></see> instance is reusable; otherwise, false.</returns>
- public bool IsReusable
- {
- get
- {
- return false;
- }
- }
-
- #endregion
-
- #region Implemented Interfaces
-
- #region IHttpHandler
-
- /// <summary>
- /// Enables processing of HTTP Web requests by a custom
- /// HttpHandler that implements the <see cref="T:System.Web.IHttpHandler"></see> interface.
- /// </summary>
- /// <param name="context">
- /// An <see cref="T:System.Web.HttpContext"></see> object that provides
- /// references to the intrinsic server objects
- /// (for example, Request, Response, Session, and Server) used to service HTTP requests.
- /// </param>
- public void ProcessRequest(HttpContext context)
- {
- var request = context.Request;
- var path = request.QueryString["path"];
-
- if (string.IsNullOrEmpty(path))
- {
- return;
- }
-
- string rawUrl = request.RawUrl.Trim();
- string cacheKey = context.Server.HtmlDecode(rawUrl);
- string script = (string)Blog.CurrentInstance.Cache[cacheKey];
- bool minify = ((request.QueryString["minify"] != null) || (BlogSettings.Instance.CompressWebResource && cacheKey.Contains("WebResource.axd")));
-
-
- if (String.IsNullOrEmpty(script))
- {
- if (path.StartsWith("http", StringComparison.OrdinalIgnoreCase))
- {
- script = RetrieveRemoteScript(path, cacheKey, minify);
- }
- else
- {
- script = RetrieveLocalScript(path, cacheKey, minify);
- }
- }
-
-
- if (string.IsNullOrEmpty(script))
- {
- return;
- }
-
- SetHeaders(script.GetHashCode(), context);
- context.Response.Write(script);
-
- if (BlogSettings.Instance.EnableHttpCompression)
- {
- CompressionModule.CompressResponse(context); // Compress(context);
- }
- }
-
- #endregion
-
- #endregion
-
- #region Methods
-
- /// <summary>
- /// Checks whether to hard minify output.
- /// </summary>
- /// <param name="file">The file name.</param>
- /// <returns>Whether to hard minify output.</returns>
- private static bool HardMinify(string file)
- {
- var lookfor = ConfigurationManager.AppSettings.Get("BlogEngine.HardMinify").Split(
- new[] { "," }, StringSplitOptions.RemoveEmptyEntries);
- return lookfor.Any(file.Contains);
- }
-
- /// <summary>
- /// Call this method for any extra processing that needs to be done on a script resource before
- /// being written to the response.
- /// </summary>
- /// <param name="script"></param>
- /// <param name="filePath"></param>
- /// <param name="shouldMinify"></param>
- /// <returns></returns>
- private static string ProcessScript(string script, string filePath, bool shouldMinify)
- {
- // The HardMinify call is for backwards compatibility. It's really not needed anymore.
- if ((shouldMinify) || HardMinify(filePath))
- {
- var min = new JavascriptMinifier();
- min.VariableMinification = VariableMinification.LocalVariablesOnly;
-
- return min.Minify(script);
- }
- else
- {
- return script;
- }
-
- }
-
- /// <summary>
- /// Retrieves the local script from the disk
- /// </summary>
- /// <param name="file">
- /// The file name.
- /// </param>
- /// <param name="cacheKey">The key used to insert this script into the cache.</param>
- /// <param name="minify">Whether or not the local script should be minfied</param>
- /// <returns>
- /// The retrieve local script.
- /// </returns>
- private static string RetrieveLocalScript(string file, string cacheKey, bool minify)
- {
-
- if (StringComparer.OrdinalIgnoreCase.Compare(Path.GetExtension(file), ".js") != 0)
- {
- throw new SecurityException("No access");
- }
-
- try
- {
- var path = HttpContext.Current.Server.MapPath(file);
- if (File.Exists(path))
- {
- string script;
- using (var reader = new StreamReader(path))
- {
- script = reader.ReadToEnd();
- }
-
- script = ProcessScript(script, file, minify);
- Blog.CurrentInstance.Cache.Insert(cacheKey, script, new CacheDependency(path));
- return script;
- }
- }
- catch (Exception ex)
- {
- }
-
- return string.Empty;
- }
-
- /// <summary>
- /// Retrieves and cached the specified remote script.
- /// </summary>
- /// <param name="file">
- /// The remote URL
- /// </param>
- /// <param name="cacheKey">The key used to insert this script into the cache.</param>
- /// <param name="minify">Whether or not the remote script should be minified</param>
- /// <returns>
- /// The retrieve remote script.
- /// </returns>
- private static string RetrieveRemoteScript(string file, string cacheKey, bool minify)
- {
-
- Uri url;
-
- if (Uri.TryCreate(file, UriKind.Absolute, out url))
- {
- try
- {
-
- var remoteFile = new RemoteFile(url, false);
- string script = remoteFile.GetFileAsString();
- script = ProcessScript(script, file, minify);
- Blog.CurrentInstance.Cache.Insert(cacheKey, script, null, Cache.NoAbsoluteExpiration, new TimeSpan(3, 0, 0, 0));
- return script;
- }
- catch (SocketException)
- {
- // The remote site is currently down. Try again next time.
- }
- }
-
- return String.Empty;
- }
-
-
-
- /// <summary>
- /// This will make the browser and server keep the output
- /// in its cache and thereby improve performance.
- /// </summary>
- /// <param name="hash">
- /// The hash number.
- /// </param>
- /// <param name="context">
- /// The context.
- /// </param>
- private static void SetHeaders(int hash, HttpContext context)
- {
-
- var response = context.Response;
-
- response.ContentType = "text/javascript";
-
- var cache = response.Cache;
-
- cache.VaryByHeaders["Accept-Encoding"] = true;
- cache.SetExpires(DateTime.Now.ToUniversalTime().AddDays(7));
- cache.SetMaxAge(new TimeSpan(7, 0, 0, 0));
- cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
-
- var etag = string.Format("\"{0}\"", hash);
- var incomingEtag = context.Request.Headers["If-None-Match"];
-
- cache.SetETag(etag);
- cache.SetCacheability(HttpCacheability.Public);
-
- if (String.Compare(incomingEtag, etag) != 0)
- {
- return;
- }
-
- response.Clear();
- response.StatusCode = (int)HttpStatusCode.NotModified;
- response.SuppressContent = true;
- }
-
- #endregion
-
- }
- }