Began integration of Stephen's new routing logic. The sln compiles and things have been refactored

from the initial concept. The module is not active as it currently will not work because of
contructor dependencies and classes marked as internal, will liase with Stephen regarding this.
This commit is contained in:
shannon@ShandemVaio
2012-07-20 01:04:35 +06:00
parent f2c084feda
commit 25b06a88ab
30 changed files with 2445 additions and 1 deletions

View File

@@ -0,0 +1,371 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Web;
using Umbraco.Core;
using Umbraco.Web.Routing;
using umbraco;
using umbraco.IO;
namespace Umbraco.Web
{
// also look at IOHelper.ResolveUrlsFromTextString - nightmarish?!
// context.RewritePath supports ~/ or else must begin with /vdir
// Request.RawUrl is still there
// response.Redirect does?! always remap to /vdir?!
public class UmbracoModule : IHttpModule
{
private static readonly TraceSource Trace = new TraceSource("UmbracoModule");
// entry point for a request
void ProcessRequest(HttpContext httpContext)
{
Trace.TraceInformation("Process request");
var uri = httpContext.Request.Url;
var lpath = uri.AbsolutePath.ToLower();
// add Umbraco's signature header
if (!UmbracoSettings.RemoveUmbracoVersionHeader)
httpContext.Response.AddHeader("X-Umbraco-Version", string.Format("{0}.{1}", GlobalSettings.VersionMajor, GlobalSettings.VersionMinor));
//create the UmbracoContext singleton, one per request!!
var umbracoContext = new UmbracoContext(new HttpContextWrapper(httpContext), ApplicationContext.Current);
UmbracoContext.Current = umbracoContext;
//create a content store
var contentStore = new ContentStore(umbracoContext);
//create the routes cache
var routesCache = new RoutesCache(umbracoContext);
//create the nice urls
var niceUrls = new NiceUrls(contentStore, umbracoContext, routesCache);
//create the RoutingEnvironment (one per http request as it relies on the umbraco context!)
var routingEnvironment = new RoutingEnvironment(
ApplicationContext.Current.Plugins.ResolveLookups().ToArray(),
new LookupFor404(contentStore),
contentStore);
// create the new document request which will cleanup the uri once and for all
var docreq = new DocumentRequest(uri, routingEnvironment, umbracoContext, niceUrls);
// initialize the document request on the UmbracoContext (this is circular dependency!!!)
umbracoContext.DocumentRequest = docreq;
//create the LegacyRequestInitializer (one per http request as it relies on the umbraco context!)
var legacyRequestInitializer = new LegacyRequestInitializer(umbracoContext);
// legacy - initialize legacy stuff
legacyRequestInitializer.InitializeRequest();
// note - at that point the original legacy module did something do handle IIS custom 404 errors
// ie pages looking like /anything.aspx?404;/path/to/document - I guess the reason was to support
// "directory urls" without having to do wildcard mapping to ASP.NET on old IIS. This is a pain
// to maintain and probably not used anymore - removed as of 06/2012. @zpqrtbnk.
//
// to trigger Umbraco's not-found, one should configure IIS and/or ASP.NET custom 404 errors
// so that they point to a non-existing page eg /redirect-404.aspx
var ok = true;
// ensure this is a document request
ok = ok && EnsureDocumentRequest(httpContext, uri, lpath);
// ensure Umbraco is ready to serve documents
ok = ok && EnsureIsReady(httpContext, uri);
// ensure Umbraco is properly configured to serve documents
ok = ok && EnsureIsConfigured(httpContext, uri);
//TODO: I like the idea of this new setting, but lets get this in to the core at a later time, for now lets just get the basics working.
//ok = ok && (!Settings.Legacy.EnableBaseRestHandler || EnsureNotBaseRestHandler(httpContext, lpath));
ok = ok && (EnsureNotBaseRestHandler(httpContext, lpath));
if (!ok)
{
Trace.TraceInformation("End");
return;
}
// legacy - no idea what this is
LegacyCleanUmbPageFromQueryString(ref uri, ref lpath);
docreq.ResolveSiteRoot();
if (docreq.IsRedirect)
httpContext.Response.Redirect(docreq.RedirectUrl, true);
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture = docreq.Culture;
docreq.ResolveDocument();
if (docreq.IsRedirect)
httpContext.Response.Redirect(docreq.RedirectUrl, true);
if (docreq.Is404)
httpContext.Response.StatusCode = 404;
// fixme - should use an IComponent here, so that we could hand the request over to MVC
Trace.TraceInformation("Transfer to UmbracoDefault (default.aspx)");
TransferRequest("~/default.aspx?" + docreq.QueryString);
// it is up to default.aspx to figure out what to display in case
// there is no document (ugly 404 page?) or no template (blank page?)
}
// ensures that the request is a document request
// ie one that the module should handle
bool EnsureDocumentRequest(HttpContext httpContext, Uri uri, string lpath)
{
bool maybeDoc = true;
// handle directory-urls used for asmx
// legacy - what's the point really?
if (maybeDoc && GlobalSettings.UseDirectoryUrls)
{
int asmxPos = lpath.IndexOf(".asmx/");
if (asmxPos >= 0)
{
// use uri.AbsolutePath, not path, 'cos path has been lowercased
httpContext.RewritePath(uri.AbsolutePath.Substring(0, asmxPos + 5), // filePath
uri.AbsolutePath.Substring(asmxPos + 5), // pathInfo
uri.Query.TrimStart('?'));
maybeDoc = false;
}
}
// a document request should be
// /foo/bar/nil
// /foo/bar/nil/
// /foo/bar/nil.aspx
// where /foo is not a reserved path
// if the path contains an extension that is not .aspx
// then it cannot be a document request
if (maybeDoc && lpath.Contains('.') && !lpath.EndsWith(".aspx"))
maybeDoc = false;
// at that point, either we have no extension, or it is .aspx
// if the path is reserved then it cannot be a document request
if (maybeDoc && GlobalSettings.IsReservedPathOrUrl(lpath))
maybeDoc = false;
if (!maybeDoc)
Trace.TraceInformation("Not a document");
return maybeDoc;
}
// ensures Umbraco is ready to handle requests
// if not, set status to 503 and transfer request, and return false
// if yes, return true
bool EnsureIsReady(HttpContext httpContext, Uri uri)
{
// ensure we are ready
if (!ApplicationContext.Current.IsReady)
{
Trace.TraceEvent(TraceEventType.Warning, 0, "Umbraco is not ready");
httpContext.Response.StatusCode = 503;
string bootUrl = null;
if (UmbracoSettings.EnableSplashWhileLoading) // legacy - should go
{
string configPath = UrlUtility.ToAbsolute(SystemDirectories.Config);
bootUrl = string.Format("{0}/splashes/booting.aspx?url={1}", configPath, HttpUtility.UrlEncode(uri.ToString()));
// fixme ?orgurl=... ?retry=...
}
//TODO: I like the idea of this new setting, but lets get this in to the core at a later time, for now lets just get the basics working.
//else if (!string.IsNullOrWhiteSpace(Settings.BootSplashPage))
//{
// bootUrl = UrlUtility.ToAbsolute(Settings.BootSplashPage);
//}
else
{
// fixme - default.aspx has to be ready for RequestContext.DocumentRequest==null
// fixme - in fact we should transfer to an empty html page...
bootUrl = UrlUtility.ToAbsolute("~/default.aspx");
}
TransferRequest(bootUrl);
return false;
}
return true;
}
// ensures Umbraco is configured
// if not, redirect to install and return false
// if yes, return true
bool EnsureIsConfigured(HttpContext httpContext, Uri uri)
{
if (!ApplicationContext.Current.IsConfigured)
{
Trace.TraceEvent(TraceEventType.Warning, 0, "Umbraco is not configured");
string installPath = UrlUtility.ToAbsolute(SystemDirectories.Install);
string installUrl = string.Format("{0}/default.aspx?redir=true&url={1}", installPath, HttpUtility.UrlEncode(uri.ToString()));
httpContext.Response.Redirect(installUrl, true);
return false;
}
return true;
}
// checks if the current request is a /base REST handler request
// returns false if it is, otherwise true
bool EnsureNotBaseRestHandler(HttpContext httpContext, string lpath)
{
// the /base REST handler still lives in umbraco.dll and has
// not been refactored at the moment. it still is a module,
// although it should be a handler, or it should be replaced
// by clean WebAPI.
// fixme - do it once when initializing the module
string baseUrl = UrlUtility.ToAbsolute(SystemDirectories.Base).ToLower();
if (!baseUrl.EndsWith("/"))
baseUrl += "/";
if (lpath.StartsWith(baseUrl))
{
Trace.TraceInformation("Detected /base REST handler");
return false;
}
return true;
}
// transfers the request using the fastest method available on the server
void TransferRequest(string path)
{
bool integrated = HttpRuntime.UsingIntegratedPipeline;
// fixme - are we doing this properly?
// fixme - handle virtual directory?
// fixme - this does not work 'cos it resets the HttpContext
// so we should move the DocumentRequest stuff etc back to default.aspx?
// but, also, with TransferRequest, auth & co will run on the new (default.aspx) url,
// is that really what we want? I need to talk about it with others. @zpqrtbnk
integrated = false;
// http://msmvps.com/blogs/luisabreu/archive/2007/10/09/are-you-using-the-new-transferrequest.aspx
// http://msdn.microsoft.com/en-us/library/aa344903.aspx
// http://forums.iis.net/t/1146511.aspx
if (integrated)
HttpContext.Current.Server.TransferRequest(path);
else
HttpContext.Current.RewritePath(path);
}
#region Legacy
// "Clean umbPage from querystring, caused by .NET 2.0 default Auth Controls"
// but really, at the moment I have no idea what this does, and why...
void LegacyCleanUmbPageFromQueryString(ref Uri uri, ref string lpath)
{
string receivedQuery = uri.Query;
string path = uri.AbsolutePath;
string query = null;
if (receivedQuery.Length > 0)
{
// Clean umbPage from querystring, caused by .NET 2.0 default Auth Controls
if (receivedQuery.IndexOf("umbPage") > 0)
{
int ampPos = receivedQuery.IndexOf('&');
// query contains no ampersand?
if (ampPos < 0)
{
// no ampersand means no original query string
query = String.Empty;
// ampersand would occur past then end the of received query
ampPos = receivedQuery.Length;
}
else
{
// original query string past ampersand
query = receivedQuery.Substring(ampPos + 1,
receivedQuery.Length - ampPos - 1);
}
// get umbPage out of query string (9 = "&umbPage".Length() + 1)
path = receivedQuery.Substring(9, ampPos - 9); //this will fail if there are < 9 characters before the &umbPage query string
// --added when refactoring--
uri = uri.Rewrite(path, query);
lpath = path.ToLower();
}
//else
//{
// // strip off question mark
// query = receivedQuery.Substring(1);
//}
}
}
#endregion
#region IHttpModule
// initialize the module, this will trigger for each new application
// and there may be more than 1 application per application domain
public void Init(HttpApplication app)
{
InitializeApplication(app);
}
public void Dispose()
{ }
#endregion
#region Initialize
private static volatile bool _appDomainInitialized = false;
static readonly object AppDomainLock = new object();
/// <summary>
/// Initializes the application one time only
/// </summary>
void InitializeDomain()
{
if (!_appDomainInitialized)
{
lock (AppDomainLock)
{
if (!_appDomainInitialized)
{
Trace.TraceInformation("Initialize AppDomain");
//create the ApplicationContext
ApplicationContext.Current = new ApplicationContext(new PluginResolver());
//TODO: Why is this necessary? if the ApplicationContext is null, then its not ready
ApplicationContext.Current.IsReady = true; // fixme
// Backwards compatibility - set the path and URL type for ClientDependency 1.5.1 [LK]
ClientDependency.Core.CompositeFiles.Providers.XmlFileMapper.FileMapVirtualFolder = "~/App_Data/TEMP/ClientDependency";
ClientDependency.Core.CompositeFiles.Providers.BaseCompositeFileProcessingProvider.UrlTypeDefault = ClientDependency.Core.CompositeFiles.Providers.CompositeUrlType.Base64QueryStrings;
_appDomainInitialized = true;
}
Trace.TraceInformation("AppDomain is initialized");
}
}
}
void InitializeApplication(HttpApplication app)
{
// we can't do in in module.Init because we need HttpContext.
app.BeginRequest += (sender, e) =>
{
Trace.TraceInformation("Welcome to Umbraco!");
InitializeDomain();
};
// used to be done in PostAuthorizeRequest but then it disabled OutputCaching due
// to rewriting happening too early in the chain (Alex Norcliffe 2010-02).
app.PostResolveRequestCache += (sender, e) =>
{
HttpContext httpContext = ((HttpApplication)sender).Context;
ProcessRequest(httpContext);
};
// todo: initialize request errors handler
// todo: initialize XML cache flush
}
#endregion
}
}