aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/RestClient.cs
diff options
context:
space:
mode:
authorRobert Adams2015-09-08 04:54:16 -0700
committerRobert Adams2015-09-08 04:54:16 -0700
commite5367d822be9b05e74c859afe2d2956a3e95aa33 (patch)
treee904050a30715df587aa527d7f313755177726a7 /OpenSim/Framework/RestClient.cs
parentadd lost admin_reset_land method (diff)
parentDeleted access control spec from [LoginService] section of standalone config.... (diff)
downloadopensim-SC_OLD-e5367d822be9b05e74c859afe2d2956a3e95aa33.zip
opensim-SC_OLD-e5367d822be9b05e74c859afe2d2956a3e95aa33.tar.gz
opensim-SC_OLD-e5367d822be9b05e74c859afe2d2956a3e95aa33.tar.bz2
opensim-SC_OLD-e5367d822be9b05e74c859afe2d2956a3e95aa33.tar.xz
Merge of ubitworkvarnew with opensim/master as of 20150905.
This integrates the OpenSim refactoring to make physics, etc into modules. AVN physics hasn't been moved to new location. Does not compile yet. Merge branch 'osmaster' into mbworknew1
Diffstat (limited to 'OpenSim/Framework/RestClient.cs')
-rw-r--r--OpenSim/Framework/RestClient.cs681
1 files changed, 681 insertions, 0 deletions
diff --git a/OpenSim/Framework/RestClient.cs b/OpenSim/Framework/RestClient.cs
new file mode 100644
index 0000000..ca19392
--- /dev/null
+++ b/OpenSim/Framework/RestClient.cs
@@ -0,0 +1,681 @@
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
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Net;
32using System.Reflection;
33using System.Text;
34using System.Threading;
35using System.Web;
36using log4net;
37
38using OpenSim.Framework.ServiceAuth;
39
40namespace OpenSim.Framework
41{
42 /// <summary>
43 /// Implementation of a generic REST client
44 /// </summary>
45 /// <remarks>
46 /// This class is a generic implementation of a REST (Representational State Transfer) web service. This
47 /// class is designed to execute both synchronously and asynchronously.
48 ///
49 /// Internally the implementation works as a two stage asynchronous web-client.
50 /// When the request is initiated, RestClient will query asynchronously for for a web-response,
51 /// sleeping until the initial response is returned by the server. Once the initial response is retrieved
52 /// the second stage of asynchronous requests will be triggered, in an attempt to read of the response
53 /// object into a memorystream as a sequence of asynchronous reads.
54 ///
55 /// The asynchronisity of RestClient is designed to move as much processing into the back-ground, allowing
56 /// other threads to execute, while it waits for a response from the web-service. RestClient itself can be
57 /// invoked by the caller in either synchronous mode or asynchronous modes.
58 /// </remarks>
59 public class RestClient : IDisposable
60 {
61 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
62
63 // private string realuri;
64
65 #region member variables
66
67 /// <summary>
68 /// The base Uri of the web-service e.g. http://www.google.com
69 /// </summary>
70 private string _url;
71
72 /// <summary>
73 /// Path elements of the query
74 /// </summary>
75 private List<string> _pathElements = new List<string>();
76
77 /// <summary>
78 /// Parameter elements of the query, e.g. min=34
79 /// </summary>
80 private Dictionary<string, string> _parameterElements = new Dictionary<string, string>();
81
82 /// <summary>
83 /// Request method. E.g. GET, POST, PUT or DELETE
84 /// </summary>
85 private string _method;
86
87 /// <summary>
88 /// Temporary buffer used to store bytes temporarily as they come in from the server
89 /// </summary>
90 private byte[] _readbuf;
91
92 /// <summary>
93 /// MemoryStream representing the resulting resource
94 /// </summary>
95 private Stream _resource;
96
97 /// <summary>
98 /// WebRequest object, held as a member variable
99 /// </summary>
100 private HttpWebRequest _request;
101
102 /// <summary>
103 /// WebResponse object, held as a member variable, so we can close it
104 /// </summary>
105 private HttpWebResponse _response;
106
107 /// <summary>
108 /// This flag will help block the main synchroneous method, in case we run in synchroneous mode
109 /// </summary>
110 //public static ManualResetEvent _allDone = new ManualResetEvent(false);
111
112 /// <summary>
113 /// Default time out period
114 /// </summary>
115 //private const int DefaultTimeout = 10*1000; // 10 seconds timeout
116
117 /// <summary>
118 /// Default Buffer size of a block requested from the web-server
119 /// </summary>
120 private const int BufferSize = 4096; // Read blocks of 4 KB.
121
122
123 /// <summary>
124 /// if an exception occours during async processing, we need to save it, so it can be
125 /// rethrown on the primary thread;
126 /// </summary>
127 private Exception _asyncException;
128
129 #endregion member variables
130
131 #region constructors
132
133 /// <summary>
134 /// Instantiate a new RestClient
135 /// </summary>
136 /// <param name="url">Web-service to query, e.g. http://osgrid.org:8003</param>
137 public RestClient(string url)
138 {
139 _url = url;
140 _readbuf = new byte[BufferSize];
141 _resource = new MemoryStream();
142 _request = null;
143 _response = null;
144 _lock = new object();
145 }
146
147 private object _lock;
148
149 #endregion constructors
150
151
152 #region Dispose
153
154 private bool disposed = false;
155
156 public void Dispose()
157 {
158 Dispose(true);
159 GC.SuppressFinalize(this);
160 }
161
162 protected virtual void Dispose(bool disposing)
163 {
164 if (disposed)
165 return;
166
167 if (disposing)
168 {
169 _resource.Dispose();
170 }
171
172 disposed = true;
173 }
174
175 #endregion Dispose
176
177
178 /// <summary>
179 /// Add a path element to the query, e.g. assets
180 /// </summary>
181 /// <param name="element">path entry</param>
182 public void AddResourcePath(string element)
183 {
184 if (isSlashed(element))
185 _pathElements.Add(element.Substring(0, element.Length - 1));
186 else
187 _pathElements.Add(element);
188 }
189
190 /// <summary>
191 /// Add a query parameter to the Url
192 /// </summary>
193 /// <param name="name">Name of the parameter, e.g. min</param>
194 /// <param name="value">Value of the parameter, e.g. 42</param>
195 public void AddQueryParameter(string name, string value)
196 {
197 try
198 {
199 _parameterElements.Add(HttpUtility.UrlEncode(name), HttpUtility.UrlEncode(value));
200 }
201 catch (ArgumentException)
202 {
203 m_log.Error("[REST]: Query parameter " + name + " is already added.");
204 }
205 catch (Exception e)
206 {
207 m_log.Error("[REST]: An exception was raised adding query parameter to dictionary. Exception: {0}",e);
208 }
209 }
210
211 /// <summary>
212 /// Add a query parameter to the Url
213 /// </summary>
214 /// <param name="name">Name of the parameter, e.g. min</param>
215 public void AddQueryParameter(string name)
216 {
217 try
218 {
219 _parameterElements.Add(HttpUtility.UrlEncode(name), null);
220 }
221 catch (ArgumentException)
222 {
223 m_log.Error("[REST]: Query parameter " + name + " is already added.");
224 }
225 catch (Exception e)
226 {
227 m_log.Error("[REST]: An exception was raised adding query parameter to dictionary. Exception: {0}",e);
228 }
229 }
230
231 /// <summary>
232 /// Web-Request method, e.g. GET, PUT, POST, DELETE
233 /// </summary>
234 public string RequestMethod
235 {
236 get { return _method; }
237 set { _method = value; }
238 }
239
240 /// <summary>
241 /// True if string contains a trailing slash '/'
242 /// </summary>
243 /// <param name="s">string to be examined</param>
244 /// <returns>true if slash is present</returns>
245 private static bool isSlashed(string s)
246 {
247 return s.Substring(s.Length - 1, 1) == "/";
248 }
249
250 /// <summary>
251 /// Build a Uri based on the initial Url, path elements and parameters
252 /// </summary>
253 /// <returns>fully constructed Uri</returns>
254 private Uri buildUri()
255 {
256 StringBuilder sb = new StringBuilder();
257 sb.Append(_url);
258
259 foreach (string e in _pathElements)
260 {
261 sb.Append("/");
262 sb.Append(e);
263 }
264
265 bool firstElement = true;
266 foreach (KeyValuePair<string, string> kv in _parameterElements)
267 {
268 if (firstElement)
269 {
270 sb.Append("?");
271 firstElement = false;
272 }
273 else
274 sb.Append("&");
275
276 sb.Append(kv.Key);
277 if (!string.IsNullOrEmpty(kv.Value))
278 {
279 sb.Append("=");
280 sb.Append(kv.Value);
281 }
282 }
283 // realuri = sb.ToString();
284 //m_log.InfoFormat("[REST CLIENT]: RestURL: {0}", realuri);
285 return new Uri(sb.ToString());
286 }
287
288 #region Async communications with server
289
290 /// <summary>
291 /// Async method, invoked when a block of data has been received from the service
292 /// </summary>
293 /// <param name="ar"></param>
294 private void StreamIsReadyDelegate(IAsyncResult ar)
295 {
296 try
297 {
298 Stream s = (Stream) ar.AsyncState;
299 int read = s.EndRead(ar);
300
301 if (read > 0)
302 {
303 _resource.Write(_readbuf, 0, read);
304 // IAsyncResult asynchronousResult =
305 // s.BeginRead(_readbuf, 0, BufferSize, new AsyncCallback(StreamIsReadyDelegate), s);
306 s.BeginRead(_readbuf, 0, BufferSize, new AsyncCallback(StreamIsReadyDelegate), s);
307
308 // TODO! Implement timeout, without killing the server
309 //ThreadPool.RegisterWaitForSingleObject(asynchronousResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true);
310 }
311 else
312 {
313 s.Close();
314 //_allDone.Set();
315 }
316 }
317 catch (Exception e)
318 {
319 //_allDone.Set();
320 _asyncException = e;
321 }
322 }
323
324 #endregion Async communications with server
325
326 /// <summary>
327 /// Perform a synchronous request
328 /// </summary>
329 public Stream Request()
330 {
331 return Request(null);
332 }
333
334 /// <summary>
335 /// Perform a synchronous request
336 /// </summary>
337 public Stream Request(IServiceAuth auth)
338 {
339 lock (_lock)
340 {
341 _request = (HttpWebRequest) WebRequest.Create(buildUri());
342 _request.KeepAlive = false;
343 _request.ContentType = "application/xml";
344 _request.Timeout = 200000;
345 _request.Method = RequestMethod;
346 _asyncException = null;
347 if (auth != null)
348 auth.AddAuthorization(_request.Headers);
349
350 int reqnum = WebUtil.RequestNumber++;
351 if (WebUtil.DebugLevel >= 3)
352 m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} REST {1} to {2}", reqnum, _request.Method, _request.RequestUri);
353
354// IAsyncResult responseAsyncResult = _request.BeginGetResponse(new AsyncCallback(ResponseIsReadyDelegate), _request);
355
356 try
357 {
358 using (_response = (HttpWebResponse) _request.GetResponse())
359 {
360 using (Stream src = _response.GetResponseStream())
361 {
362 int length = src.Read(_readbuf, 0, BufferSize);
363 while (length > 0)
364 {
365 _resource.Write(_readbuf, 0, length);
366 length = src.Read(_readbuf, 0, BufferSize);
367 }
368
369 // TODO! Implement timeout, without killing the server
370 // this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted
371 //ThreadPool.RegisterWaitForSingleObject(responseAsyncResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true);
372
373 // _allDone.WaitOne();
374 }
375 }
376 }
377 catch (WebException e)
378 {
379 using (HttpWebResponse errorResponse = e.Response as HttpWebResponse)
380 {
381 if (null != errorResponse && HttpStatusCode.NotFound == errorResponse.StatusCode)
382 {
383 // This is often benign. E.g., requesting a missing asset will return 404.
384 m_log.DebugFormat("[REST CLIENT] Resource not found (404): {0}", _request.Address.ToString());
385 }
386 else
387 {
388 m_log.Error(string.Format("[REST CLIENT] Error fetching resource from server: {0} ", _request.Address.ToString()), e);
389 }
390 }
391 return null;
392 }
393
394
395 if (_asyncException != null)
396 throw _asyncException;
397
398 if (_resource != null)
399 {
400 _resource.Flush();
401 _resource.Seek(0, SeekOrigin.Begin);
402 }
403
404 if (WebUtil.DebugLevel >= 5)
405 WebUtil.LogResponseDetail(reqnum, _resource);
406
407 return _resource;
408 }
409 }
410
411 public Stream Request(Stream src, IServiceAuth auth)
412 {
413 _request = (HttpWebRequest) WebRequest.Create(buildUri());
414 _request.KeepAlive = false;
415 _request.ContentType = "application/xml";
416 _request.Timeout = 90000;
417 _request.Method = RequestMethod;
418 _asyncException = null;
419 _request.ContentLength = src.Length;
420 if (auth != null)
421 auth.AddAuthorization(_request.Headers);
422
423 src.Seek(0, SeekOrigin.Begin);
424
425 int reqnum = WebUtil.RequestNumber++;
426 if (WebUtil.DebugLevel >= 3)
427 m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} REST {1} to {2}", reqnum, _request.Method, _request.RequestUri);
428 if (WebUtil.DebugLevel >= 5)
429 WebUtil.LogOutgoingDetail(string.Format("SEND {0}: ", reqnum), src);
430
431 using (Stream dst = _request.GetRequestStream())
432 {
433 m_log.Info("[REST]: GetRequestStream is ok");
434
435 byte[] buf = new byte[1024];
436 int length = src.Read(buf, 0, 1024);
437 m_log.Info("[REST]: First Read is ok");
438 while (length > 0)
439 {
440 dst.Write(buf, 0, length);
441 length = src.Read(buf, 0, 1024);
442 }
443 }
444
445 try
446 {
447 _response = (HttpWebResponse)_request.GetResponse();
448 }
449 catch (WebException e)
450 {
451 m_log.WarnFormat("[REST]: Request {0} {1} failed with status {2} and message {3}",
452 RequestMethod, _request.RequestUri, e.Status, e.Message);
453 return null;
454 }
455 catch (Exception e)
456 {
457 m_log.WarnFormat(
458 "[REST]: Request {0} {1} failed with exception {2} {3}",
459 RequestMethod, _request.RequestUri, e.Message, e.StackTrace);
460 return null;
461 }
462
463 if (WebUtil.DebugLevel >= 5)
464 {
465 using (Stream responseStream = _response.GetResponseStream())
466 {
467 using (StreamReader reader = new StreamReader(responseStream))
468 {
469 string responseStr = reader.ReadToEnd();
470 WebUtil.LogResponseDetail(reqnum, responseStr);
471 }
472 }
473 }
474
475 if (_response != null)
476 _response.Close();
477
478// IAsyncResult responseAsyncResult = _request.BeginGetResponse(new AsyncCallback(ResponseIsReadyDelegate), _request);
479
480 // TODO! Implement timeout, without killing the server
481 // this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted
482 //ThreadPool.RegisterWaitForSingleObject(responseAsyncResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true);
483
484 return null;
485 }
486
487 #region Async Invocation
488
489 public IAsyncResult BeginRequest(AsyncCallback callback, object state)
490 {
491 /// <summary>
492 /// In case, we are invoked asynchroneously this object will keep track of the state
493 /// </summary>
494 AsyncResult<Stream> ar = new AsyncResult<Stream>(callback, state);
495 Util.FireAndForget(RequestHelper, ar, "RestClient.BeginRequest");
496 return ar;
497 }
498
499 public Stream EndRequest(IAsyncResult asyncResult)
500 {
501 AsyncResult<Stream> ar = (AsyncResult<Stream>) asyncResult;
502
503 // Wait for operation to complete, then return result or
504 // throw exception
505 return ar.EndInvoke();
506 }
507
508 private void RequestHelper(Object asyncResult)
509 {
510 // We know that it's really an AsyncResult<DateTime> object
511 AsyncResult<Stream> ar = (AsyncResult<Stream>) asyncResult;
512 try
513 {
514 // Perform the operation; if sucessful set the result
515 Stream s = Request(null);
516 ar.SetAsCompleted(s, false);
517 }
518 catch (Exception e)
519 {
520 // If operation fails, set the exception
521 ar.HandleException(e, false);
522 }
523 }
524
525 #endregion Async Invocation
526 }
527
528 internal class SimpleAsyncResult : IAsyncResult
529 {
530 private readonly AsyncCallback m_callback;
531
532 /// <summary>
533 /// Is process completed?
534 /// </summary>
535 /// <remarks>Should really be boolean, but VolatileRead has no boolean method</remarks>
536 private byte m_completed;
537
538 /// <summary>
539 /// Did process complete synchronously?
540 /// </summary>
541 /// <remarks>I have a hard time imagining a scenario where this is the case, again, same issue about
542 /// booleans and VolatileRead as m_completed
543 /// </remarks>
544 private byte m_completedSynchronously;
545
546 private readonly object m_asyncState;
547 private ManualResetEvent m_waitHandle;
548 private Exception m_exception;
549
550 internal SimpleAsyncResult(AsyncCallback cb, object state)
551 {
552 m_callback = cb;
553 m_asyncState = state;
554 m_completed = 0;
555 m_completedSynchronously = 1;
556 }
557
558 #region IAsyncResult Members
559
560 public object AsyncState
561 {
562 get { return m_asyncState; }
563 }
564
565 public WaitHandle AsyncWaitHandle
566 {
567 get
568 {
569 if (m_waitHandle == null)
570 {
571 bool done = IsCompleted;
572 ManualResetEvent mre = new ManualResetEvent(done);
573 if (Interlocked.CompareExchange(ref m_waitHandle, mre, null) != null)
574 {
575 mre.Close();
576 }
577 else
578 {
579 if (!done && IsCompleted)
580 {
581 m_waitHandle.Set();
582 }
583 }
584 }
585
586 return m_waitHandle;
587 }
588 }
589
590
591 public bool CompletedSynchronously
592 {
593 get { return Thread.VolatileRead(ref m_completedSynchronously) == 1; }
594 }
595
596
597 public bool IsCompleted
598 {
599 get { return Thread.VolatileRead(ref m_completed) == 1; }
600 }
601
602 #endregion
603
604 #region class Methods
605
606 internal void SetAsCompleted(bool completedSynchronously)
607 {
608 m_completed = 1;
609 if (completedSynchronously)
610 m_completedSynchronously = 1;
611 else
612 m_completedSynchronously = 0;
613
614 SignalCompletion();
615 }
616
617 internal void HandleException(Exception e, bool completedSynchronously)
618 {
619 m_completed = 1;
620 if (completedSynchronously)
621 m_completedSynchronously = 1;
622 else
623 m_completedSynchronously = 0;
624 m_exception = e;
625
626 SignalCompletion();
627 }
628
629 private void SignalCompletion()
630 {
631 if (m_waitHandle != null) m_waitHandle.Set();
632
633 if (m_callback != null) m_callback(this);
634 }
635
636 public void EndInvoke()
637 {
638 // This method assumes that only 1 thread calls EndInvoke
639 if (!IsCompleted)
640 {
641 // If the operation isn't done, wait for it
642 AsyncWaitHandle.WaitOne();
643 AsyncWaitHandle.Close();
644 m_waitHandle.Close();
645 m_waitHandle = null; // Allow early GC
646 }
647
648 // Operation is done: if an exception occured, throw it
649 if (m_exception != null) throw m_exception;
650 }
651
652 #endregion
653 }
654
655 internal class AsyncResult<T> : SimpleAsyncResult
656 {
657 private T m_result = default(T);
658
659 public AsyncResult(AsyncCallback asyncCallback, Object state) :
660 base(asyncCallback, state)
661 {
662 }
663
664 public void SetAsCompleted(T result, bool completedSynchronously)
665 {
666 // Save the asynchronous operation's result
667 m_result = result;
668
669 // Tell the base class that the operation completed
670 // sucessfully (no exception)
671 base.SetAsCompleted(completedSynchronously);
672 }
673
674 public new T EndInvoke()
675 {
676 base.EndInvoke();
677 return m_result;
678 }
679 }
680
681}