diff options
Diffstat (limited to 'OpenSim/Region/CoreModules/Scripting/LSLHttp')
-rw-r--r-- | OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs | 265 |
1 files changed, 248 insertions, 17 deletions
diff --git a/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs b/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs index 6a2a6c8..b885420 100644 --- a/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs +++ b/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs | |||
@@ -55,14 +55,19 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
55 | public UUID requestID; | 55 | public UUID requestID; |
56 | public Dictionary<string, string> headers; | 56 | public Dictionary<string, string> headers; |
57 | public string body; | 57 | public string body; |
58 | public int responseCode; | ||
59 | public string responseBody; | ||
58 | public ManualResetEvent ev; | 60 | public ManualResetEvent ev; |
61 | public bool requestDone; | ||
62 | public int startTime; | ||
63 | public string uri; | ||
59 | } | 64 | } |
60 | 65 | ||
61 | public class UrlModule : ISharedRegionModule, IUrlModule | 66 | public class UrlModule : ISharedRegionModule, IUrlModule |
62 | { | 67 | { |
63 | // private static readonly ILog m_log = | 68 | private static readonly ILog m_log = |
64 | // LogManager.GetLogger( | 69 | LogManager.GetLogger( |
65 | // MethodBase.GetCurrentMethod().DeclaringType); | 70 | MethodBase.GetCurrentMethod().DeclaringType); |
66 | 71 | ||
67 | private Dictionary<UUID, UrlData> m_RequestMap = | 72 | private Dictionary<UUID, UrlData> m_RequestMap = |
68 | new Dictionary<UUID, UrlData>(); | 73 | new Dictionary<UUID, UrlData>(); |
@@ -70,15 +75,23 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
70 | private Dictionary<string, UrlData> m_UrlMap = | 75 | private Dictionary<string, UrlData> m_UrlMap = |
71 | new Dictionary<string, UrlData>(); | 76 | new Dictionary<string, UrlData>(); |
72 | 77 | ||
78 | |||
73 | private int m_TotalUrls = 100; | 79 | private int m_TotalUrls = 100; |
74 | 80 | ||
75 | private IHttpServer m_HttpServer = null; | 81 | private IHttpServer m_HttpServer = null; |
76 | 82 | ||
83 | private string m_ExternalHostNameForLSL = ""; | ||
84 | |||
77 | public Type ReplaceableInterface | 85 | public Type ReplaceableInterface |
78 | { | 86 | { |
79 | get { return null; } | 87 | get { return null; } |
80 | } | 88 | } |
81 | 89 | ||
90 | private Hashtable HandleHttpPoll(Hashtable request) | ||
91 | { | ||
92 | return new Hashtable(); | ||
93 | } | ||
94 | |||
82 | public string Name | 95 | public string Name |
83 | { | 96 | { |
84 | get { return "UrlModule"; } | 97 | get { return "UrlModule"; } |
@@ -86,6 +99,7 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
86 | 99 | ||
87 | public void Initialise(IConfigSource config) | 100 | public void Initialise(IConfigSource config) |
88 | { | 101 | { |
102 | m_ExternalHostNameForLSL = config.Configs["Network"].GetString("ExternalHostNameForLSL", System.Environment.MachineName); | ||
89 | } | 103 | } |
90 | 104 | ||
91 | public void PostInitialise() | 105 | public void PostInitialise() |
@@ -117,7 +131,6 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
117 | public void Close() | 131 | public void Close() |
118 | { | 132 | { |
119 | } | 133 | } |
120 | |||
121 | public UUID RequestURL(IScriptModule engine, SceneObjectPart host, UUID itemID) | 134 | public UUID RequestURL(IScriptModule engine, SceneObjectPart host, UUID itemID) |
122 | { | 135 | { |
123 | UUID urlcode = UUID.Random(); | 136 | UUID urlcode = UUID.Random(); |
@@ -129,7 +142,7 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
129 | engine.PostScriptEvent(itemID, "http_request", new Object[] { urlcode.ToString(), "URL_REQUEST_DENIED", "" }); | 142 | engine.PostScriptEvent(itemID, "http_request", new Object[] { urlcode.ToString(), "URL_REQUEST_DENIED", "" }); |
130 | return urlcode; | 143 | return urlcode; |
131 | } | 144 | } |
132 | string url = "http://"+System.Environment.MachineName+":"+m_HttpServer.Port.ToString()+"/lslhttp/"+urlcode.ToString()+"/"; | 145 | string url = "http://" + m_ExternalHostNameForLSL + ":" + m_HttpServer.Port.ToString() + "/lslhttp/" + urlcode.ToString() + "/"; |
133 | 146 | ||
134 | UrlData urlData = new UrlData(); | 147 | UrlData urlData = new UrlData(); |
135 | urlData.hostID = host.UUID; | 148 | urlData.hostID = host.UUID; |
@@ -139,9 +152,14 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
139 | urlData.urlcode = urlcode; | 152 | urlData.urlcode = urlcode; |
140 | urlData.requests = new Dictionary<UUID, RequestData>(); | 153 | urlData.requests = new Dictionary<UUID, RequestData>(); |
141 | 154 | ||
155 | |||
142 | m_UrlMap[url] = urlData; | 156 | m_UrlMap[url] = urlData; |
143 | 157 | ||
144 | m_HttpServer.AddHTTPHandler("/lslhttp/"+urlcode.ToString()+"/", HttpRequestHandler); | 158 | string uri = "/lslhttp/" + urlcode.ToString() + "/"; |
159 | |||
160 | m_HttpServer.AddPollServiceHTTPHandler(uri,HandleHttpPoll, | ||
161 | new PollServiceEventArgs(HttpRequestHandler,HasEvents, GetEvents, NoEvents, | ||
162 | urlcode)); | ||
145 | 163 | ||
146 | engine.PostScriptEvent(itemID, "http_request", new Object[] { urlcode.ToString(), "URL_REQUEST_GRANTED", url }); | 164 | engine.PostScriptEvent(itemID, "http_request", new Object[] { urlcode.ToString(), "URL_REQUEST_GRANTED", url }); |
147 | } | 165 | } |
@@ -165,7 +183,9 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
165 | UrlData data; | 183 | UrlData data; |
166 | 184 | ||
167 | if (!m_UrlMap.TryGetValue(url, out data)) | 185 | if (!m_UrlMap.TryGetValue(url, out data)) |
186 | { | ||
168 | return; | 187 | return; |
188 | } | ||
169 | 189 | ||
170 | foreach (UUID req in data.requests.Keys) | 190 | foreach (UUID req in data.requests.Keys) |
171 | m_RequestMap.Remove(req); | 191 | m_RequestMap.Remove(req); |
@@ -174,13 +194,36 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
174 | m_UrlMap.Remove(url); | 194 | m_UrlMap.Remove(url); |
175 | } | 195 | } |
176 | } | 196 | } |
177 | 197 | ||
178 | public void HttpResponse(UUID request, int status, string body) | 198 | public void HttpResponse(UUID request, int status, string body) |
179 | { | 199 | { |
200 | if (m_RequestMap.ContainsKey(request)) | ||
201 | { | ||
202 | UrlData urlData = m_RequestMap[request]; | ||
203 | urlData.requests[request].responseCode = status; | ||
204 | urlData.requests[request].responseBody = body; | ||
205 | //urlData.requests[request].ev.Set(); | ||
206 | urlData.requests[request].requestDone =true; | ||
207 | } | ||
208 | else | ||
209 | { | ||
210 | m_log.Info("[HttpRequestHandler] There is no http-in request with id " + request.ToString()); | ||
211 | } | ||
180 | } | 212 | } |
181 | 213 | ||
182 | public string GetHttpHeader(UUID request, string header) | 214 | public string GetHttpHeader(UUID requestId, string header) |
183 | { | 215 | { |
216 | if (m_RequestMap.ContainsKey(requestId)) | ||
217 | { | ||
218 | UrlData urlData=m_RequestMap[requestId]; | ||
219 | string value; | ||
220 | if (urlData.requests[requestId].headers.TryGetValue(header,out value)) | ||
221 | return value; | ||
222 | } | ||
223 | else | ||
224 | { | ||
225 | m_log.Warn("[HttpRequestHandler] There was no http-in request with id " + requestId); | ||
226 | } | ||
184 | return String.Empty; | 227 | return String.Empty; |
185 | } | 228 | } |
186 | 229 | ||
@@ -233,26 +276,214 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
233 | } | 276 | } |
234 | } | 277 | } |
235 | 278 | ||
279 | |||
236 | private void RemoveUrl(UrlData data) | 280 | private void RemoveUrl(UrlData data) |
237 | { | 281 | { |
238 | m_HttpServer.RemoveHTTPHandler("", "/lslhttp/"+data.urlcode.ToString()+"/"); | 282 | m_HttpServer.RemoveHTTPHandler("", "/lslhttp/"+data.urlcode.ToString()+"/"); |
239 | } | 283 | } |
240 | 284 | ||
241 | private Hashtable HttpRequestHandler(Hashtable request) | 285 | private Hashtable NoEvents(UUID requestID, UUID sessionID) |
242 | { | 286 | { |
243 | string uri = request["uri"].ToString(); | 287 | Hashtable response = new Hashtable(); |
244 | //A solution to this ugly mess would be to use only the /lslhttp/<UUID>/ part of the URI as the key. | 288 | UrlData url; |
245 | UrlData url = m_UrlMap["http://"+System.Environment.MachineName+":"+m_HttpServer.Port.ToString()+uri]; | 289 | lock (m_RequestMap) |
290 | { | ||
291 | if (!m_RequestMap.ContainsKey(requestID)) | ||
292 | return response; | ||
293 | url = m_RequestMap[requestID]; | ||
294 | } | ||
295 | |||
296 | if (System.Environment.TickCount - url.requests[requestID].startTime > 25000) | ||
297 | { | ||
298 | response["int_response_code"] = 500; | ||
299 | response["str_response_string"] = "Script timeout"; | ||
300 | response["content_type"] = "text/plain"; | ||
301 | response["keepalive"] = false; | ||
302 | response["reusecontext"] = false; | ||
303 | |||
304 | //remove from map | ||
305 | lock (url) | ||
306 | { | ||
307 | url.requests.Remove(requestID); | ||
308 | m_RequestMap.Remove(requestID); | ||
309 | } | ||
310 | |||
311 | return response; | ||
312 | } | ||
313 | |||
314 | |||
315 | return response; | ||
316 | } | ||
317 | |||
318 | private bool HasEvents(UUID requestID, UUID sessionID) | ||
319 | { | ||
320 | UrlData url=null; | ||
246 | 321 | ||
247 | //UUID.Random() below is a hack! Eventually we will do HTTP requests and responses properly. | 322 | lock (m_RequestMap) |
248 | url.engine.PostScriptEvent(url.itemID, "http_request", new Object[] { UUID.Random().ToString(), request["http-method"].ToString(), request["body"].ToString() }); | 323 | { |
324 | if (!m_RequestMap.ContainsKey(requestID)) | ||
325 | { | ||
326 | return false; | ||
327 | } | ||
328 | url = m_RequestMap[requestID]; | ||
329 | if (!url.requests.ContainsKey(requestID)) | ||
330 | { | ||
331 | return false; | ||
332 | } | ||
333 | } | ||
249 | 334 | ||
335 | if (System.Environment.TickCount-url.requests[requestID].startTime>25000) | ||
336 | { | ||
337 | return true; | ||
338 | } | ||
339 | |||
340 | if (url.requests[requestID].requestDone) | ||
341 | return true; | ||
342 | else | ||
343 | return false; | ||
344 | |||
345 | } | ||
346 | private Hashtable GetEvents(UUID requestID, UUID sessionID, string request) | ||
347 | { | ||
348 | UrlData url = null; | ||
349 | RequestData requestData = null; | ||
350 | |||
351 | lock (m_RequestMap) | ||
352 | { | ||
353 | if (!m_RequestMap.ContainsKey(requestID)) | ||
354 | return NoEvents(requestID,sessionID); | ||
355 | url = m_RequestMap[requestID]; | ||
356 | requestData = url.requests[requestID]; | ||
357 | } | ||
358 | |||
359 | if (!requestData.requestDone) | ||
360 | return NoEvents(requestID,sessionID); | ||
361 | |||
250 | Hashtable response = new Hashtable(); | 362 | Hashtable response = new Hashtable(); |
251 | response["int_response_code"] = 200; | 363 | |
252 | response["str_response_string"] = "This is a generic response as OpenSim does not yet support proper responses. Your request has been passed to the object."; | 364 | if (System.Environment.TickCount - requestData.startTime > 25000) |
365 | { | ||
366 | response["int_response_code"] = 500; | ||
367 | response["str_response_string"] = "Script timeout"; | ||
368 | response["content_type"] = "text/plain"; | ||
369 | response["keepalive"] = false; | ||
370 | response["reusecontext"] = false; | ||
371 | return response; | ||
372 | } | ||
373 | //put response | ||
374 | response["int_response_code"] = requestData.responseCode; | ||
375 | response["str_response_string"] = requestData.responseBody; | ||
376 | response["content_type"] = "text/plain"; | ||
377 | response["keepalive"] = false; | ||
378 | response["reusecontext"] = false; | ||
379 | |||
380 | //remove from map | ||
381 | lock (url) | ||
382 | { | ||
383 | url.requests.Remove(requestID); | ||
384 | m_RequestMap.Remove(requestID); | ||
385 | } | ||
253 | 386 | ||
254 | return response; | 387 | return response; |
255 | } | 388 | } |
389 | public void HttpRequestHandler(UUID requestID, Hashtable request) | ||
390 | { | ||
391 | lock (request) | ||
392 | { | ||
393 | string uri = request["uri"].ToString(); | ||
394 | |||
395 | try | ||
396 | { | ||
397 | Hashtable headers = (Hashtable)request["headers"]; | ||
398 | |||
399 | // string uri_full = "http://" + m_ExternalHostNameForLSL + ":" + m_HttpServer.Port.ToString() + uri;// "/lslhttp/" + urlcode.ToString() + "/"; | ||
400 | |||
401 | int pos1 = uri.IndexOf("/");// /lslhttp | ||
402 | int pos2 = uri.IndexOf("/", pos1 + 1);// /lslhttp/ | ||
403 | int pos3 = uri.IndexOf("/", pos2 + 1);// /lslhttp/<UUID>/ | ||
404 | string uri_tmp = uri.Substring(0, pos3 + 1); | ||
405 | //HTTP server code doesn't provide us with QueryStrings | ||
406 | string pathInfo; | ||
407 | string queryString; | ||
408 | queryString = ""; | ||
409 | |||
410 | pathInfo = uri.Substring(pos3); | ||
411 | |||
412 | UrlData url = m_UrlMap["http://" + m_ExternalHostNameForLSL + ":" + m_HttpServer.Port.ToString() + uri_tmp]; | ||
413 | |||
414 | //for llGetHttpHeader support we need to store original URI here | ||
415 | //to make x-path-info / x-query-string / x-script-url / x-remote-ip headers | ||
416 | //as per http://wiki.secondlife.com/wiki/LlGetHTTPHeader | ||
417 | |||
418 | RequestData requestData = new RequestData(); | ||
419 | requestData.requestID = requestID; | ||
420 | requestData.requestDone = false; | ||
421 | requestData.startTime = System.Environment.TickCount; | ||
422 | requestData.uri = uri; | ||
423 | if (requestData.headers == null) | ||
424 | requestData.headers = new Dictionary<string, string>(); | ||
425 | |||
426 | foreach (DictionaryEntry header in headers) | ||
427 | { | ||
428 | string key = (string)header.Key; | ||
429 | string value = (string)header.Value; | ||
430 | requestData.headers.Add(key, value); | ||
431 | } | ||
432 | foreach (DictionaryEntry de in request) | ||
433 | { | ||
434 | if (de.Key.ToString() == "querystringkeys") | ||
435 | { | ||
436 | System.String[] keys = (System.String[])de.Value; | ||
437 | foreach (String key in keys) | ||
438 | { | ||
439 | if (request.ContainsKey(key)) | ||
440 | { | ||
441 | string val = (String)request[key]; | ||
442 | queryString = queryString + key + "=" + val + "&"; | ||
443 | } | ||
444 | } | ||
445 | if (queryString.Length > 1) | ||
446 | queryString = queryString.Substring(0, queryString.Length - 1); | ||
447 | |||
448 | } | ||
449 | |||
450 | } | ||
451 | |||
452 | //if this machine is behind DNAT/port forwarding, currently this is being | ||
453 | //set to address of port forwarding router | ||
454 | requestData.headers["x-remote-ip"] = requestData.headers["remote_addr"]; | ||
455 | requestData.headers["x-path-info"] = pathInfo; | ||
456 | requestData.headers["x-query-string"] = queryString; | ||
457 | requestData.headers["x-script-url"] = url.url; | ||
458 | |||
459 | requestData.ev = new ManualResetEvent(false); | ||
460 | lock (url.requests) | ||
461 | { | ||
462 | url.requests.Add(requestID, requestData); | ||
463 | } | ||
464 | lock (m_RequestMap) | ||
465 | { | ||
466 | //add to request map | ||
467 | m_RequestMap.Add(requestID, url); | ||
468 | } | ||
469 | |||
470 | url.engine.PostScriptEvent(url.itemID, "http_request", new Object[] { requestID.ToString(), request["http-method"].ToString(), request["body"].ToString() }); | ||
471 | |||
472 | //send initial response? | ||
473 | // Hashtable response = new Hashtable(); | ||
474 | |||
475 | return; | ||
476 | |||
477 | } | ||
478 | catch (Exception we) | ||
479 | { | ||
480 | //Hashtable response = new Hashtable(); | ||
481 | m_log.Warn("[HttpRequestHandler]: http-in request failed"); | ||
482 | m_log.Warn(we.Message); | ||
483 | m_log.Warn(we.StackTrace); | ||
484 | } | ||
485 | } | ||
486 | } | ||
256 | 487 | ||
257 | private void OnScriptReset(uint localID, UUID itemID) | 488 | private void OnScriptReset(uint localID, UUID itemID) |
258 | { | 489 | { |