diff options
author | Justin Clark-Casey (justincc) | 2012-06-22 23:49:52 +0100 |
---|---|---|
committer | Justin Clark-Casey (justincc) | 2012-06-22 23:49:52 +0100 |
commit | 78143769bfdf316bbec63e6232bf9be993eb078a (patch) | |
tree | 1b215148f8c1e1eaf4caa0c077a8d5087d26f3b5 /OpenSim/Region/CoreModules | |
parent | Avoid a race condition where an incoming request to a script external URL can... (diff) | |
download | opensim-SC-78143769bfdf316bbec63e6232bf9be993eb078a.zip opensim-SC-78143769bfdf316bbec63e6232bf9be993eb078a.tar.gz opensim-SC-78143769bfdf316bbec63e6232bf9be993eb078a.tar.bz2 opensim-SC-78143769bfdf316bbec63e6232bf9be993eb078a.tar.xz |
Resolve various race conditions between accessing and removing external script URLs by more consistently locking on m_UrlMap
Diffstat (limited to 'OpenSim/Region/CoreModules')
-rw-r--r-- | OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs | 280 |
1 files changed, 148 insertions, 132 deletions
diff --git a/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs b/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs index 5c05500..05d54f0 100644 --- a/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs +++ b/OpenSim/Region/CoreModules/Scripting/LSLHttp/UrlModule.cs | |||
@@ -41,13 +41,39 @@ using OpenSim.Region.Framework.Scenes; | |||
41 | 41 | ||
42 | namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | 42 | namespace OpenSim.Region.CoreModules.Scripting.LSLHttp |
43 | { | 43 | { |
44 | /// <summary> | ||
45 | /// Data describing an external URL set up by a script. | ||
46 | /// </summary> | ||
44 | public class UrlData | 47 | public class UrlData |
45 | { | 48 | { |
49 | /// <summary> | ||
50 | /// Scene object part hosting the script | ||
51 | /// </summary> | ||
46 | public UUID hostID; | 52 | public UUID hostID; |
53 | |||
54 | /// <summary> | ||
55 | /// The item ID of the script that requested the URL. | ||
56 | /// </summary> | ||
47 | public UUID itemID; | 57 | public UUID itemID; |
58 | |||
59 | /// <summary> | ||
60 | /// The script engine that runs the script. | ||
61 | /// </summary> | ||
48 | public IScriptModule engine; | 62 | public IScriptModule engine; |
63 | |||
64 | /// <summary> | ||
65 | /// The generated URL. | ||
66 | /// </summary> | ||
49 | public string url; | 67 | public string url; |
68 | |||
69 | /// <summary> | ||
70 | /// The random UUID component of the generated URL. | ||
71 | /// </summary> | ||
50 | public UUID urlcode; | 72 | public UUID urlcode; |
73 | |||
74 | /// <summary> | ||
75 | /// The external requests currently being processed or awaiting retrieval for this URL. | ||
76 | /// </summary> | ||
51 | public Dictionary<UUID, RequestData> requests; | 77 | public Dictionary<UUID, RequestData> requests; |
52 | } | 78 | } |
53 | 79 | ||
@@ -77,6 +103,10 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
77 | /// Indexs the URL request metadata (which script requested it, outstanding requests, etc.) by the request ID | 103 | /// 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. | 104 | /// randomly generated when a request is received for this URL. |
79 | /// </summary> | 105 | /// </summary> |
106 | /// <remarks> | ||
107 | /// Manipulation or retrieval from this dictionary must be locked on m_UrlMap to preserve consistency with | ||
108 | /// m_UrlMap | ||
109 | /// </remarks> | ||
80 | private Dictionary<UUID, UrlData> m_RequestMap = new Dictionary<UUID, UrlData>(); | 110 | private Dictionary<UUID, UrlData> m_RequestMap = new Dictionary<UUID, UrlData>(); |
81 | 111 | ||
82 | /// <summary> | 112 | /// <summary> |
@@ -113,10 +143,9 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
113 | { | 143 | { |
114 | m_ExternalHostNameForLSL = config.Configs["Network"].GetString("ExternalHostNameForLSL", System.Environment.MachineName); | 144 | m_ExternalHostNameForLSL = config.Configs["Network"].GetString("ExternalHostNameForLSL", System.Environment.MachineName); |
115 | bool ssl_enabled = config.Configs["Network"].GetBoolean("https_listener",false); | 145 | bool ssl_enabled = config.Configs["Network"].GetBoolean("https_listener",false); |
146 | |||
116 | if (ssl_enabled) | 147 | if (ssl_enabled) |
117 | { | ||
118 | https_port = (uint) config.Configs["Network"].GetInt("https_port",0); | 148 | https_port = (uint) config.Configs["Network"].GetInt("https_port",0); |
119 | } | ||
120 | 149 | ||
121 | IConfig llFunctionsConfig = config.Configs["LL-Functions"]; | 150 | IConfig llFunctionsConfig = config.Configs["LL-Functions"]; |
122 | 151 | ||
@@ -275,32 +304,38 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
275 | 304 | ||
276 | public void HttpResponse(UUID request, int status, string body) | 305 | public void HttpResponse(UUID request, int status, string body) |
277 | { | 306 | { |
278 | if (m_RequestMap.ContainsKey(request)) | 307 | lock (m_UrlMap) |
279 | { | ||
280 | UrlData urlData = m_RequestMap[request]; | ||
281 | urlData.requests[request].responseCode = status; | ||
282 | urlData.requests[request].responseBody = body; | ||
283 | //urlData.requests[request].ev.Set(); | ||
284 | urlData.requests[request].requestDone =true; | ||
285 | } | ||
286 | else | ||
287 | { | 308 | { |
288 | m_log.Info("[HttpRequestHandler] There is no http-in request with id " + request.ToString()); | 309 | if (m_RequestMap.ContainsKey(request)) |
310 | { | ||
311 | UrlData urlData = m_RequestMap[request]; | ||
312 | urlData.requests[request].responseCode = status; | ||
313 | urlData.requests[request].responseBody = body; | ||
314 | //urlData.requests[request].ev.Set(); | ||
315 | urlData.requests[request].requestDone =true; | ||
316 | } | ||
317 | else | ||
318 | { | ||
319 | m_log.Info("[HttpRequestHandler] There is no http-in request with id " + request.ToString()); | ||
320 | } | ||
289 | } | 321 | } |
290 | } | 322 | } |
291 | 323 | ||
292 | public string GetHttpHeader(UUID requestId, string header) | 324 | public string GetHttpHeader(UUID requestId, string header) |
293 | { | 325 | { |
294 | if (m_RequestMap.ContainsKey(requestId)) | 326 | lock (m_UrlMap) |
295 | { | ||
296 | UrlData urlData = m_RequestMap[requestId]; | ||
297 | string value; | ||
298 | if (urlData.requests[requestId].headers.TryGetValue(header,out value)) | ||
299 | return value; | ||
300 | } | ||
301 | else | ||
302 | { | 327 | { |
303 | m_log.Warn("[HttpRequestHandler] There was no http-in request with id " + requestId); | 328 | if (m_RequestMap.ContainsKey(requestId)) |
329 | { | ||
330 | UrlData urlData = m_RequestMap[requestId]; | ||
331 | string value; | ||
332 | if (urlData.requests[requestId].headers.TryGetValue(header, out value)) | ||
333 | return value; | ||
334 | } | ||
335 | else | ||
336 | { | ||
337 | m_log.Warn("[HttpRequestHandler] There was no http-in request with id " + requestId); | ||
338 | } | ||
304 | } | 339 | } |
305 | 340 | ||
306 | return String.Empty; | 341 | return String.Empty; |
@@ -308,7 +343,8 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
308 | 343 | ||
309 | public int GetFreeUrls() | 344 | public int GetFreeUrls() |
310 | { | 345 | { |
311 | return m_TotalUrls - m_UrlMap.Count; | 346 | lock (m_UrlMap) |
347 | return m_TotalUrls - m_UrlMap.Count; | ||
312 | } | 348 | } |
313 | 349 | ||
314 | public void ScriptRemoved(UUID itemID) | 350 | public void ScriptRemoved(UUID itemID) |
@@ -366,9 +402,9 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
366 | private Hashtable NoEvents(UUID requestID, UUID sessionID) | 402 | private Hashtable NoEvents(UUID requestID, UUID sessionID) |
367 | { | 403 | { |
368 | Hashtable response = new Hashtable(); | 404 | Hashtable response = new Hashtable(); |
369 | UrlData url; | 405 | UrlData urlData; |
370 | 406 | ||
371 | lock (m_RequestMap) | 407 | lock (m_UrlMap) |
372 | { | 408 | { |
373 | // We need to return a 404 here in case the request URL was removed at exactly the same time that a | 409 | // 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 | 410 | // request was made. In this case, the request thread can outrace llRemoveURL() and still be polling |
@@ -383,25 +419,22 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
383 | return response; | 419 | return response; |
384 | } | 420 | } |
385 | 421 | ||
386 | url = m_RequestMap[requestID]; | 422 | urlData = m_RequestMap[requestID]; |
387 | } | ||
388 | |||
389 | if (System.Environment.TickCount - url.requests[requestID].startTime > 25000) | ||
390 | { | ||
391 | response["int_response_code"] = 500; | ||
392 | response["str_response_string"] = "Script timeout"; | ||
393 | response["content_type"] = "text/plain"; | ||
394 | response["keepalive"] = false; | ||
395 | response["reusecontext"] = false; | ||
396 | 423 | ||
397 | //remove from map | 424 | if (System.Environment.TickCount - urlData.requests[requestID].startTime > 25000) |
398 | lock (url) | ||
399 | { | 425 | { |
400 | url.requests.Remove(requestID); | 426 | response["int_response_code"] = 500; |
427 | response["str_response_string"] = "Script timeout"; | ||
428 | response["content_type"] = "text/plain"; | ||
429 | response["keepalive"] = false; | ||
430 | response["reusecontext"] = false; | ||
431 | |||
432 | //remove from map | ||
433 | urlData.requests.Remove(requestID); | ||
401 | m_RequestMap.Remove(requestID); | 434 | m_RequestMap.Remove(requestID); |
402 | } | ||
403 | 435 | ||
404 | return response; | 436 | return response; |
437 | } | ||
405 | } | 438 | } |
406 | 439 | ||
407 | return response; | 440 | return response; |
@@ -409,9 +442,7 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
409 | 442 | ||
410 | private bool HasEvents(UUID requestID, UUID sessionID) | 443 | private bool HasEvents(UUID requestID, UUID sessionID) |
411 | { | 444 | { |
412 | UrlData url = null; | 445 | lock (m_UrlMap) |
413 | |||
414 | lock (m_RequestMap) | ||
415 | { | 446 | { |
416 | // We return true here because an external URL request that happened at the same time as an llRemoveURL() | 447 | // 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 | 448 | // can still make it through to HttpRequestHandler(). That will return without setting up a request |
@@ -423,61 +454,61 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
423 | return true; | 454 | return true; |
424 | } | 455 | } |
425 | 456 | ||
426 | url = m_RequestMap[requestID]; | 457 | UrlData urlData = m_RequestMap[requestID]; |
427 | if (!url.requests.ContainsKey(requestID)) | 458 | |
459 | if (!urlData.requests.ContainsKey(requestID)) | ||
428 | { | 460 | { |
429 | return true; | 461 | return true; |
430 | } | 462 | } |
431 | } | ||
432 | 463 | ||
433 | // Trigger return of timeout response. | 464 | // Trigger return of timeout response. |
434 | if (System.Environment.TickCount - url.requests[requestID].startTime > 25000) | 465 | if (System.Environment.TickCount - urlData.requests[requestID].startTime > 25000) |
435 | { | 466 | { |
436 | return true; | 467 | return true; |
437 | } | 468 | } |
438 | 469 | ||
439 | return url.requests[requestID].requestDone; | 470 | return urlData.requests[requestID].requestDone; |
471 | } | ||
440 | } | 472 | } |
441 | 473 | ||
442 | private Hashtable GetEvents(UUID requestID, UUID sessionID, string request) | 474 | private Hashtable GetEvents(UUID requestID, UUID sessionID, string request) |
443 | { | 475 | { |
444 | UrlData url = null; | 476 | Hashtable response; |
445 | RequestData requestData = null; | ||
446 | 477 | ||
447 | lock (m_RequestMap) | 478 | lock (m_UrlMap) |
448 | { | 479 | { |
480 | UrlData url = null; | ||
481 | RequestData requestData = null; | ||
482 | |||
449 | if (!m_RequestMap.ContainsKey(requestID)) | 483 | if (!m_RequestMap.ContainsKey(requestID)) |
450 | return NoEvents(requestID, sessionID); | 484 | return NoEvents(requestID, sessionID); |
451 | 485 | ||
452 | url = m_RequestMap[requestID]; | 486 | url = m_RequestMap[requestID]; |
453 | requestData = url.requests[requestID]; | 487 | requestData = url.requests[requestID]; |
454 | } | ||
455 | 488 | ||
456 | if (!requestData.requestDone) | 489 | if (!requestData.requestDone) |
457 | return NoEvents(requestID, sessionID); | 490 | return NoEvents(requestID, sessionID); |
458 | |||
459 | Hashtable response = new Hashtable(); | ||
460 | 491 | ||
461 | if (System.Environment.TickCount - requestData.startTime > 25000) | 492 | response = new Hashtable(); |
462 | { | 493 | |
463 | response["int_response_code"] = 500; | 494 | if (System.Environment.TickCount - requestData.startTime > 25000) |
464 | response["str_response_string"] = "Script timeout"; | 495 | { |
496 | response["int_response_code"] = 500; | ||
497 | response["str_response_string"] = "Script timeout"; | ||
498 | response["content_type"] = "text/plain"; | ||
499 | response["keepalive"] = false; | ||
500 | response["reusecontext"] = false; | ||
501 | return response; | ||
502 | } | ||
503 | |||
504 | //put response | ||
505 | response["int_response_code"] = requestData.responseCode; | ||
506 | response["str_response_string"] = requestData.responseBody; | ||
465 | response["content_type"] = "text/plain"; | 507 | response["content_type"] = "text/plain"; |
466 | response["keepalive"] = false; | 508 | response["keepalive"] = false; |
467 | response["reusecontext"] = false; | 509 | response["reusecontext"] = false; |
468 | return response; | ||
469 | } | ||
470 | 510 | ||
471 | //put response | 511 | //remove from map |
472 | response["int_response_code"] = requestData.responseCode; | ||
473 | response["str_response_string"] = requestData.responseBody; | ||
474 | response["content_type"] = "text/plain"; | ||
475 | response["keepalive"] = false; | ||
476 | response["reusecontext"] = false; | ||
477 | |||
478 | //remove from map | ||
479 | lock (url) | ||
480 | { | ||
481 | url.requests.Remove(requestID); | 512 | url.requests.Remove(requestID); |
482 | m_RequestMap.Remove(requestID); | 513 | m_RequestMap.Remove(requestID); |
483 | } | 514 | } |
@@ -487,44 +518,41 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
487 | 518 | ||
488 | public void HttpRequestHandler(UUID requestID, Hashtable request) | 519 | public void HttpRequestHandler(UUID requestID, Hashtable request) |
489 | { | 520 | { |
490 | lock (request) | 521 | string uri = request["uri"].ToString(); |
491 | { | 522 | bool is_ssl = uri.Contains("lslhttps"); |
492 | string uri = request["uri"].ToString(); | ||
493 | bool is_ssl = uri.Contains("lslhttps"); | ||
494 | 523 | ||
495 | try | 524 | try |
496 | { | 525 | { |
497 | Hashtable headers = (Hashtable)request["headers"]; | 526 | Hashtable headers = (Hashtable)request["headers"]; |
498 | 527 | ||
499 | // string uri_full = "http://" + m_ExternalHostNameForLSL + ":" + m_HttpServer.Port.ToString() + uri;// "/lslhttp/" + urlcode.ToString() + "/"; | 528 | // string uri_full = "http://" + m_ExternalHostNameForLSL + ":" + m_HttpServer.Port.ToString() + uri;// "/lslhttp/" + urlcode.ToString() + "/"; |
500 | 529 | ||
501 | int pos1 = uri.IndexOf("/");// /lslhttp | 530 | int pos1 = uri.IndexOf("/");// /lslhttp |
502 | int pos2 = uri.IndexOf("/", pos1 + 1);// /lslhttp/ | 531 | int pos2 = uri.IndexOf("/", pos1 + 1);// /lslhttp/ |
503 | int pos3 = uri.IndexOf("/", pos2 + 1);// /lslhttp/<UUID>/ | 532 | int pos3 = uri.IndexOf("/", pos2 + 1);// /lslhttp/<UUID>/ |
504 | string uri_tmp = uri.Substring(0, pos3 + 1); | 533 | string uri_tmp = uri.Substring(0, pos3 + 1); |
505 | //HTTP server code doesn't provide us with QueryStrings | 534 | //HTTP server code doesn't provide us with QueryStrings |
506 | string pathInfo; | 535 | string pathInfo; |
507 | string queryString; | 536 | string queryString; |
508 | queryString = ""; | 537 | queryString = ""; |
509 | 538 | ||
510 | pathInfo = uri.Substring(pos3); | 539 | pathInfo = uri.Substring(pos3); |
511 | 540 | ||
512 | UrlData urlData = null; | 541 | UrlData urlData = null; |
513 | 542 | ||
514 | lock (m_UrlMap) | 543 | lock (m_UrlMap) |
515 | { | 544 | { |
516 | string url; | 545 | string url; |
517 | 546 | ||
518 | if (is_ssl) | 547 | if (is_ssl) |
519 | url = "https://" + m_ExternalHostNameForLSL + ":" + m_HttpsServer.Port.ToString() + uri_tmp; | 548 | url = "https://" + m_ExternalHostNameForLSL + ":" + m_HttpsServer.Port.ToString() + uri_tmp; |
520 | else | 549 | else |
521 | url = "http://" + m_ExternalHostNameForLSL + ":" + m_HttpServer.Port.ToString() + uri_tmp; | 550 | url = "http://" + m_ExternalHostNameForLSL + ":" + m_HttpServer.Port.ToString() + uri_tmp; |
522 | 551 | ||
523 | // Avoid a race - the request URL may have been released via llRequestUrl() whilst this | 552 | // Avoid a race - the request URL may have been released via llRequestUrl() whilst this |
524 | // request was being processed. | 553 | // request was being processed. |
525 | if (!m_UrlMap.TryGetValue(url, out urlData)) | 554 | if (!m_UrlMap.TryGetValue(url, out urlData)) |
526 | return; | 555 | return; |
527 | } | ||
528 | 556 | ||
529 | //for llGetHttpHeader support we need to store original URI here | 557 | //for llGetHttpHeader support we need to store original URI here |
530 | //to make x-path-info / x-query-string / x-script-url / x-remote-ip headers | 558 | //to make x-path-info / x-query-string / x-script-url / x-remote-ip headers |
@@ -544,6 +572,7 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
544 | string value = (string)header.Value; | 572 | string value = (string)header.Value; |
545 | requestData.headers.Add(key, value); | 573 | requestData.headers.Add(key, value); |
546 | } | 574 | } |
575 | |||
547 | foreach (DictionaryEntry de in request) | 576 | foreach (DictionaryEntry de in request) |
548 | { | 577 | { |
549 | if (de.Key.ToString() == "querystringkeys") | 578 | if (de.Key.ToString() == "querystringkeys") |
@@ -570,34 +599,21 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
570 | requestData.headers["x-query-string"] = queryString; | 599 | requestData.headers["x-query-string"] = queryString; |
571 | requestData.headers["x-script-url"] = urlData.url; | 600 | requestData.headers["x-script-url"] = urlData.url; |
572 | 601 | ||
573 | //requestData.ev = new ManualResetEvent(false); | 602 | urlData.requests.Add(requestID, requestData); |
574 | lock (urlData.requests) | 603 | m_RequestMap.Add(requestID, urlData); |
575 | { | ||
576 | urlData.requests.Add(requestID, requestData); | ||
577 | } | ||
578 | |||
579 | lock (m_RequestMap) | ||
580 | { | ||
581 | m_RequestMap.Add(requestID, urlData); | ||
582 | } | ||
583 | |||
584 | urlData.engine.PostScriptEvent( | ||
585 | urlData.itemID, | ||
586 | "http_request", | ||
587 | new Object[] { requestID.ToString(), request["http-method"].ToString(), request["body"].ToString() }); | ||
588 | |||
589 | //send initial response? | ||
590 | // Hashtable response = new Hashtable(); | ||
591 | |||
592 | return; | ||
593 | } | ||
594 | catch (Exception we) | ||
595 | { | ||
596 | //Hashtable response = new Hashtable(); | ||
597 | m_log.Warn("[HttpRequestHandler]: http-in request failed"); | ||
598 | m_log.Warn(we.Message); | ||
599 | m_log.Warn(we.StackTrace); | ||
600 | } | 604 | } |
605 | |||
606 | urlData.engine.PostScriptEvent( | ||
607 | urlData.itemID, | ||
608 | "http_request", | ||
609 | new Object[] { requestID.ToString(), request["http-method"].ToString(), request["body"].ToString() }); | ||
610 | } | ||
611 | catch (Exception we) | ||
612 | { | ||
613 | //Hashtable response = new Hashtable(); | ||
614 | m_log.Warn("[HttpRequestHandler]: http-in request failed"); | ||
615 | m_log.Warn(we.Message); | ||
616 | m_log.Warn(we.StackTrace); | ||
601 | } | 617 | } |
602 | } | 618 | } |
603 | 619 | ||
@@ -606,4 +622,4 @@ namespace OpenSim.Region.CoreModules.Scripting.LSLHttp | |||
606 | ScriptRemoved(itemID); | 622 | ScriptRemoved(itemID); |
607 | } | 623 | } |
608 | } | 624 | } |
609 | } | 625 | } \ No newline at end of file |