29 using System.Collections.Generic;
32 using System.Reflection;
34 using System.Threading;
38 using OpenSim.Framework.ServiceAuth;
40 namespace OpenSim.Framework
61 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
65 #region member variables
75 private List<string> _pathElements =
new List<string>();
80 private Dictionary<string, string> _parameterElements =
new Dictionary<string, string>();
85 private string _method;
90 private byte[] _readbuf;
95 private Stream _resource;
100 private HttpWebRequest _request;
105 private HttpWebResponse _response;
120 private const int BufferSize = 4096;
127 private Exception _asyncException;
129 #endregion member variables
140 _readbuf =
new byte[BufferSize];
141 _resource =
new MemoryStream();
144 _lock =
new object();
147 private object _lock;
149 #endregion constructors
154 private bool disposed =
false;
159 GC.SuppressFinalize(
this);
162 protected virtual void Dispose(
bool disposing)
184 if (isSlashed(element))
185 _pathElements.Add(element.Substring(0, element.Length - 1));
187 _pathElements.Add(element);
199 _parameterElements.Add(HttpUtility.UrlEncode(name), HttpUtility.UrlEncode(value));
201 catch (ArgumentException)
203 m_log.Error(
"[REST]: Query parameter " + name +
" is already added.");
207 m_log.Error(
"[REST]: An exception was raised adding query parameter to dictionary. Exception: {0}",e);
219 _parameterElements.Add(HttpUtility.UrlEncode(name), null);
221 catch (ArgumentException)
223 m_log.Error(
"[REST]: Query parameter " + name +
" is already added.");
227 m_log.Error(
"[REST]: An exception was raised adding query parameter to dictionary. Exception: {0}",e);
236 get {
return _method; }
237 set { _method = value; }
245 private static bool isSlashed(
string s)
247 return s.Substring(s.Length - 1, 1) ==
"/";
254 private Uri buildUri()
256 StringBuilder sb =
new StringBuilder();
259 foreach (
string e
in _pathElements)
265 bool firstElement =
true;
266 foreach (KeyValuePair<string, string> kv
in _parameterElements)
271 firstElement =
false;
277 if (!
string.IsNullOrEmpty(kv.Value))
285 return new Uri(sb.ToString());
288 #region Async communications with server
294 private void StreamIsReadyDelegate(IAsyncResult ar)
298 Stream s = (Stream) ar.AsyncState;
299 int read = s.EndRead(ar);
303 _resource.Write(_readbuf, 0, read);
306 s.BeginRead(_readbuf, 0, BufferSize,
new AsyncCallback(StreamIsReadyDelegate), s);
324 #endregion Async communications with server
331 return Request(null);
341 _request = (HttpWebRequest) WebRequest.Create(buildUri());
342 _request.KeepAlive =
false;
343 _request.ContentType =
"application/xml";
344 _request.Timeout = 200000;
346 _asyncException = null;
348 auth.AddAuthorization(_request.Headers);
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);
358 using (_response = (HttpWebResponse) _request.GetResponse())
360 using (Stream src = _response.GetResponseStream())
362 int length = src.Read(_readbuf, 0, BufferSize);
365 _resource.Write(_readbuf, 0, length);
366 length = src.Read(_readbuf, 0, BufferSize);
377 catch (WebException e)
379 using (HttpWebResponse errorResponse = e.Response as HttpWebResponse)
381 if (null != errorResponse && HttpStatusCode.NotFound == errorResponse.StatusCode)
384 m_log.DebugFormat(
"[REST CLIENT] Resource not found (404): {0}", _request.Address.ToString());
388 m_log.Error(string.Format(
"[REST CLIENT] Error fetching resource from server: {0} ", _request.Address.ToString()), e);
395 if (_asyncException != null)
396 throw _asyncException;
398 if (_resource != null)
401 _resource.Seek(0, SeekOrigin.Begin);
404 if (WebUtil.DebugLevel >= 5)
405 WebUtil.LogResponseDetail(reqnum, _resource);
413 _request = (HttpWebRequest) WebRequest.Create(buildUri());
414 _request.KeepAlive =
false;
415 _request.ContentType =
"application/xml";
416 _request.Timeout = 90000;
418 _asyncException = null;
419 _request.ContentLength = src.Length;
421 auth.AddAuthorization(_request.Headers);
423 src.Seek(0, SeekOrigin.Begin);
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);
431 using (Stream dst = _request.GetRequestStream())
433 m_log.Info(
"[REST]: GetRequestStream is ok");
435 byte[] buf =
new byte[1024];
436 int length = src.Read(buf, 0, 1024);
437 m_log.Info(
"[REST]: First Read is ok");
440 dst.Write(buf, 0, length);
441 length = src.Read(buf, 0, 1024);
447 _response = (HttpWebResponse)_request.GetResponse();
449 catch (WebException e)
451 m_log.WarnFormat(
"[REST]: Request {0} {1} failed with status {2} and message {3}",
458 "[REST]: Request {0} {1} failed with exception {2} {3}",
459 RequestMethod, _request.RequestUri, e.Message, e.StackTrace);
463 if (WebUtil.DebugLevel >= 5)
465 using (Stream responseStream = _response.GetResponseStream())
467 using (StreamReader reader =
new StreamReader(responseStream))
469 string responseStr = reader.ReadToEnd();
470 WebUtil.LogResponseDetail(reqnum, responseStr);
475 if (_response != null)
487 #region Async Invocation
494 AsyncResult<Stream> ar =
new AsyncResult<Stream>(callback, state);
495 Util.FireAndForget(RequestHelper, ar,
"RestClient.BeginRequest");
501 AsyncResult<Stream> ar = (AsyncResult<Stream>) asyncResult;
505 return ar.EndInvoke();
508 private void RequestHelper(Object asyncResult)
511 AsyncResult<Stream> ar = (AsyncResult<Stream>) asyncResult;
515 Stream s = Request(null);
516 ar.SetAsCompleted(s,
false);
521 ar.HandleException(e,
false);
525 #endregion Async Invocation
528 internal class SimpleAsyncResult : IAsyncResult
530 private readonly AsyncCallback m_callback;
536 private byte m_completed;
544 private byte m_completedSynchronously;
546 private readonly
object m_asyncState;
547 private ManualResetEvent m_waitHandle;
548 private Exception m_exception;
550 internal SimpleAsyncResult(AsyncCallback cb,
object state)
553 m_asyncState = state;
555 m_completedSynchronously = 1;
558 #region IAsyncResult Members
560 public object AsyncState
562 get {
return m_asyncState; }
565 public WaitHandle AsyncWaitHandle
569 if (m_waitHandle == null)
571 bool done = IsCompleted;
572 ManualResetEvent mre =
new ManualResetEvent(done);
573 if (Interlocked.CompareExchange(ref m_waitHandle, mre, null) != null)
579 if (!done && IsCompleted)
591 public bool CompletedSynchronously
593 get {
return Thread.VolatileRead(ref m_completedSynchronously) == 1; }
597 public bool IsCompleted
599 get {
return Thread.VolatileRead(ref m_completed) == 1; }
604 #region class Methods
606 internal void SetAsCompleted(
bool completedSynchronously)
609 if (completedSynchronously)
610 m_completedSynchronously = 1;
612 m_completedSynchronously = 0;
617 internal void HandleException(Exception e,
bool completedSynchronously)
620 if (completedSynchronously)
621 m_completedSynchronously = 1;
623 m_completedSynchronously = 0;
629 private void SignalCompletion()
631 if (m_waitHandle != null) m_waitHandle.Set();
633 if (m_callback != null) m_callback(
this);
636 public void EndInvoke()
642 AsyncWaitHandle.WaitOne();
643 AsyncWaitHandle.Close();
644 m_waitHandle.Close();
649 if (m_exception != null)
throw m_exception;
655 internal class AsyncResult<T> : SimpleAsyncResult
657 private T m_result =
default(T);
659 public AsyncResult(AsyncCallback asyncCallback, Object state) :
660 base(asyncCallback, state)
664 public void SetAsCompleted(T result,
bool completedSynchronously)
671 base.SetAsCompleted(completedSynchronously);
674 public new T EndInvoke()
Stream Request()
Perform a synchronous request
void AddResourcePath(string element)
Add a path element to the query, e.g. assets
IAsyncResult BeginRequest(AsyncCallback callback, object state)
virtual void Dispose(bool disposing)
Stream Request(IServiceAuth auth)
Perform a synchronous request
delegate void RequestMethod(UUID requestID, Hashtable request)
void AddQueryParameter(string name, string value)
Add a query parameter to the Url
Stream Request(Stream src, IServiceAuth auth)
RestClient(string url)
Instantiate a new RestClient
void AddQueryParameter(string name)
Add a query parameter to the Url
Implementation of a generic REST client
Stream EndRequest(IAsyncResult asyncResult)