aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/CoreModules/Scripting/LSLHttp
diff options
context:
space:
mode:
authorJustin Clark-Casey (justincc)2012-06-22 23:16:18 +0100
committerJustin Clark-Casey (justincc)2012-06-22 23:16:18 +0100
commitdca04c7b61abb7b7ea70299a192425ce3bd05937 (patch)
treec5af21989c732e8bd90b7c95a8ea1b3978c2e5b1 /OpenSim/Region/CoreModules/Scripting/LSLHttp
parentAvoid race condition between m_PrimObjects iteration in XEngine.PostObjectEve... (diff)
downloadopensim-SC-dca04c7b61abb7b7ea70299a192425ce3bd05937.zip
opensim-SC-dca04c7b61abb7b7ea70299a192425ce3bd05937.tar.gz
opensim-SC-dca04c7b61abb7b7ea70299a192425ce3bd05937.tar.bz2
opensim-SC-dca04c7b61abb7b7ea70299a192425ce3bd05937.tar.xz
Avoid a race condition where an incoming request to a script external URL can trigger an exception is the URL was being removed at the same time.
This involves three steps 1) Return gracefully in UrlModule.HttpRequestHandler() instead of throwing an exception when the url cannot be found in its index 2) Return true instead of false in HasEvents() if no matching request is found in the map. This call will only happen in the first place for raced requests. 3) Return a 404 in GetEvents() if the request is not in the index, rather than a blank 200 OK. Many thanks to Tom Haines in http://opensimulator.org/mantis/view.php?id=6051 for doing some of the work on this.
Diffstat (limited to 'OpenSim/Region/CoreModules/Scripting/LSLHttp')
-rw-r--r--OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs108
1 files changed, 73 insertions, 35 deletions
diff --git a/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs b/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs
index 61afc76..5c05500 100644
--- a/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs
+++ b/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs
@@ -64,17 +64,25 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
64 public string uri; 64 public string uri;
65 } 65 }
66 66
67 /// <summary>
68 /// This module provides external URLs for in-world scripts.
69 /// </summary>
67 public class UrlModule : ISharedRegionModule, IUrlModule 70 public class UrlModule : ISharedRegionModule, IUrlModule
68 { 71 {
69 private static readonly ILog m_log = 72 private static readonly ILog m_log =
70 LogManager.GetLogger( 73 LogManager.GetLogger(
71 MethodBase.GetCurrentMethod().DeclaringType); 74 MethodBase.GetCurrentMethod().DeclaringType);
72 75
73 private Dictionary<UUID, UrlData> m_RequestMap = 76 /// <summary>
74 new Dictionary<UUID, UrlData>(); 77 /// Indexs the URL request metadata (which script requested it, outstanding requests, etc.) by the request ID
78 /// randomly generated when a request is received for this URL.
79 /// </summary>
80 private Dictionary<UUID, UrlData> m_RequestMap = new Dictionary<UUID, UrlData>();
75 81
76 private Dictionary<string, UrlData> m_UrlMap = 82 /// <summary>
77 new Dictionary<string, UrlData>(); 83 /// Indexs the URL request metadata (which script requested it, outstanding requests, etc.) by the full URL
84 /// </summary>
85 private Dictionary<string, UrlData> m_UrlMap = new Dictionary<string, UrlData>();
78 86
79 /// <summary> 87 /// <summary>
80 /// Maximum number of external urls that can be set up by this module. 88 /// Maximum number of external urls that can be set up by this module.
@@ -224,7 +232,6 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
224 urlData.urlcode = urlcode; 232 urlData.urlcode = urlcode;
225 urlData.requests = new Dictionary<UUID, RequestData>(); 233 urlData.requests = new Dictionary<UUID, RequestData>();
226 234
227
228 m_UrlMap[url] = urlData; 235 m_UrlMap[url] = urlData;
229 236
230 string uri = "/lslhttps/" + urlcode.ToString() + "/"; 237 string uri = "/lslhttps/" + urlcode.ToString() + "/";
@@ -286,7 +293,7 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
286 { 293 {
287 if (m_RequestMap.ContainsKey(requestId)) 294 if (m_RequestMap.ContainsKey(requestId))
288 { 295 {
289 UrlData urlData=m_RequestMap[requestId]; 296 UrlData urlData = m_RequestMap[requestId];
290 string value; 297 string value;
291 if (urlData.requests[requestId].headers.TryGetValue(header,out value)) 298 if (urlData.requests[requestId].headers.TryGetValue(header,out value))
292 return value; 299 return value;
@@ -295,6 +302,7 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
295 { 302 {
296 m_log.Warn("[HttpRequestHandler] There was no http-in request with id " + requestId); 303 m_log.Warn("[HttpRequestHandler] There was no http-in request with id " + requestId);
297 } 304 }
305
298 return String.Empty; 306 return String.Empty;
299 } 307 }
300 308
@@ -339,6 +347,7 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
339 { 347 {
340 RemoveUrl(url.Value); 348 RemoveUrl(url.Value);
341 removeURLs.Add(url.Key); 349 removeURLs.Add(url.Key);
350
342 foreach (UUID req in url.Value.requests.Keys) 351 foreach (UUID req in url.Value.requests.Keys)
343 m_RequestMap.Remove(req); 352 m_RequestMap.Remove(req);
344 } 353 }
@@ -349,20 +358,31 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
349 } 358 }
350 } 359 }
351 360
352
353 private void RemoveUrl(UrlData data) 361 private void RemoveUrl(UrlData data)
354 { 362 {
355 m_HttpServer.RemoveHTTPHandler("", "/lslhttp/"+data.urlcode.ToString()+"/"); 363 m_HttpServer.RemoveHTTPHandler("", "/lslhttp/" + data.urlcode.ToString() + "/");
356 } 364 }
357 365
358 private Hashtable NoEvents(UUID requestID, UUID sessionID) 366 private Hashtable NoEvents(UUID requestID, UUID sessionID)
359 { 367 {
360 Hashtable response = new Hashtable(); 368 Hashtable response = new Hashtable();
361 UrlData url; 369 UrlData url;
370
362 lock (m_RequestMap) 371 lock (m_RequestMap)
363 { 372 {
373 // We need to return a 404 here in case the request URL was removed at exactly the same time that a
374 // request was made. In this case, the request thread can outrace llRemoveURL() and still be polling
375 // for the request ID.
364 if (!m_RequestMap.ContainsKey(requestID)) 376 if (!m_RequestMap.ContainsKey(requestID))
377 {
378 response["int_response_code"] = 404;
379 response["str_response_string"] = "";
380 response["keepalive"] = false;
381 response["reusecontext"] = false;
382
365 return response; 383 return response;
384 }
385
366 url = m_RequestMap[requestID]; 386 url = m_RequestMap[requestID];
367 } 387 }
368 388
@@ -384,53 +404,57 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
384 return response; 404 return response;
385 } 405 }
386 406
387
388 return response; 407 return response;
389 } 408 }
390 409
391 private bool HasEvents(UUID requestID, UUID sessionID) 410 private bool HasEvents(UUID requestID, UUID sessionID)
392 { 411 {
393 UrlData url=null; 412 UrlData url = null;
394 413
395 lock (m_RequestMap) 414 lock (m_RequestMap)
396 { 415 {
416 // We return true here because an external URL request that happened at the same time as an llRemoveURL()
417 // can still make it through to HttpRequestHandler(). That will return without setting up a request
418 // when it detects that the URL has been removed. The poller, however, will continue to ask for
419 // events for that request, so here we will signal that there are events and in GetEvents we will
420 // return a 404.
397 if (!m_RequestMap.ContainsKey(requestID)) 421 if (!m_RequestMap.ContainsKey(requestID))
398 { 422 {
399 return false; 423 return true;
400 } 424 }
425
401 url = m_RequestMap[requestID]; 426 url = m_RequestMap[requestID];
402 if (!url.requests.ContainsKey(requestID)) 427 if (!url.requests.ContainsKey(requestID))
403 { 428 {
404 return false; 429 return true;
405 } 430 }
406 } 431 }
407 432
408 if (System.Environment.TickCount-url.requests[requestID].startTime>25000) 433 // Trigger return of timeout response.
434 if (System.Environment.TickCount - url.requests[requestID].startTime > 25000)
409 { 435 {
410 return true; 436 return true;
411 } 437 }
412 438
413 if (url.requests[requestID].requestDone) 439 return url.requests[requestID].requestDone;
414 return true;
415 else
416 return false;
417
418 } 440 }
441
419 private Hashtable GetEvents(UUID requestID, UUID sessionID, string request) 442 private Hashtable GetEvents(UUID requestID, UUID sessionID, string request)
420 { 443 {
421 UrlData url = null; 444 UrlData url = null;
422 RequestData requestData = null; 445 RequestData requestData = null;
423 446
424 lock (m_RequestMap) 447 lock (m_RequestMap)
425 { 448 {
426 if (!m_RequestMap.ContainsKey(requestID)) 449 if (!m_RequestMap.ContainsKey(requestID))
427 return NoEvents(requestID,sessionID); 450 return NoEvents(requestID, sessionID);
451
428 url = m_RequestMap[requestID]; 452 url = m_RequestMap[requestID];
429 requestData = url.requests[requestID]; 453 requestData = url.requests[requestID];
430 } 454 }
431 455
432 if (!requestData.requestDone) 456 if (!requestData.requestDone)
433 return NoEvents(requestID,sessionID); 457 return NoEvents(requestID, sessionID);
434 458
435 Hashtable response = new Hashtable(); 459 Hashtable response = new Hashtable();
436 460
@@ -443,6 +467,7 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
443 response["reusecontext"] = false; 467 response["reusecontext"] = false;
444 return response; 468 return response;
445 } 469 }
470
446 //put response 471 //put response
447 response["int_response_code"] = requestData.responseCode; 472 response["int_response_code"] = requestData.responseCode;
448 response["str_response_string"] = requestData.responseBody; 473 response["str_response_string"] = requestData.responseBody;
@@ -459,6 +484,7 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
459 484
460 return response; 485 return response;
461 } 486 }
487
462 public void HttpRequestHandler(UUID requestID, Hashtable request) 488 public void HttpRequestHandler(UUID requestID, Hashtable request)
463 { 489 {
464 lock (request) 490 lock (request)
@@ -483,11 +509,22 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
483 509
484 pathInfo = uri.Substring(pos3); 510 pathInfo = uri.Substring(pos3);
485 511
486 UrlData url = null; 512 UrlData urlData = null;
487 if (!is_ssl) 513
488 url = m_UrlMap["http://" + m_ExternalHostNameForLSL + ":" + m_HttpServer.Port.ToString() + uri_tmp]; 514 lock (m_UrlMap)
489 else 515 {
490 url = m_UrlMap["https://" + m_ExternalHostNameForLSL + ":" + m_HttpsServer.Port.ToString() + uri_tmp]; 516 string url;
517
518 if (is_ssl)
519 url = "https://" + m_ExternalHostNameForLSL + ":" + m_HttpsServer.Port.ToString() + uri_tmp;
520 else
521 url = "http://" + m_ExternalHostNameForLSL + ":" + m_HttpServer.Port.ToString() + uri_tmp;
522
523 // Avoid a race - the request URL may have been released via llRequestUrl() whilst this
524 // request was being processed.
525 if (!m_UrlMap.TryGetValue(url, out urlData))
526 return;
527 }
491 528
492 //for llGetHttpHeader support we need to store original URI here 529 //for llGetHttpHeader support we need to store original URI here
493 //to make x-path-info / x-query-string / x-script-url / x-remote-ip headers 530 //to make x-path-info / x-query-string / x-script-url / x-remote-ip headers
@@ -520,11 +557,10 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
520 queryString = queryString + key + "=" + val + "&"; 557 queryString = queryString + key + "=" + val + "&";
521 } 558 }
522 } 559 }
560
523 if (queryString.Length > 1) 561 if (queryString.Length > 1)
524 queryString = queryString.Substring(0, queryString.Length - 1); 562 queryString = queryString.Substring(0, queryString.Length - 1);
525
526 } 563 }
527
528 } 564 }
529 565
530 //if this machine is behind DNAT/port forwarding, currently this is being 566 //if this machine is behind DNAT/port forwarding, currently this is being
@@ -532,26 +568,28 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
532 requestData.headers["x-remote-ip"] = requestData.headers["remote_addr"]; 568 requestData.headers["x-remote-ip"] = requestData.headers["remote_addr"];
533 requestData.headers["x-path-info"] = pathInfo; 569 requestData.headers["x-path-info"] = pathInfo;
534 requestData.headers["x-query-string"] = queryString; 570 requestData.headers["x-query-string"] = queryString;
535 requestData.headers["x-script-url"] = url.url; 571 requestData.headers["x-script-url"] = urlData.url;
536 572
537 //requestData.ev = new ManualResetEvent(false); 573 //requestData.ev = new ManualResetEvent(false);
538 lock (url.requests) 574 lock (urlData.requests)
539 { 575 {
540 url.requests.Add(requestID, requestData); 576 urlData.requests.Add(requestID, requestData);
541 } 577 }
578
542 lock (m_RequestMap) 579 lock (m_RequestMap)
543 { 580 {
544 //add to request map 581 m_RequestMap.Add(requestID, urlData);
545 m_RequestMap.Add(requestID, url);
546 } 582 }
547 583
548 url.engine.PostScriptEvent(url.itemID, "http_request", new Object[] { requestID.ToString(), request["http-method"].ToString(), request["body"].ToString() }); 584 urlData.engine.PostScriptEvent(
585 urlData.itemID,
586 "http_request",
587 new Object[] { requestID.ToString(), request["http-method"].ToString(), request["body"].ToString() });
549 588
550 //send initial response? 589 //send initial response?
551// Hashtable response = new Hashtable(); 590// Hashtable response = new Hashtable();
552 591
553 return; 592 return;
554
555 } 593 }
556 catch (Exception we) 594 catch (Exception we)
557 { 595 {