aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--OpenSim/Framework/Communications/RestClient/GenericAsyncResult.cs163
-rw-r--r--OpenSim/Framework/Communications/RestClient/RestClient.cs328
-rw-r--r--prebuild.xml1
3 files changed, 492 insertions, 0 deletions
diff --git a/OpenSim/Framework/Communications/RestClient/GenericAsyncResult.cs b/OpenSim/Framework/Communications/RestClient/GenericAsyncResult.cs
new file mode 100644
index 0000000..4be459d
--- /dev/null
+++ b/OpenSim/Framework/Communications/RestClient/GenericAsyncResult.cs
@@ -0,0 +1,163 @@
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Threading;
5
6namespace OpenSim.Framework.RestClient
7{
8 internal class SimpleAsyncResult : IAsyncResult
9 {
10
11 private readonly AsyncCallback m_callback;
12
13 /// <summary>
14 /// Is process completed?
15 /// </summary>
16 /// <remarks>Should really be boolean, but VolatileRead has no boolean method</remarks>
17 private byte m_completed;
18
19 /// <summary>
20 /// Did process complete synchroneously?
21 /// </summary>
22 /// <remarks>I have a hard time imagining a scenario where this is the case, again, same issue about
23 /// booleans and VolatileRead as m_completed
24 /// </remarks>
25 private byte m_completedSynchronously;
26
27 private readonly object m_asyncState;
28 private ManualResetEvent m_waitHandle;
29 private Exception m_exception;
30
31 internal SimpleAsyncResult(AsyncCallback cb, object state)
32 {
33 m_callback = cb;
34 m_asyncState = state;
35 m_completed = 0;
36 m_completedSynchronously = 1;
37 }
38
39
40 #region IAsyncResult Members
41
42 public object AsyncState
43 {
44 get { return m_asyncState; }
45 }
46
47
48
49 public WaitHandle AsyncWaitHandle
50 {
51 get
52 {
53 if (m_waitHandle == null)
54 {
55 bool done = IsCompleted;
56 ManualResetEvent mre = new ManualResetEvent(done);
57 if (Interlocked.CompareExchange(ref m_waitHandle, mre, null) != null)
58 {
59 mre.Close();
60 }
61 else
62 {
63 if (!done && IsCompleted)
64 {
65 m_waitHandle.Set();
66 }
67 }
68 }
69 return m_waitHandle;
70 }
71 }
72
73
74 public bool CompletedSynchronously
75 {
76 get { return Thread.VolatileRead(ref m_completedSynchronously) == 1; }
77 }
78
79
80 public bool IsCompleted
81 {
82 get { return Thread.VolatileRead(ref m_completed) == 1; }
83 }
84
85
86 #endregion
87
88
89 #region class Methods
90 internal void SetAsCompleted(bool completedSynchronously)
91 {
92 m_completed = 1;
93 if(completedSynchronously)
94 m_completedSynchronously = 1;
95 else
96 m_completedSynchronously = 0;
97
98 SignalCompletion();
99 }
100
101 internal void HandleException(Exception e, bool completedSynchronously)
102 {
103 m_completed = 1;
104 if (completedSynchronously)
105 m_completedSynchronously = 1;
106 else
107 m_completedSynchronously = 0;
108 m_exception = e;
109
110 SignalCompletion();
111 }
112
113 private void SignalCompletion()
114 {
115 if(m_waitHandle != null) m_waitHandle.Set();
116
117 if(m_callback != null) m_callback(this);
118 }
119
120 public void EndInvoke()
121 {
122 // This method assumes that only 1 thread calls EndInvoke
123 if (!IsCompleted)
124 {
125 // If the operation isn't done, wait for it
126 AsyncWaitHandle.WaitOne();
127 AsyncWaitHandle.Close();
128 m_waitHandle = null; // Allow early GC
129 }
130
131 // Operation is done: if an exception occured, throw it
132 if (m_exception != null) throw m_exception;
133 }
134
135 #endregion
136 }
137
138 internal class AsyncResult<T> : SimpleAsyncResult
139 {
140 private T m_result = default(T);
141
142 public AsyncResult(AsyncCallback asyncCallback, Object state) :
143 base(asyncCallback, state) { }
144
145
146 public void SetAsCompleted(T result, bool completedSynchronously)
147 {
148 // Save the asynchronous operation's result
149 m_result = result;
150
151 // Tell the base class that the operation completed
152 // sucessfully (no exception)
153 base.SetAsCompleted(completedSynchronously);
154 }
155
156 new public T EndInvoke()
157 {
158 base.EndInvoke();
159 return m_result;
160 }
161
162 }
163}
diff --git a/OpenSim/Framework/Communications/RestClient/RestClient.cs b/OpenSim/Framework/Communications/RestClient/RestClient.cs
new file mode 100644
index 0000000..ccf8376
--- /dev/null
+++ b/OpenSim/Framework/Communications/RestClient/RestClient.cs
@@ -0,0 +1,328 @@
1using System;
2using System.IO;
3using System.Net;
4using System.Web;
5using System.Text;
6using System.Collections.Generic;
7using System.Threading;
8
9namespace OpenSim.Framework.RestClient
10{
11 /// <summary>
12 /// Implementation of a generic REST client
13 /// </summary>
14 /// <remarks>
15 /// This class is a generic implementation of a REST (Representational State Transfer) web service. This
16 /// class is designed to execute both synchroneously and asynchroneously.
17 ///
18 /// Internally the implementation works as a two stage asynchroneous web-client.
19 /// When the request is initiated, RestClient will query asynchroneously for for a web-response,
20 /// sleeping until the initial response is returned by the server. Once the initial response is retrieved
21 /// the second stage of asynchroneous requests will be triggered, in an attempt to read of the response
22 /// object into a memorystream as a sequence of asynchroneous reads.
23 ///
24 /// The asynchronisity of RestClient is designed to move as much processing into the back-ground, allowing
25 /// other threads to execute, while it waits for a response from the web-service. RestClient it self, can be
26 /// invoked by the caller in either synchroneous mode or asynchroneous mode.
27 /// </remarks>
28 public class RestClient
29 {
30 /// <summary>
31 /// The base Uri of the web-service e.g. http://www.google.com
32 /// </summary>
33 private string _url;
34
35 /// <summary>
36 /// Path elements of the query
37 /// </summary>
38 private List<string> _pathElements = new List<string>();
39
40 /// <summary>
41 /// Parameter elements of the query, e.g. min=34
42 /// </summary>
43 private Dictionary<string, string> _parameterElements = new Dictionary<string, string>();
44
45 /// <summary>
46 /// Request method. E.g. GET, POST, PUT or DELETE
47 /// </summary>
48 private string _method;
49
50 /// <summary>
51 /// Temporary buffer used to store bytes temporarily as they come in from the server
52 /// </summary>
53 private byte[] _readbuf;
54
55 /// <summary>
56 /// MemoryStream representing the resultiong resource
57 /// </summary>
58 MemoryStream _resource;
59
60 /// <summary>
61 /// WebRequest object, held as a member variable
62 /// </summary>
63 private HttpWebRequest _request;
64
65 /// <summary>
66 /// WebResponse object, held as a member variable, so we can close it
67 /// </summary>
68 private HttpWebResponse _response;
69
70 /// <summary>
71 /// This flag will help block the main synchroneous method, in case we run in synchroneous mode
72 /// </summary>
73 public static ManualResetEvent _allDone = new ManualResetEvent(false);
74
75 /// <summary>
76 /// Default time out period
77 /// </summary>
78 const int DefaultTimeout = 10 * 1000; // 10 seconds timeout
79
80 /// <summary>
81 /// Default Buffer size of a block requested from the web-server
82 /// </summary>
83 const int BufferSize = 4096; // Read blocks of 4 KB.
84
85
86 /// <summary>
87 /// if an exception occours during async processing, we need to save it, so it can be
88 /// rethrown on the primary thread;
89 /// </summary>
90 private Exception _asyncException;
91
92 /// <summary>
93 /// Instantiate a new RestClient
94 /// </summary>
95 /// <param name="url">Web-service to query, e.g. http://osgrid.org:8003</param>
96 public RestClient(string url)
97 {
98 _url = url;
99 _readbuf = new byte[BufferSize];
100 _resource = new MemoryStream();
101 _request = null;
102 _response = null;
103 }
104
105 /// <summary>
106 /// Add a path element to the query, e.g. assets
107 /// </summary>
108 /// <param name="element">path entry</param>
109 public void AddResourcePath(string element)
110 {
111 if(isSlashed(element))
112 _pathElements.Add(element.Substring(0, element.Length-1));
113 else
114 _pathElements.Add(element);
115 }
116
117 /// <summary>
118 /// Add a query parameter to the Url
119 /// </summary>
120 /// <param name="name">Name of the parameter, e.g. min</param>
121 /// <param name="value">Value of the parameter, e.g. 42</param>
122 public void AddQueryParameter(string name, string value)
123 {
124 _parameterElements.Add(HttpUtility.UrlEncode(name), HttpUtility.UrlEncode(value));
125 }
126
127 /// <summary>
128 /// Web-Request method, e.g. GET, PUT, POST, DELETE
129 /// </summary>
130 public string RequestMethod
131 {
132 get { return _method; }
133 set { _method = value; }
134 }
135
136 /// <summary>
137 /// True if string contains a trailing slash '/'
138 /// </summary>
139 /// <param name="s">string to be examined</param>
140 /// <returns>true if slash is present</returns>
141 private bool isSlashed(string s)
142 {
143 return s.Substring(s.Length - 1, 1) == "/";
144 }
145
146 /// <summary>
147 /// return a slash or blank. A slash will be returned if the string does not contain one
148 /// </summary>
149 /// <param name="s">stromg to be examined</param>
150 /// <returns>slash '/' if not already present</returns>
151 private string slash(string s)
152 {
153 return isSlashed(s) ? "" : "/";
154 }
155
156 /// <summary>
157 /// Build a Uri based on the intial Url, path elements and parameters
158 /// </summary>
159 /// <returns>fully constructed Uri</returns>
160 Uri buildUri()
161 {
162 StringBuilder sb = new StringBuilder();
163 sb.Append(_url);
164
165 foreach (string e in _pathElements)
166 {
167 sb.Append("/");
168 sb.Append(e);
169 }
170
171 bool firstElement = true;
172 foreach (KeyValuePair<string, string> kv in _parameterElements)
173 {
174 if (firstElement)
175 {
176 sb.Append("?");
177 firstElement = false;
178 } else
179 sb.Append("&");
180
181 sb.Append(kv.Key);
182 if (kv.Value != null && kv.Value.Length != 0)
183 {
184 sb.Append("=");
185 sb.Append(kv.Value);
186 }
187 }
188 return new Uri(sb.ToString());
189 }
190
191 /// <summary>
192 /// Async method, invoked when a block of data has been received from the service
193 /// </summary>
194 /// <param name="ar"></param>
195 private void StreamIsReadyDelegate(IAsyncResult ar)
196 {
197 try
198 {
199 Stream s = (Stream)ar.AsyncState;
200 int read = s.EndRead(ar);
201
202 // Read the HTML page and then print it to the console.
203 if (read > 0)
204 {
205 _resource.Write(_readbuf, 0, read);
206 IAsyncResult asynchronousResult = s.BeginRead(_readbuf, 0, BufferSize, new AsyncCallback(StreamIsReadyDelegate), s);
207
208 // TODO! Implement timeout, without killing the server
209 //ThreadPool.RegisterWaitForSingleObject(asynchronousResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true);
210 return;
211 }
212 else
213 {
214 s.Close();
215 _allDone.Set();
216 }
217 }
218 catch (Exception e)
219 {
220 _allDone.Set();
221 _asyncException = e;
222 }
223 }
224
225 /// <summary>
226 /// Async method, invoked when the intial response if received from the server
227 /// </summary>
228 /// <param name="ar"></param>
229 private void ResponseIsReadyDelegate(IAsyncResult ar)
230 {
231 try
232 {
233 // grab response
234 WebRequest wr = (WebRequest)ar.AsyncState;
235 _response = (HttpWebResponse)wr.EndGetResponse(ar);
236
237 // get response stream, and setup async reading
238 Stream s = _response.GetResponseStream();
239 IAsyncResult asynchronousResult = s.BeginRead(_readbuf, 0, BufferSize, new AsyncCallback(StreamIsReadyDelegate), s);
240
241 // TODO! Implement timeout, without killing the server
242 // wait until completed, or we timed out
243 // ThreadPool.RegisterWaitForSingleObject(asynchronousResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true);
244 }
245 catch (Exception e)
246 {
247 _allDone.Set();
248 _asyncException = e;
249 }
250 }
251
252 // Abort the request if the timer fires.
253 private static void TimeoutCallback(object state, bool timedOut)
254 {
255 if (timedOut)
256 {
257 HttpWebRequest request = state as HttpWebRequest;
258 if (request != null)
259 {
260 request.Abort();
261 }
262 }
263 }
264
265 /// <summary>
266 /// Perform synchroneous request
267 /// </summary>
268 public Stream Request()
269 {
270 _request = (HttpWebRequest)WebRequest.Create(buildUri());
271 _request.KeepAlive = false;
272 _request.ContentType = "text/html";
273 _request.Timeout = 200;
274 _asyncException = null;
275
276 IAsyncResult responseAsyncResult = _request.BeginGetResponse(new AsyncCallback(ResponseIsReadyDelegate), _request);
277
278 // TODO! Implement timeout, without killing the server
279 // this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted
280 //ThreadPool.RegisterWaitForSingleObject(responseAsyncResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true);
281
282 _allDone.WaitOne();
283 if(_response != null)
284 _response.Close();
285 if (_asyncException != null)
286 throw _asyncException;
287 return _resource;
288 }
289
290 #region Async Invocation
291 public IAsyncResult BeginRequest(AsyncCallback callback, object state)
292 {
293 /// <summary>
294 /// In case, we are invoked asynchroneously this object will keep track of the state
295 /// </summary>
296 AsyncResult<Stream> ar = new AsyncResult<Stream>(callback, state);
297 ThreadPool.QueueUserWorkItem(RequestHelper, ar);
298 return ar;
299 }
300
301 public Stream EndRequest(IAsyncResult asyncResult)
302 {
303 AsyncResult<Stream> ar = (AsyncResult<Stream>)asyncResult;
304
305 // Wait for operation to complete, then return result or
306 // throw exception
307 return ar.EndInvoke();
308 }
309
310 private void RequestHelper(Object asyncResult)
311 {
312 // We know that it's really an AsyncResult<DateTime> object
313 AsyncResult<Stream> ar = (AsyncResult<Stream>)asyncResult;
314 try
315 {
316 // Perform the operation; if sucessful set the result
317 Stream s = Request();
318 ar.SetAsCompleted(s, false);
319 }
320 catch (Exception e)
321 {
322 // If operation fails, set the exception
323 ar.HandleException(e, false);
324 }
325 }
326 #endregion Async Invocation
327 }
328}
diff --git a/prebuild.xml b/prebuild.xml
index 536d650..57ace78 100644
--- a/prebuild.xml
+++ b/prebuild.xml
@@ -376,6 +376,7 @@
376 <ReferencePath>../../../bin/</ReferencePath> 376 <ReferencePath>../../../bin/</ReferencePath>
377 <Reference name="System"/> 377 <Reference name="System"/>
378 <Reference name="System.Xml"/> 378 <Reference name="System.Xml"/>
379 <Reference name="System.Web"/>
379 <Reference name="OpenSim.Framework"/> 380 <Reference name="OpenSim.Framework"/>
380 <Reference name="OpenSim.Framework.Data" /> 381 <Reference name="OpenSim.Framework.Data" />
381 <Reference name="OpenSim.Framework.Servers"/> 382 <Reference name="OpenSim.Framework.Servers"/>