2012-07-20 01:04:35 +06:00
using System ;
2013-07-31 17:08:56 +10:00
using System.Collections ;
2013-09-03 17:19:31 +10:00
using System.Collections.Generic ;
2012-08-06 23:04:08 +06:00
using System.IO ;
2012-07-20 01:04:35 +06:00
using System.Linq ;
2013-07-31 17:08:56 +10:00
using System.Security.Principal ;
using System.Threading ;
2015-01-29 12:45:44 +11:00
using System.Threading.Tasks ;
2012-07-20 01:04:35 +06:00
using System.Web ;
2013-10-15 18:46:44 +11:00
using System.Web.Mvc ;
2012-08-08 23:10:34 +06:00
using System.Web.Routing ;
2013-08-15 17:14:05 +10:00
using Newtonsoft.Json ;
2012-07-20 01:04:35 +06:00
using Umbraco.Core ;
2013-08-31 11:28:19 +10:00
using Umbraco.Core.Configuration ;
2013-04-05 23:28:43 +06:00
using Umbraco.Core.IO ;
2012-07-28 23:40:48 +06:00
using Umbraco.Core.Logging ;
2013-07-31 17:08:56 +10:00
using Umbraco.Core.Security ;
2013-10-15 18:46:44 +11:00
using Umbraco.Web.Editors ;
2012-07-20 01:04:35 +06:00
using Umbraco.Web.Routing ;
2013-07-31 17:08:56 +10:00
using Umbraco.Web.Security ;
2012-07-29 00:04:11 +06:00
using umbraco ;
using GlobalSettings = Umbraco . Core . Configuration . GlobalSettings ;
2013-08-31 11:28:19 +10:00
using ObjectExtensions = Umbraco . Core . ObjectExtensions ;
using RenderingEngine = Umbraco . Core . RenderingEngine ;
2012-07-20 01:04:35 +06:00
namespace Umbraco.Web
{
2012-07-30 22:52:59 +06:00
// also look at IOHelper.ResolveUrlsFromTextString - nightmarish?!
2012-07-20 01:04:35 +06:00
2012-07-30 22:52:59 +06:00
// context.RewritePath supports ~/ or else must begin with /vdir
// Request.RawUrl is still there
// response.Redirect does?! always remap to /vdir?!
2012-07-20 01:04:35 +06:00
2014-03-21 14:50:24 +11:00
public class
UmbracoModule : IHttpModule
2012-07-30 22:52:59 +06:00
{
2012-08-08 23:10:34 +06:00
#region HttpModule event handlers
2012-08-06 22:40:06 +06:00
2012-07-30 22:52:59 +06:00
/// <summary>
2012-09-24 12:49:36 -02:00
/// Begins to process a request.
2012-07-30 22:52:59 +06:00
/// </summary>
/// <param name="httpContext"></param>
2013-07-31 17:08:56 +10:00
static void BeginRequest ( HttpContextBase httpContext )
2012-07-30 22:52:59 +06:00
{
2013-08-14 13:38:28 +10:00
2013-04-05 23:28:43 +06:00
//we need to set the initial url in our ApplicationContext, this is so our keep alive service works and this must
//exist on a global context because the keep alive service doesn't run in a web context.
//we are NOT going to put a lock on this because locking will slow down the application and we don't really care
//if two threads write to this at the exact same time during first page hit.
//see: http://issues.umbraco.org/issue/U4-2059
if ( ApplicationContext . Current . OriginalRequestUrl . IsNullOrWhiteSpace ( ) )
{
2014-12-09 13:38:59 -05:00
// If (HTTP and SSL not required) or (HTTPS and SSL required), use ports from request to configure OriginalRequestUrl.
2014-12-10 03:13:49 -05:00
// Otherwise, user may need to set baseUrl manually per http://our.umbraco.org/documentation/Using-Umbraco/Config-files/umbracoSettings/#ScheduledTasks if non-standard ports used.
2014-12-09 13:38:59 -05:00
if ( ( ! httpContext . Request . IsSecureConnection & & ! GlobalSettings . UseSSL ) | | ( httpContext . Request . IsSecureConnection & & GlobalSettings . UseSSL ) )
{
// Use port from request.
ApplicationContext . Current . OriginalRequestUrl = string . Format ( "{0}:{1}{2}" , httpContext . Request . ServerVariables [ "SERVER_NAME" ] , httpContext . Request . ServerVariables [ "SERVER_PORT" ] , IOHelper . ResolveUrl ( SystemDirectories . Umbraco ) ) ;
}
else
{
// Omit port entirely.
ApplicationContext . Current . OriginalRequestUrl = string . Format ( "{0}{1}" , httpContext . Request . ServerVariables [ "SERVER_NAME" ] , IOHelper . ResolveUrl ( SystemDirectories . Umbraco ) ) ;
}
2014-09-30 12:44:52 -04:00
2014-08-25 16:40:45 +10:00
LogHelper . Info < UmbracoModule > ( "Setting OriginalRequestUrl: " + ApplicationContext . Current . OriginalRequestUrl ) ;
2013-04-05 23:28:43 +06:00
}
2012-09-28 07:04:33 -02:00
// do not process if client-side request
2013-05-12 15:25:46 -10:00
if ( httpContext . Request . Url . IsClientSideRequest ( ) )
2012-11-11 09:09:06 +05:00
return ;
//write the trace output for diagnostics at the end of the request
httpContext . Trace . Write ( "UmbracoModule" , "Umbraco request begins" ) ;
2012-08-07 02:33:08 +06:00
2013-07-31 17:08:56 +10:00
// ok, process
2012-09-28 07:04:33 -02:00
2013-07-31 17:08:56 +10:00
// create the LegacyRequestInitializer
// and initialize legacy stuff
var legacyRequestInitializer = new LegacyRequestInitializer ( httpContext . Request . Url , httpContext ) ;
legacyRequestInitializer . InitializeRequest ( ) ;
2012-07-21 00:47:17 +06:00
2013-07-31 17:08:56 +10:00
// create the UmbracoContext singleton, one per request, and assign
// NOTE: we assign 'true' to ensure the context is replaced if it is already set (i.e. during app startup)
2013-07-31 18:21:27 +10:00
UmbracoContext . EnsureContext (
httpContext ,
ApplicationContext . Current ,
new WebSecurity ( httpContext , ApplicationContext . Current ) ,
true ) ;
2012-09-24 12:49:36 -02:00
}
/// <summary>
/// Processses the Umbraco Request
/// </summary>
/// <param name="httpContext"></param>
2013-07-23 13:33:37 +10:00
/// <remarks>
///
/// This will check if we are trying to route to the default back office page (i.e. ~/Umbraco/ or ~/Umbraco or ~/Umbraco/Default )
/// and ensure that the MVC handler executes for that. This is required because the route for /Umbraco will never execute because
/// files/folders exist there and we cannot set the RouteCollection.RouteExistingFiles = true since that will muck a lot of other things up.
/// So we handle it here and explicitly execute the MVC controller.
///
/// </remarks>
2012-09-24 12:49:36 -02:00
void ProcessRequest ( HttpContextBase httpContext )
{
2012-09-28 07:04:33 -02:00
// do not process if client-side request
2013-05-12 15:25:46 -10:00
if ( httpContext . Request . Url . IsClientSideRequest ( ) )
2012-09-24 12:49:36 -02:00
return ;
2012-10-26 02:55:57 +05:00
if ( UmbracoContext . Current = = null )
2013-01-18 13:13:06 -01:00
throw new InvalidOperationException ( "The UmbracoContext.Current is null, ProcessRequest cannot proceed unless there is a current UmbracoContext" ) ;
2012-10-26 02:55:57 +05:00
if ( UmbracoContext . Current . RoutingContext = = null )
2013-01-18 13:13:06 -01:00
throw new InvalidOperationException ( "The UmbracoContext.RoutingContext has not been assigned, ProcessRequest cannot proceed unless there is a RoutingContext assigned to the UmbracoContext" ) ;
2012-10-26 02:55:57 +05:00
2014-01-22 15:26:45 +11:00
var umbracoContext = UmbracoContext . Current ;
2012-09-13 12:19:56 +07:00
2013-07-23 13:33:37 +10:00
//re-write for the default back office path
if ( httpContext . Request . Url . IsDefaultBackOfficeRequest ( ) )
{
2013-11-01 09:35:39 +11:00
if ( EnsureIsConfigured ( httpContext , umbracoContext . OriginalRequestUrl ) )
{
RewriteToBackOfficeHandler ( httpContext ) ;
}
2013-07-23 13:33:37 +10:00
return ;
}
2012-09-28 07:04:33 -02:00
// do not process but remap to handler if it is a base rest request
2012-10-26 02:55:57 +05:00
if ( BaseRest . BaseRestHandler . IsBaseRestRequest ( umbracoContext . OriginalRequestUrl ) )
2012-09-18 08:54:54 -02:00
{
2012-09-20 14:37:23 -02:00
httpContext . RemapHandler ( new BaseRest . BaseRestHandler ( ) ) ;
2012-09-28 07:04:33 -02:00
return ;
2012-09-18 08:54:54 -02:00
}
2012-09-28 07:04:33 -02:00
// do not process if this request is not a front-end routable page
2013-02-13 06:12:43 +06:00
var isRoutableAttempt = EnsureUmbracoRoutablePage ( umbracoContext , httpContext ) ;
//raise event here
OnRouteAttempt ( new RoutableAttemptEventArgs ( isRoutableAttempt . Result , umbracoContext , httpContext ) ) ;
if ( ! isRoutableAttempt . Success )
{
return ;
}
2012-09-28 07:04:33 -02:00
2012-11-11 09:09:06 +05:00
httpContext . Trace . Write ( "UmbracoModule" , "Umbraco request confirmed" ) ;
2012-09-28 07:04:33 -02:00
// ok, process
2013-01-18 16:08:01 -01:00
// note: requestModule.UmbracoRewrite also did some stripping of &umbPage
// from the querystring... that was in v3.x to fix some issues with pre-forms
// auth. Paul Sterling confirmed in jan. 2013 that we can get rid of it.
2012-09-28 07:04:33 -02:00
2013-01-18 13:13:06 -01:00
// instanciate, prepare and process the published content request
2012-10-29 12:35:39 -01:00
// important to use CleanedUmbracoUrl - lowercase path-only version of the current url
2013-01-18 16:08:01 -01:00
var pcr = new PublishedContentRequest ( umbracoContext . CleanedUmbracoUrl , umbracoContext . RoutingContext ) ;
2013-01-18 13:13:06 -01:00
umbracoContext . PublishedContentRequest = pcr ;
pcr . Prepare ( ) ;
2013-02-26 16:52:42 -01:00
// HandleHttpResponseStatus returns a value indicating that the request should
// not be processed any further, eg because it has been redirect. then, exit.
if ( HandleHttpResponseStatus ( httpContext , pcr ) )
return ;
if ( ! pcr . HasPublishedContent )
2013-01-18 13:13:06 -01:00
httpContext . RemapHandler ( new PublishedContentNotFoundHandler ( ) ) ;
else
RewriteToUmbracoHandler ( httpContext , pcr ) ;
2012-07-30 22:52:59 +06:00
}
2012-07-20 01:04:35 +06:00
2013-07-31 17:08:56 +10:00
/// <summary>
/// Authenticates the request by reading the FormsAuthentication cookie and setting the
/// context and thread principle object
/// </summary>
/// <param name="sender"></param>
2014-12-05 10:29:18 +11:00
/// <param name="e"></param>
2013-08-05 16:20:39 +10:00
/// <remarks>
/// We will set the identity, culture, etc... for any request that is:
/// * A back office request
/// * An installer request
/// * A /base request (since these can be back office web service requests)
/// </remarks>
2013-07-31 17:08:56 +10:00
static void AuthenticateRequest ( object sender , EventArgs e )
2013-02-26 16:52:42 -01:00
{
2013-07-31 17:08:56 +10:00
var app = ( HttpApplication ) sender ;
var http = new HttpContextWrapper ( app . Context ) ;
2013-02-26 16:52:42 -01:00
2013-07-31 17:08:56 +10:00
// do not process if client-side request
if ( http . Request . Url . IsClientSideRequest ( ) )
return ;
2013-02-26 16:52:42 -01:00
2013-09-30 16:06:56 +10:00
var req = new HttpRequestWrapper ( app . Request ) ;
if ( ShouldAuthenticateRequest ( req , UmbracoContext . Current . OriginalRequestUrl ) )
2013-02-26 16:52:42 -01:00
{
2014-12-05 10:29:18 +11:00
//TODO: Here we should have an authentication mechanism, this mechanism should be smart in the way that the ASP.Net 5 pipeline works
// in which each registered handler will attempt to authenticate and if it fails it will just call Next() so the next handler
// executes. If it is successful, it doesn't call next and assigns the current user/principal.
// This might actually all be possible with ASP.Net Identity and how it is setup to work already, need to investigate.
2013-07-31 17:08:56 +10:00
var ticket = http . GetUmbracoAuthTicket ( ) ;
2013-11-01 15:37:59 +11:00
http . AuthenticateCurrentRequest ( ticket , ShouldIgnoreTicketRenew ( UmbracoContext . Current . OriginalRequestUrl , http ) = = false ) ;
2013-02-26 16:52:42 -01:00
}
}
2012-08-08 23:10:34 +06:00
#endregion
2013-07-31 17:08:56 +10:00
#region Methods
2012-08-08 23:10:34 +06:00
2013-09-30 16:06:56 +10:00
/// <summary>
/// Determines if we should authenticate the request
/// </summary>
/// <param name="request"></param>
/// <param name="originalRequestUrl"></param>
/// <returns></returns>
/// <remarks>
/// We auth the request when:
/// * it is a back office request
/// * it is an installer request
/// * it is a /base request
/// * it is a preview request
/// </remarks>
internal static bool ShouldAuthenticateRequest ( HttpRequestBase request , Uri originalRequestUrl )
{
if ( //check back office
2014-01-15 13:28:41 +11:00
request . Url . IsBackOfficeRequest ( HttpRuntime . AppDomainAppVirtualPath )
2013-09-30 16:06:56 +10:00
//check installer
| | request . Url . IsInstallerRequest ( )
//detect in preview
| | ( request . HasPreviewCookie ( ) & & request . Url ! = null & & request . Url . AbsolutePath . StartsWith ( IOHelper . ResolveUrl ( SystemDirectories . Umbraco ) ) = = false )
//check for base
| | BaseRest . BaseRestHandler . IsBaseRestRequest ( originalRequestUrl ) )
{
return true ;
}
return false ;
}
2013-10-15 18:46:44 +11:00
private static readonly ConcurrentHashSet < string > IgnoreTicketRenewUrls = new ConcurrentHashSet < string > ( ) ;
/// <summary>
/// Determines if the authentication ticket should be renewed with a new timeout
/// </summary>
/// <param name="url"></param>
/// <param name="httpContext"></param>
/// <returns></returns>
/// <remarks>
2013-11-12 17:38:32 +11:00
/// We do not want to renew the ticket when we are checking for the user's remaining timeout unless -
/// UmbracoConfig.For.UmbracoSettings().Security.KeepUserLoggedIn == true
2013-10-15 18:46:44 +11:00
/// </remarks>
internal static bool ShouldIgnoreTicketRenew ( Uri url , HttpContextBase httpContext )
{
2013-11-12 17:38:32 +11:00
//this setting will renew the ticket for all requests.
if ( UmbracoConfig . For . UmbracoSettings ( ) . Security . KeepUserLoggedIn )
{
return false ;
}
2013-10-15 18:46:44 +11:00
//initialize the ignore ticket urls - we don't need to lock this, it's concurrent and a hashset
// we don't want to have to gen the url each request so this will speed things up a teeny bit.
if ( IgnoreTicketRenewUrls . Any ( ) = = false )
{
var urlHelper = new UrlHelper ( new RequestContext ( httpContext , new RouteData ( ) ) ) ;
2013-10-16 11:31:35 +11:00
var checkSessionUrl = urlHelper . GetUmbracoApiService < AuthenticationController > ( controller = > controller . GetRemainingTimeoutSeconds ( ) ) ;
2013-10-15 18:46:44 +11:00
IgnoreTicketRenewUrls . Add ( checkSessionUrl ) ;
}
if ( IgnoreTicketRenewUrls . Any ( x = > url . AbsolutePath . StartsWith ( x ) ) )
{
return true ;
}
return false ;
}
2012-08-08 23:10:34 +06:00
/// <summary>
/// Checks the current request and ensures that it is routable based on the structure of the request and URI
2012-09-13 09:00:21 +07:00
/// </summary>
/// <param name="context"></param>
2012-08-08 23:10:34 +06:00
/// <param name="httpContext"></param>
/// <returns></returns>
2013-02-13 06:12:43 +06:00
internal Attempt < EnsureRoutableOutcome > EnsureUmbracoRoutablePage ( UmbracoContext context , HttpContextBase httpContext )
2012-08-08 23:10:34 +06:00
{
2012-10-26 02:55:57 +05:00
var uri = context . OriginalRequestUrl ;
2012-09-13 09:00:21 +07:00
2013-02-19 06:37:25 -01:00
var reason = EnsureRoutableOutcome . IsRoutable ;
2013-02-13 06:12:43 +06:00
2012-08-08 23:10:34 +06:00
// ensure this is a document request
2012-09-13 09:00:21 +07:00
if ( ! EnsureDocumentRequest ( httpContext , uri ) )
2013-02-13 06:12:43 +06:00
{
reason = EnsureRoutableOutcome . NotDocumentRequest ;
}
2012-08-08 23:10:34 +06:00
// ensure Umbraco is ready to serve documents
2013-02-13 06:12:43 +06:00
else if ( ! EnsureIsReady ( httpContext , uri ) )
{
reason = EnsureRoutableOutcome . NotReady ;
}
2012-08-08 23:10:34 +06:00
// ensure Umbraco is properly configured to serve documents
2013-02-13 06:12:43 +06:00
else if ( ! EnsureIsConfigured ( httpContext , uri ) )
{
reason = EnsureRoutableOutcome . NotConfigured ;
}
2012-09-28 07:04:33 -02:00
// ensure Umbraco has documents to serve
2013-02-13 06:12:43 +06:00
else if ( ! EnsureHasContent ( context , httpContext ) )
{
reason = EnsureRoutableOutcome . NoContent ;
}
2012-08-08 23:10:34 +06:00
2013-09-04 17:14:06 +02:00
return Attempt . If ( reason = = EnsureRoutableOutcome . IsRoutable , reason ) ;
2012-07-29 00:04:11 +06:00
}
2012-07-30 22:52:59 +06:00
2012-07-28 23:40:48 +06:00
/// <summary>
/// Ensures that the request is a document request (i.e. one that the module should handle)
/// </summary>
/// <param name="httpContext"></param>
/// <param name="uri"></param>
/// <returns></returns>
2013-07-31 17:08:56 +10:00
static bool EnsureDocumentRequest ( HttpContextBase httpContext , Uri uri )
2012-07-30 22:52:59 +06:00
{
var maybeDoc = true ;
2012-09-13 09:00:21 +07:00
var lpath = uri . AbsolutePath . ToLowerInvariant ( ) ;
2012-07-30 22:52:59 +06:00
// handle directory-urls used for asmx
// legacy - what's the point really?
2013-02-19 06:37:25 -01:00
if ( /*maybeDoc &&*/ GlobalSettings . UseDirectoryUrls )
2012-07-30 22:52:59 +06:00
{
2013-02-19 06:37:25 -01:00
int asmxPos = lpath . IndexOf ( ".asmx/" , StringComparison . OrdinalIgnoreCase ) ;
2012-07-30 22:52:59 +06:00
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
2012-11-19 19:56:52 +05:00
if ( maybeDoc & & GlobalSettings . IsReservedPathOrUrl ( lpath , httpContext , RouteTable . Routes ) )
2012-07-30 22:52:59 +06:00
maybeDoc = false ;
2012-10-11 02:30:48 +05:00
//NOTE: No need to warn, plus if we do we should log the document, as this message doesn't really tell us anything :)
//if (!maybeDoc)
//{
// LogHelper.Warn<UmbracoModule>("Not a document");
//}
2012-07-30 22:52:59 +06:00
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
2013-07-31 17:08:56 +10:00
static bool EnsureIsReady ( HttpContextBase httpContext , Uri uri )
2012-07-30 22:52:59 +06:00
{
2012-09-28 11:11:47 -02:00
var ready = ApplicationContext . Current . IsReady ;
2012-07-30 22:52:59 +06:00
// ensure we are ready
2012-09-28 11:11:47 -02:00
if ( ! ready )
2012-07-28 23:40:48 +06:00
{
LogHelper . Warn < UmbracoModule > ( "Umbraco is not ready" ) ;
2012-07-30 22:52:59 +06:00
2013-09-25 19:23:41 +10:00
if ( UmbracoConfig . For . UmbracoSettings ( ) . Content . EnableSplashWhileLoading = = false )
2012-07-30 22:52:59 +06:00
{
2012-09-28 11:11:47 -02:00
// let requests pile up and wait for 10s then show the splash anyway
ready = ApplicationContext . Current . WaitForReady ( 10 * 1000 ) ;
2012-07-30 22:52:59 +06:00
}
2012-09-28 11:11:47 -02:00
if ( ! ready )
{
httpContext . Response . StatusCode = 503 ;
2012-08-07 02:33:08 +06:00
2013-09-13 18:11:20 +10:00
var bootUrl = "~/config/splashes/booting.aspx" ;
2012-09-28 11:11:47 -02:00
httpContext . RewritePath ( UriUtility . ToAbsolute ( bootUrl ) + "?url=" + HttpUtility . UrlEncode ( uri . ToString ( ) ) ) ;
return false ;
}
2012-07-30 22:52:59 +06:00
}
return true ;
}
2012-09-28 11:11:47 -02:00
// ensures Umbraco has at least one published node
// if not, rewrites to splash and return false
// if yes, return true
2013-02-19 06:37:25 -01:00
private static bool EnsureHasContent ( UmbracoContext context , HttpContextBase httpContext )
2012-09-28 11:11:47 -02:00
{
2013-03-19 17:54:41 -01:00
if ( context . ContentCache . HasContent ( ) )
2013-02-19 06:37:25 -01:00
return true ;
2012-09-28 11:11:47 -02:00
2013-02-19 06:37:25 -01:00
LogHelper . Warn < UmbracoModule > ( "Umbraco has no content" ) ;
2014-03-11 17:28:55 +01:00
2013-02-19 06:37:25 -01:00
const string noContentUrl = "~/config/splashes/noNodes.aspx" ;
httpContext . RewritePath ( UriUtility . ToAbsolute ( noContentUrl ) ) ;
return false ;
2012-09-28 11:11:47 -02:00
}
2012-09-28 07:04:33 -02:00
2012-07-30 22:52:59 +06:00
// ensures Umbraco is configured
// if not, redirect to install and return false
// if yes, return true
2013-02-19 06:37:25 -01:00
private static bool EnsureIsConfigured ( HttpContextBase httpContext , Uri uri )
{
if ( ApplicationContext . Current . IsConfigured )
return true ;
2012-07-28 23:40:48 +06:00
2013-02-19 06:37:25 -01:00
LogHelper . Warn < UmbracoModule > ( "Umbraco is not configured" ) ;
2014-02-20 13:09:54 +11:00
var installPath = UriUtility . ToAbsolute ( SystemDirectories . Install ) ;
2014-02-26 04:15:14 +11:00
var installUrl = string . Format ( "{0}/?redir=true&url={1}" , installPath , HttpUtility . UrlEncode ( uri . ToString ( ) ) ) ;
2013-02-19 06:37:25 -01:00
httpContext . Response . Redirect ( installUrl , true ) ;
return false ;
2012-07-30 22:52:59 +06:00
}
2013-07-31 17:08:56 +10:00
// returns a value indicating whether redirection took place and the request has
// been completed - because we don't want to Response.End() here to terminate
// everything properly.
internal static bool HandleHttpResponseStatus ( HttpContextBase context , PublishedContentRequest pcr )
{
var end = false ;
var response = context . Response ;
LogHelper . Debug < UmbracoModule > ( "Response status: Redirect={0}, Is404={1}, StatusCode={2}" ,
( ) = > pcr . IsRedirect ? ( pcr . IsRedirectPermanent ? "permanent" : "redirect" ) : "none" ,
( ) = > pcr . Is404 ? "true" : "false" , ( ) = > pcr . ResponseStatusCode ) ;
if ( pcr . IsRedirect )
{
if ( pcr . IsRedirectPermanent )
response . RedirectPermanent ( pcr . RedirectUrl , false ) ; // do not end response
else
response . Redirect ( pcr . RedirectUrl , false ) ; // do not end response
end = true ;
}
else if ( pcr . Is404 )
{
response . StatusCode = 404 ;
2013-09-25 19:23:41 +10:00
response . TrySkipIisCustomErrors = UmbracoConfig . For . UmbracoSettings ( ) . WebRouting . TrySkipIisCustomErrors ;
2013-07-31 17:08:56 +10:00
}
if ( pcr . ResponseStatusCode > 0 )
{
// set status code -- even for redirects
response . StatusCode = pcr . ResponseStatusCode ;
response . StatusDescription = pcr . ResponseStatusDescription ;
}
//if (pcr.IsRedirect)
// response.End(); // end response -- kills the thread and does not return!
if ( pcr . IsRedirect )
{
response . Flush ( ) ;
// bypass everything and directly execute EndRequest event -- but returns
context . ApplicationInstance . CompleteRequest ( ) ;
// though some say that .CompleteRequest() does not properly shutdown the response
// and the request will hang until the whole code has run... would need to test?
LogHelper . Debug < UmbracoModule > ( "Response status: redirecting, complete request now." ) ;
}
return end ;
}
2012-07-20 01:04:35 +06:00
2013-07-23 13:33:37 +10:00
/// <summary>
/// Rewrites to the default back office page.
/// </summary>
/// <param name="context"></param>
2013-07-31 17:08:56 +10:00
private static void RewriteToBackOfficeHandler ( HttpContextBase context )
2013-07-23 13:33:37 +10:00
{
// GlobalSettings.Path has already been through IOHelper.ResolveUrl() so it begins with / and vdir (if any)
var rewritePath = GlobalSettings . Path . TrimEnd ( new [ ] { '/' } ) + "/Default" ;
// rewrite the path to the path of the handler (i.e. /umbraco/RenderMvc)
context . RewritePath ( rewritePath , "" , "" , false ) ;
//if it is MVC we need to do something special, we are not using TransferRequest as this will
//require us to rewrite the path with query strings and then reparse the query strings, this would
//also mean that we need to handle IIS 7 vs pre-IIS 7 differently. Instead we are just going to create
//an instance of the UrlRoutingModule and call it's PostResolveRequestCache method. This does:
// * Looks up the route based on the new rewritten URL
// * Creates the RequestContext with all route parameters and then executes the correct handler that matches the route
//we also cannot re-create this functionality because the setter for the HttpContext.Request.RequestContext is internal
//so really, this is pretty much the only way without using Server.TransferRequest and if we did that, we'd have to rethink
//a bunch of things!
var urlRouting = new UrlRoutingModule ( ) ;
urlRouting . PostResolveRequestCache ( context ) ;
}
2013-07-31 17:08:56 +10:00
/// <summary>
2014-02-13 13:19:51 +11:00
/// Rewrites to the Umbraco handler - we always send the request via our MVC rendering engine, this will deal with
/// requests destined for webforms.
2013-07-31 17:08:56 +10:00
/// </summary>
/// <param name="context"></param>
2013-02-13 06:12:43 +06:00
/// <param name="pcr"> </param>
2013-07-31 17:08:56 +10:00
private static void RewriteToUmbracoHandler ( HttpContextBase context , PublishedContentRequest pcr )
{
// NOTE: we do not want to use TransferRequest even though many docs say it is better with IIS7, turns out this is
// not what we need. The purpose of TransferRequest is to ensure that .net processes all of the rules for the newly
// rewritten url, but this is not what we want!
// read: http://forums.iis.net/t/1146511.aspx
2012-07-30 22:52:59 +06:00
2014-02-13 13:19:51 +11:00
var query = pcr . Uri . Query . TrimStart ( new [ ] { '?' } ) ;
2012-08-07 04:55:27 +06:00
2014-02-13 13:19:51 +11:00
// GlobalSettings.Path has already been through IOHelper.ResolveUrl() so it begins with / and vdir (if any)
var rewritePath = GlobalSettings . Path . TrimEnd ( new [ ] { '/' } ) + "/RenderMvc" ;
// rewrite the path to the path of the handler (i.e. /umbraco/RenderMvc)
context . RewritePath ( rewritePath , "" , query , false ) ;
2013-01-28 18:36:58 -01:00
2014-02-13 13:19:51 +11:00
//if it is MVC we need to do something special, we are not using TransferRequest as this will
//require us to rewrite the path with query strings and then reparse the query strings, this would
//also mean that we need to handle IIS 7 vs pre-IIS 7 differently. Instead we are just going to create
//an instance of the UrlRoutingModule and call it's PostResolveRequestCache method. This does:
// * Looks up the route based on the new rewritten URL
// * Creates the RequestContext with all route parameters and then executes the correct handler that matches the route
//we also cannot re-create this functionality because the setter for the HttpContext.Request.RequestContext is internal
//so really, this is pretty much the only way without using Server.TransferRequest and if we did that, we'd have to rethink
//a bunch of things!
var urlRouting = new UrlRoutingModule ( ) ;
urlRouting . PostResolveRequestCache ( context ) ;
2013-07-31 17:08:56 +10:00
}
2015-01-29 12:45:44 +11:00
/// <summary>
2013-07-31 17:08:56 +10:00
/// Any object that is in the HttpContext.Items collection that is IDisposable will get disposed on the end of the request
/// </summary>
/// <param name="http"></param>
private static void DisposeHttpContextItems ( HttpContext http )
{
2013-09-30 12:02:35 +10:00
// do not process if client-side request
if ( http . Request . Url . IsClientSideRequest ( ) )
return ;
2013-09-03 17:19:31 +10:00
//get a list of keys to dispose
var keys = new HashSet < object > ( ) ;
2013-07-31 17:08:56 +10:00
foreach ( DictionaryEntry i in http . Items )
{
2013-09-30 12:02:35 +10:00
if ( i . Value is IDisposeOnRequestEnd | | i . Key is IDisposeOnRequestEnd )
2013-09-03 17:19:31 +10:00
{
keys . Add ( i . Key ) ;
}
}
//dispose each item and key that was found as disposable.
foreach ( var k in keys )
{
try
{
http . Items [ k ] . DisposeIfDisposable ( ) ;
}
catch ( Exception ex )
{
LogHelper . Error < UmbracoModule > ( "Could not dispose item with key " + k , ex ) ;
}
try
{
k . DisposeIfDisposable ( ) ;
}
catch ( Exception ex )
{
LogHelper . Error < UmbracoModule > ( "Could not dispose item key " + k , ex ) ;
}
2013-07-31 17:08:56 +10:00
}
}
#endregion
2012-07-30 22:52:59 +06:00
#region IHttpModule
2012-08-08 23:10:34 +06:00
/// <summary>
/// Initialize the module, this will trigger for each new application
/// and there may be more than 1 application per application domain
/// </summary>
/// <param name="app"></param>
2012-07-30 22:52:59 +06:00
public void Init ( HttpApplication app )
{
2012-09-24 12:49:36 -02:00
app . BeginRequest + = ( sender , e ) = >
{
2013-02-26 16:52:42 -01:00
var httpContext = ( ( HttpApplication ) sender ) . Context ;
LogHelper . Debug < UmbracoModule > ( "Begin request: {0}." , ( ) = > httpContext . Request . Url ) ;
BeginRequest ( new HttpContextWrapper ( httpContext ) ) ;
2012-09-24 12:49:36 -02:00
} ;
2012-08-07 02:33:08 +06:00
2013-07-31 17:08:56 +10:00
app . AuthenticateRequest + = AuthenticateRequest ;
2013-02-19 06:37:25 -01:00
app . PostResolveRequestCache + = ( sender , e ) = >
2012-08-08 23:10:34 +06:00
{
2012-09-13 12:19:56 +07:00
var httpContext = ( ( HttpApplication ) sender ) . Context ;
2012-08-08 23:10:34 +06:00
ProcessRequest ( new HttpContextWrapper ( httpContext ) ) ;
} ;
2012-09-13 12:19:56 +07:00
2012-11-11 09:09:06 +05:00
app . EndRequest + = ( sender , args ) = >
{
2012-12-11 12:03:36 +05:00
var httpContext = ( ( HttpApplication ) sender ) . Context ;
2012-11-12 08:10:12 +05:00
if ( UmbracoContext . Current ! = null & & UmbracoContext . Current . IsFrontEndUmbracoRequest )
{
2014-06-25 18:56:51 +10:00
LogHelper . Debug < UmbracoModule > (
"Total milliseconds for umbraco request to process: {0}" , ( ) = > DateTime . Now . Subtract ( UmbracoContext . Current . ObjectCreated ) . TotalMilliseconds ) ;
2012-12-11 12:03:36 +05:00
}
2014-03-21 14:50:24 +11:00
OnEndRequest ( new EventArgs ( ) ) ;
2012-12-11 12:03:36 +05:00
DisposeHttpContextItems ( httpContext ) ;
2012-11-11 09:09:06 +05:00
} ;
2013-04-25 13:25:25 -10:00
//disable asp.net headers (security)
app . PreSendRequestHeaders + = ( sender , args ) = >
{
var httpContext = ( ( HttpApplication ) sender ) . Context ;
2013-06-06 00:48:19 +12:00
try
{
httpContext . Response . Headers . Remove ( "Server" ) ;
//this doesn't normally work since IIS sets it but we'll keep it here anyways.
httpContext . Response . Headers . Remove ( "X-Powered-By" ) ;
}
catch ( PlatformNotSupportedException ex )
{
// can't remove headers this way on IIS6 or cassini.
}
2013-04-25 13:25:25 -10:00
} ;
2012-07-30 22:52:59 +06:00
}
2012-07-20 01:04:35 +06:00
2012-07-30 22:52:59 +06:00
public void Dispose ( )
2012-08-08 23:10:34 +06:00
{
2012-09-13 12:19:56 +07:00
2012-08-08 23:10:34 +06:00
}
2012-07-20 01:04:35 +06:00
2012-07-30 22:52:59 +06:00
#endregion
2012-07-20 01:04:35 +06:00
2013-02-13 06:12:43 +06:00
#region Events
internal static event EventHandler < RoutableAttemptEventArgs > RouteAttempt ;
private void OnRouteAttempt ( RoutableAttemptEventArgs args )
{
if ( RouteAttempt ! = null )
RouteAttempt ( this , args ) ;
2014-03-21 14:50:24 +11:00
}
internal static event EventHandler < EventArgs > EndRequest ;
private void OnEndRequest ( EventArgs args )
{
if ( EndRequest ! = null )
EndRequest ( this , args ) ;
2013-02-13 06:12:43 +06:00
}
#endregion
2012-07-30 22:52:59 +06:00
}
2012-07-20 01:04:35 +06:00
}