diff options
Diffstat (limited to 'OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs')
-rw-r--r-- | OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs | 306 |
1 files changed, 269 insertions, 37 deletions
diff --git a/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs b/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs index 6a2a6c8..2d81e4c 100644 --- a/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs +++ b/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs | |||
@@ -54,29 +54,42 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
54 | { | 54 | { |
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 ManualResetEvent ev; | 58 | public int responseCode; |
59 | public string responseBody; | ||
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>(); |
69 | 74 | ||
70 | private Dictionary<string, UrlData> m_UrlMap = | 75 | private Dictionary<string, UrlData> m_UrlMap = |
71 | new Dictionary<string, UrlData>(); | 76 | new Dictionary<string, UrlData>(); |
77 | |||
72 | 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; |
82 | |||
83 | private string m_ExternalHostNameForLSL = ""; | ||
76 | 84 | ||
77 | public Type ReplaceableInterface | 85 | public Type ReplaceableInterface |
78 | { | 86 | { |
79 | get { return null; } | 87 | get { return null; } |
88 | } | ||
89 | |||
90 | private Hashtable HandleHttpPoll(Hashtable request) | ||
91 | { | ||
92 | return new Hashtable(); | ||
80 | } | 93 | } |
81 | 94 | ||
82 | public string Name | 95 | public string Name |
@@ -85,7 +98,8 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
85 | } | 98 | } |
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() |
@@ -116,8 +130,7 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
116 | 130 | ||
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(); |
@@ -128,8 +141,8 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
128 | { | 141 | { |
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 | ||
142 | m_UrlMap[url] = urlData; | 155 | |
143 | 156 | m_UrlMap[url] = urlData; | |
144 | m_HttpServer.AddHTTPHandler("/lslhttp/"+urlcode.ToString()+"/", HttpRequestHandler); | 157 | |
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 | } |
@@ -162,10 +180,12 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
162 | { | 180 | { |
163 | lock (m_UrlMap) | 181 | lock (m_UrlMap) |
164 | { | 182 | { |
165 | UrlData data; | 183 | UrlData data; |
166 | 184 | ||
167 | if (!m_UrlMap.TryGetValue(url, out data)) | 185 | if (!m_UrlMap.TryGetValue(url, out data)) |
168 | return; | 186 | { |
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,14 +194,38 @@ 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 | RequestData requestData=urlData.requests[request]; | ||
204 | urlData.requests[request].responseCode = status; | ||
205 | urlData.requests[request].responseBody = body; | ||
206 | //urlData.requests[request].ev.Set(); | ||
207 | urlData.requests[request].requestDone=true; | ||
208 | } | ||
209 | else | ||
210 | { | ||
211 | m_log.Info("[HttpRequestHandler] There is no http-in request with id " + request.ToString()); | ||
212 | } | ||
180 | } | 213 | } |
181 | 214 | ||
182 | public string GetHttpHeader(UUID request, string header) | 215 | public string GetHttpHeader(UUID requestId, string header) |
183 | { | 216 | { |
184 | return String.Empty; | 217 | if (m_RequestMap.ContainsKey(requestId)) |
218 | { | ||
219 | UrlData urlData=m_RequestMap[requestId]; | ||
220 | string value; | ||
221 | if (urlData.requests[requestId].headers.TryGetValue(header,out value)) | ||
222 | return value; | ||
223 | } | ||
224 | else | ||
225 | { | ||
226 | m_log.Warn("[HttpRequestHandler] There was no http-in request with id " + requestId); | ||
227 | } | ||
228 | return String.Empty; | ||
185 | } | 229 | } |
186 | 230 | ||
187 | public int GetFreeUrls() | 231 | public int GetFreeUrls() |
@@ -231,27 +275,215 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
231 | foreach (string urlname in removeURLs) | 275 | foreach (string urlname in removeURLs) |
232 | m_UrlMap.Remove(urlname); | 276 | m_UrlMap.Remove(urlname); |
233 | } | 277 | } |
234 | } | 278 | } |
279 | |||
235 | 280 | ||
236 | private void RemoveUrl(UrlData data) | 281 | private void RemoveUrl(UrlData data) |
237 | { | 282 | { |
238 | m_HttpServer.RemoveHTTPHandler("", "/lslhttp/"+data.urlcode.ToString()+"/"); | 283 | m_HttpServer.RemoveHTTPHandler("", "/lslhttp/"+data.urlcode.ToString()+"/"); |
239 | } | 284 | } |
285 | |||
286 | private Hashtable NoEvents(UUID requestID, UUID sessionID) | ||
287 | { | ||
288 | Hashtable response = new Hashtable(); | ||
289 | UrlData url; | ||
290 | lock (m_RequestMap) | ||
291 | { | ||
292 | if (!m_RequestMap.ContainsKey(requestID)) | ||
293 | return response; | ||
294 | url = m_RequestMap[requestID]; | ||
295 | } | ||
296 | |||
297 | if (System.Environment.TickCount - url.requests[requestID].startTime > 25000) | ||
298 | { | ||
299 | response["int_response_code"] = 500; | ||
300 | response["str_response_string"] = "Script timeout"; | ||
301 | response["content_type"] = "text/plain"; | ||
302 | response["keepalive"] = false; | ||
303 | response["reusecontext"] = false; | ||
304 | |||
305 | //remove from map | ||
306 | lock (url) | ||
307 | { | ||
308 | url.requests.Remove(requestID); | ||
309 | m_RequestMap.Remove(requestID); | ||
310 | } | ||
311 | |||
312 | return response; | ||
313 | } | ||
240 | 314 | ||
241 | private Hashtable HttpRequestHandler(Hashtable request) | 315 | |
316 | return response; | ||
317 | } | ||
318 | |||
319 | private bool HasEvents(UUID requestID, UUID sessionID) | ||
242 | { | 320 | { |
243 | string uri = request["uri"].ToString(); | 321 | UrlData url=null; |
244 | //A solution to this ugly mess would be to use only the /lslhttp/<UUID>/ part of the URI as the key. | ||
245 | UrlData url = m_UrlMap["http://"+System.Environment.MachineName+":"+m_HttpServer.Port.ToString()+uri]; | ||
246 | 322 | ||
247 | //UUID.Random() below is a hack! Eventually we will do HTTP requests and responses properly. | 323 | lock (m_RequestMap) |
248 | url.engine.PostScriptEvent(url.itemID, "http_request", new Object[] { UUID.Random().ToString(), request["http-method"].ToString(), request["body"].ToString() }); | 324 | { |
325 | if (!m_RequestMap.ContainsKey(requestID)) | ||
326 | { | ||
327 | return false; | ||
328 | } | ||
329 | url = m_RequestMap[requestID]; | ||
330 | if (!url.requests.ContainsKey(requestID)) | ||
331 | { | ||
332 | return false; | ||
333 | } | ||
334 | } | ||
249 | 335 | ||
250 | Hashtable response = new Hashtable(); | 336 | if (System.Environment.TickCount-url.requests[requestID].startTime>25000) |
251 | response["int_response_code"] = 200; | 337 | { |
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."; | 338 | return true; |
339 | } | ||
253 | 340 | ||
254 | return response; | 341 | if (url.requests[requestID].requestDone) |
342 | return true; | ||
343 | else | ||
344 | return false; | ||
345 | |||
346 | } | ||
347 | private Hashtable GetEvents(UUID requestID, UUID sessionID, string request) | ||
348 | { | ||
349 | UrlData url = null; | ||
350 | RequestData requestData = null; | ||
351 | |||
352 | lock (m_RequestMap) | ||
353 | { | ||
354 | if (!m_RequestMap.ContainsKey(requestID)) | ||
355 | return NoEvents(requestID,sessionID); | ||
356 | url = m_RequestMap[requestID]; | ||
357 | requestData = url.requests[requestID]; | ||
358 | } | ||
359 | |||
360 | if (!requestData.requestDone) | ||
361 | return NoEvents(requestID,sessionID); | ||
362 | |||
363 | Hashtable response = new Hashtable(); | ||
364 | |||
365 | if (System.Environment.TickCount - requestData.startTime > 25000) | ||
366 | { | ||
367 | response["int_response_code"] = 500; | ||
368 | response["str_response_string"] = "Script timeout"; | ||
369 | response["content_type"] = "text/plain"; | ||
370 | response["keepalive"] = false; | ||
371 | response["reusecontext"] = false; | ||
372 | return response; | ||
373 | } | ||
374 | //put response | ||
375 | response["int_response_code"] = requestData.responseCode; | ||
376 | response["str_response_string"] = requestData.responseBody; | ||
377 | response["content_type"] = "text/plain"; | ||
378 | response["keepalive"] = false; | ||
379 | response["reusecontext"] = false; | ||
380 | |||
381 | //remove from map | ||
382 | lock (url) | ||
383 | { | ||
384 | url.requests.Remove(requestID); | ||
385 | m_RequestMap.Remove(requestID); | ||
386 | } | ||
387 | |||
388 | return response; | ||
389 | } | ||
390 | public void HttpRequestHandler(UUID requestID, Hashtable request) | ||
391 | { | ||
392 | lock (request) | ||
393 | { | ||
394 | string uri = request["uri"].ToString(); | ||
395 | |||
396 | try | ||
397 | { | ||
398 | Hashtable headers = (Hashtable)request["headers"]; | ||
399 | |||
400 | string uri_full = "http://" + m_ExternalHostNameForLSL + ":" + m_HttpServer.Port.ToString() + uri;// "/lslhttp/" + urlcode.ToString() + "/"; | ||
401 | |||
402 | int pos1 = uri.IndexOf("/");// /lslhttp | ||
403 | int pos2 = uri.IndexOf("/", pos1 + 1);// /lslhttp/ | ||
404 | int pos3 = uri.IndexOf("/", pos2 + 1);// /lslhttp/<UUID>/ | ||
405 | string uri_tmp = uri.Substring(0, pos3 + 1); | ||
406 | //HTTP server code doesn't provide us with QueryStrings | ||
407 | string pathInfo; | ||
408 | string queryString; | ||
409 | queryString = ""; | ||
410 | |||
411 | pathInfo = uri.Substring(pos3); | ||
412 | |||
413 | UrlData url = m_UrlMap["http://" + m_ExternalHostNameForLSL + ":" + m_HttpServer.Port.ToString() + uri_tmp]; | ||
414 | |||
415 | //for llGetHttpHeader support we need to store original URI here | ||
416 | //to make x-path-info / x-query-string / x-script-url / x-remote-ip headers | ||
417 | //as per http://wiki.secondlife.com/wiki/LlGetHTTPHeader | ||
418 | |||
419 | RequestData requestData = new RequestData(); | ||
420 | requestData.requestID = requestID; | ||
421 | requestData.requestDone = false; | ||
422 | requestData.startTime = System.Environment.TickCount; | ||
423 | requestData.uri = uri; | ||
424 | if (requestData.headers == null) | ||
425 | requestData.headers = new Dictionary<string, string>(); | ||
426 | |||
427 | foreach (DictionaryEntry header in headers) | ||
428 | { | ||
429 | string key = (string)header.Key; | ||
430 | string value = (string)header.Value; | ||
431 | requestData.headers.Add(key, value); | ||
432 | } | ||
433 | foreach (DictionaryEntry de in request) | ||
434 | { | ||
435 | if (de.Key.ToString() == "querystringkeys") | ||
436 | { | ||
437 | System.String[] keys = (System.String[])de.Value; | ||
438 | foreach (String key in keys) | ||
439 | { | ||
440 | if (request.ContainsKey(key)) | ||
441 | { | ||
442 | string val = (String)request[key]; | ||
443 | queryString = queryString + key + "=" + val + "&"; | ||
444 | } | ||
445 | } | ||
446 | if (queryString.Length > 1) | ||
447 | queryString = queryString.Substring(0, queryString.Length - 1); | ||
448 | |||
449 | } | ||
450 | |||
451 | } | ||
452 | |||
453 | //if this machine is behind DNAT/port forwarding, currently this is being | ||
454 | //set to address of port forwarding router | ||
455 | requestData.headers["x-remote-ip"] = requestData.headers["remote_addr"]; | ||
456 | requestData.headers["x-path-info"] = pathInfo; | ||
457 | requestData.headers["x-query-string"] = queryString; | ||
458 | requestData.headers["x-script-url"] = url.url; | ||
459 | |||
460 | requestData.ev = new ManualResetEvent(false); | ||
461 | lock (url.requests) | ||
462 | { | ||
463 | url.requests.Add(requestID, requestData); | ||
464 | } | ||
465 | lock (m_RequestMap) | ||
466 | { | ||
467 | //add to request map | ||
468 | m_RequestMap.Add(requestID, url); | ||
469 | } | ||
470 | |||
471 | url.engine.PostScriptEvent(url.itemID, "http_request", new Object[] { requestID.ToString(), request["http-method"].ToString(), request["body"].ToString() }); | ||
472 | |||
473 | //send initial response? | ||
474 | Hashtable response = new Hashtable(); | ||
475 | |||
476 | return; | ||
477 | |||
478 | } | ||
479 | catch (Exception we) | ||
480 | { | ||
481 | //Hashtable response = new Hashtable(); | ||
482 | m_log.Warn("[HttpRequestHandler]: http-in request failed"); | ||
483 | m_log.Warn(we.Message); | ||
484 | m_log.Warn(we.StackTrace); | ||
485 | } | ||
486 | } | ||
255 | } | 487 | } |
256 | 488 | ||
257 | private void OnScriptReset(uint localID, UUID itemID) | 489 | private void OnScriptReset(uint localID, UUID itemID) |