diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs | 662 |
1 files changed, 0 insertions, 662 deletions
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs deleted file mode 100644 index 072bd6f..0000000 --- a/OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs +++ /dev/null | |||
@@ -1,662 +0,0 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Reflection; | ||
31 | using OpenSim.Framework.Servers; | ||
32 | using OpenSim.Framework.Servers.HttpServer; | ||
33 | |||
34 | namespace OpenSim.ApplicationPlugins.Rest.Inventory | ||
35 | { | ||
36 | /// <remarks> | ||
37 | /// The class signature reveals the roles that RestHandler plays. | ||
38 | /// | ||
39 | /// [1] It is a sub-class of RestPlugin. It inherits and extends | ||
40 | /// the functionality of this class, constraining it to the | ||
41 | /// specific needs of this REST implementation. This relates | ||
42 | /// to the plug-in mechanism supported by OpenSim, the specifics | ||
43 | /// of which are mostly hidden by RestPlugin. | ||
44 | /// [2] IRestHandler describes the interface that this class | ||
45 | /// exports to service implementations. This is the services | ||
46 | /// management interface. | ||
47 | /// [3] IHttpAgentHandler describes the interface that is exported | ||
48 | /// to the BaseHttpServer in support of this particular HTTP | ||
49 | /// processing model. This is the request interface of the | ||
50 | /// handler. | ||
51 | /// </remarks> | ||
52 | |||
53 | public class RestHandler : RestPlugin, IRestHandler, IHttpAgentHandler | ||
54 | { | ||
55 | // Handler tables: both stream and REST are supported. The path handlers and their | ||
56 | // respective allocators are stored in separate tables. | ||
57 | |||
58 | internal Dictionary<string,RestMethodHandler> pathHandlers = new Dictionary<string,RestMethodHandler>(); | ||
59 | internal Dictionary<string,RestMethodAllocator> pathAllocators = new Dictionary<string,RestMethodAllocator>(); | ||
60 | internal Dictionary<string,RestStreamHandler> streamHandlers = new Dictionary<string,RestStreamHandler>(); | ||
61 | |||
62 | #region local static state | ||
63 | |||
64 | private static bool handlersLoaded = false; | ||
65 | private static List<Type> classes = new List<Type>(); | ||
66 | private static List<IRest> handlers = new List<IRest>(); | ||
67 | private static Type[] parms = new Type[0]; | ||
68 | private static Object[] args = new Object[0]; | ||
69 | |||
70 | /// <summary> | ||
71 | /// This static initializer scans the ASSEMBLY for classes that | ||
72 | /// export the IRest interface and builds a list of them. These | ||
73 | /// are later activated by the handler. To add a new handler it | ||
74 | /// is only necessary to create a new services class that implements | ||
75 | /// the IRest interface, and recompile the handler. This gives | ||
76 | /// all of the build-time flexibility of a modular approach | ||
77 | /// while not introducing yet-another module loader. Note that | ||
78 | /// multiple assembles can still be built, each with its own set | ||
79 | /// of handlers. Examples of services classes are RestInventoryServices | ||
80 | /// and RestSkeleton. | ||
81 | /// </summary> | ||
82 | |||
83 | static RestHandler() | ||
84 | { | ||
85 | Module[] mods = Assembly.GetExecutingAssembly().GetModules(); | ||
86 | |||
87 | foreach (Module m in mods) | ||
88 | { | ||
89 | Type[] types = m.GetTypes(); | ||
90 | foreach (Type t in types) | ||
91 | { | ||
92 | try | ||
93 | { | ||
94 | if (t.GetInterface("IRest") != null) | ||
95 | { | ||
96 | classes.Add(t); | ||
97 | } | ||
98 | } | ||
99 | catch (Exception) | ||
100 | { | ||
101 | Rest.Log.WarnFormat("[STATIC-HANDLER]: #0 Error scanning {1}", t); | ||
102 | Rest.Log.InfoFormat("[STATIC-HANDLER]: #0 {1} is not included", t); | ||
103 | } | ||
104 | } | ||
105 | } | ||
106 | } | ||
107 | |||
108 | #endregion local static state | ||
109 | |||
110 | #region local instance state | ||
111 | |||
112 | /// <summary> | ||
113 | /// This routine loads all of the handlers discovered during | ||
114 | /// instance initialization. | ||
115 | /// A table of all loaded and successfully constructed handlers | ||
116 | /// is built, and this table is then used by the constructor to | ||
117 | /// initialize each of the handlers in turn. | ||
118 | /// NOTE: The loading process does not automatically imply that | ||
119 | /// the handler has registered any kind of an interface, that | ||
120 | /// may be (optionally) done by the handler either during | ||
121 | /// construction, or during initialization. | ||
122 | /// | ||
123 | /// I was not able to make this code work within a constructor | ||
124 | /// so it is isolated within this method. | ||
125 | /// </summary> | ||
126 | |||
127 | private void LoadHandlers() | ||
128 | { | ||
129 | lock (handlers) | ||
130 | { | ||
131 | if (!handlersLoaded) | ||
132 | { | ||
133 | ConstructorInfo ci; | ||
134 | Object ht; | ||
135 | |||
136 | foreach (Type t in classes) | ||
137 | { | ||
138 | try | ||
139 | { | ||
140 | ci = t.GetConstructor(parms); | ||
141 | ht = ci.Invoke(args); | ||
142 | handlers.Add((IRest)ht); | ||
143 | } | ||
144 | catch (Exception e) | ||
145 | { | ||
146 | Rest.Log.WarnFormat("{0} Unable to load {1} : {2}", MsgId, t, e.Message); | ||
147 | } | ||
148 | } | ||
149 | handlersLoaded = true; | ||
150 | } | ||
151 | } | ||
152 | } | ||
153 | |||
154 | #endregion local instance state | ||
155 | |||
156 | #region overriding properties | ||
157 | |||
158 | // These properties override definitions | ||
159 | // in the base class. | ||
160 | |||
161 | // Name is used to differentiate the message header. | ||
162 | |||
163 | public override string Name | ||
164 | { | ||
165 | get { return "HANDLER"; } | ||
166 | } | ||
167 | |||
168 | // Used to partition the .ini configuration space. | ||
169 | |||
170 | public override string ConfigName | ||
171 | { | ||
172 | get { return "RestHandler"; } | ||
173 | } | ||
174 | |||
175 | // We have to rename these because we want | ||
176 | // to be able to share the values with other | ||
177 | // classes in our assembly and the base | ||
178 | // names are protected. | ||
179 | |||
180 | public string MsgId | ||
181 | { | ||
182 | get { return base.MsgID; } | ||
183 | } | ||
184 | |||
185 | public string RequestId | ||
186 | { | ||
187 | get { return base.RequestID; } | ||
188 | } | ||
189 | |||
190 | #endregion overriding properties | ||
191 | |||
192 | #region overriding methods | ||
193 | |||
194 | /// <summary> | ||
195 | /// This method is called by OpenSimMain immediately after loading the | ||
196 | /// plugin and after basic server setup, but before running any server commands. | ||
197 | /// </summary> | ||
198 | /// <remarks> | ||
199 | /// Note that entries MUST be added to the active configuration files before | ||
200 | /// the plugin can be enabled. | ||
201 | /// </remarks> | ||
202 | |||
203 | public override void Initialise(OpenSimBase openSim) | ||
204 | { | ||
205 | try | ||
206 | { | ||
207 | // This plugin will only be enabled if the broader | ||
208 | // REST plugin mechanism is enabled. | ||
209 | |||
210 | //Rest.Log.InfoFormat("{0} Plugin is initializing", MsgId); | ||
211 | |||
212 | base.Initialise(openSim); | ||
213 | |||
214 | // IsEnabled is implemented by the base class and | ||
215 | // reflects an overall RestPlugin status | ||
216 | |||
217 | if (!IsEnabled) | ||
218 | { | ||
219 | //Rest.Log.WarnFormat("{0} Plugins are disabled", MsgId); | ||
220 | return; | ||
221 | } | ||
222 | |||
223 | Rest.Log.InfoFormat("{0} Rest <{1}> plugin will be enabled", MsgId, Name); | ||
224 | Rest.Log.InfoFormat("{0} Configuration parameters read from <{1}>", MsgId, ConfigName); | ||
225 | |||
226 | // These are stored in static variables to make | ||
227 | // them easy to reach from anywhere in the assembly. | ||
228 | |||
229 | Rest.main = openSim; | ||
230 | if (Rest.main == null) | ||
231 | throw new Exception("OpenSim base pointer is null"); | ||
232 | |||
233 | Rest.Plugin = this; | ||
234 | Rest.Config = Config; | ||
235 | Rest.Prefix = Prefix; | ||
236 | Rest.GodKey = GodKey; | ||
237 | Rest.Authenticate = Rest.Config.GetBoolean("authenticate", Rest.Authenticate); | ||
238 | Rest.Scheme = Rest.Config.GetString("auth-scheme", Rest.Scheme); | ||
239 | Rest.Secure = Rest.Config.GetBoolean("secured", Rest.Secure); | ||
240 | Rest.ExtendedEscape = Rest.Config.GetBoolean("extended-escape", Rest.ExtendedEscape); | ||
241 | Rest.Realm = Rest.Config.GetString("realm", Rest.Realm); | ||
242 | Rest.DumpAsset = Rest.Config.GetBoolean("dump-asset", Rest.DumpAsset); | ||
243 | Rest.Fill = Rest.Config.GetBoolean("path-fill", Rest.Fill); | ||
244 | Rest.DumpLineSize = Rest.Config.GetInt("dump-line-size", Rest.DumpLineSize); | ||
245 | Rest.FlushEnabled = Rest.Config.GetBoolean("flush-on-error", Rest.FlushEnabled); | ||
246 | |||
247 | // Note: Odd spacing is required in the following strings | ||
248 | |||
249 | Rest.Log.InfoFormat("{0} Authentication is {1}required", MsgId, | ||
250 | (Rest.Authenticate ? "" : "not ")); | ||
251 | |||
252 | Rest.Log.InfoFormat("{0} Security is {1}enabled", MsgId, | ||
253 | (Rest.Secure ? "" : "not ")); | ||
254 | |||
255 | Rest.Log.InfoFormat("{0} Extended URI escape processing is {1}enabled", MsgId, | ||
256 | (Rest.ExtendedEscape ? "" : "not ")); | ||
257 | |||
258 | Rest.Log.InfoFormat("{0} Dumping of asset data is {1}enabled", MsgId, | ||
259 | (Rest.DumpAsset ? "" : "not ")); | ||
260 | |||
261 | // The supplied prefix MUST be absolute | ||
262 | |||
263 | if (Rest.Prefix.Substring(0,1) != Rest.UrlPathSeparator) | ||
264 | { | ||
265 | Rest.Log.WarnFormat("{0} Prefix <{1}> is not absolute and must be", MsgId, Rest.Prefix); | ||
266 | Rest.Log.InfoFormat("{0} Prefix changed to </{1}>", MsgId, Rest.Prefix); | ||
267 | Rest.Prefix = String.Format("{0}{1}", Rest.UrlPathSeparator, Rest.Prefix); | ||
268 | } | ||
269 | |||
270 | // If data dumping is requested, report on the chosen line | ||
271 | // length. | ||
272 | |||
273 | if (Rest.DumpAsset) | ||
274 | { | ||
275 | Rest.Log.InfoFormat("{0} Dump {1} bytes per line", MsgId, Rest.DumpLineSize); | ||
276 | } | ||
277 | |||
278 | // Load all of the handlers present in the | ||
279 | // assembly | ||
280 | |||
281 | // In principle, as we're an application plug-in, | ||
282 | // most of what needs to be done could be done using | ||
283 | // static resources, however the Open Sim plug-in | ||
284 | // model makes this an instance, so that's what we | ||
285 | // need to be. | ||
286 | // There is only one Communications manager per | ||
287 | // server, and by inference, only one each of the | ||
288 | // user, asset, and inventory servers. So we can cache | ||
289 | // those using a static initializer. | ||
290 | // We move all of this processing off to another | ||
291 | // services class to minimize overlap between function | ||
292 | // and infrastructure. | ||
293 | |||
294 | LoadHandlers(); | ||
295 | |||
296 | // The intention of a post construction initializer | ||
297 | // is to allow for setup that is dependent upon other | ||
298 | // activities outside of the agency. | ||
299 | |||
300 | foreach (IRest handler in handlers) | ||
301 | { | ||
302 | try | ||
303 | { | ||
304 | handler.Initialize(); | ||
305 | } | ||
306 | catch (Exception e) | ||
307 | { | ||
308 | Rest.Log.ErrorFormat("{0} initialization error: {1}", MsgId, e.Message); | ||
309 | } | ||
310 | } | ||
311 | |||
312 | // Now that everything is setup we can proceed to | ||
313 | // add THIS agent to the HTTP server's handler list | ||
314 | |||
315 | // FIXME: If this code is ever to be re-enabled (most of it is disabled already) then this will | ||
316 | // have to be handled through the AddHttpHandler interface. | ||
317 | // if (!AddAgentHandler(Rest.Name,this)) | ||
318 | // { | ||
319 | // Rest.Log.ErrorFormat("{0} Unable to activate handler interface", MsgId); | ||
320 | // foreach (IRest handler in handlers) | ||
321 | // { | ||
322 | // handler.Close(); | ||
323 | // } | ||
324 | // } | ||
325 | |||
326 | } | ||
327 | catch (Exception e) | ||
328 | { | ||
329 | Rest.Log.ErrorFormat("{0} Plugin initialization has failed: {1}", MsgId, e.Message); | ||
330 | } | ||
331 | } | ||
332 | |||
333 | /// <summary> | ||
334 | /// In the interests of efficiency, and because we cannot determine whether | ||
335 | /// or not this instance will actually be harvested, we clobber the only | ||
336 | /// anchoring reference to the working state for this plug-in. What the | ||
337 | /// call to close does is irrelevant to this class beyond knowing that it | ||
338 | /// can nullify the reference when it returns. | ||
339 | /// To make sure everything is copacetic we make sure the primary interface | ||
340 | /// is disabled by deleting the handler from the HTTP server tables. | ||
341 | /// </summary> | ||
342 | |||
343 | public override void Close() | ||
344 | { | ||
345 | Rest.Log.InfoFormat("{0} Plugin is terminating", MsgId); | ||
346 | |||
347 | // FIXME: If this code is ever to be re-enabled (most of it is disabled already) then this will | ||
348 | // have to be handled through the AddHttpHandler interface. | ||
349 | // try | ||
350 | // { | ||
351 | // RemoveAgentHandler(Rest.Name, this); | ||
352 | // } | ||
353 | // catch (KeyNotFoundException){} | ||
354 | |||
355 | foreach (IRest handler in handlers) | ||
356 | { | ||
357 | handler.Close(); | ||
358 | } | ||
359 | } | ||
360 | |||
361 | #endregion overriding methods | ||
362 | |||
363 | #region interface methods | ||
364 | |||
365 | /// <summary> | ||
366 | /// This method is called by the HTTP server to match an incoming | ||
367 | /// request. It scans all of the strings registered by the | ||
368 | /// underlying handlers and looks for the best match. It returns | ||
369 | /// true if a match is found. | ||
370 | /// The matching process could be made arbitrarily complex. | ||
371 | /// Note: The match is case-insensitive. | ||
372 | /// </summary> | ||
373 | |||
374 | public bool Match(OSHttpRequest request, OSHttpResponse response) | ||
375 | { | ||
376 | |||
377 | string path = request.RawUrl.ToLower(); | ||
378 | |||
379 | // Rest.Log.DebugFormat("{0} Match ENTRY", MsgId); | ||
380 | |||
381 | try | ||
382 | { | ||
383 | foreach (string key in pathHandlers.Keys) | ||
384 | { | ||
385 | // Rest.Log.DebugFormat("{0} Match testing {1} against agent prefix <{2}>", MsgId, path, key); | ||
386 | |||
387 | // Note that Match will not necessarily find the handler that will | ||
388 | // actually be used - it does no test for the "closest" fit. It | ||
389 | // simply reflects that at least one possible handler exists. | ||
390 | |||
391 | if (path.StartsWith(key)) | ||
392 | { | ||
393 | // Rest.Log.DebugFormat("{0} Matched prefix <{1}>", MsgId, key); | ||
394 | |||
395 | // This apparently odd evaluation is needed to prevent a match | ||
396 | // on anything other than a URI token boundary. Otherwise we | ||
397 | // may match on URL's that were not intended for this handler. | ||
398 | |||
399 | return (path.Length == key.Length || | ||
400 | path.Substring(key.Length, 1) == Rest.UrlPathSeparator); | ||
401 | } | ||
402 | } | ||
403 | |||
404 | path = String.Format("{0}{1}{2}", request.HttpMethod, Rest.UrlMethodSeparator, path); | ||
405 | |||
406 | foreach (string key in streamHandlers.Keys) | ||
407 | { | ||
408 | // Rest.Log.DebugFormat("{0} Match testing {1} against stream prefix <{2}>", MsgId, path, key); | ||
409 | |||
410 | // Note that Match will not necessarily find the handler that will | ||
411 | // actually be used - it does no test for the "closest" fit. It | ||
412 | // simply reflects that at least one possible handler exists. | ||
413 | |||
414 | if (path.StartsWith(key)) | ||
415 | { | ||
416 | // Rest.Log.DebugFormat("{0} Matched prefix <{1}>", MsgId, key); | ||
417 | |||
418 | // This apparently odd evaluation is needed to prevent a match | ||
419 | // on anything other than a URI token boundary. Otherwise we | ||
420 | // may match on URL's that were not intended for this handler. | ||
421 | |||
422 | return (path.Length == key.Length || | ||
423 | path.Substring(key.Length, 1) == Rest.UrlPathSeparator); | ||
424 | } | ||
425 | } | ||
426 | } | ||
427 | catch (Exception e) | ||
428 | { | ||
429 | Rest.Log.ErrorFormat("{0} matching exception for path <{1}> : {2}", MsgId, path, e.Message); | ||
430 | } | ||
431 | |||
432 | return false; | ||
433 | } | ||
434 | |||
435 | /// <summary> | ||
436 | /// This is called by the HTTP server once the handler has indicated | ||
437 | /// that it is able to handle the request. | ||
438 | /// Preconditions: | ||
439 | /// [1] request != null and is a valid request object | ||
440 | /// [2] response != null and is a valid response object | ||
441 | /// Behavior is undefined if preconditions are not satisfied. | ||
442 | /// </summary> | ||
443 | |||
444 | public bool Handle(OSHttpRequest request, OSHttpResponse response) | ||
445 | { | ||
446 | bool handled; | ||
447 | base.MsgID = base.RequestID; | ||
448 | |||
449 | // Debug only | ||
450 | |||
451 | if (Rest.DEBUG) | ||
452 | { | ||
453 | Rest.Log.DebugFormat("{0} ENTRY", MsgId); | ||
454 | Rest.Log.DebugFormat("{0} Agent: {1}", MsgId, request.UserAgent); | ||
455 | Rest.Log.DebugFormat("{0} Method: {1}", MsgId, request.HttpMethod); | ||
456 | |||
457 | for (int i = 0; i < request.Headers.Count; i++) | ||
458 | { | ||
459 | Rest.Log.DebugFormat("{0} Header [{1}] : <{2}> = <{3}>", | ||
460 | MsgId, i, request.Headers.GetKey(i), request.Headers.Get(i)); | ||
461 | } | ||
462 | Rest.Log.DebugFormat("{0} URI: {1}", MsgId, request.RawUrl); | ||
463 | } | ||
464 | |||
465 | // If a path handler worked we're done, otherwise try any | ||
466 | // available stream handlers too. | ||
467 | |||
468 | try | ||
469 | { | ||
470 | handled = (FindPathHandler(request, response) || | ||
471 | FindStreamHandler(request, response)); | ||
472 | } | ||
473 | catch (Exception e) | ||
474 | { | ||
475 | // A raw exception indicates that something we weren't expecting has | ||
476 | // happened. This should always reflect a shortcoming in the plugin, | ||
477 | // or a failure to satisfy the preconditions. It should not reflect | ||
478 | // an error in the request itself. Under such circumstances the state | ||
479 | // of the request cannot be determined and we are obliged to mark it | ||
480 | // as 'handled'. | ||
481 | |||
482 | Rest.Log.ErrorFormat("{0} Plugin error: {1}", MsgId, e.Message); | ||
483 | handled = true; | ||
484 | } | ||
485 | |||
486 | Rest.Log.DebugFormat("{0} EXIT", MsgId); | ||
487 | |||
488 | return handled; | ||
489 | } | ||
490 | |||
491 | #endregion interface methods | ||
492 | |||
493 | /// <summary> | ||
494 | /// If there is a stream handler registered that can handle the | ||
495 | /// request, then fine. If the request is not matched, do | ||
496 | /// nothing. | ||
497 | /// Note: The selection is case-insensitive | ||
498 | /// </summary> | ||
499 | |||
500 | private bool FindStreamHandler(OSHttpRequest request, OSHttpResponse response) | ||
501 | { | ||
502 | RequestData rdata = new RequestData(request, response, String.Empty); | ||
503 | |||
504 | string bestMatch = String.Empty; | ||
505 | string path = String.Format("{0}:{1}", rdata.method, rdata.path).ToLower(); | ||
506 | |||
507 | Rest.Log.DebugFormat("{0} Checking for stream handler for <{1}>", MsgId, path); | ||
508 | |||
509 | if (!IsEnabled) | ||
510 | { | ||
511 | return false; | ||
512 | } | ||
513 | |||
514 | foreach (string pattern in streamHandlers.Keys) | ||
515 | { | ||
516 | if (path.StartsWith(pattern)) | ||
517 | { | ||
518 | if (pattern.Length > bestMatch.Length) | ||
519 | { | ||
520 | bestMatch = pattern; | ||
521 | } | ||
522 | } | ||
523 | } | ||
524 | |||
525 | // Handle using the best match available | ||
526 | |||
527 | if (bestMatch.Length > 0) | ||
528 | { | ||
529 | Rest.Log.DebugFormat("{0} Stream-based handler matched with <{1}>", MsgId, bestMatch); | ||
530 | RestStreamHandler handler = streamHandlers[bestMatch]; | ||
531 | rdata.buffer = handler.Handle(rdata.path, rdata.request.InputStream, rdata.request, rdata.response); | ||
532 | rdata.AddHeader(rdata.response.ContentType,handler.ContentType); | ||
533 | rdata.Respond("FindStreamHandler Completion"); | ||
534 | } | ||
535 | |||
536 | return rdata.handled; | ||
537 | } | ||
538 | |||
539 | /// <summary> | ||
540 | /// Add a stream handler for the designated HTTP method and path prefix. | ||
541 | /// If the handler is not enabled, the request is ignored. If the path | ||
542 | /// does not start with the REST prefix, it is added. If method-qualified | ||
543 | /// path has not already been registered, the method is added to the active | ||
544 | /// handler table. | ||
545 | /// </summary> | ||
546 | public void AddStreamHandler(string httpMethod, string path, RestMethod method) | ||
547 | { | ||
548 | if (!IsEnabled) | ||
549 | { | ||
550 | return; | ||
551 | } | ||
552 | |||
553 | if (!path.StartsWith(Rest.Prefix)) | ||
554 | { | ||
555 | path = String.Format("{0}{1}", Rest.Prefix, path); | ||
556 | } | ||
557 | |||
558 | path = String.Format("{0}{1}{2}", httpMethod, Rest.UrlMethodSeparator, path); | ||
559 | |||
560 | // Conditionally add to the list | ||
561 | |||
562 | if (!streamHandlers.ContainsKey(path)) | ||
563 | { | ||
564 | streamHandlers.Add(path, new RestStreamHandler(httpMethod, path, method)); | ||
565 | Rest.Log.DebugFormat("{0} Added handler for {1}", MsgId, path); | ||
566 | } | ||
567 | else | ||
568 | { | ||
569 | Rest.Log.WarnFormat("{0} Ignoring duplicate handler for {1}", MsgId, path); | ||
570 | } | ||
571 | } | ||
572 | |||
573 | /// <summary> | ||
574 | /// Given the supplied request/response, if the handler is enabled, the inbound | ||
575 | /// information is used to match an entry in the active path handler tables, using | ||
576 | /// the method-qualified path information. If a match is found, then the handler is | ||
577 | /// invoked. The result is the boolean result of the handler, or false if no | ||
578 | /// handler was located. The boolean indicates whether or not the request has been | ||
579 | /// handled, not whether or not the request was successful - that information is in | ||
580 | /// the response. | ||
581 | /// Note: The selection process is case-insensitive | ||
582 | /// </summary> | ||
583 | |||
584 | internal bool FindPathHandler(OSHttpRequest request, OSHttpResponse response) | ||
585 | { | ||
586 | RequestData rdata = null; | ||
587 | string bestMatch = null; | ||
588 | |||
589 | if (!IsEnabled) | ||
590 | { | ||
591 | return false; | ||
592 | } | ||
593 | |||
594 | // Conditionally add to the list | ||
595 | |||
596 | Rest.Log.DebugFormat("{0} Checking for path handler for <{1}>", MsgId, request.RawUrl); | ||
597 | |||
598 | foreach (string pattern in pathHandlers.Keys) | ||
599 | { | ||
600 | if (request.RawUrl.ToLower().StartsWith(pattern)) | ||
601 | { | ||
602 | if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length) | ||
603 | { | ||
604 | bestMatch = pattern; | ||
605 | } | ||
606 | } | ||
607 | } | ||
608 | |||
609 | if (!String.IsNullOrEmpty(bestMatch)) | ||
610 | { | ||
611 | rdata = pathAllocators[bestMatch](request, response, bestMatch); | ||
612 | |||
613 | Rest.Log.DebugFormat("{0} Path based REST handler matched with <{1}>", MsgId, bestMatch); | ||
614 | |||
615 | try | ||
616 | { | ||
617 | pathHandlers[bestMatch](rdata); | ||
618 | } | ||
619 | |||
620 | // A plugin generated error indicates a request-related error | ||
621 | // that has been handled by the plugin. | ||
622 | |||
623 | catch (RestException r) | ||
624 | { | ||
625 | Rest.Log.WarnFormat("{0} Request failed: {1}", MsgId, r.Message); | ||
626 | } | ||
627 | } | ||
628 | |||
629 | return (rdata == null) ? false : rdata.handled; | ||
630 | } | ||
631 | |||
632 | /// <summary> | ||
633 | /// A method handler and a request allocator are stored using the designated | ||
634 | /// path as a key. If an entry already exists, it is replaced by the new one. | ||
635 | /// </summary> | ||
636 | |||
637 | public void AddPathHandler(RestMethodHandler mh, string path, RestMethodAllocator ra) | ||
638 | { | ||
639 | if (!IsEnabled) | ||
640 | { | ||
641 | return; | ||
642 | } | ||
643 | |||
644 | if (pathHandlers.ContainsKey(path)) | ||
645 | { | ||
646 | Rest.Log.DebugFormat("{0} Replacing handler for <${1}>", MsgId, path); | ||
647 | pathHandlers.Remove(path); | ||
648 | } | ||
649 | |||
650 | if (pathAllocators.ContainsKey(path)) | ||
651 | { | ||
652 | Rest.Log.DebugFormat("{0} Replacing allocator for <${1}>", MsgId, path); | ||
653 | pathAllocators.Remove(path); | ||
654 | } | ||
655 | |||
656 | Rest.Log.DebugFormat("{0} Adding path handler for {1}", MsgId, path); | ||
657 | |||
658 | pathHandlers.Add(path, mh); | ||
659 | pathAllocators.Add(path, ra); | ||
660 | } | ||
661 | } | ||
662 | } | ||