diff options
Diffstat (limited to 'OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs')
-rw-r--r-- | OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs | 242 |
1 files changed, 164 insertions, 78 deletions
diff --git a/OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs b/OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs index 50412c9..7bd83c1 100644 --- a/OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs +++ b/OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs | |||
@@ -38,10 +38,33 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
38 | public class RestHandler : RestPlugin, IHttpAgentHandler | 38 | public class RestHandler : RestPlugin, IHttpAgentHandler |
39 | { | 39 | { |
40 | 40 | ||
41 | /// <remarks> | ||
42 | /// The handler delegates are not noteworthy. The allocator allows | ||
43 | /// a given handler to optionally subclass the base RequestData | ||
44 | /// structure to carry any locally required per-request state | ||
45 | /// needed. | ||
46 | /// </remarks> | ||
47 | |||
48 | internal delegate void RestMethodHandler(RequestData rdata); | ||
49 | internal delegate RequestData RestMethodAllocator(OSHttpRequest request, OSHttpResponse response); | ||
50 | |||
51 | // Handler tables: both stream and REST are supported. The path handlers and their | ||
52 | // respective allocators are stored in separate tables. | ||
53 | |||
54 | internal Dictionary<string,RestMethodHandler> pathHandlers = new Dictionary<string,RestMethodHandler>(); | ||
55 | internal Dictionary<string,RestMethodAllocator> pathAllocators = new Dictionary<string,RestMethodAllocator>(); | ||
56 | internal Dictionary<string,RestStreamHandler> streamHandlers = new Dictionary<string,RestStreamHandler>(); | ||
57 | |||
41 | #region local static state | 58 | #region local static state |
42 | 59 | ||
60 | private static bool handlersLoaded = false; | ||
61 | private static List<Type> classes = new List<Type>(); | ||
62 | private static List<IRest> handlers = new List<IRest>(); | ||
63 | private static Type[] parms = new Type[0]; | ||
64 | private static Object[] args = new Object[0]; | ||
65 | |||
43 | /// <summary> | 66 | /// <summary> |
44 | /// This static initializer scans the assembly for classes that | 67 | /// This static initializer scans the ASSEMBLY for classes that |
45 | /// export the IRest interface and builds a list of them. These | 68 | /// export the IRest interface and builds a list of them. These |
46 | /// are later activated by the handler. To add a new handler it | 69 | /// are later activated by the handler. To add a new handler it |
47 | /// is only necessary to create a new services class that implements | 70 | /// is only necessary to create a new services class that implements |
@@ -49,73 +72,78 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
49 | /// all of the build-time flexibility of a modular approach | 72 | /// all of the build-time flexibility of a modular approach |
50 | /// while not introducing yet-another module loader. Note that | 73 | /// while not introducing yet-another module loader. Note that |
51 | /// multiple assembles can still be built, each with its own set | 74 | /// multiple assembles can still be built, each with its own set |
52 | /// of handlers. | 75 | /// of handlers. Examples of services classes are RestInventoryServices |
76 | /// and RestSkeleton. | ||
53 | /// </summary> | 77 | /// </summary> |
54 | 78 | ||
55 | private static bool handlersLoaded = false; | ||
56 | private static List<Type> classes = new List<Type>(); | ||
57 | private static List<IRest> handlers = new List<IRest>(); | ||
58 | private static Type[] parms = new Type[1]; | ||
59 | private static Object[] args = new Object[1]; | ||
60 | |||
61 | static RestHandler() | 79 | static RestHandler() |
62 | { | 80 | { |
81 | |||
63 | Module[] mods = Assembly.GetExecutingAssembly().GetModules(); | 82 | Module[] mods = Assembly.GetExecutingAssembly().GetModules(); |
83 | |||
64 | foreach (Module m in mods) | 84 | foreach (Module m in mods) |
65 | { | 85 | { |
66 | Type[] types = m.GetTypes(); | 86 | Type[] types = m.GetTypes(); |
67 | foreach (Type t in types) | 87 | foreach (Type t in types) |
68 | { | 88 | { |
69 | if (t.GetInterface("IRest") != null) | 89 | try |
70 | { | 90 | { |
71 | classes.Add(t); | 91 | if (t.GetInterface("IRest") != null) |
92 | { | ||
93 | classes.Add(t); | ||
94 | } | ||
95 | } | ||
96 | catch (Exception) | ||
97 | { | ||
98 | Rest.Log.WarnFormat("[STATIC-HANDLER]: #0 Error scanning {1}", t); | ||
99 | Rest.Log.InfoFormat("[STATIC-HANDLER]: #0 {1} is not included", t); | ||
72 | } | 100 | } |
73 | } | 101 | } |
74 | } | 102 | } |
103 | |||
75 | } | 104 | } |
76 | 105 | ||
77 | #endregion local static state | 106 | #endregion local static state |
78 | 107 | ||
79 | #region local instance state | 108 | #region local instance state |
80 | 109 | ||
81 | /// <remarks> | ||
82 | /// The handler delegate is not noteworthy. The allocator allows | ||
83 | /// a given handler to optionally subclass the base RequestData | ||
84 | /// structure to carry any locally required per-request state | ||
85 | /// needed. | ||
86 | /// </remarks> | ||
87 | internal delegate void RestMethodHandler(RequestData rdata); | ||
88 | internal delegate RequestData RestMethodAllocator(OSHttpRequest request, OSHttpResponse response); | ||
89 | |||
90 | // Handler tables: both stream and REST are supported | ||
91 | |||
92 | internal Dictionary<string,RestMethodHandler> pathHandlers = new Dictionary<string,RestMethodHandler>(); | ||
93 | internal Dictionary<string,RestMethodAllocator> pathAllocators = new Dictionary<string,RestMethodAllocator>(); | ||
94 | internal Dictionary<string,RestStreamHandler> streamHandlers = new Dictionary<string,RestStreamHandler>(); | ||
95 | |||
96 | /// <summary> | 110 | /// <summary> |
97 | /// This routine loads all of the handlers discovered during | 111 | /// This routine loads all of the handlers discovered during |
98 | /// instance initialization. Each handler is responsible for | 112 | /// instance initialization. |
99 | /// registering itself with this handler. | 113 | /// A table of all loaded and successfully constructed handlers |
100 | /// I was not able to make this code work in a constructor. | 114 | /// is built, and this table is then used by the constructor to |
115 | /// initialize each of the handlers in turn. | ||
116 | /// NOTE: The loading process does not automatically imply that | ||
117 | /// the handler has registered any kind of an interface, that | ||
118 | /// may be (optionally) done by the handler either during | ||
119 | /// construction, or during initialization. | ||
120 | /// | ||
121 | /// I was not able to make this code work within a constructor | ||
122 | /// so it is islated within this method. | ||
101 | /// </summary> | 123 | /// </summary> |
124 | |||
102 | private void LoadHandlers() | 125 | private void LoadHandlers() |
103 | { | 126 | { |
104 | lock (handlers) | 127 | lock (handlers) |
105 | { | 128 | { |
106 | if (!handlersLoaded) | 129 | if (!handlersLoaded) |
107 | { | 130 | { |
108 | parms[0] = this.GetType(); | ||
109 | args[0] = this; | ||
110 | 131 | ||
111 | ConstructorInfo ci; | 132 | ConstructorInfo ci; |
112 | Object ht; | 133 | Object ht; |
113 | 134 | ||
114 | foreach (Type t in classes) | 135 | foreach (Type t in classes) |
115 | { | 136 | { |
116 | ci = t.GetConstructor(parms); | 137 | try |
117 | ht = ci.Invoke(args); | 138 | { |
118 | handlers.Add((IRest)ht); | 139 | ci = t.GetConstructor(parms); |
140 | ht = ci.Invoke(args); | ||
141 | handlers.Add((IRest)ht); | ||
142 | } | ||
143 | catch (Exception e) | ||
144 | { | ||
145 | Rest.Log.WarnFormat("{0} Unable to load {1} : {2}", MsgId, t, e.Message); | ||
146 | } | ||
119 | } | 147 | } |
120 | handlersLoaded = true; | 148 | handlersLoaded = true; |
121 | } | 149 | } |
@@ -126,14 +154,17 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
126 | 154 | ||
127 | #region overriding properties | 155 | #region overriding properties |
128 | 156 | ||
129 | // Used to differentiate the message header. | 157 | // These properties override definitions |
158 | // in the base class. | ||
159 | |||
160 | // Name is used to differentiate the message header. | ||
130 | 161 | ||
131 | public override string Name | 162 | public override string Name |
132 | { | 163 | { |
133 | get { return "HANDLER"; } | 164 | get { return "HANDLER"; } |
134 | } | 165 | } |
135 | 166 | ||
136 | // Used to partition the configuration space. | 167 | // Used to partition the .ini configuration space. |
137 | 168 | ||
138 | public override string ConfigName | 169 | public override string ConfigName |
139 | { | 170 | { |
@@ -167,32 +198,32 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
167 | /// Note that entries MUST be added to the active configuration files before | 198 | /// Note that entries MUST be added to the active configuration files before |
168 | /// the plugin can be enabled. | 199 | /// the plugin can be enabled. |
169 | /// </remarks> | 200 | /// </remarks> |
201 | |||
170 | public override void Initialise(OpenSimBase openSim) | 202 | public override void Initialise(OpenSimBase openSim) |
171 | { | 203 | { |
172 | try | 204 | try |
173 | { | 205 | { |
174 | 206 | ||
175 | /// <remarks> | 207 | // This plugin will only be enabled if the broader |
176 | /// This plugin will only be enabled if the broader | 208 | // REST plugin mechanism is enabled. |
177 | /// REST plugin mechanism is enabled. | ||
178 | /// </remarks> | ||
179 | 209 | ||
180 | Rest.Log.InfoFormat("{0} Plugin is initializing", MsgID); | 210 | Rest.Log.InfoFormat("{0} Plugin is initializing", MsgId); |
181 | 211 | ||
182 | base.Initialise(openSim); | 212 | base.Initialise(openSim); |
183 | 213 | ||
214 | // IsEnabled is implemented by the base class and | ||
215 | // reflects an overall RestPlugin status | ||
216 | |||
184 | if (!IsEnabled) | 217 | if (!IsEnabled) |
185 | { | 218 | { |
186 | Rest.Log.WarnFormat("{0} Plugins are disabled", MsgID); | 219 | Rest.Log.WarnFormat("{0} Plugins are disabled", MsgId); |
187 | return; | 220 | return; |
188 | } | 221 | } |
189 | 222 | ||
190 | Rest.Log.InfoFormat("{0} Plugin will be enabled", MsgID); | 223 | Rest.Log.InfoFormat("{0} Plugin will be enabled", MsgId); |
191 | 224 | ||
192 | /// <remarks> | 225 | // These are stored in static variables to make |
193 | /// These are stored in static variables to make | 226 | // them easy to reach from anywhere in the assembly. |
194 | /// them easy to reach from anywhere in the assembly. | ||
195 | /// </remarks> | ||
196 | 227 | ||
197 | Rest.main = openSim; | 228 | Rest.main = openSim; |
198 | Rest.Plugin = this; | 229 | Rest.Plugin = this; |
@@ -223,6 +254,9 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
223 | Rest.Log.InfoFormat("{0} Dumping of asset data is {1}enabled", MsgId, | 254 | Rest.Log.InfoFormat("{0} Dumping of asset data is {1}enabled", MsgId, |
224 | (Rest.DumpAsset ? "" : "not ")); | 255 | (Rest.DumpAsset ? "" : "not ")); |
225 | 256 | ||
257 | // If data dumping is requested, report on the chosen line | ||
258 | // length. | ||
259 | |||
226 | if (Rest.DumpAsset) | 260 | if (Rest.DumpAsset) |
227 | { | 261 | { |
228 | Rest.Log.InfoFormat("{0} Dump {1} bytes per line", MsgId, | 262 | Rest.Log.InfoFormat("{0} Dump {1} bytes per line", MsgId, |
@@ -247,22 +281,24 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
247 | 281 | ||
248 | LoadHandlers(); | 282 | LoadHandlers(); |
249 | 283 | ||
250 | /// <remarks> | 284 | // The intention of a post construction initializer |
251 | /// The intention of a post construction initializer | 285 | // is to allow for setup that is dependent upon other |
252 | /// is to allow for setup that is dependent upon other | 286 | // activities outside of the agency. |
253 | /// activities outside of the agency. We don't currently | ||
254 | /// have any, but the design allows for it. | ||
255 | /// </remarks> | ||
256 | 287 | ||
257 | foreach (IRest handler in handlers) | 288 | foreach (IRest handler in handlers) |
258 | { | 289 | { |
259 | handler.Initialize(); | 290 | try |
291 | { | ||
292 | handler.Initialize(); | ||
293 | } | ||
294 | catch (Exception e) | ||
295 | { | ||
296 | Rest.Log.ErrorFormat("{0} initialization error: {1}", MsgId, e.Message); | ||
297 | } | ||
260 | } | 298 | } |
261 | 299 | ||
262 | /// <remarks> | 300 | // Now that everything is setup we can proceed to |
263 | /// Now that everything is setup we can proceed and | 301 | // add THIS agent to the HTTP server's handler list |
264 | /// add this agent to the HTTP server's handler list | ||
265 | /// </remarks> | ||
266 | 302 | ||
267 | if (!AddAgentHandler(Rest.Name,this)) | 303 | if (!AddAgentHandler(Rest.Name,this)) |
268 | { | 304 | { |
@@ -276,7 +312,7 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
276 | } | 312 | } |
277 | catch (Exception e) | 313 | catch (Exception e) |
278 | { | 314 | { |
279 | Rest.Log.ErrorFormat("{0} Plugin initialization has failed: {1}", MsgID, e.Message); | 315 | Rest.Log.ErrorFormat("{0} Plugin initialization has failed: {1}", MsgId, e.Message); |
280 | } | 316 | } |
281 | 317 | ||
282 | } | 318 | } |
@@ -290,10 +326,11 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
290 | /// To make sure everything is copacetic we make sure the primary interface | 326 | /// To make sure everything is copacetic we make sure the primary interface |
291 | /// is disabled by deleting the handler from the HTTP server tables. | 327 | /// is disabled by deleting the handler from the HTTP server tables. |
292 | /// </summary> | 328 | /// </summary> |
329 | |||
293 | public override void Close() | 330 | public override void Close() |
294 | { | 331 | { |
295 | 332 | ||
296 | Rest.Log.InfoFormat("{0} Plugin is terminating", MsgID); | 333 | Rest.Log.InfoFormat("{0} Plugin is terminating", MsgId); |
297 | 334 | ||
298 | try | 335 | try |
299 | { | 336 | { |
@@ -313,45 +350,62 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
313 | #region interface methods | 350 | #region interface methods |
314 | 351 | ||
315 | /// <summary> | 352 | /// <summary> |
316 | /// This method is called by the server to match the client, it could | 353 | /// This method is called by the HTTP server to match an incoming |
317 | /// just return true if we only want one such handler. For now we | 354 | /// request. It scans all of the strings registered by the |
318 | /// match any explicitly specified client. | 355 | /// underlying handlers and looks for the best match. It returns |
356 | /// true if a match is found. | ||
357 | /// The matching process could be made arbitrarily complex. | ||
319 | /// </summary> | 358 | /// </summary> |
359 | |||
320 | public bool Match(OSHttpRequest request, OSHttpResponse response) | 360 | public bool Match(OSHttpRequest request, OSHttpResponse response) |
321 | { | 361 | { |
322 | string path = request.RawUrl; | 362 | string path = request.RawUrl; |
323 | foreach (string key in pathHandlers.Keys) | 363 | |
364 | try | ||
324 | { | 365 | { |
325 | if (path.StartsWith(key)) | 366 | foreach (string key in pathHandlers.Keys) |
326 | { | 367 | { |
327 | return ( path.Length == key.Length || | 368 | if (path.StartsWith(key)) |
328 | path.Substring(key.Length,1) == Rest.UrlPathSeparator); | 369 | { |
370 | return ( path.Length == key.Length || | ||
371 | path.Substring(key.Length,1) == Rest.UrlPathSeparator); | ||
372 | } | ||
329 | } | 373 | } |
330 | } | ||
331 | 374 | ||
332 | path = String.Format("{0}{1}{2}", request.HttpMethod, Rest.UrlMethodSeparator, path); | 375 | path = String.Format("{0}{1}{2}", request.HttpMethod, Rest.UrlMethodSeparator, path); |
333 | foreach (string key in streamHandlers.Keys) | 376 | foreach (string key in streamHandlers.Keys) |
334 | { | ||
335 | if (path.StartsWith(key)) | ||
336 | { | 377 | { |
337 | return true; | 378 | if (path.StartsWith(key)) |
379 | { | ||
380 | return true; | ||
381 | } | ||
338 | } | 382 | } |
383 | |||
384 | } | ||
385 | catch (Exception e) | ||
386 | { | ||
387 | Rest.Log.ErrorFormat("{0} matching exception for path <{1}> : {2}", MsgId, path, e.Message); | ||
339 | } | 388 | } |
340 | 389 | ||
341 | return false; | 390 | return false; |
342 | } | 391 | } |
343 | 392 | ||
344 | /// <summary> | 393 | /// <summary> |
394 | /// This is called by the HTTP server once the handler has indicated | ||
395 | /// that t is able to handle the request. | ||
345 | /// Preconditions: | 396 | /// Preconditions: |
346 | /// [1] request != null and is a valid request object | 397 | /// [1] request != null and is a valid request object |
347 | /// [2] response != null and is a valid response object | 398 | /// [2] response != null and is a valid response object |
348 | /// Behavior is undefined if preconditions are not satisfied. | 399 | /// Behavior is undefined if preconditions are not satisfied. |
349 | /// </summary> | 400 | /// </summary> |
401 | |||
350 | public bool Handle(OSHttpRequest request, OSHttpResponse response) | 402 | public bool Handle(OSHttpRequest request, OSHttpResponse response) |
351 | { | 403 | { |
352 | bool handled; | 404 | bool handled; |
353 | base.MsgID = base.RequestID; | 405 | base.MsgID = base.RequestID; |
354 | 406 | ||
407 | // Debug only | ||
408 | |||
355 | if (Rest.DEBUG) | 409 | if (Rest.DEBUG) |
356 | { | 410 | { |
357 | Rest.Log.DebugFormat("{0} ENTRY", MsgId); | 411 | Rest.Log.DebugFormat("{0} ENTRY", MsgId); |
@@ -371,8 +425,8 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
371 | 425 | ||
372 | try | 426 | try |
373 | { | 427 | { |
374 | handled = FindPathHandler(request, response) || | 428 | handled = ( FindPathHandler(request, response) || |
375 | FindStreamHandler(request, response); | 429 | FindStreamHandler(request, response) ); |
376 | } | 430 | } |
377 | catch (Exception e) | 431 | catch (Exception e) |
378 | { | 432 | { |
@@ -406,6 +460,11 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
406 | 460 | ||
407 | Rest.Log.DebugFormat("{0} Checking for stream handler for <{1}>", MsgId, path); | 461 | Rest.Log.DebugFormat("{0} Checking for stream handler for <{1}>", MsgId, path); |
408 | 462 | ||
463 | if (!IsEnabled) | ||
464 | { | ||
465 | return false; | ||
466 | } | ||
467 | |||
409 | foreach (string pattern in streamHandlers.Keys) | 468 | foreach (string pattern in streamHandlers.Keys) |
410 | { | 469 | { |
411 | if (path.StartsWith(pattern)) | 470 | if (path.StartsWith(pattern)) |
@@ -432,7 +491,13 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
432 | 491 | ||
433 | } | 492 | } |
434 | 493 | ||
435 | // Preserves the original handler's semantics | 494 | /// <summary> |
495 | /// Add a stream handler for the designated HTTP method and path prefix. | ||
496 | /// If the handler is not enabled, the request is ignored. If the path | ||
497 | /// does not start with the REST prefix, it is added. If method-qualified | ||
498 | /// path has not already been registered, the method is added to the active | ||
499 | /// handler table. | ||
500 | /// </summary> | ||
436 | 501 | ||
437 | public void AddStreamHandler(string httpMethod, string path, RestMethod method) | 502 | public void AddStreamHandler(string httpMethod, string path, RestMethod method) |
438 | { | 503 | { |
@@ -454,17 +519,26 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
454 | if (!streamHandlers.ContainsKey(path)) | 519 | if (!streamHandlers.ContainsKey(path)) |
455 | { | 520 | { |
456 | streamHandlers.Add(path, new RestStreamHandler(httpMethod, path, method)); | 521 | streamHandlers.Add(path, new RestStreamHandler(httpMethod, path, method)); |
457 | Rest.Log.DebugFormat("{0} Added handler for {1}", MsgID, path); | 522 | Rest.Log.DebugFormat("{0} Added handler for {1}", MsgId, path); |
458 | } | 523 | } |
459 | else | 524 | else |
460 | { | 525 | { |
461 | Rest.Log.WarnFormat("{0} Ignoring duplicate handler for {1}", MsgID, path); | 526 | Rest.Log.WarnFormat("{0} Ignoring duplicate handler for {1}", MsgId, path); |
462 | } | 527 | } |
463 | 528 | ||
464 | } | 529 | } |
465 | 530 | ||
531 | /// <summary> | ||
532 | /// Given the supplied request/response, if the handler is enabled, the inbound | ||
533 | /// information is used to match an entry in the active path handler tables, using | ||
534 | /// the method-qualified path information. If a match is found, then the handler is | ||
535 | /// invoked. The result is the boolean result of the handler, or false if no | ||
536 | /// handler was located. The boolean indicates whether or not the request has been | ||
537 | /// handled, not whether or not the request was successful - that information is in | ||
538 | /// the response. | ||
539 | /// </summary> | ||
466 | 540 | ||
467 | internal bool FindPathHandler(OSHttpRequest request, OSHttpResponse response) | 541 | internal bool FindPathHandler(OSHttpRequest request, OSHttpResponse response) |
468 | { | 542 | { |
469 | 543 | ||
470 | RequestData rdata = null; | 544 | RequestData rdata = null; |
@@ -516,8 +590,19 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
516 | 590 | ||
517 | } | 591 | } |
518 | 592 | ||
593 | /// <summary> | ||
594 | /// A method handler and a request allocator are stored using the designated | ||
595 | /// path as a key. If an entry already exists, it is replaced by the new one. | ||
596 | /// </summary> | ||
597 | |||
519 | internal void AddPathHandler(RestMethodHandler mh, string path, RestMethodAllocator ra) | 598 | internal void AddPathHandler(RestMethodHandler mh, string path, RestMethodAllocator ra) |
520 | { | 599 | { |
600 | |||
601 | if (!IsEnabled) | ||
602 | { | ||
603 | return; | ||
604 | } | ||
605 | |||
521 | if (pathHandlers.ContainsKey(path)) | 606 | if (pathHandlers.ContainsKey(path)) |
522 | { | 607 | { |
523 | Rest.Log.DebugFormat("{0} Replacing handler for <${1}>", MsgId, path); | 608 | Rest.Log.DebugFormat("{0} Replacing handler for <${1}>", MsgId, path); |
@@ -537,4 +622,5 @@ namespace OpenSim.ApplicationPlugins.Rest.Inventory | |||
537 | 622 | ||
538 | } | 623 | } |
539 | } | 624 | } |
625 | |||
540 | } | 626 | } |