aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs')
-rw-r--r--OpenSim/ApplicationPlugins/Rest/Inventory/RestHandler.cs242
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}