aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs')
-rw-r--r--OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs437
1 files changed, 437 insertions, 0 deletions
diff --git a/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs
new file mode 100644
index 0000000..9f3bd09
--- /dev/null
+++ b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs
@@ -0,0 +1,437 @@
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 OpenSim 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.Text;
33using System.Threading;
34using OpenMetaverse;
35using Nini.Config;
36using OpenSim.Framework;
37using OpenSim.Framework.Servers;
38using OpenSim.Region.Framework.Interfaces;
39using OpenSim.Region.Framework.Scenes;
40using System.Collections;
41
42/*****************************************************
43 *
44 * ScriptsHttpRequests
45 *
46 * Implements the llHttpRequest and http_response
47 * callback.
48 *
49 * Some stuff was already in LSLLongCmdHandler, and then
50 * there was this file with a stub class in it. So,
51 * I am moving some of the objects and functions out of
52 * LSLLongCmdHandler, such as the HttpRequestClass, the
53 * start and stop methods, and setting up pending and
54 * completed queues. These are processed in the
55 * LSLLongCmdHandler polling loop. Similiar to the
56 * XMLRPCModule, since that seems to work.
57 *
58 * //TODO
59 *
60 * This probably needs some throttling mechanism but
61 * it's wide open right now. This applies to both
62 * number of requests and data volume.
63 *
64 * Linden puts all kinds of header fields in the requests.
65 * Not doing any of that:
66 * User-Agent
67 * X-SecondLife-Shard
68 * X-SecondLife-Object-Name
69 * X-SecondLife-Object-Key
70 * X-SecondLife-Region
71 * X-SecondLife-Local-Position
72 * X-SecondLife-Local-Velocity
73 * X-SecondLife-Local-Rotation
74 * X-SecondLife-Owner-Name
75 * X-SecondLife-Owner-Key
76 *
77 * HTTPS support
78 *
79 * Configurable timeout?
80 * Configurable max response size?
81 * Configurable
82 *
83 * **************************************************/
84
85namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
86{
87 public class HttpRequestModule : IRegionModule, IHttpRequestModule
88 {
89 private object HttpListLock = new object();
90 private int httpTimeout = 30000;
91 private string m_name = "HttpScriptRequests";
92
93 private string m_proxyurl = "";
94 private string m_proxyexcepts = "";
95
96 // <request id, HttpRequestClass>
97 private Dictionary<UUID, HttpRequestClass> m_pendingRequests;
98 private Scene m_scene;
99 // private Queue<HttpRequestClass> rpcQueue = new Queue<HttpRequestClass>();
100
101 public HttpRequestModule()
102 {
103 }
104
105 #region IHttpRequestModule Members
106
107 public UUID MakeHttpRequest(string url, string parameters, string body)
108 {
109 return UUID.Zero;
110 }
111
112 public UUID StartHttpRequest(uint localID, UUID itemID, string url, List<string> parameters, Dictionary<string, string> headers, string body)
113 {
114 UUID reqID = UUID.Random();
115 HttpRequestClass htc = new HttpRequestClass();
116
117 // Partial implementation: support for parameter flags needed
118 // see http://wiki.secondlife.com/wiki/LlHTTPRequest
119 //
120 // Parameters are expected in {key, value, ... , key, value}
121 if (parameters != null)
122 {
123 string[] parms = parameters.ToArray();
124 for (int i = 0; i < parms.Length; i += 2)
125 {
126 switch (Int32.Parse(parms[i]))
127 {
128 case (int)HttpRequestConstants.HTTP_METHOD:
129
130 htc.HttpMethod = parms[i + 1];
131 break;
132
133 case (int)HttpRequestConstants.HTTP_MIMETYPE:
134
135 htc.HttpMIMEType = parms[i + 1];
136 break;
137
138 case (int)HttpRequestConstants.HTTP_BODY_MAXLENGTH:
139
140 // TODO implement me
141 break;
142
143 case (int)HttpRequestConstants.HTTP_VERIFY_CERT:
144
145 // TODO implement me
146 break;
147 }
148 }
149 }
150
151 htc.LocalID = localID;
152 htc.ItemID = itemID;
153 htc.Url = url;
154 htc.ReqID = reqID;
155 htc.HttpTimeout = httpTimeout;
156 htc.OutboundBody = body;
157 htc.ResponseHeaders = headers;
158 htc.proxyurl = m_proxyurl;
159 htc.proxyexcepts = m_proxyexcepts;
160
161 lock (HttpListLock)
162 {
163 m_pendingRequests.Add(reqID, htc);
164 }
165
166 htc.Process();
167
168 return reqID;
169 }
170
171 public void StopHttpRequest(uint m_localID, UUID m_itemID)
172 {
173 if (m_pendingRequests != null)
174 {
175 lock (HttpListLock)
176 {
177 HttpRequestClass tmpReq;
178 if (m_pendingRequests.TryGetValue(m_itemID, out tmpReq))
179 {
180 tmpReq.Stop();
181 m_pendingRequests.Remove(m_itemID);
182 }
183 }
184 }
185 }
186
187 /*
188 * TODO
189 * Not sure how important ordering is is here - the next first
190 * one completed in the list is returned, based soley on its list
191 * position, not the order in which the request was started or
192 * finsihed. I thought about setting up a queue for this, but
193 * it will need some refactoring and this works 'enough' right now
194 */
195
196 public IServiceRequest GetNextCompletedRequest()
197 {
198 lock (HttpListLock)
199 {
200 foreach (UUID luid in m_pendingRequests.Keys)
201 {
202 HttpRequestClass tmpReq;
203
204 if (m_pendingRequests.TryGetValue(luid, out tmpReq))
205 {
206 if (tmpReq.Finished)
207 {
208 return tmpReq;
209 }
210 }
211 }
212 }
213 return null;
214 }
215
216 public void RemoveCompletedRequest(UUID id)
217 {
218 lock (HttpListLock)
219 {
220 HttpRequestClass tmpReq;
221 if (m_pendingRequests.TryGetValue(id, out tmpReq))
222 {
223 tmpReq.Stop();
224 tmpReq = null;
225 m_pendingRequests.Remove(id);
226 }
227 }
228 }
229
230 #endregion
231
232 #region IRegionModule Members
233
234 public void Initialise(Scene scene, IConfigSource config)
235 {
236 m_scene = scene;
237
238 m_scene.RegisterModuleInterface<IHttpRequestModule>(this);
239
240 m_proxyurl = config.Configs["Startup"].GetString("HttpProxy");
241 m_proxyexcepts = config.Configs["Startup"].GetString("HttpProxyExceptions");
242
243 m_pendingRequests = new Dictionary<UUID, HttpRequestClass>();
244 }
245
246 public void PostInitialise()
247 {
248 }
249
250 public void Close()
251 {
252 }
253
254 public string Name
255 {
256 get { return m_name; }
257 }
258
259 public bool IsSharedModule
260 {
261 get { return true; }
262 }
263
264 #endregion
265 }
266
267 public class HttpRequestClass: IServiceRequest
268 {
269 // Constants for parameters
270 // public const int HTTP_BODY_MAXLENGTH = 2;
271 // public const int HTTP_METHOD = 0;
272 // public const int HTTP_MIMETYPE = 1;
273 // public const int HTTP_VERIFY_CERT = 3;
274 private bool _finished;
275 public bool Finished
276 {
277 get { return _finished; }
278 }
279 // public int HttpBodyMaxLen = 2048; // not implemented
280
281 // Parameter members and default values
282 public string HttpMethod = "GET";
283 public string HttpMIMEType = "text/plain;charset=utf-8";
284 public int HttpTimeout;
285 // public bool HttpVerifyCert = true; // not implemented
286 private Thread httpThread;
287
288 // Request info
289 private UUID _itemID;
290 public UUID ItemID
291 {
292 get { return _itemID; }
293 set { _itemID = value; }
294 }
295 private uint _localID;
296 public uint LocalID
297 {
298 get { return _localID; }
299 set { _localID = value; }
300 }
301 public DateTime Next;
302 public string proxyurl;
303 public string proxyexcepts;
304 public string OutboundBody;
305 private UUID _reqID;
306 public UUID ReqID
307 {
308 get { return _reqID; }
309 set { _reqID = value; }
310 }
311 public HttpWebRequest Request;
312 public string ResponseBody;
313 public List<string> ResponseMetadata;
314 public Dictionary<string, string> ResponseHeaders;
315 public int Status;
316 public string Url;
317
318 public void Process()
319 {
320 httpThread = new Thread(SendRequest);
321 httpThread.Name = "HttpRequestThread";
322 httpThread.Priority = ThreadPriority.BelowNormal;
323 httpThread.IsBackground = true;
324 _finished = false;
325 httpThread.Start();
326 ThreadTracker.Add(httpThread);
327 }
328
329 /*
330 * TODO: More work on the response codes. Right now
331 * returning 200 for success or 499 for exception
332 */
333
334 public void SendRequest()
335 {
336 HttpWebResponse response = null;
337 StringBuilder sb = new StringBuilder();
338 byte[] buf = new byte[8192];
339 string tempString = null;
340 int count = 0;
341
342 try
343 {
344 Request = (HttpWebRequest) WebRequest.Create(Url);
345 Request.Method = HttpMethod;
346 Request.ContentType = HttpMIMEType;
347
348 if (proxyurl != null && proxyurl.Length > 0)
349 {
350 if (proxyexcepts != null && proxyexcepts.Length > 0)
351 {
352 string[] elist = proxyexcepts.Split(';');
353 Request.Proxy = new WebProxy(proxyurl, true, elist);
354 }
355 else
356 {
357 Request.Proxy = new WebProxy(proxyurl, true);
358 }
359 }
360
361 foreach (KeyValuePair<string, string> entry in ResponseHeaders)
362 Request.Headers[entry.Key] = entry.Value;
363
364 // Encode outbound data
365 if (OutboundBody.Length > 0)
366 {
367 byte[] data = Encoding.UTF8.GetBytes(OutboundBody);
368
369 Request.ContentLength = data.Length;
370 Stream bstream = Request.GetRequestStream();
371 bstream.Write(data, 0, data.Length);
372 bstream.Close();
373 }
374
375 Request.Timeout = HttpTimeout;
376 // execute the request
377 response = (HttpWebResponse) Request.GetResponse();
378
379 Stream resStream = response.GetResponseStream();
380
381 do
382 {
383 // fill the buffer with data
384 count = resStream.Read(buf, 0, buf.Length);
385
386 // make sure we read some data
387 if (count != 0)
388 {
389 // translate from bytes to ASCII text
390 tempString = Encoding.UTF8.GetString(buf, 0, count);
391
392 // continue building the string
393 sb.Append(tempString);
394 }
395 } while (count > 0); // any more data to read?
396
397 ResponseBody = sb.ToString();
398 }
399 catch (Exception e)
400 {
401 if (e is WebException && ((WebException)e).Status == WebExceptionStatus.ProtocolError)
402 {
403 HttpWebResponse webRsp = (HttpWebResponse)((WebException)e).Response;
404 Status = (int)webRsp.StatusCode;
405 ResponseBody = webRsp.StatusDescription;
406 }
407 else
408 {
409 Status = (int)OSHttpStatusCode.ClientErrorJoker;
410 ResponseBody = e.Message;
411 }
412
413 _finished = true;
414 return;
415 }
416 finally
417 {
418 if (response != null)
419 response.Close();
420 }
421
422 Status = (int)OSHttpStatusCode.SuccessOk;
423 _finished = true;
424 }
425
426 public void Stop()
427 {
428 try
429 {
430 httpThread.Abort();
431 }
432 catch (Exception)
433 {
434 }
435 }
436 }
437}