From 180be7de07014aa33bc6066f12a0819b731c1c9d Mon Sep 17 00:00:00 2001 From: Dr Scofield Date: Tue, 10 Feb 2009 13:10:57 +0000 Subject: this is step 2 of 2 of the OpenSim.Region.Environment refactor. NOTHING has been deleted or moved off to forge at this point. what has happened is that OpenSim.Region.Environment.Modules has been split in two: - OpenSim.Region.CoreModules: all those modules that are either directly or indirectly referenced from other OpenSim packages, or that provide functionality that the OpenSim developer community considers core functionality: CoreModules/Agent/AssetTransaction CoreModules/Agent/Capabilities CoreModules/Agent/TextureDownload CoreModules/Agent/TextureSender CoreModules/Agent/TextureSender/Tests CoreModules/Agent/Xfer CoreModules/Avatar/AvatarFactory CoreModules/Avatar/Chat/ChatModule CoreModules/Avatar/Combat CoreModules/Avatar/Currency/SampleMoney CoreModules/Avatar/Dialog CoreModules/Avatar/Friends CoreModules/Avatar/Gestures CoreModules/Avatar/Groups CoreModules/Avatar/InstantMessage CoreModules/Avatar/Inventory CoreModules/Avatar/Inventory/Archiver CoreModules/Avatar/Inventory/Transfer CoreModules/Avatar/Lure CoreModules/Avatar/ObjectCaps CoreModules/Avatar/Profiles CoreModules/Communications/Local CoreModules/Communications/REST CoreModules/Framework/EventQueue CoreModules/Framework/InterfaceCommander CoreModules/Hypergrid CoreModules/InterGrid CoreModules/Scripting/DynamicTexture CoreModules/Scripting/EMailModules CoreModules/Scripting/HttpRequest CoreModules/Scripting/LoadImageURL CoreModules/Scripting/VectorRender CoreModules/Scripting/WorldComm CoreModules/Scripting/XMLRPC CoreModules/World/Archiver CoreModules/World/Archiver/Tests CoreModules/World/Estate CoreModules/World/Land CoreModules/World/Permissions CoreModules/World/Serialiser CoreModules/World/Sound CoreModules/World/Sun CoreModules/World/Terrain CoreModules/World/Terrain/DefaultEffects CoreModules/World/Terrain/DefaultEffects/bin CoreModules/World/Terrain/DefaultEffects/bin/Debug CoreModules/World/Terrain/Effects CoreModules/World/Terrain/FileLoaders CoreModules/World/Terrain/FloodBrushes CoreModules/World/Terrain/PaintBrushes CoreModules/World/Terrain/Tests CoreModules/World/Vegetation CoreModules/World/Wind CoreModules/World/WorldMap - OpenSim.Region.OptionalModules: all those modules that are not core modules: OptionalModules/Avatar/Chat/IRC-stuff OptionalModules/Avatar/Concierge OptionalModules/Avatar/Voice/AsterixVoice OptionalModules/Avatar/Voice/SIPVoice OptionalModules/ContentManagementSystem OptionalModules/Grid/Interregion OptionalModules/Python OptionalModules/SvnSerialiser OptionalModules/World/NPC OptionalModules/World/TreePopulator --- .../Scripting/HttpRequest/ScriptsHttpRequests.cs | 437 +++++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs (limited to 'OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs') diff --git a/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs new file mode 100644 index 0000000..9f3bd09 --- /dev/null +++ b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs @@ -0,0 +1,437 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSim Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text; +using System.Threading; +using OpenMetaverse; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Framework.Servers; +using OpenSim.Region.Framework.Interfaces; +using OpenSim.Region.Framework.Scenes; +using System.Collections; + +/***************************************************** + * + * ScriptsHttpRequests + * + * Implements the llHttpRequest and http_response + * callback. + * + * Some stuff was already in LSLLongCmdHandler, and then + * there was this file with a stub class in it. So, + * I am moving some of the objects and functions out of + * LSLLongCmdHandler, such as the HttpRequestClass, the + * start and stop methods, and setting up pending and + * completed queues. These are processed in the + * LSLLongCmdHandler polling loop. Similiar to the + * XMLRPCModule, since that seems to work. + * + * //TODO + * + * This probably needs some throttling mechanism but + * it's wide open right now. This applies to both + * number of requests and data volume. + * + * Linden puts all kinds of header fields in the requests. + * Not doing any of that: + * User-Agent + * X-SecondLife-Shard + * X-SecondLife-Object-Name + * X-SecondLife-Object-Key + * X-SecondLife-Region + * X-SecondLife-Local-Position + * X-SecondLife-Local-Velocity + * X-SecondLife-Local-Rotation + * X-SecondLife-Owner-Name + * X-SecondLife-Owner-Key + * + * HTTPS support + * + * Configurable timeout? + * Configurable max response size? + * Configurable + * + * **************************************************/ + +namespace OpenSim.Region.CoreModules.Scripting.HttpRequest +{ + public class HttpRequestModule : IRegionModule, IHttpRequestModule + { + private object HttpListLock = new object(); + private int httpTimeout = 30000; + private string m_name = "HttpScriptRequests"; + + private string m_proxyurl = ""; + private string m_proxyexcepts = ""; + + // + private Dictionary m_pendingRequests; + private Scene m_scene; + // private Queue rpcQueue = new Queue(); + + public HttpRequestModule() + { + } + + #region IHttpRequestModule Members + + public UUID MakeHttpRequest(string url, string parameters, string body) + { + return UUID.Zero; + } + + public UUID StartHttpRequest(uint localID, UUID itemID, string url, List parameters, Dictionary headers, string body) + { + UUID reqID = UUID.Random(); + HttpRequestClass htc = new HttpRequestClass(); + + // Partial implementation: support for parameter flags needed + // see http://wiki.secondlife.com/wiki/LlHTTPRequest + // + // Parameters are expected in {key, value, ... , key, value} + if (parameters != null) + { + string[] parms = parameters.ToArray(); + for (int i = 0; i < parms.Length; i += 2) + { + switch (Int32.Parse(parms[i])) + { + case (int)HttpRequestConstants.HTTP_METHOD: + + htc.HttpMethod = parms[i + 1]; + break; + + case (int)HttpRequestConstants.HTTP_MIMETYPE: + + htc.HttpMIMEType = parms[i + 1]; + break; + + case (int)HttpRequestConstants.HTTP_BODY_MAXLENGTH: + + // TODO implement me + break; + + case (int)HttpRequestConstants.HTTP_VERIFY_CERT: + + // TODO implement me + break; + } + } + } + + htc.LocalID = localID; + htc.ItemID = itemID; + htc.Url = url; + htc.ReqID = reqID; + htc.HttpTimeout = httpTimeout; + htc.OutboundBody = body; + htc.ResponseHeaders = headers; + htc.proxyurl = m_proxyurl; + htc.proxyexcepts = m_proxyexcepts; + + lock (HttpListLock) + { + m_pendingRequests.Add(reqID, htc); + } + + htc.Process(); + + return reqID; + } + + public void StopHttpRequest(uint m_localID, UUID m_itemID) + { + if (m_pendingRequests != null) + { + lock (HttpListLock) + { + HttpRequestClass tmpReq; + if (m_pendingRequests.TryGetValue(m_itemID, out tmpReq)) + { + tmpReq.Stop(); + m_pendingRequests.Remove(m_itemID); + } + } + } + } + + /* + * TODO + * Not sure how important ordering is is here - the next first + * one completed in the list is returned, based soley on its list + * position, not the order in which the request was started or + * finsihed. I thought about setting up a queue for this, but + * it will need some refactoring and this works 'enough' right now + */ + + public IServiceRequest GetNextCompletedRequest() + { + lock (HttpListLock) + { + foreach (UUID luid in m_pendingRequests.Keys) + { + HttpRequestClass tmpReq; + + if (m_pendingRequests.TryGetValue(luid, out tmpReq)) + { + if (tmpReq.Finished) + { + return tmpReq; + } + } + } + } + return null; + } + + public void RemoveCompletedRequest(UUID id) + { + lock (HttpListLock) + { + HttpRequestClass tmpReq; + if (m_pendingRequests.TryGetValue(id, out tmpReq)) + { + tmpReq.Stop(); + tmpReq = null; + m_pendingRequests.Remove(id); + } + } + } + + #endregion + + #region IRegionModule Members + + public void Initialise(Scene scene, IConfigSource config) + { + m_scene = scene; + + m_scene.RegisterModuleInterface(this); + + m_proxyurl = config.Configs["Startup"].GetString("HttpProxy"); + m_proxyexcepts = config.Configs["Startup"].GetString("HttpProxyExceptions"); + + m_pendingRequests = new Dictionary(); + } + + public void PostInitialise() + { + } + + public void Close() + { + } + + public string Name + { + get { return m_name; } + } + + public bool IsSharedModule + { + get { return true; } + } + + #endregion + } + + public class HttpRequestClass: IServiceRequest + { + // Constants for parameters + // public const int HTTP_BODY_MAXLENGTH = 2; + // public const int HTTP_METHOD = 0; + // public const int HTTP_MIMETYPE = 1; + // public const int HTTP_VERIFY_CERT = 3; + private bool _finished; + public bool Finished + { + get { return _finished; } + } + // public int HttpBodyMaxLen = 2048; // not implemented + + // Parameter members and default values + public string HttpMethod = "GET"; + public string HttpMIMEType = "text/plain;charset=utf-8"; + public int HttpTimeout; + // public bool HttpVerifyCert = true; // not implemented + private Thread httpThread; + + // Request info + private UUID _itemID; + public UUID ItemID + { + get { return _itemID; } + set { _itemID = value; } + } + private uint _localID; + public uint LocalID + { + get { return _localID; } + set { _localID = value; } + } + public DateTime Next; + public string proxyurl; + public string proxyexcepts; + public string OutboundBody; + private UUID _reqID; + public UUID ReqID + { + get { return _reqID; } + set { _reqID = value; } + } + public HttpWebRequest Request; + public string ResponseBody; + public List ResponseMetadata; + public Dictionary ResponseHeaders; + public int Status; + public string Url; + + public void Process() + { + httpThread = new Thread(SendRequest); + httpThread.Name = "HttpRequestThread"; + httpThread.Priority = ThreadPriority.BelowNormal; + httpThread.IsBackground = true; + _finished = false; + httpThread.Start(); + ThreadTracker.Add(httpThread); + } + + /* + * TODO: More work on the response codes. Right now + * returning 200 for success or 499 for exception + */ + + public void SendRequest() + { + HttpWebResponse response = null; + StringBuilder sb = new StringBuilder(); + byte[] buf = new byte[8192]; + string tempString = null; + int count = 0; + + try + { + Request = (HttpWebRequest) WebRequest.Create(Url); + Request.Method = HttpMethod; + Request.ContentType = HttpMIMEType; + + if (proxyurl != null && proxyurl.Length > 0) + { + if (proxyexcepts != null && proxyexcepts.Length > 0) + { + string[] elist = proxyexcepts.Split(';'); + Request.Proxy = new WebProxy(proxyurl, true, elist); + } + else + { + Request.Proxy = new WebProxy(proxyurl, true); + } + } + + foreach (KeyValuePair entry in ResponseHeaders) + Request.Headers[entry.Key] = entry.Value; + + // Encode outbound data + if (OutboundBody.Length > 0) + { + byte[] data = Encoding.UTF8.GetBytes(OutboundBody); + + Request.ContentLength = data.Length; + Stream bstream = Request.GetRequestStream(); + bstream.Write(data, 0, data.Length); + bstream.Close(); + } + + Request.Timeout = HttpTimeout; + // execute the request + response = (HttpWebResponse) Request.GetResponse(); + + Stream resStream = response.GetResponseStream(); + + do + { + // fill the buffer with data + count = resStream.Read(buf, 0, buf.Length); + + // make sure we read some data + if (count != 0) + { + // translate from bytes to ASCII text + tempString = Encoding.UTF8.GetString(buf, 0, count); + + // continue building the string + sb.Append(tempString); + } + } while (count > 0); // any more data to read? + + ResponseBody = sb.ToString(); + } + catch (Exception e) + { + if (e is WebException && ((WebException)e).Status == WebExceptionStatus.ProtocolError) + { + HttpWebResponse webRsp = (HttpWebResponse)((WebException)e).Response; + Status = (int)webRsp.StatusCode; + ResponseBody = webRsp.StatusDescription; + } + else + { + Status = (int)OSHttpStatusCode.ClientErrorJoker; + ResponseBody = e.Message; + } + + _finished = true; + return; + } + finally + { + if (response != null) + response.Close(); + } + + Status = (int)OSHttpStatusCode.SuccessOk; + _finished = true; + } + + public void Stop() + { + try + { + httpThread.Abort(); + } + catch (Exception) + { + } + } + } +} -- cgit v1.1