UrlModule.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  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 OpenSimulator 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. using System;
  28. using System.Threading;
  29. using System.Collections.Generic;
  30. using System.Collections;
  31. using System.Reflection;
  32. using log4net;
  33. using Nini.Config;
  34. using OpenMetaverse;
  35. using OpenSim.Framework;
  36. using OpenSim.Framework.Servers.HttpServer;
  37. using OpenSim.Region.Framework.Interfaces;
  38. using OpenSim.Region.Framework.Scenes;
  39. namespace OpenSim.Region.CoreModules.Scripting.LSLHttp
  40. {
  41. public class UrlData
  42. {
  43. public UUID hostID;
  44. public UUID itemID;
  45. public IScriptModule engine;
  46. public string url;
  47. public UUID urlcode;
  48. public Dictionary<UUID, RequestData> requests;
  49. }
  50. public class RequestData
  51. {
  52. public UUID requestID;
  53. public Dictionary<string, string> headers;
  54. public string body;
  55. public int responseCode;
  56. public string responseBody;
  57. //public ManualResetEvent ev;
  58. public bool requestDone;
  59. public int startTime;
  60. public string uri;
  61. }
  62. public class UrlModule : ISharedRegionModule, IUrlModule
  63. {
  64. private static readonly ILog m_log =
  65. LogManager.GetLogger(
  66. MethodBase.GetCurrentMethod().DeclaringType);
  67. private Dictionary<UUID, UrlData> m_RequestMap =
  68. new Dictionary<UUID, UrlData>();
  69. private Dictionary<string, UrlData> m_UrlMap =
  70. new Dictionary<string, UrlData>();
  71. private int m_TotalUrls = 100;
  72. private uint https_port = 0;
  73. private IHttpServer m_HttpServer = null;
  74. private IHttpServer m_HttpsServer = null;
  75. private string m_ExternalHostNameForLSL = "";
  76. public Type ReplaceableInterface
  77. {
  78. get { return null; }
  79. }
  80. private Hashtable HandleHttpPoll(Hashtable request)
  81. {
  82. return new Hashtable();
  83. }
  84. public string Name
  85. {
  86. get { return "UrlModule"; }
  87. }
  88. public void Initialise(IConfigSource config)
  89. {
  90. m_ExternalHostNameForLSL = config.Configs["Network"].GetString("ExternalHostNameForLSL", System.Environment.MachineName);
  91. bool ssl_enabled = config.Configs["Network"].GetBoolean("https_listener",false);
  92. if (ssl_enabled)
  93. {
  94. https_port = (uint) config.Configs["Network"].GetInt("https_port",0);
  95. }
  96. }
  97. public void PostInitialise()
  98. {
  99. }
  100. public void AddRegion(Scene scene)
  101. {
  102. if (m_HttpServer == null)
  103. {
  104. // There can only be one
  105. //
  106. m_HttpServer = MainServer.Instance;
  107. //
  108. // We can use the https if it is enabled
  109. if (https_port > 0)
  110. {
  111. m_HttpsServer = MainServer.GetHttpServer(https_port);
  112. }
  113. }
  114. scene.RegisterModuleInterface<IUrlModule>(this);
  115. scene.EventManager.OnScriptReset += OnScriptReset;
  116. }
  117. public void RegionLoaded(Scene scene)
  118. {
  119. }
  120. public void RemoveRegion(Scene scene)
  121. {
  122. }
  123. public void Close()
  124. {
  125. }
  126. public UUID RequestURL(IScriptModule engine, SceneObjectPart host, UUID itemID)
  127. {
  128. UUID urlcode = UUID.Random();
  129. lock (m_UrlMap)
  130. {
  131. if (m_UrlMap.Count >= m_TotalUrls)
  132. {
  133. engine.PostScriptEvent(itemID, "http_request", new Object[] { urlcode.ToString(), "URL_REQUEST_DENIED", "" });
  134. return urlcode;
  135. }
  136. string url = "http://" + m_ExternalHostNameForLSL + ":" + m_HttpServer.Port.ToString() + "/lslhttp/" + urlcode.ToString() + "/";
  137. UrlData urlData = new UrlData();
  138. urlData.hostID = host.UUID;
  139. urlData.itemID = itemID;
  140. urlData.engine = engine;
  141. urlData.url = url;
  142. urlData.urlcode = urlcode;
  143. urlData.requests = new Dictionary<UUID, RequestData>();
  144. m_UrlMap[url] = urlData;
  145. string uri = "/lslhttp/" + urlcode.ToString() + "/";
  146. m_HttpServer.AddPollServiceHTTPHandler(uri,HandleHttpPoll,
  147. new PollServiceEventArgs(HttpRequestHandler,HasEvents, GetEvents, NoEvents,
  148. urlcode));
  149. engine.PostScriptEvent(itemID, "http_request", new Object[] { urlcode.ToString(), "URL_REQUEST_GRANTED", url });
  150. }
  151. return urlcode;
  152. }
  153. public UUID RequestSecureURL(IScriptModule engine, SceneObjectPart host, UUID itemID)
  154. {
  155. UUID urlcode = UUID.Random();
  156. if (m_HttpsServer == null)
  157. {
  158. engine.PostScriptEvent(itemID, "http_request", new Object[] { urlcode.ToString(), "URL_REQUEST_DENIED", "" });
  159. return urlcode;
  160. }
  161. lock (m_UrlMap)
  162. {
  163. if (m_UrlMap.Count >= m_TotalUrls)
  164. {
  165. engine.PostScriptEvent(itemID, "http_request", new Object[] { urlcode.ToString(), "URL_REQUEST_DENIED", "" });
  166. return urlcode;
  167. }
  168. string url = "https://" + m_ExternalHostNameForLSL + ":" + m_HttpsServer.Port.ToString() + "/lslhttps/" + urlcode.ToString() + "/";
  169. UrlData urlData = new UrlData();
  170. urlData.hostID = host.UUID;
  171. urlData.itemID = itemID;
  172. urlData.engine = engine;
  173. urlData.url = url;
  174. urlData.urlcode = urlcode;
  175. urlData.requests = new Dictionary<UUID, RequestData>();
  176. m_UrlMap[url] = urlData;
  177. string uri = "/lslhttps/" + urlcode.ToString() + "/";
  178. m_HttpsServer.AddPollServiceHTTPHandler(uri,HandleHttpPoll,
  179. new PollServiceEventArgs(HttpRequestHandler,HasEvents, GetEvents, NoEvents,
  180. urlcode));
  181. engine.PostScriptEvent(itemID, "http_request", new Object[] { urlcode.ToString(), "URL_REQUEST_GRANTED", url });
  182. }
  183. return urlcode;
  184. }
  185. public void ReleaseURL(string url)
  186. {
  187. lock (m_UrlMap)
  188. {
  189. UrlData data;
  190. if (!m_UrlMap.TryGetValue(url, out data))
  191. {
  192. return;
  193. }
  194. foreach (UUID req in data.requests.Keys)
  195. m_RequestMap.Remove(req);
  196. RemoveUrl(data);
  197. m_UrlMap.Remove(url);
  198. }
  199. }
  200. public void HttpResponse(UUID request, int status, string body)
  201. {
  202. if (m_RequestMap.ContainsKey(request))
  203. {
  204. UrlData urlData = m_RequestMap[request];
  205. urlData.requests[request].responseCode = status;
  206. urlData.requests[request].responseBody = body;
  207. //urlData.requests[request].ev.Set();
  208. urlData.requests[request].requestDone =true;
  209. }
  210. else
  211. {
  212. m_log.Info("[HttpRequestHandler] There is no http-in request with id " + request.ToString());
  213. }
  214. }
  215. public string GetHttpHeader(UUID requestId, string header)
  216. {
  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;
  229. }
  230. public int GetFreeUrls()
  231. {
  232. return m_TotalUrls - m_UrlMap.Count;
  233. }
  234. public void ScriptRemoved(UUID itemID)
  235. {
  236. lock (m_UrlMap)
  237. {
  238. List<string> removeURLs = new List<string>();
  239. foreach (KeyValuePair<string, UrlData> url in m_UrlMap)
  240. {
  241. if (url.Value.itemID == itemID)
  242. {
  243. RemoveUrl(url.Value);
  244. removeURLs.Add(url.Key);
  245. foreach (UUID req in url.Value.requests.Keys)
  246. m_RequestMap.Remove(req);
  247. }
  248. }
  249. foreach (string urlname in removeURLs)
  250. m_UrlMap.Remove(urlname);
  251. }
  252. }
  253. public void ObjectRemoved(UUID objectID)
  254. {
  255. lock (m_UrlMap)
  256. {
  257. List<string> removeURLs = new List<string>();
  258. foreach (KeyValuePair<string, UrlData> url in m_UrlMap)
  259. {
  260. if (url.Value.hostID == objectID)
  261. {
  262. RemoveUrl(url.Value);
  263. removeURLs.Add(url.Key);
  264. foreach (UUID req in url.Value.requests.Keys)
  265. m_RequestMap.Remove(req);
  266. }
  267. }
  268. foreach (string urlname in removeURLs)
  269. m_UrlMap.Remove(urlname);
  270. }
  271. }
  272. private void RemoveUrl(UrlData data)
  273. {
  274. m_HttpServer.RemoveHTTPHandler("", "/lslhttp/"+data.urlcode.ToString()+"/");
  275. }
  276. private Hashtable NoEvents(UUID requestID, UUID sessionID)
  277. {
  278. Hashtable response = new Hashtable();
  279. UrlData url;
  280. lock (m_RequestMap)
  281. {
  282. if (!m_RequestMap.ContainsKey(requestID))
  283. return response;
  284. url = m_RequestMap[requestID];
  285. }
  286. if (System.Environment.TickCount - url.requests[requestID].startTime > 25000)
  287. {
  288. response["int_response_code"] = 500;
  289. response["str_response_string"] = "Script timeout";
  290. response["content_type"] = "text/plain";
  291. response["keepalive"] = false;
  292. response["reusecontext"] = false;
  293. //remove from map
  294. lock (url)
  295. {
  296. url.requests.Remove(requestID);
  297. m_RequestMap.Remove(requestID);
  298. }
  299. return response;
  300. }
  301. return response;
  302. }
  303. private bool HasEvents(UUID requestID, UUID sessionID)
  304. {
  305. UrlData url=null;
  306. lock (m_RequestMap)
  307. {
  308. if (!m_RequestMap.ContainsKey(requestID))
  309. {
  310. return false;
  311. }
  312. url = m_RequestMap[requestID];
  313. if (!url.requests.ContainsKey(requestID))
  314. {
  315. return false;
  316. }
  317. }
  318. if (System.Environment.TickCount-url.requests[requestID].startTime>25000)
  319. {
  320. return true;
  321. }
  322. if (url.requests[requestID].requestDone)
  323. return true;
  324. else
  325. return false;
  326. }
  327. private Hashtable GetEvents(UUID requestID, UUID sessionID, string request)
  328. {
  329. UrlData url = null;
  330. RequestData requestData = null;
  331. lock (m_RequestMap)
  332. {
  333. if (!m_RequestMap.ContainsKey(requestID))
  334. return NoEvents(requestID,sessionID);
  335. url = m_RequestMap[requestID];
  336. requestData = url.requests[requestID];
  337. }
  338. if (!requestData.requestDone)
  339. return NoEvents(requestID,sessionID);
  340. Hashtable response = new Hashtable();
  341. if (System.Environment.TickCount - requestData.startTime > 25000)
  342. {
  343. response["int_response_code"] = 500;
  344. response["str_response_string"] = "Script timeout";
  345. response["content_type"] = "text/plain";
  346. response["keepalive"] = false;
  347. response["reusecontext"] = false;
  348. return response;
  349. }
  350. //put response
  351. response["int_response_code"] = requestData.responseCode;
  352. response["str_response_string"] = requestData.responseBody;
  353. response["content_type"] = "text/plain";
  354. response["keepalive"] = false;
  355. response["reusecontext"] = false;
  356. //remove from map
  357. lock (url)
  358. {
  359. url.requests.Remove(requestID);
  360. m_RequestMap.Remove(requestID);
  361. }
  362. return response;
  363. }
  364. public void HttpRequestHandler(UUID requestID, Hashtable request)
  365. {
  366. lock (request)
  367. {
  368. string uri = request["uri"].ToString();
  369. bool is_ssl = uri.Contains("lslhttps");
  370. try
  371. {
  372. Hashtable headers = (Hashtable)request["headers"];
  373. // string uri_full = "http://" + m_ExternalHostNameForLSL + ":" + m_HttpServer.Port.ToString() + uri;// "/lslhttp/" + urlcode.ToString() + "/";
  374. int pos1 = uri.IndexOf("/");// /lslhttp
  375. int pos2 = uri.IndexOf("/", pos1 + 1);// /lslhttp/
  376. int pos3 = uri.IndexOf("/", pos2 + 1);// /lslhttp/<UUID>/
  377. string uri_tmp = uri.Substring(0, pos3 + 1);
  378. //HTTP server code doesn't provide us with QueryStrings
  379. string pathInfo;
  380. string queryString;
  381. queryString = "";
  382. pathInfo = uri.Substring(pos3);
  383. UrlData url = null;
  384. if (!is_ssl)
  385. url = m_UrlMap["http://" + m_ExternalHostNameForLSL + ":" + m_HttpServer.Port.ToString() + uri_tmp];
  386. else
  387. url = m_UrlMap["https://" + m_ExternalHostNameForLSL + ":" + m_HttpsServer.Port.ToString() + uri_tmp];
  388. //for llGetHttpHeader support we need to store original URI here
  389. //to make x-path-info / x-query-string / x-script-url / x-remote-ip headers
  390. //as per http://wiki.secondlife.com/wiki/LlGetHTTPHeader
  391. RequestData requestData = new RequestData();
  392. requestData.requestID = requestID;
  393. requestData.requestDone = false;
  394. requestData.startTime = System.Environment.TickCount;
  395. requestData.uri = uri;
  396. if (requestData.headers == null)
  397. requestData.headers = new Dictionary<string, string>();
  398. foreach (DictionaryEntry header in headers)
  399. {
  400. string key = (string)header.Key;
  401. string value = (string)header.Value;
  402. requestData.headers.Add(key, value);
  403. }
  404. foreach (DictionaryEntry de in request)
  405. {
  406. if (de.Key.ToString() == "querystringkeys")
  407. {
  408. System.String[] keys = (System.String[])de.Value;
  409. foreach (String key in keys)
  410. {
  411. if (request.ContainsKey(key))
  412. {
  413. string val = (String)request[key];
  414. queryString = queryString + key + "=" + val + "&";
  415. }
  416. }
  417. if (queryString.Length > 1)
  418. queryString = queryString.Substring(0, queryString.Length - 1);
  419. }
  420. }
  421. //if this machine is behind DNAT/port forwarding, currently this is being
  422. //set to address of port forwarding router
  423. requestData.headers["x-remote-ip"] = requestData.headers["remote_addr"];
  424. requestData.headers["x-path-info"] = pathInfo;
  425. requestData.headers["x-query-string"] = queryString;
  426. requestData.headers["x-script-url"] = url.url;
  427. //requestData.ev = new ManualResetEvent(false);
  428. lock (url.requests)
  429. {
  430. url.requests.Add(requestID, requestData);
  431. }
  432. lock (m_RequestMap)
  433. {
  434. //add to request map
  435. m_RequestMap.Add(requestID, url);
  436. }
  437. url.engine.PostScriptEvent(url.itemID, "http_request", new Object[] { requestID.ToString(), request["http-method"].ToString(), request["body"].ToString() });
  438. //send initial response?
  439. // Hashtable response = new Hashtable();
  440. return;
  441. }
  442. catch (Exception we)
  443. {
  444. //Hashtable response = new Hashtable();
  445. m_log.Warn("[HttpRequestHandler]: http-in request failed");
  446. m_log.Warn(we.Message);
  447. m_log.Warn(we.StackTrace);
  448. }
  449. }
  450. }
  451. private void OnScriptReset(uint localID, UUID itemID)
  452. {
  453. ScriptRemoved(itemID);
  454. }
  455. }
  456. }