UrlModule.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  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 IHttpServer m_HttpServer = null;
  73. private string m_ExternalHostNameForLSL = "";
  74. public Type ReplaceableInterface
  75. {
  76. get { return null; }
  77. }
  78. private Hashtable HandleHttpPoll(Hashtable request)
  79. {
  80. return new Hashtable();
  81. }
  82. public string Name
  83. {
  84. get { return "UrlModule"; }
  85. }
  86. public void Initialise(IConfigSource config)
  87. {
  88. m_ExternalHostNameForLSL = config.Configs["Network"].GetString("ExternalHostNameForLSL", System.Environment.MachineName);
  89. }
  90. public void PostInitialise()
  91. {
  92. }
  93. public void AddRegion(Scene scene)
  94. {
  95. if (m_HttpServer == null)
  96. {
  97. // There can only be one
  98. //
  99. m_HttpServer = MainServer.Instance;
  100. }
  101. scene.RegisterModuleInterface<IUrlModule>(this);
  102. scene.EventManager.OnScriptReset += OnScriptReset;
  103. }
  104. public void RegionLoaded(Scene scene)
  105. {
  106. }
  107. public void RemoveRegion(Scene scene)
  108. {
  109. }
  110. public void Close()
  111. {
  112. }
  113. public UUID RequestURL(IScriptModule engine, SceneObjectPart host, UUID itemID)
  114. {
  115. UUID urlcode = UUID.Random();
  116. lock (m_UrlMap)
  117. {
  118. if (m_UrlMap.Count >= m_TotalUrls)
  119. {
  120. engine.PostScriptEvent(itemID, "http_request", new Object[] { urlcode.ToString(), "URL_REQUEST_DENIED", "" });
  121. return urlcode;
  122. }
  123. string url = "http://" + m_ExternalHostNameForLSL + ":" + m_HttpServer.Port.ToString() + "/lslhttp/" + urlcode.ToString() + "/";
  124. UrlData urlData = new UrlData();
  125. urlData.hostID = host.UUID;
  126. urlData.itemID = itemID;
  127. urlData.engine = engine;
  128. urlData.url = url;
  129. urlData.urlcode = urlcode;
  130. urlData.requests = new Dictionary<UUID, RequestData>();
  131. m_UrlMap[url] = urlData;
  132. string uri = "/lslhttp/" + urlcode.ToString() + "/";
  133. m_HttpServer.AddPollServiceHTTPHandler(uri,HandleHttpPoll,
  134. new PollServiceEventArgs(HttpRequestHandler,HasEvents, GetEvents, NoEvents,
  135. urlcode));
  136. engine.PostScriptEvent(itemID, "http_request", new Object[] { urlcode.ToString(), "URL_REQUEST_GRANTED", url });
  137. }
  138. return urlcode;
  139. }
  140. public UUID RequestSecureURL(IScriptModule engine, SceneObjectPart host, UUID itemID)
  141. {
  142. UUID urlcode = UUID.Random();
  143. engine.PostScriptEvent(itemID, "http_request", new Object[] { urlcode.ToString(), "URL_REQUEST_DENIED", "" });
  144. return urlcode;
  145. }
  146. public void ReleaseURL(string url)
  147. {
  148. lock (m_UrlMap)
  149. {
  150. UrlData data;
  151. if (!m_UrlMap.TryGetValue(url, out data))
  152. {
  153. return;
  154. }
  155. foreach (UUID req in data.requests.Keys)
  156. m_RequestMap.Remove(req);
  157. RemoveUrl(data);
  158. m_UrlMap.Remove(url);
  159. }
  160. }
  161. public void HttpResponse(UUID request, int status, string body)
  162. {
  163. if (m_RequestMap.ContainsKey(request))
  164. {
  165. UrlData urlData = m_RequestMap[request];
  166. urlData.requests[request].responseCode = status;
  167. urlData.requests[request].responseBody = body;
  168. //urlData.requests[request].ev.Set();
  169. urlData.requests[request].requestDone =true;
  170. }
  171. else
  172. {
  173. m_log.Info("[HttpRequestHandler] There is no http-in request with id " + request.ToString());
  174. }
  175. }
  176. public string GetHttpHeader(UUID requestId, string header)
  177. {
  178. if (m_RequestMap.ContainsKey(requestId))
  179. {
  180. UrlData urlData=m_RequestMap[requestId];
  181. string value;
  182. if (urlData.requests[requestId].headers.TryGetValue(header,out value))
  183. return value;
  184. }
  185. else
  186. {
  187. m_log.Warn("[HttpRequestHandler] There was no http-in request with id " + requestId);
  188. }
  189. return String.Empty;
  190. }
  191. public int GetFreeUrls()
  192. {
  193. return m_TotalUrls - m_UrlMap.Count;
  194. }
  195. public void ScriptRemoved(UUID itemID)
  196. {
  197. lock (m_UrlMap)
  198. {
  199. List<string> removeURLs = new List<string>();
  200. foreach (KeyValuePair<string, UrlData> url in m_UrlMap)
  201. {
  202. if (url.Value.itemID == itemID)
  203. {
  204. RemoveUrl(url.Value);
  205. removeURLs.Add(url.Key);
  206. foreach (UUID req in url.Value.requests.Keys)
  207. m_RequestMap.Remove(req);
  208. }
  209. }
  210. foreach (string urlname in removeURLs)
  211. m_UrlMap.Remove(urlname);
  212. }
  213. }
  214. public void ObjectRemoved(UUID objectID)
  215. {
  216. lock (m_UrlMap)
  217. {
  218. List<string> removeURLs = new List<string>();
  219. foreach (KeyValuePair<string, UrlData> url in m_UrlMap)
  220. {
  221. if (url.Value.hostID == objectID)
  222. {
  223. RemoveUrl(url.Value);
  224. removeURLs.Add(url.Key);
  225. foreach (UUID req in url.Value.requests.Keys)
  226. m_RequestMap.Remove(req);
  227. }
  228. }
  229. foreach (string urlname in removeURLs)
  230. m_UrlMap.Remove(urlname);
  231. }
  232. }
  233. private void RemoveUrl(UrlData data)
  234. {
  235. m_HttpServer.RemoveHTTPHandler("", "/lslhttp/"+data.urlcode.ToString()+"/");
  236. }
  237. private Hashtable NoEvents(UUID requestID, UUID sessionID)
  238. {
  239. Hashtable response = new Hashtable();
  240. UrlData url;
  241. lock (m_RequestMap)
  242. {
  243. if (!m_RequestMap.ContainsKey(requestID))
  244. return response;
  245. url = m_RequestMap[requestID];
  246. }
  247. if (System.Environment.TickCount - url.requests[requestID].startTime > 25000)
  248. {
  249. response["int_response_code"] = 500;
  250. response["str_response_string"] = "Script timeout";
  251. response["content_type"] = "text/plain";
  252. response["keepalive"] = false;
  253. response["reusecontext"] = false;
  254. //remove from map
  255. lock (url)
  256. {
  257. url.requests.Remove(requestID);
  258. m_RequestMap.Remove(requestID);
  259. }
  260. return response;
  261. }
  262. return response;
  263. }
  264. private bool HasEvents(UUID requestID, UUID sessionID)
  265. {
  266. UrlData url=null;
  267. lock (m_RequestMap)
  268. {
  269. if (!m_RequestMap.ContainsKey(requestID))
  270. {
  271. return false;
  272. }
  273. url = m_RequestMap[requestID];
  274. if (!url.requests.ContainsKey(requestID))
  275. {
  276. return false;
  277. }
  278. }
  279. if (System.Environment.TickCount-url.requests[requestID].startTime>25000)
  280. {
  281. return true;
  282. }
  283. if (url.requests[requestID].requestDone)
  284. return true;
  285. else
  286. return false;
  287. }
  288. private Hashtable GetEvents(UUID requestID, UUID sessionID, string request)
  289. {
  290. UrlData url = null;
  291. RequestData requestData = null;
  292. lock (m_RequestMap)
  293. {
  294. if (!m_RequestMap.ContainsKey(requestID))
  295. return NoEvents(requestID,sessionID);
  296. url = m_RequestMap[requestID];
  297. requestData = url.requests[requestID];
  298. }
  299. if (!requestData.requestDone)
  300. return NoEvents(requestID,sessionID);
  301. Hashtable response = new Hashtable();
  302. if (System.Environment.TickCount - requestData.startTime > 25000)
  303. {
  304. response["int_response_code"] = 500;
  305. response["str_response_string"] = "Script timeout";
  306. response["content_type"] = "text/plain";
  307. response["keepalive"] = false;
  308. response["reusecontext"] = false;
  309. return response;
  310. }
  311. //put response
  312. response["int_response_code"] = requestData.responseCode;
  313. response["str_response_string"] = requestData.responseBody;
  314. response["content_type"] = "text/plain";
  315. response["keepalive"] = false;
  316. response["reusecontext"] = false;
  317. //remove from map
  318. lock (url)
  319. {
  320. url.requests.Remove(requestID);
  321. m_RequestMap.Remove(requestID);
  322. }
  323. return response;
  324. }
  325. public void HttpRequestHandler(UUID requestID, Hashtable request)
  326. {
  327. lock (request)
  328. {
  329. string uri = request["uri"].ToString();
  330. try
  331. {
  332. Hashtable headers = (Hashtable)request["headers"];
  333. // string uri_full = "http://" + m_ExternalHostNameForLSL + ":" + m_HttpServer.Port.ToString() + uri;// "/lslhttp/" + urlcode.ToString() + "/";
  334. int pos1 = uri.IndexOf("/");// /lslhttp
  335. int pos2 = uri.IndexOf("/", pos1 + 1);// /lslhttp/
  336. int pos3 = uri.IndexOf("/", pos2 + 1);// /lslhttp/<UUID>/
  337. string uri_tmp = uri.Substring(0, pos3 + 1);
  338. //HTTP server code doesn't provide us with QueryStrings
  339. string pathInfo;
  340. string queryString;
  341. queryString = "";
  342. pathInfo = uri.Substring(pos3);
  343. UrlData url = m_UrlMap["http://" + m_ExternalHostNameForLSL + ":" + m_HttpServer.Port.ToString() + uri_tmp];
  344. //for llGetHttpHeader support we need to store original URI here
  345. //to make x-path-info / x-query-string / x-script-url / x-remote-ip headers
  346. //as per http://wiki.secondlife.com/wiki/LlGetHTTPHeader
  347. RequestData requestData = new RequestData();
  348. requestData.requestID = requestID;
  349. requestData.requestDone = false;
  350. requestData.startTime = System.Environment.TickCount;
  351. requestData.uri = uri;
  352. if (requestData.headers == null)
  353. requestData.headers = new Dictionary<string, string>();
  354. foreach (DictionaryEntry header in headers)
  355. {
  356. string key = (string)header.Key;
  357. string value = (string)header.Value;
  358. requestData.headers.Add(key, value);
  359. }
  360. foreach (DictionaryEntry de in request)
  361. {
  362. if (de.Key.ToString() == "querystringkeys")
  363. {
  364. System.String[] keys = (System.String[])de.Value;
  365. foreach (String key in keys)
  366. {
  367. if (request.ContainsKey(key))
  368. {
  369. string val = (String)request[key];
  370. queryString = queryString + key + "=" + val + "&";
  371. }
  372. }
  373. if (queryString.Length > 1)
  374. queryString = queryString.Substring(0, queryString.Length - 1);
  375. }
  376. }
  377. //if this machine is behind DNAT/port forwarding, currently this is being
  378. //set to address of port forwarding router
  379. requestData.headers["x-remote-ip"] = requestData.headers["remote_addr"];
  380. requestData.headers["x-path-info"] = pathInfo;
  381. requestData.headers["x-query-string"] = queryString;
  382. requestData.headers["x-script-url"] = url.url;
  383. //requestData.ev = new ManualResetEvent(false);
  384. lock (url.requests)
  385. {
  386. url.requests.Add(requestID, requestData);
  387. }
  388. lock (m_RequestMap)
  389. {
  390. //add to request map
  391. m_RequestMap.Add(requestID, url);
  392. }
  393. url.engine.PostScriptEvent(url.itemID, "http_request", new Object[] { requestID.ToString(), request["http-method"].ToString(), request["body"].ToString() });
  394. //send initial response?
  395. // Hashtable response = new Hashtable();
  396. return;
  397. }
  398. catch (Exception we)
  399. {
  400. //Hashtable response = new Hashtable();
  401. m_log.Warn("[HttpRequestHandler]: http-in request failed");
  402. m_log.Warn(we.Message);
  403. m_log.Warn(we.StackTrace);
  404. }
  405. }
  406. }
  407. private void OnScriptReset(uint localID, UUID itemID)
  408. {
  409. ScriptRemoved(itemID);
  410. }
  411. }
  412. }