/// <summary> /// Invokes the requested method with no exception handling /// </summary> /// <param name="context">The context.</param> /// <param name="requestPoint">The request point.</param> /// <param name="requestContext">The request context.</param> protected void InvokeMethodDirect(HttpContextBase context, string requestPoint, IContext requestContext) { ControllerInvocationInfo[] controllers = dispatcher.GetControllers(requestPoint); if (controllers.Length == 0) { logger.Report(Messages.ControllerNotFound, requestPoint); throw new WebException(StatusCode.NotFound, String.Format("'{0} could not be found", requestPoint)); } bool securityCheckComplete = false; bool securityCheckFailed = false; var failedPermissions = new Dictionary<string, KeyValuePair<FailAction, string>>(); foreach (ControllerInvocationInfo info in controllers) { IController controller = manager.GetController(info, context, requestContext); try { // all security controllers are guaranteed to be at the top of the list, in proper order. // we need to run through all of them, because an inner controller may override the decision // of an outer controller. therefore, the final decision is deferred until all security checks // have passed if (!securityCheckComplete) { ISecurityController securityController = controller as ISecurityController; if (securityController == null) securityCheckComplete = true; else { // this needs to run prior to the check controller.ProcessRequest(context, requestContext); // we only care about the actual return value of the last controller, as intermediate // results can be overriden by subsequent controllers. discard intermediate return values. securityCheckFailed = !securityController.HasAccess(requestContext, failedPermissions); } } // we have to do the check a second time, because the securityCheckComplete flag // gets set inside the top if. doing this in an else up top would skip the first // non-security controller if (securityCheckComplete) { if (securityCheckFailed || failedPermissions.Count != 0) { StringBuilder builder = new StringBuilder(); foreach (string perm in failedPermissions.Keys) builder.AppendLine(perm); foreach (KeyValuePair<FailAction, string> kvp in failedPermissions.Values) if (kvp.Key == FailAction.Redirect) { // currently you can only house one transfer request per context, // however, that may change in the future. requestContext.ClearTransferRequest(); // give the fail action target a chance to redirect after re-validating requestContext.Transfer( kvp.Value + (kvp.Value.Contains("?") ? "&" : "?") + "originalRequest=" + HttpUtility.UrlEncode(requestPoint)); logger.Report(Messages.SecurityRedirect, kvp.Value, builder.ToString()); break; } if (!requestContext.TransferRequested) throw new WebException(StatusCode.Unauthorized, "Access denied"); else // break out of the controller loop. we shouldn't be processing any more // controllers for this request, and need to get into whatever the security // guys requested break; } else { if (info.BindPoint.Controller.DefaultTemplates.Count > 0) requestContext.Response.RenderWith(info.BindPoint.Controller.DefaultTemplates); controller.ProcessRequest(context, requestContext); } } } finally { manager.ReturnController(controller, context, requestContext); } } if (requestContext.TransferRequested) { string transferRequestPoint = BindPointUtilities.VerbQualify(requestContext.TransferTarget, "get"); requestContext.ClearTransferRequest(); InvokeMethod(context, transferRequestPoint, requestContext); } }
/// <summary> /// Enables processing of HTTP Web requests by a custom HttpHandler that implements the <see cref="T:System.Web.IHttpHandler"/> interface. /// </summary> /// <param name="context">An <see cref="T:System.Web.HttpContext"/> object that provides references to the intrinsic server objects (for example, Request, Response, Session, and Server) used to service HTTP requests.</param> /// <summary> /// Obtains and processes the chain of controllers servicing this request. If a transfer is requested /// during the processing of a controller, the computed chain of controllers finishes, and then a /// recursive call is made to process the new contrller. /// </summary> /// <param name="context">The context.</param> /// <param name="requestPoint">The request point.</param> /// <param name="requestContext">The request context.</param> public virtual void InvokeMethod(HttpContextBase context, string requestPoint, IContext requestContext, bool handleException) { logger.Report(Messages.ProcessingRequest, requestPoint); try { List<ControllerInvocationInfo> invocationInfos = dispatcher.GetControllers(requestPoint); // TODO: this code should be replaced with call to the check method // when it will be implemented on the dispatcher correctly if (invocationInfos.Count() == 0) { logger.Report(Messages.ControllerNotFound, requestPoint); throw new WebException(StatusCode.NotFound, String.Format("'{0} could not be found", requestPoint)); } StringBuilder path = new StringBuilder(); foreach (ControllerInvocationInfo info in invocationInfos) path.Append(info.BindPoint.Controller.ControllerTypeName).Append(" based on ").Append(info.BindPoint.Target).Append("\r\n"); logger.Report(Messages.ExecutionPath, requestPoint, path.ToString()); bool securityCheckComplete = false; bool securityCheckFailed = false; var failedPermissions = new Dictionary<string, KeyValuePair<FailAction, string>>(); Stopwatch sw = new Stopwatch(); Stopwatch sw1 = new Stopwatch(); foreach (ControllerInvocationInfo invocationInfo in invocationInfos) { sw.Reset(); sw.Start(); IController controller = manager.GetController(invocationInfo, context, requestContext); try { // all security controllers are guaranteed to be at the top of the list, in proper order. // we need to run through all of them, because an inner controller may override the decision // of an outer controller. therefore, the final decision is deferred until all security checks // have passed if (!securityCheckComplete) { ISecurityController securityController = controller as ISecurityController; if (securityController == null) securityCheckComplete = true; else { // this needs to run prior to the check controller.ProcessRequest(context, requestContext); // we only care about the actual return value of the last controller, as intermediate // results can be overriden by subsequent controllers. discard intermediate return values. securityCheckFailed = !securityController.HasAccess(requestContext, failedPermissions); } } // we have to do the check a second time, because the securityCheckComplete flag // gets set inside the top if. doing this in an else up top would skip the first // non-security controller if (securityCheckComplete) { if (securityCheckFailed || failedPermissions.Count != 0) { StringBuilder builder = new StringBuilder(); foreach (string perm in failedPermissions.Keys) builder.AppendLine(perm); foreach (KeyValuePair<FailAction, string> kvp in failedPermissions.Values) if (kvp.Key == FailAction.Redirect) { // currently you can only house one transfer request per context, // however, that may change in the future. requestContext.ClearTransferRequest(); // give the fail action target a chance to redirect after re-validating requestContext.Transfer( kvp.Value + (kvp.Value.Contains("?") ? "&" : "?") + "originalRequest=" + HttpUtility.UrlEncode(requestPoint)); logger.Report(Messages.SecurityRedirect, kvp.Value, builder.ToString()); break; } if (!requestContext.TransferRequested) throw new WebException(StatusCode.Unauthorized, "Access denied"); else // break out of the controller loop. we shouldn't be processing any more // controllers for this request, and need to get into whatever the security // guys requested break; } else { if (invocationInfo.BindPoint.Controller.DefaultTemplates.Count > 0) requestContext.Response.RenderWith(invocationInfo.BindPoint.Controller.DefaultTemplates); sw1.Reset(); sw1.Start(); controller.ProcessRequest(context, requestContext); sw1.Stop(); } } } finally { manager.ReturnController(controller, context, requestContext); sw.Stop(); logger.Report(Messages.ControllerInvoked, sw.ElapsedMilliseconds.ToString(),sw1.ElapsedMilliseconds.ToString(), invocationInfo.BindPoint.Controller.ControllerTypeName); } } if (requestContext.TransferRequested) { string transferRequestPoint = BindPointUtilities.VerbQualify(requestContext.TransferTarget, "get"); requestContext.ClearTransferRequest(); InvokeMethod(context, transferRequestPoint, requestContext); } } catch (Exception ex) { try { logger.Report(Messages.UnhandledException,ex.Message,ex.StackTrace); } catch {} //Assume that there are some other ctrls which match UnhandledException url and cause an exception. //In this case, removing this check may cause an infinite recursion of InvokeMethod and StackOverflow at the end. if (handleException) throw new ApplicationException("Cannot process UnhandledException url, maybe some other controllers also match this url and cause an exception", ex); if (!IsMethodDefinedExplicitly(Application.UnhandledException)) { throw new ApplicationException("Unhandled exception, and no binding to " + Application.UnhandledException + " found.", ex); } requestContext.Clear(); requestContext.Add("unhandledException", ex); InvokeMethod(context, Application.UnhandledException, requestContext, true); } }