2005-08-04 Ben Maurer <bmaurer@ximian.com>
[mono.git] / mcs / class / System.Web / System.Web.Configuration / HandlerFactoryConfiguration.cs
1 //
2 // System.Web.Configuration.HandlerFactoryConfiguration.cs
3 //  
4 //
5 // Authors:
6 //      Miguel de Icaza (miguel@novell.com
7 //
8 // (C) 2002 Ximian, Inc (http://www.ximian.com)
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System;
33 using System.Collections;
34 using System.Web.Util;
35 using System.Text.RegularExpressions;
36
37 namespace System.Web.Configuration {
38
39         class FileMatchingInfo {
40                 public string MatchExact;
41                 public string MatchExpr;
42
43                 // If set, we can fast-path the patch with string.EndsWith (FMI.EndsWith)
44                 public string EndsWith;
45                 
46                 public FileMatchingInfo (string s)
47                 {
48                         MatchExpr = s;
49
50                         if (s[0] == '*' && (s.IndexOf ('*', 1) == -1))
51                                 EndsWith = s.Substring (1);
52
53                         if (s.IndexOf ('*') == -1)
54                                 MatchExact = "/" + s;
55                 }
56         }
57         
58         class HttpHandler {
59                 // If `null', we are the "*" match
60                 public string OriginalVerb;
61                 public string OriginalPath;
62                 
63                 public string [] Verbs;
64                 public FileMatchingInfo [] files;
65
66                 // To support lazy loading we keep the name around.
67                 public string TypeName;
68                 Type type;
69
70                 object instance;
71                 
72                 public HttpHandler (string verb, string path, string typename, Type t)
73                 {
74                         OriginalVerb = verb;
75                         OriginalPath = path;
76                         
77                         if (verb != "*")
78                                 Verbs = verb.Split (',');
79                         string [] paths = path.Split (',');
80                         files = new FileMatchingInfo [paths.Length];
81
82                         int i = 0;
83                         foreach (string s in paths)
84                                 files [i++] = new FileMatchingInfo (s);
85                         
86                         this.TypeName = typename;
87                         type = t;
88                 }
89
90                 //
91                 // Loads the a type by name and verifies that it implements
92                 // IHttpHandler or IHttpHandlerFactory
93                 //
94                 public static Type LoadType (string type_name)
95                 {
96                         Type t;
97                         
98                         try {
99                                 t = Type.GetType (type_name, true);
100                         } catch (Exception e) {
101                                 throw new HttpException (String.Format ("Failed to load httpHandler type `{0}'", type_name));
102                         }
103
104                         if (typeof (IHttpHandler).IsAssignableFrom (t) ||
105                             typeof (IHttpHandlerFactory).IsAssignableFrom (t))
106                                 return t;
107                         
108                         throw new HttpException (String.Format ("Type {0} does not implement IHttpHandler or IHttpHandlerFactory", type_name));
109                 }
110
111                 public bool PathMatches (string p)
112                 {
113                         for (int j = files.Length; j > 0; ){
114                                 j--;
115                                 FileMatchingInfo fm = files [j];
116
117                                 if (fm.MatchExact != null)
118                                         return fm.MatchExact.Length == p.Length && StrUtils.EndsWith (p, fm.MatchExact);
119                                         
120                                 if (fm.EndsWith != null)
121                                         return StrUtils.EndsWith (p, fm.EndsWith);
122
123                                 if (fm.MatchExpr == "*")
124                                         return true;
125
126                                 /* convert to regexp */
127                                 string match_regexp = fm.MatchExpr.Replace(".", "\\.").Replace("?", "\\?").Replace("*", ".*");
128                                 return Regex.IsMatch (p, match_regexp);
129                         }
130                         return false;
131                 }
132
133                 // Loads the handler, possibly delay-loaded.
134                 public object GetHandlerInstance ()
135                 {
136                         IHttpHandler ihh = instance as IHttpHandler;
137                         
138                         if (instance == null || (ihh != null && !ihh.IsReusable)){
139                                 if (type == null)
140                                         type = LoadType (TypeName);
141
142                                 instance = Activator.CreateInstance (type);
143                         } 
144                         
145                         return instance;
146                 }
147         }
148         
149         class HandlerFactoryConfiguration {
150                 ArrayList handlers;
151                 HandlerFactoryConfiguration parent;
152                 public HandlerFactoryConfiguration (HandlerFactoryConfiguration parent)
153                 {
154                         this.parent = parent;
155
156                         handlers = new ArrayList ();
157                 }
158
159                 public void Clear ()
160                 {
161                         handlers.Clear ();
162                 }
163
164                 public void Add (string verb, string path, string type_name, bool validate)
165                 {
166                         Type type;
167
168                         if (validate){
169                                 type = HttpHandler.LoadType (type_name);
170                                 if (type == null)
171                                         throw new HttpException (String.Format ("Can not load {0}", type_name));
172                         } else
173                                 type = null;
174                         
175                         handlers.Add (new HttpHandler (verb, path, type_name, type));
176                 }
177
178                 public HttpHandler Remove (string verb, string path)
179                 {
180                         int top = handlers.Count;
181
182                         for (int i = 0; i < top; i++){
183                                 HttpHandler handler = (HttpHandler) handlers [i];
184
185                                 if (verb == handler.OriginalVerb && path == handler.OriginalPath){
186                                         handlers.Remove (i);
187                                         return handler;
188                                 }
189                         }
190                         return null;
191                 }
192
193                 public object LocateHandler (string verb, string filepath)
194                 {
195                         int top = handlers.Count;
196
197                         for (int i = 0; i < top; i++){
198                                 HttpHandler handler = (HttpHandler) handlers [i];
199
200                                 if (handler.Verbs == null){
201                                         if (handler.PathMatches (filepath))
202                                                 return handler.GetHandlerInstance ();
203                                         continue;
204                                 }
205
206                                 string [] verbs = handler.Verbs;
207                                 for (int j = verbs.Length; j > 0; ){
208                                         j--;
209                                         if (verbs [j] != verb)
210                                                 continue;
211                                         if (handler.PathMatches (filepath))
212                                                 return handler.GetHandlerInstance ();
213                                 }
214                         }
215
216                         if (parent != null)
217                                 return parent.LocateHandler (verb, filepath);
218                         else
219                                 return null;
220                 }
221         }
222 }