aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/Communications/RestClient.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Framework/Communications/RestClient.cs')
-rw-r--r--OpenSim/Framework/Communications/RestClient.cs368
1 files changed, 368 insertions, 0 deletions
diff --git a/OpenSim/Framework/Communications/RestClient.cs b/OpenSim/Framework/Communications/RestClient.cs
new file mode 100644
index 0000000..91284e8
--- /dev/null
+++ b/OpenSim/Framework/Communications/RestClient.cs
@@ -0,0 +1,368 @@
1using System;
2using System.Collections.Generic;
3using System.IO;
4using System.Net;
5using System.Reflection;
6using System.Text;
7using System.Threading;
8using System.Web;
9using log4net;
10
11namespace OpenSim.Framework.Communications
12{
13 /// <summary>
14 /// Implementation of a generic REST client
15 /// </summary>
16 /// <remarks>
17 /// This class is a generic implementation of a REST (Representational State Transfer) web service. This
18 /// class is designed to execute both synchroneously and asynchroneously.
19 ///
20 /// Internally the implementation works as a two stage asynchroneous web-client.
21 /// When the request is initiated, RestClient will query asynchroneously for for a web-response,
22 /// sleeping until the initial response is returned by the server. Once the initial response is retrieved
23 /// the second stage of asynchroneous requests will be triggered, in an attempt to read of the response
24 /// object into a memorystream as a sequence of asynchroneous reads.
25 ///
26 /// The asynchronisity of RestClient is designed to move as much processing into the back-ground, allowing
27 /// other threads to execute, while it waits for a response from the web-service. RestClient it self, can be
28 /// invoked by the caller in either synchroneous mode or asynchroneous mode.
29 /// </remarks>
30 public class RestClient
31 {
32 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
33
34 private string realuri;
35
36 #region member variables
37
38 /// <summary>
39 /// The base Uri of the web-service e.g. http://www.google.com
40 /// </summary>
41 private string _url;
42
43 /// <summary>
44 /// Path elements of the query
45 /// </summary>
46 private List<string> _pathElements = new List<string>();
47
48 /// <summary>
49 /// Parameter elements of the query, e.g. min=34
50 /// </summary>
51 private Dictionary<string, string> _parameterElements = new Dictionary<string, string>();
52
53 /// <summary>
54 /// Request method. E.g. GET, POST, PUT or DELETE
55 /// </summary>
56 private string _method;
57
58 /// <summary>
59 /// Temporary buffer used to store bytes temporarily as they come in from the server
60 /// </summary>
61 private byte[] _readbuf;
62
63 /// <summary>
64 /// MemoryStream representing the resultiong resource
65 /// </summary>
66 private Stream _resource;
67
68 /// <summary>
69 /// WebRequest object, held as a member variable
70 /// </summary>
71 private HttpWebRequest _request;
72
73 /// <summary>
74 /// WebResponse object, held as a member variable, so we can close it
75 /// </summary>
76 private HttpWebResponse _response;
77
78 /// <summary>
79 /// This flag will help block the main synchroneous method, in case we run in synchroneous mode
80 /// </summary>
81 public static ManualResetEvent _allDone = new ManualResetEvent(false);
82
83 /// <summary>
84 /// Default time out period
85 /// </summary>
86 private const int DefaultTimeout = 10*1000; // 10 seconds timeout
87
88 /// <summary>
89 /// Default Buffer size of a block requested from the web-server
90 /// </summary>
91 private const int BufferSize = 4096; // Read blocks of 4 KB.
92
93
94 /// <summary>
95 /// if an exception occours during async processing, we need to save it, so it can be
96 /// rethrown on the primary thread;
97 /// </summary>
98 private Exception _asyncException;
99
100 #endregion member variables
101
102 #region constructors
103
104 /// <summary>
105 /// Instantiate a new RestClient
106 /// </summary>
107 /// <param name="url">Web-service to query, e.g. http://osgrid.org:8003</param>
108 public RestClient(string url)
109 {
110 _url = url;
111 _readbuf = new byte[BufferSize];
112 _resource = new MemoryStream();
113 _request = null;
114 _response = null;
115 _lock = new object();
116 }
117
118 private object _lock;
119
120 #endregion constructors
121
122 /// <summary>
123 /// Add a path element to the query, e.g. assets
124 /// </summary>
125 /// <param name="element">path entry</param>
126 public void AddResourcePath(string element)
127 {
128 if (isSlashed(element))
129 _pathElements.Add(element.Substring(0, element.Length - 1));
130 else
131 _pathElements.Add(element);
132 }
133
134 /// <summary>
135 /// Add a query parameter to the Url
136 /// </summary>
137 /// <param name="name">Name of the parameter, e.g. min</param>
138 /// <param name="value">Value of the parameter, e.g. 42</param>
139 public void AddQueryParameter(string name, string value)
140 {
141 _parameterElements.Add(HttpUtility.UrlEncode(name), HttpUtility.UrlEncode(value));
142 }
143
144 /// <summary>
145 /// Add a query parameter to the Url
146 /// </summary>
147 /// <param name="name">Name of the parameter, e.g. min</param>
148 public void AddQueryParameter(string name)
149 {
150 _parameterElements.Add(HttpUtility.UrlEncode(name), null);
151 }
152
153 /// <summary>
154 /// Web-Request method, e.g. GET, PUT, POST, DELETE
155 /// </summary>
156 public string RequestMethod
157 {
158 get { return _method; }
159 set { _method = value; }
160 }
161
162 /// <summary>
163 /// True if string contains a trailing slash '/'
164 /// </summary>
165 /// <param name="s">string to be examined</param>
166 /// <returns>true if slash is present</returns>
167 private bool isSlashed(string s)
168 {
169 return s.Substring(s.Length - 1, 1) == "/";
170 }
171
172 /// <summary>
173 /// Build a Uri based on the initial Url, path elements and parameters
174 /// </summary>
175 /// <returns>fully constructed Uri</returns>
176 private Uri buildUri()
177 {
178 StringBuilder sb = new StringBuilder();
179 sb.Append(_url);
180
181 foreach (string e in _pathElements)
182 {
183 sb.Append("/");
184 sb.Append(e);
185 }
186
187 bool firstElement = true;
188 foreach (KeyValuePair<string, string> kv in _parameterElements)
189 {
190 if (firstElement)
191 {
192 sb.Append("?");
193 firstElement = false;
194 }
195 else
196 sb.Append("&");
197
198 sb.Append(kv.Key);
199 if (kv.Value != null && kv.Value.Length != 0)
200 {
201 sb.Append("=");
202 sb.Append(kv.Value);
203 }
204 }
205 realuri = sb.ToString();
206 //m_log.InfoFormat("[REST CLIENT]: RestURL: {0}", realuri);
207 return new Uri(sb.ToString());
208 }
209
210 #region Async communications with server
211
212 /// <summary>
213 /// Async method, invoked when a block of data has been received from the service
214 /// </summary>
215 /// <param name="ar"></param>
216 private void StreamIsReadyDelegate(IAsyncResult ar)
217 {
218 try
219 {
220 Stream s = (Stream) ar.AsyncState;
221 int read = s.EndRead(ar);
222
223 if (read > 0)
224 {
225 _resource.Write(_readbuf, 0, read);
226 IAsyncResult asynchronousResult =
227 s.BeginRead(_readbuf, 0, BufferSize, new AsyncCallback(StreamIsReadyDelegate), s);
228
229 // TODO! Implement timeout, without killing the server
230 //ThreadPool.RegisterWaitForSingleObject(asynchronousResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true);
231 }
232 else
233 {
234 s.Close();
235 _allDone.Set();
236 }
237 }
238 catch (Exception e)
239 {
240 _allDone.Set();
241 _asyncException = e;
242 }
243 }
244
245 #endregion Async communications with server
246
247 /// <summary>
248 /// Perform synchroneous request
249 /// </summary>
250 public Stream Request()
251 {
252 lock (_lock)
253 {
254 _request = (HttpWebRequest) WebRequest.Create(buildUri());
255 _request.KeepAlive = false;
256 _request.ContentType = "application/xml";
257 _request.Timeout = 200000;
258 _asyncException = null;
259
260// IAsyncResult responseAsyncResult = _request.BeginGetResponse(new AsyncCallback(ResponseIsReadyDelegate), _request);
261 _response = (HttpWebResponse) _request.GetResponse();
262 Stream src = _response.GetResponseStream();
263 int length = src.Read(_readbuf, 0, BufferSize);
264 while (length > 0)
265 {
266 _resource.Write(_readbuf, 0, length);
267 length = src.Read(_readbuf, 0, BufferSize);
268 }
269
270
271 // TODO! Implement timeout, without killing the server
272 // this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted
273 //ThreadPool.RegisterWaitForSingleObject(responseAsyncResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true);
274
275// _allDone.WaitOne();
276 if (_response != null)
277 _response.Close();
278 if (_asyncException != null)
279 throw _asyncException;
280
281 if (_resource != null)
282 {
283 _resource.Flush();
284 _resource.Seek(0, SeekOrigin.Begin);
285 }
286
287 return _resource;
288 }
289 }
290
291 public Stream Request(Stream src)
292 {
293 _request = (HttpWebRequest) WebRequest.Create(buildUri());
294 _request.KeepAlive = false;
295 _request.ContentType = "application/xml";
296 _request.Timeout = 900000;
297 _request.Method = RequestMethod;
298 _asyncException = null;
299 _request.ContentLength = src.Length;
300
301 m_log.InfoFormat("[REST]: Request Length {0}", _request.ContentLength);
302 m_log.InfoFormat("[REST]: Sending Web Request {0}", buildUri());
303 src.Seek(0, SeekOrigin.Begin);
304 m_log.Info("[REST]: Seek is ok");
305 Stream dst = _request.GetRequestStream();
306 m_log.Info("[REST]: GetRequestStream is ok");
307
308 byte[] buf = new byte[1024];
309 int length = src.Read(buf, 0, 1024);
310 m_log.Info("[REST]: First Read is ok");
311 while (length > 0)
312 {
313 dst.Write(buf, 0, length);
314 length = src.Read(buf, 0, 1024);
315 }
316
317 _response = (HttpWebResponse) _request.GetResponse();
318
319// IAsyncResult responseAsyncResult = _request.BeginGetResponse(new AsyncCallback(ResponseIsReadyDelegate), _request);
320
321 // TODO! Implement timeout, without killing the server
322 // this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted
323 //ThreadPool.RegisterWaitForSingleObject(responseAsyncResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true);
324
325 return null;
326 }
327
328 #region Async Invocation
329
330 public IAsyncResult BeginRequest(AsyncCallback callback, object state)
331 {
332 /// <summary>
333 /// In case, we are invoked asynchroneously this object will keep track of the state
334 /// </summary>
335 AsyncResult<Stream> ar = new AsyncResult<Stream>(callback, state);
336 ThreadPool.QueueUserWorkItem(RequestHelper, ar);
337 return ar;
338 }
339
340 public Stream EndRequest(IAsyncResult asyncResult)
341 {
342 AsyncResult<Stream> ar = (AsyncResult<Stream>) asyncResult;
343
344 // Wait for operation to complete, then return result or
345 // throw exception
346 return ar.EndInvoke();
347 }
348
349 private void RequestHelper(Object asyncResult)
350 {
351 // We know that it's really an AsyncResult<DateTime> object
352 AsyncResult<Stream> ar = (AsyncResult<Stream>) asyncResult;
353 try
354 {
355 // Perform the operation; if sucessful set the result
356 Stream s = Request();
357 ar.SetAsCompleted(s, false);
358 }
359 catch (Exception e)
360 {
361 // If operation fails, set the exception
362 ar.HandleException(e, false);
363 }
364 }
365
366 #endregion Async Invocation
367 }
368} \ No newline at end of file