New tests.
[mono.git] / mcs / class / System.Web.Mvc2 / System.Web.Mvc / MvcHandler.cs
1 /* ****************************************************************************\r
2  *\r
3  * Copyright (c) Microsoft Corporation. All rights reserved.\r
4  *\r
5  * This software is subject to the Microsoft Public License (Ms-PL). \r
6  * A copy of the license can be found in the license.htm file included \r
7  * in this distribution.\r
8  *\r
9  * You must not remove this notice, or any other, from this software.\r
10  *\r
11  * ***************************************************************************/\r
12 \r
13 namespace System.Web.Mvc {\r
14     using System;\r
15     using System.Diagnostics.CodeAnalysis;\r
16     using System.Globalization;\r
17     using System.Linq;\r
18     using System.Reflection;\r
19     using System.Threading;\r
20     using System.Web;\r
21     using System.Web.Mvc.Async;\r
22     using System.Web.Mvc.Resources;\r
23     using System.Web.Routing;\r
24     using System.Web.SessionState;\r
25 \r
26     [SuppressMessage("Microsoft.Security", "CA2112:SecuredTypesShouldNotExposeFields", Justification = "There's nothing secret about the value of this field.")]\r
27     public class MvcHandler : IHttpAsyncHandler, IHttpHandler, IRequiresSessionState {\r
28         private static readonly object _processRequestTag = new object();\r
29         private ControllerBuilder _controllerBuilder;\r
30 \r
31         internal static readonly string MvcVersion = GetMvcVersionString();\r
32         public static readonly string MvcVersionHeaderName = "X-AspNetMvc-Version";\r
33 \r
34         public MvcHandler(RequestContext requestContext) {\r
35             if (requestContext == null) {\r
36                 throw new ArgumentNullException("requestContext");\r
37             }\r
38 \r
39             RequestContext = requestContext;\r
40         }\r
41 \r
42         internal ControllerBuilder ControllerBuilder {\r
43             get {\r
44                 if (_controllerBuilder == null) {\r
45                     _controllerBuilder = ControllerBuilder.Current;\r
46                 }\r
47                 return _controllerBuilder;\r
48             }\r
49             set {\r
50                 _controllerBuilder = value;\r
51             }\r
52         }\r
53 \r
54         public static bool DisableMvcResponseHeader {\r
55             get;\r
56             set;\r
57         }\r
58 \r
59         protected virtual bool IsReusable {\r
60             get {\r
61                 return false;\r
62             }\r
63         }\r
64 \r
65         public RequestContext RequestContext {\r
66             get;\r
67             private set;\r
68         }\r
69 \r
70         protected internal virtual void AddVersionHeader(HttpContextBase httpContext) {\r
71             if (!DisableMvcResponseHeader) {\r
72                 httpContext.Response.AppendHeader(MvcVersionHeaderName, MvcVersion);\r
73             }\r
74         }\r
75 \r
76         protected virtual IAsyncResult BeginProcessRequest(HttpContext httpContext, AsyncCallback callback, object state) {\r
77             HttpContextBase iHttpContext = new HttpContextWrapper(httpContext);\r
78             return BeginProcessRequest(iHttpContext, callback, state);\r
79         }\r
80 \r
81         protected internal virtual IAsyncResult BeginProcessRequest(HttpContextBase httpContext, AsyncCallback callback, object state) {\r
82             IController controller;\r
83             IControllerFactory factory;\r
84             ProcessRequestInit(httpContext, out controller, out factory);\r
85 \r
86             IAsyncController asyncController = controller as IAsyncController;\r
87             if (asyncController != null) {\r
88                 // asynchronous controller\r
89                 BeginInvokeDelegate beginDelegate = delegate(AsyncCallback asyncCallback, object asyncState) {\r
90                     try {\r
91                         return asyncController.BeginExecute(RequestContext, asyncCallback, asyncState);\r
92                     }\r
93                     catch {\r
94                         factory.ReleaseController(asyncController);\r
95                         throw;\r
96                     }\r
97                 };\r
98 \r
99                 EndInvokeDelegate endDelegate = delegate(IAsyncResult asyncResult) {\r
100                     try {\r
101                         asyncController.EndExecute(asyncResult);\r
102                     }\r
103                     finally {\r
104                         factory.ReleaseController(asyncController);\r
105                     }\r
106                 };\r
107 \r
108                 SynchronizationContext syncContext = SynchronizationContextUtil.GetSynchronizationContext();\r
109                 AsyncCallback newCallback = AsyncUtil.WrapCallbackForSynchronizedExecution(callback, syncContext);\r
110                 return AsyncResultWrapper.Begin(newCallback, state, beginDelegate, endDelegate, _processRequestTag);\r
111             }\r
112             else {\r
113                 // synchronous controller\r
114                 Action action = delegate {\r
115                     try {\r
116                         controller.Execute(RequestContext);\r
117                     }\r
118                     finally {\r
119                         factory.ReleaseController(controller);\r
120                     }\r
121                 };\r
122 \r
123                 return AsyncResultWrapper.BeginSynchronous(callback, state, action, _processRequestTag);\r
124             }\r
125         }\r
126 \r
127         protected internal virtual void EndProcessRequest(IAsyncResult asyncResult) {\r
128             AsyncResultWrapper.End(asyncResult, _processRequestTag);\r
129         }\r
130 \r
131         private static string GetMvcVersionString() {\r
132             // DevDiv 216459:\r
133             // This code originally used Assembly.GetName(), but that requires FileIOPermission, which isn't granted in\r
134             // medium trust. However, Assembly.FullName *is* accessible in medium trust.\r
135             return new AssemblyName(typeof(MvcHandler).Assembly.FullName).Version.ToString(2);\r
136         }\r
137 \r
138         protected virtual void ProcessRequest(HttpContext httpContext) {\r
139             HttpContextBase iHttpContext = new HttpContextWrapper(httpContext);\r
140             ProcessRequest(iHttpContext);\r
141         }\r
142 \r
143         protected internal virtual void ProcessRequest(HttpContextBase httpContext) {\r
144             IController controller;\r
145             IControllerFactory factory;\r
146             ProcessRequestInit(httpContext, out controller, out factory);\r
147 \r
148             try {\r
149                 controller.Execute(RequestContext);\r
150             }\r
151             finally {\r
152                 factory.ReleaseController(controller);\r
153             }\r
154         }\r
155 \r
156         private void ProcessRequestInit(HttpContextBase httpContext, out IController controller, out IControllerFactory factory) {\r
157             AddVersionHeader(httpContext);\r
158             RemoveOptionalRoutingParameters();\r
159 \r
160             // Get the controller type\r
161             string controllerName = RequestContext.RouteData.GetRequiredString("controller");\r
162 \r
163             // Instantiate the controller and call Execute\r
164             factory = ControllerBuilder.GetControllerFactory();\r
165             controller = factory.CreateController(RequestContext, controllerName);\r
166             if (controller == null) {\r
167                 throw new InvalidOperationException(\r
168                     String.Format(\r
169                         CultureInfo.CurrentUICulture,\r
170                         MvcResources.ControllerBuilder_FactoryReturnedNull,\r
171                         factory.GetType(),\r
172                         controllerName));\r
173             }\r
174         }\r
175 \r
176         private void RemoveOptionalRoutingParameters() {\r
177             RouteValueDictionary rvd = RequestContext.RouteData.Values;\r
178 \r
179             // Get all keys for which the corresponding value is 'Optional'.\r
180             // ToArray() necessary so that we don't manipulate the dictionary while enumerating.\r
181             string[] matchingKeys = (from entry in rvd\r
182                                      where entry.Value == UrlParameter.Optional\r
183                                      select entry.Key).ToArray();\r
184 \r
185             foreach (string key in matchingKeys) {\r
186                 rvd.Remove(key);\r
187             }\r
188         }\r
189 \r
190         #region IHttpHandler Members\r
191         bool IHttpHandler.IsReusable {\r
192             get {\r
193                 return IsReusable;\r
194             }\r
195         }\r
196 \r
197         void IHttpHandler.ProcessRequest(HttpContext httpContext) {\r
198             ProcessRequest(httpContext);\r
199         }\r
200         #endregion\r
201 \r
202         #region IHttpAsyncHandler Members\r
203         IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) {\r
204             return BeginProcessRequest(context, cb, extraData);\r
205         }\r
206 \r
207         void IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) {\r
208             EndProcessRequest(result);\r
209         }\r
210         #endregion\r
211     }\r
212 }\r