From 134f86e8d5c414409631b25b8c6f0ee45fbd8631 Mon Sep 17 00:00:00 2001 From: David Walter Seikel Date: Thu, 3 Nov 2016 21:44:39 +1000 Subject: Initial update to OpenSim 0.8.2.1 source code. --- OpenSim/Framework/AgentCircuitData.cs | 28 +- OpenSim/Framework/Animation.cs | 24 + OpenSim/Framework/AssemblyInfo.cs | 3 +- OpenSim/Framework/AssetBase.cs | 13 +- .../Filesystem/Properties/AssemblyInfo.cs | 4 +- OpenSim/Framework/AvatarAppearance.cs | 335 ++++- OpenSim/Framework/AvatarWearable.cs | 4 +- OpenSim/Framework/BasicDOSProtector.cs | 275 ++++ OpenSim/Framework/BlockingQueue.cs | 32 +- OpenSim/Framework/CachedTextureEventArg.cs | 46 + OpenSim/Framework/ChildAgentDataUpdate.cs | 60 +- OpenSim/Framework/CircularBuffer.cs | 312 ++++ OpenSim/Framework/ClientInfo.cs | 16 +- .../Framework/Communications/GenericAsyncResult.cs | 185 --- OpenSim/Framework/Communications/IUserService.cs | 157 -- .../Communications/Limit/IRequestLimitStrategy.cs | 66 - .../Communications/Limit/NullLimitStrategy.cs | 40 - .../Communications/Limit/RepeatLimitStrategy.cs | 109 -- .../Communications/Limit/TimeLimitStrategy.cs | 140 -- .../Communications/Properties/AssemblyInfo.cs | 65 - OpenSim/Framework/Communications/RestClient.cs | 438 ------ OpenSim/Framework/Communications/XMPP/XmppError.cs | 39 - .../Framework/Communications/XMPP/XmppIqStanza.cs | 60 - .../Communications/XMPP/XmppMessageStanza.cs | 93 -- .../Communications/XMPP/XmppPresenceStanza.cs | 69 - .../Communications/XMPP/XmppSerializer.cs | 79 - .../Framework/Communications/XMPP/XmppStanza.cs | 70 - .../Framework/Communications/XMPP/XmppWriter.cs | 57 - OpenSim/Framework/ConfigSettings.cs | 1 - .../Configuration/HTTP/HTTPConfiguration.cs | 119 -- .../Configuration/HTTP/Properties/AssemblyInfo.cs | 33 - .../Configuration/HTTP/RemoteConfigSettings.cs | 63 - .../Configuration/XML/Properties/AssemblyInfo.cs | 33 - .../Configuration/XML/XmlConfiguration.cs | 141 -- OpenSim/Framework/ConfigurationMember.cs | 530 ------- OpenSim/Framework/Console/AssemblyInfo.cs | 2 +- OpenSim/Framework/Console/CommandConsole.cs | 46 +- OpenSim/Framework/Console/ConsoleDisplayUtil.cs | 48 + OpenSim/Framework/Console/ConsoleUtil.cs | 175 ++- OpenSim/Framework/Console/LocalConsole.cs | 85 +- OpenSim/Framework/Console/MockConsole.cs | 3 + OpenSim/Framework/Console/RemoteConsole.cs | 4 +- OpenSim/Framework/Constants.cs | 11 +- OpenSim/Framework/DAMap.cs | 328 +++++ OpenSim/Framework/DOMap.cs | 98 ++ OpenSim/Framework/EstateBan.cs | 49 + OpenSim/Framework/EstateSettings.cs | 147 ++ OpenSim/Framework/ExtraPhysicsData.cs | 50 + OpenSim/Framework/ForeignUserProfileData.cs | 77 - OpenSim/Framework/GridInstantMessage.cs | 18 + OpenSim/Framework/IClientAPI.cs | 29 +- OpenSim/Framework/ICommandConsole.cs | 11 +- OpenSim/Framework/IImprovedAssetCache.cs | 27 +- OpenSim/Framework/ILandChannel.cs | 7 + OpenSim/Framework/ILandObject.cs | 2 + OpenSim/Framework/IMoneyModule.cs | 3 +- OpenSim/Framework/IPeople.cs | 49 + OpenSim/Framework/IRegionLoader.cs | 37 - OpenSim/Framework/IScene.cs | 21 +- OpenSim/Framework/ISceneAgent.cs | 7 +- OpenSim/Framework/ISceneObject.cs | 2 + OpenSim/Framework/InventoryCollection.cs | 4 +- OpenSim/Framework/InventoryFolderBase.cs | 3 + OpenSim/Framework/InventoryItemBase.cs | 9 +- OpenSim/Framework/Location.cs | 16 +- OpenSim/Framework/LogWriter.cs | 181 +++ OpenSim/Framework/MapBlockData.cs | 18 + OpenSim/Framework/MapItemReplyStruct.cs | 33 + OpenSim/Framework/MetricsCollector.cs | 223 +++ .../Framework/Monitoring/AssetStatsCollector.cs | 26 + OpenSim/Framework/Monitoring/BaseStatsCollector.cs | 19 +- OpenSim/Framework/Monitoring/Checks/Check.cs | 118 ++ OpenSim/Framework/Monitoring/ChecksManager.cs | 262 ++++ .../Monitoring/Interfaces/IStatsCollector.cs | 9 + OpenSim/Framework/Monitoring/JobEngine.cs | 341 +++++ OpenSim/Framework/Monitoring/MemoryWatchdog.cs | 8 +- .../Monitoring/Properties/AssemblyInfo.cs | 4 +- .../Framework/Monitoring/ServerStatsCollector.cs | 346 +++++ .../Framework/Monitoring/SimExtraStatsCollector.cs | 106 +- OpenSim/Framework/Monitoring/Stats/CounterStat.cs | 119 ++ .../Framework/Monitoring/Stats/EventHistogram.cs | 173 +++ .../Framework/Monitoring/Stats/PercentageStat.cs | 16 + OpenSim/Framework/Monitoring/Stats/Stat.cs | 102 +- OpenSim/Framework/Monitoring/StatsLogger.cs | 151 ++ OpenSim/Framework/Monitoring/StatsManager.cs | 403 +++++- OpenSim/Framework/Monitoring/UserStatsCollector.cs | 18 + OpenSim/Framework/Monitoring/Watchdog.cs | 106 +- OpenSim/Framework/Monitoring/WorkManager.cs | 290 ++++ OpenSim/Framework/OutboundUrlFilter.cs | 256 ++++ OpenSim/Framework/PermissionsUtil.cs | 87 ++ OpenSim/Framework/PluginLoader.cs | 17 +- OpenSim/Framework/PluginManager.cs | 4 +- OpenSim/Framework/PrimitiveBaseShape.cs | 22 +- OpenSim/Framework/RegionFlags.cs | 3 +- OpenSim/Framework/RegionInfo.cs | 463 +++--- .../Filesystem/Properties/AssemblyInfo.cs | 33 - .../Filesystem/RegionLoaderFileSystem.cs | 116 -- .../RegionLoader/Web/Properties/AssemblyInfo.cs | 33 - .../RegionLoader/Web/RegionLoaderWebServer.cs | 130 -- OpenSim/Framework/RegionSettings.cs | 23 +- OpenSim/Framework/RestClient.cs | 676 +++++++++ OpenSim/Framework/SLUtil.cs | 430 ++++-- .../Framework/Serialization/ArchiveConstants.cs | 11 +- .../External/ExternalRepresentationUtils.cs | 267 +++- .../Serialization/External/LandDataSerializer.cs | 28 +- .../External/UserInventoryItemSerializer.cs | 49 +- .../Serialization/Properties/AssemblyInfo.cs | 4 +- .../Serialization/Tests/LandDataSerializerTests.cs | 3 +- OpenSim/Framework/Servers/BaseOpenSimServer.cs | 82 +- .../Framework/Servers/HttpServer/BaseHttpServer.cs | 383 ++++- .../Servers/HttpServer/BaseOutputStreamHandler.cs | 60 + .../Servers/HttpServer/BaseRequestHandler.cs | 23 + .../Servers/HttpServer/BaseStreamHandler.cs | 49 +- .../BaseStreamHandlerBasicDOSProtector.cs | 107 ++ .../Servers/HttpServer/BinaryStreamHandler.cs | 2 +- .../HttpServer/GenericHTTPBasicDOSProtector.cs | 119 ++ .../Servers/HttpServer/Interfaces/IHttpServer.cs | 14 + .../HttpServer/Interfaces/IStreamHandler.cs | 15 +- .../Framework/Servers/HttpServer/JsonRPCMethod.cs | 34 + .../Servers/HttpServer/JsonRpcRequestManager.cs | 190 +++ .../Servers/HttpServer/JsonRpcResponse.cs | 150 ++ .../Framework/Servers/HttpServer/OSHttpRequest.cs | 13 +- .../Servers/HttpServer/OSHttpRequestPump.cs | 4 +- .../Framework/Servers/HttpServer/OSHttpServer.cs | 2 +- .../Servers/HttpServer/PollServiceEventArgs.cs | 31 +- .../Servers/HttpServer/PollServiceHttpRequest.cs | 45 + .../HttpServer/PollServiceRequestManager.cs | 318 +++- .../Servers/HttpServer/PollServiceWorkerThread.cs | 165 --- .../Servers/HttpServer/Properties/AssemblyInfo.cs | 4 +- .../Servers/HttpServer/RestDeserialiseHandler.cs | 4 +- .../Servers/HttpServer/RestObjectPoster.cs | 28 +- .../Servers/HttpServer/RestObjectPosterResponse.cs | 29 +- .../Servers/HttpServer/RestSessionService.cs | 89 +- .../Servers/HttpServer/RestStreamHandler.cs | 2 +- .../Servers/HttpServer/WebsocketServerHandler.cs | 1159 +++++++++++++++ .../Servers/HttpServer/XmlRpcBasicDOSProtector.cs | 91 ++ OpenSim/Framework/Servers/MainServer.cs | 24 +- .../Framework/Servers/Properties/AssemblyInfo.cs | 2 +- OpenSim/Framework/Servers/ServerBase.cs | 381 ++++- OpenSim/Framework/Servers/Tests/OSHttpTests.cs | 315 +--- .../Framework/Servers/Tests/VersionInfoTests.cs | 2 +- OpenSim/Framework/Servers/VersionInfo.cs | 77 - .../ServiceAuth/BasicHttpAuthentication.cs | 113 ++ .../ServiceAuth/CompoundAuthentication.cs | 82 ++ .../Framework/ServiceAuth/DisallowLlHttpRequest.cs | 59 + OpenSim/Framework/ServiceAuth/IServiceAuth.cs | 48 + OpenSim/Framework/ServiceAuth/ServiceAuth.cs | 68 + OpenSim/Framework/TaskInventoryItem.cs | 2 +- OpenSim/Framework/TerrainData.cs | 464 ++++++ OpenSim/Framework/Tests/AnimationTests.cs | 1 - OpenSim/Framework/Tests/AssetBaseTest.cs | 4 - OpenSim/Framework/Tests/LocationTest.cs | 21 +- OpenSim/Framework/Tests/MundaneFrameworkTests.cs | 2 +- OpenSim/Framework/Tests/UtilTest.cs | 101 +- OpenSim/Framework/ThreadSafeRandom.cs | 72 + OpenSim/Framework/ThrottleOutPacketType.cs | 4 - OpenSim/Framework/UntrustedWebRequest.cs | 230 --- OpenSim/Framework/UserProfileData.cs | 14 +- OpenSim/Framework/UserProfiles.cs | 126 ++ OpenSim/Framework/Util.cs | 1512 ++++++++++++++++---- OpenSim/Framework/VersionInfo.cs | 92 ++ OpenSim/Framework/WearableCacheItem.cs | 157 ++ OpenSim/Framework/WebUtil.cs | 928 +++++++----- 163 files changed, 13403 insertions(+), 5544 deletions(-) create mode 100644 OpenSim/Framework/BasicDOSProtector.cs create mode 100644 OpenSim/Framework/CachedTextureEventArg.cs create mode 100644 OpenSim/Framework/CircularBuffer.cs delete mode 100644 OpenSim/Framework/Communications/GenericAsyncResult.cs delete mode 100644 OpenSim/Framework/Communications/IUserService.cs delete mode 100644 OpenSim/Framework/Communications/Limit/IRequestLimitStrategy.cs delete mode 100644 OpenSim/Framework/Communications/Limit/NullLimitStrategy.cs delete mode 100644 OpenSim/Framework/Communications/Limit/RepeatLimitStrategy.cs delete mode 100644 OpenSim/Framework/Communications/Limit/TimeLimitStrategy.cs delete mode 100644 OpenSim/Framework/Communications/Properties/AssemblyInfo.cs delete mode 100644 OpenSim/Framework/Communications/RestClient.cs delete mode 100644 OpenSim/Framework/Communications/XMPP/XmppError.cs delete mode 100644 OpenSim/Framework/Communications/XMPP/XmppIqStanza.cs delete mode 100644 OpenSim/Framework/Communications/XMPP/XmppMessageStanza.cs delete mode 100644 OpenSim/Framework/Communications/XMPP/XmppPresenceStanza.cs delete mode 100644 OpenSim/Framework/Communications/XMPP/XmppSerializer.cs delete mode 100644 OpenSim/Framework/Communications/XMPP/XmppStanza.cs delete mode 100644 OpenSim/Framework/Communications/XMPP/XmppWriter.cs delete mode 100644 OpenSim/Framework/Configuration/HTTP/HTTPConfiguration.cs delete mode 100644 OpenSim/Framework/Configuration/HTTP/Properties/AssemblyInfo.cs delete mode 100644 OpenSim/Framework/Configuration/HTTP/RemoteConfigSettings.cs delete mode 100644 OpenSim/Framework/Configuration/XML/Properties/AssemblyInfo.cs delete mode 100644 OpenSim/Framework/Configuration/XML/XmlConfiguration.cs delete mode 100644 OpenSim/Framework/ConfigurationMember.cs create mode 100644 OpenSim/Framework/Console/ConsoleDisplayUtil.cs create mode 100644 OpenSim/Framework/DAMap.cs create mode 100644 OpenSim/Framework/DOMap.cs create mode 100644 OpenSim/Framework/ExtraPhysicsData.cs delete mode 100644 OpenSim/Framework/ForeignUserProfileData.cs create mode 100644 OpenSim/Framework/IPeople.cs delete mode 100644 OpenSim/Framework/IRegionLoader.cs create mode 100755 OpenSim/Framework/LogWriter.cs create mode 100644 OpenSim/Framework/MetricsCollector.cs create mode 100644 OpenSim/Framework/Monitoring/Checks/Check.cs create mode 100644 OpenSim/Framework/Monitoring/ChecksManager.cs create mode 100644 OpenSim/Framework/Monitoring/JobEngine.cs create mode 100644 OpenSim/Framework/Monitoring/ServerStatsCollector.cs mode change 100644 => 100755 OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs create mode 100755 OpenSim/Framework/Monitoring/Stats/CounterStat.cs create mode 100755 OpenSim/Framework/Monitoring/Stats/EventHistogram.cs create mode 100644 OpenSim/Framework/Monitoring/StatsLogger.cs create mode 100644 OpenSim/Framework/Monitoring/WorkManager.cs create mode 100644 OpenSim/Framework/OutboundUrlFilter.cs create mode 100644 OpenSim/Framework/PermissionsUtil.cs delete mode 100644 OpenSim/Framework/RegionLoader/Filesystem/Properties/AssemblyInfo.cs delete mode 100644 OpenSim/Framework/RegionLoader/Filesystem/RegionLoaderFileSystem.cs delete mode 100644 OpenSim/Framework/RegionLoader/Web/Properties/AssemblyInfo.cs delete mode 100644 OpenSim/Framework/RegionLoader/Web/RegionLoaderWebServer.cs create mode 100644 OpenSim/Framework/RestClient.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/BaseOutputStreamHandler.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/JsonRPCMethod.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/JsonRpcRequestManager.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/JsonRpcResponse.cs delete mode 100644 OpenSim/Framework/Servers/HttpServer/PollServiceWorkerThread.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs create mode 100644 OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs delete mode 100644 OpenSim/Framework/Servers/VersionInfo.cs create mode 100644 OpenSim/Framework/ServiceAuth/BasicHttpAuthentication.cs create mode 100644 OpenSim/Framework/ServiceAuth/CompoundAuthentication.cs create mode 100644 OpenSim/Framework/ServiceAuth/DisallowLlHttpRequest.cs create mode 100644 OpenSim/Framework/ServiceAuth/IServiceAuth.cs create mode 100644 OpenSim/Framework/ServiceAuth/ServiceAuth.cs create mode 100644 OpenSim/Framework/TerrainData.cs create mode 100644 OpenSim/Framework/ThreadSafeRandom.cs delete mode 100644 OpenSim/Framework/UntrustedWebRequest.cs create mode 100644 OpenSim/Framework/UserProfiles.cs create mode 100644 OpenSim/Framework/VersionInfo.cs create mode 100644 OpenSim/Framework/WearableCacheItem.cs (limited to 'OpenSim/Framework') diff --git a/OpenSim/Framework/AgentCircuitData.cs b/OpenSim/Framework/AgentCircuitData.cs index ffcc584..0d053e4 100644 --- a/OpenSim/Framework/AgentCircuitData.cs +++ b/OpenSim/Framework/AgentCircuitData.cs @@ -128,7 +128,31 @@ namespace OpenSim.Framework /// /// Viewer's version string as reported by the viewer at login /// - public string Viewer; + private string m_viewerInternal; + + /// + /// Viewer's version string + /// + public string Viewer + { + set { m_viewerInternal = value; } + + // Try to return consistent viewer string taking into account + // that viewers have chaagned how version is reported + // See http://opensimulator.org/mantis/view.php?id=6851 + get + { + // Old style version string contains viewer name followed by a space followed by a version number + if (m_viewerInternal == null || m_viewerInternal.Contains(" ")) + { + return m_viewerInternal; + } + else // New style version contains no spaces, just version number + { + return Channel + " " + m_viewerInternal; + } + } + } /// /// The channel strinf sent by the viewer at login @@ -297,6 +321,8 @@ namespace OpenSim.Framework Mac = args["mac"].AsString(); if (args["id0"] != null) Id0 = args["id0"].AsString(); + if (args["teleport_flags"] != null) + teleportFlags = args["teleport_flags"].AsUInteger(); if (args["start_pos"] != null) Vector3.TryParse(args["start_pos"].AsString(), out startpos); diff --git a/OpenSim/Framework/Animation.cs b/OpenSim/Framework/Animation.cs index 232f5a1..e958b75 100644 --- a/OpenSim/Framework/Animation.cs +++ b/OpenSim/Framework/Animation.cs @@ -120,5 +120,29 @@ namespace OpenSim.Framework sequenceNum = args["seq_num"].AsInteger(); } + public override bool Equals(object obj) + { + Animation other = obj as Animation; + if (other != null) + { + return (other.AnimID.Equals(this.AnimID) + && other.SequenceNum == this.SequenceNum + && other.ObjectID.Equals(this.ObjectID) ); + } + return base.Equals(obj); + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } + + public override string ToString() + { + return "AnimID=" + AnimID.ToString() + + "/seq=" + SequenceNum.ToString() + + "/objID=" + ObjectID.ToString(); + } + } } diff --git a/OpenSim/Framework/AssemblyInfo.cs b/OpenSim/Framework/AssemblyInfo.cs index 02986d5..a797424 100644 --- a/OpenSim/Framework/AssemblyInfo.cs +++ b/OpenSim/Framework/AssemblyInfo.cs @@ -59,5 +59,4 @@ using System.Runtime.InteropServices; // Revision // -[assembly : AssemblyVersion("0.7.5.*")] -[assembly : AssemblyFileVersion("0.6.5.0")] \ No newline at end of file +[assembly : AssemblyVersion("0.8.2.*")] diff --git a/OpenSim/Framework/AssetBase.cs b/OpenSim/Framework/AssetBase.cs index d2c6c57..2f04d2e 100644 --- a/OpenSim/Framework/AssetBase.cs +++ b/OpenSim/Framework/AssetBase.cs @@ -50,6 +50,9 @@ namespace OpenSim.Framework { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + public static readonly int MAX_ASSET_NAME = 64; + public static readonly int MAX_ASSET_DESC = 64; + /// /// Data of the Asset /// @@ -133,7 +136,7 @@ namespace OpenSim.Framework get { return - (Type == (sbyte) AssetType.Animation || + (Type == (sbyte)AssetType.Animation || Type == (sbyte)AssetType.Gesture || Type == (sbyte)AssetType.Simstate || Type == (sbyte)AssetType.Unknown || @@ -143,13 +146,9 @@ namespace OpenSim.Framework Type == (sbyte)AssetType.Texture || Type == (sbyte)AssetType.TextureTGA || Type == (sbyte)AssetType.Folder || - Type == (sbyte)AssetType.RootFolder || - Type == (sbyte)AssetType.LostAndFoundFolder || - Type == (sbyte)AssetType.SnapshotFolder || - Type == (sbyte)AssetType.TrashFolder || Type == (sbyte)AssetType.ImageJPEG || - Type == (sbyte) AssetType.ImageTGA || - Type == (sbyte) AssetType.LSLBytecode); + Type == (sbyte)AssetType.ImageTGA || + Type == (sbyte)AssetType.LSLBytecode); } } diff --git a/OpenSim/Framework/AssetLoader/Filesystem/Properties/AssemblyInfo.cs b/OpenSim/Framework/AssetLoader/Filesystem/Properties/AssemblyInfo.cs index 0498ed4..76df564 100644 --- a/OpenSim/Framework/AssetLoader/Filesystem/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/AssetLoader/Filesystem/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("0.7.5.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("0.8.3.*")] + diff --git a/OpenSim/Framework/AvatarAppearance.cs b/OpenSim/Framework/AvatarAppearance.cs index 95e9667..26dd5df 100644 --- a/OpenSim/Framework/AvatarAppearance.cs +++ b/OpenSim/Framework/AvatarAppearance.cs @@ -40,8 +40,17 @@ namespace OpenSim.Framework /// public class AvatarAppearance { + // SL box diferent to size + const float AVBOXAJUST = 0.2f; + // constrains for ubitode physics + const float AVBOXMINX = 0.2f; + const float AVBOXMINY = 0.3f; + const float AVBOXMINZ = 1.2f; + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + // this is viewer capabilities and weared things dependent + // should be only used as initial default value ( V1 viewers ) public readonly static int VISUALPARAM_COUNT = 218; public readonly static int TEXTURE_COUNT = 21; @@ -53,7 +62,15 @@ namespace OpenSim.Framework protected AvatarWearable[] m_wearables; protected Dictionary> m_attachments; protected float m_avatarHeight = 0; + protected Vector3 m_avatarSize = new Vector3(0.45f, 0.6f, 1.9f); // sl Z cloud value + protected Vector3 m_avatarBoxSize = new Vector3(0.45f, 0.6f, 1.9f); + protected float m_avatarFeetOffset = 0; + protected float m_avatarAnimOffset = 0; + protected WearableCacheItem[] m_cacheitems; + protected bool m_cacheItemsDirty = true; + + public bool PackLegacyWearables {get; set; } public virtual int Serial { get { return m_serial; } @@ -66,6 +83,21 @@ namespace OpenSim.Framework set { m_visualparams = value; } } + public virtual Vector3 AvatarSize + { + get { return m_avatarSize; } + } + + public virtual Vector3 AvatarBoxSize + { + get { return m_avatarBoxSize; } + } + + public virtual float AvatarFeetOffset + { + get { return m_avatarFeetOffset + m_avatarAnimOffset; } + } + public virtual Primitive.TextureEntry Texture { get { return m_texture; } @@ -87,16 +119,29 @@ namespace OpenSim.Framework get { return m_avatarHeight; } set { m_avatarHeight = value; } } + + public virtual WearableCacheItem[] WearableCacheItems + { + get { return m_cacheitems; } + set { m_cacheitems = value; } + } + + public virtual bool WearableCacheItemsDirty + { + get { return m_cacheItemsDirty; } + set { m_cacheItemsDirty = value; } + } public AvatarAppearance() { // m_log.WarnFormat("[AVATAR APPEARANCE]: create empty appearance"); - + PackLegacyWearables = false; m_serial = 0; SetDefaultWearables(); SetDefaultTexture(); SetDefaultParams(); - SetHeight(); +// SetHeight(); + SetSize(new Vector3(0.45f,0.6f,1.9f)); m_attachments = new Dictionary>(); } @@ -105,7 +150,7 @@ namespace OpenSim.Framework // m_log.WarnFormat("[AVATAR APPEARANCE]: create appearance from OSDMap"); Unpack(map); - SetHeight(); +// SetHeight(); done in Unpack } public AvatarAppearance(AvatarWearable[] wearables, Primitive.TextureEntry textureEntry, byte[] visualParams) @@ -129,7 +174,9 @@ namespace OpenSim.Framework else SetDefaultParams(); - SetHeight(); +// SetHeight(); + if(m_avatarHeight == 0) + SetSize(new Vector3(0.45f,0.6f,1.9f)); m_attachments = new Dictionary>(); } @@ -148,7 +195,8 @@ namespace OpenSim.Framework SetDefaultWearables(); SetDefaultTexture(); SetDefaultParams(); - SetHeight(); +// SetHeight(); + SetSize(new Vector3(0.45f, 0.6f, 1.9f)); m_attachments = new Dictionary>(); return; @@ -162,7 +210,10 @@ namespace OpenSim.Framework if (copyWearables && (appearance.Wearables != null)) { - for (int i = 0; i < AvatarWearable.MAX_WEARABLES; i++) + int len = appearance.Wearables.Length; + if(len > AvatarWearable.MAX_WEARABLES) + len = AvatarWearable.MAX_WEARABLES; + for (int i = 0; i < len; i++) SetWearable(i,appearance.Wearables[i]); } @@ -177,7 +228,8 @@ namespace OpenSim.Framework if (appearance.VisualParams != null) m_visualparams = (byte[])appearance.VisualParams.Clone(); - m_avatarHeight = appearance.m_avatarHeight; +// m_avatarHeight = appearance.m_avatarHeight; + SetSize(appearance.AvatarSize); // Copy the attachment, force append mode since that ensures consistency m_attachments = new Dictionary>(); @@ -240,6 +292,21 @@ namespace OpenSim.Framework // } } + /// + /// Invalidate all of the baked textures in the appearance, useful + /// if you know that none are valid + /// + public virtual void ResetBakedTextures() + { + SetDefaultTexture(); + + //for (int i = 0; i < BAKE_INDICES.Length; i++) + // { + // int idx = BAKE_INDICES[i]; + // m_texture.FaceTextures[idx].TextureID = UUID.Zero; + // } + } + protected virtual void SetDefaultTexture() { m_texture = new Primitive.TextureEntry(new UUID(AppearanceManager.DEFAULT_AVATAR_TEXTURE)); @@ -304,22 +371,33 @@ namespace OpenSim.Framework // made. We determine if any of the visual parameters actually // changed to know if the appearance should be saved later bool changed = false; - for (int i = 0; i < AvatarAppearance.VISUALPARAM_COUNT; i++) + + int newsize = visualParams.Length; + + if (newsize != m_visualparams.Length) { - if (visualParams[i] != m_visualparams[i]) + changed = true; + m_visualparams = (byte[])visualParams.Clone(); + } + else + { + + for (int i = 0; i < newsize; i++) { -// DEBUG ON -// m_log.WarnFormat("[AVATARAPPEARANCE] vparams changed [{0}] {1} ==> {2}", -// i,m_visualparams[i],visualParams[i]); -// DEBUG OFF - m_visualparams[i] = visualParams[i]; - changed = true; + if (visualParams[i] != m_visualparams[i]) + { + // DEBUG ON + // m_log.WarnFormat("[AVATARAPPEARANCE] vparams changed [{0}] {1} ==> {2}", + // i,m_visualparams[i],visualParams[i]); + // DEBUG OFF + m_visualparams[i] = visualParams[i]; + changed = true; + } } } - // Reset the height if the visual parameters actually changed - if (changed) - SetHeight(); +// if (changed) +// SetHeight(); return changed; } @@ -335,6 +413,7 @@ namespace OpenSim.Framework /// public virtual void SetHeight() { +/* // Start with shortest possible female avatar height m_avatarHeight = 1.14597f; // Add offset for male avatars @@ -347,6 +426,35 @@ namespace OpenSim.Framework + 0.07f * (float)m_visualparams[(int)VPElement.SHOES_PLATFORM_HEIGHT] / 255.0f + 0.08f * (float)m_visualparams[(int)VPElement.SHOES_HEEL_HEIGHT] / 255.0f + 0.076f * (float)m_visualparams[(int)VPElement.SHAPE_NECK_LENGTH] / 255.0f; +*/ + } + + public void SetSize(Vector3 avSize) + { + if (avSize.X > 32f) + avSize.X = 32f; + else if (avSize.X < 0.1f) + avSize.X = 0.1f; + + if (avSize.Y > 32f) + avSize.Y = 32f; + else if (avSize.Y < 0.1f) + avSize.Y = 0.1f; + if (avSize.Z > 32f) + avSize.Z = 32f; + else if (avSize.Z < 0.1f) + avSize.Z = 0.1f; + + m_avatarSize = avSize; + m_avatarBoxSize = avSize; + m_avatarBoxSize.Z += AVBOXAJUST; + if (m_avatarBoxSize.X < AVBOXMINX) + m_avatarBoxSize.X = AVBOXMINX; + if (m_avatarBoxSize.Y < AVBOXMINY) + m_avatarBoxSize.Y = AVBOXMINY; + if (m_avatarBoxSize.Z < AVBOXMINZ) + m_avatarBoxSize.Z = AVBOXMINZ; + m_avatarHeight = m_avatarSize.Z; } public virtual void SetWearable(int wearableId, AvatarWearable wearable) @@ -355,8 +463,11 @@ namespace OpenSim.Framework // m_log.WarnFormat("[AVATARAPPEARANCE] set wearable {0} --> {1}:{2}",wearableId,wearable.ItemID,wearable.AssetID); // DEBUG OFF m_wearables[wearableId].Clear(); - for (int i = 0; i < wearable.Count; i++) - m_wearables[wearableId].Add(wearable[i].ItemID, wearable[i].AssetID); + int count = wearable.Count; + if (count > AvatarWearable.MAX_WEARABLES) + count = AvatarWearable.MAX_WEARABLES; + for (int i = 0; i < count; i++) + m_wearables[wearableId].Add(wearable[i].ItemID, wearable[i].AssetID); } // DEBUG ON @@ -377,7 +488,8 @@ namespace OpenSim.Framework } s += "Visual Params: "; - for (uint j = 0; j < AvatarAppearance.VISUALPARAM_COUNT; j++) + // for (uint j = 0; j < AvatarAppearance.VISUALPARAM_COUNT; j++) + for (uint j = 0; j < m_visualparams.Length; j++) s += String.Format("{0},",m_visualparams[j]); s += "\n"; @@ -393,18 +505,16 @@ namespace OpenSim.Framework /// public List GetAttachments() { - List alist = new List(); - lock (m_attachments) { + List alist = new List(); foreach (KeyValuePair> kvp in m_attachments) { foreach (AvatarAttachment attach in kvp.Value) alist.Add(new AvatarAttachment(attach)); } - } - - return alist; + return alist; + } } internal void AppendAttachment(AvatarAttachment attach) @@ -418,6 +528,12 @@ namespace OpenSim.Framework if (!m_attachments.ContainsKey(attach.AttachPoint)) m_attachments[attach.AttachPoint] = new List(); + foreach (AvatarAttachment prev in m_attachments[attach.AttachPoint]) + { + if (prev.ItemID == attach.ItemID) + return; + } + m_attachments[attach.AttachPoint].Add(attach); } } @@ -459,45 +575,59 @@ namespace OpenSim.Framework if (attachpoint == 0) return false; - if (item == UUID.Zero) + lock (m_attachments) { - lock (m_attachments) + if (item == UUID.Zero) { if (m_attachments.ContainsKey(attachpoint)) { m_attachments.Remove(attachpoint); return true; } + + return false; } - - return false; - } - // When a user logs in, the attachment item ids are pulled from persistence in the Avatars table. However, - // the asset ids are not saved. When the avatar enters a simulator the attachments are set again. If - // we simply perform an item check here then the asset ids (which are now present) are never set, and NPC attachments - // later fail unless the attachment is detached and reattached. - // - // Therefore, we will carry on with the set if the existing attachment has no asset id. - AvatarAttachment existingAttachment = GetAttachmentForItem(item); - if (existingAttachment != null - && existingAttachment.AssetID != UUID.Zero - && existingAttachment.AttachPoint == (attachpoint & 0x7F)) - { - // m_log.DebugFormat("[AVATAR APPEARANCE] attempt to attach an already attached item {0}",item); - return false; - } - - // check if this is an append or a replace, 0x80 marks it as an append - if ((attachpoint & 0x80) > 0) - { - // strip the append bit - int point = attachpoint & 0x7F; - AppendAttachment(new AvatarAttachment(point, item, asset)); - } - else - { - ReplaceAttachment(new AvatarAttachment(attachpoint,item, asset)); + // When a user logs in, the attachment item ids are pulled from persistence in the Avatars table. However, + // the asset ids are not saved. When the avatar enters a simulator the attachments are set again. If + // we simply perform an item check here then the asset ids (which are now present) are never set, and NPC attachments + // later fail unless the attachment is detached and reattached. + // + // Therefore, we will carry on with the set if the existing attachment has no asset id. + AvatarAttachment existingAttachment = GetAttachmentForItem(item); + if (existingAttachment != null) + { +// m_log.DebugFormat( +// "[AVATAR APPEARANCE]: Found existing attachment for {0}, asset {1} at point {2}", +// existingAttachment.ItemID, existingAttachment.AssetID, existingAttachment.AttachPoint); + + if (existingAttachment.AssetID != UUID.Zero && existingAttachment.AttachPoint == (attachpoint & 0x7F)) + { + m_log.DebugFormat( + "[AVATAR APPEARANCE]: Ignoring attempt to attach an already attached item {0} at point {1}", + item, attachpoint); + + return false; + } + else + { + // Remove it here so that the later append does not add a second attachment but we still update + // the assetID + DetachAttachment(existingAttachment.ItemID); + } + } + + // check if this is an append or a replace, 0x80 marks it as an append + if ((attachpoint & 0x80) > 0) + { + // strip the append bit + int point = attachpoint & 0x7F; + AppendAttachment(new AvatarAttachment(point, item, asset)); + } + else + { + ReplaceAttachment(new AvatarAttachment(attachpoint,item, asset)); + } } return true; @@ -534,7 +664,6 @@ namespace OpenSim.Framework return kvp.Key; } } - return 0; } @@ -547,6 +676,10 @@ namespace OpenSim.Framework int index = kvp.Value.FindIndex(delegate(AvatarAttachment a) { return a.ItemID == itemID; }); if (index >= 0) { +// m_log.DebugFormat( +// "[AVATAR APPEARANCE]: Detaching attachment {0}, index {1}, point {2}", +// m_attachments[kvp.Key][index].ItemID, index, m_attachments[kvp.Key][index].AttachPoint); + // Remove it from the list of attachments at that attach point m_attachments[kvp.Key].RemoveAt(index); @@ -581,8 +714,15 @@ namespace OpenSim.Framework data["height"] = OSD.FromReal(m_avatarHeight); // Wearables - OSDArray wears = new OSDArray(AvatarWearable.MAX_WEARABLES); - for (int i = 0; i < AvatarWearable.MAX_WEARABLES; i++) + + int wearsCount; + if(PackLegacyWearables) + wearsCount = AvatarWearable.LEGACY_VERSION_MAX_WEARABLES; + else + wearsCount = AvatarWearable.MAX_WEARABLES; + + OSDArray wears = new OSDArray(wearsCount); + for (int i = 0; i < wearsCount; i++) wears.Add(m_wearables[i].Pack()); data["wearables"] = wears; @@ -601,12 +741,14 @@ namespace OpenSim.Framework OSDBinary visualparams = new OSDBinary(m_visualparams); data["visualparams"] = visualparams; - // Attachments - List attachments = GetAttachments(); - OSDArray attachs = new OSDArray(attachments.Count); - foreach (AvatarAttachment attach in GetAttachments()) - attachs.Add(attach.Pack()); - data["attachments"] = attachs; + lock (m_attachments) + { + // Attachments + OSDArray attachs = new OSDArray(m_attachments.Count); + foreach (AvatarAttachment attach in GetAttachments()) + attachs.Add(attach.Pack()); + data["attachments"] = attachs; + } return data; } @@ -620,7 +762,8 @@ namespace OpenSim.Framework if ((data != null) && (data["serial"] != null)) m_serial = data["serial"].AsInteger(); if ((data != null) && (data["height"] != null)) - m_avatarHeight = (float)data["height"].AsReal(); +// m_avatarHeight = (float)data["height"].AsReal(); + SetSize(new Vector3(0.45f,0.6f, (float)data["height"].AsReal())); try { @@ -629,7 +772,12 @@ namespace OpenSim.Framework if ((data != null) && (data["wearables"] != null) && (data["wearables"]).Type == OSDType.Array) { OSDArray wears = (OSDArray)(data["wearables"]); - for (int i = 0; i < wears.Count; i++) + + int count = wears.Count; + if (count > AvatarWearable.MAX_WEARABLES) + count = AvatarWearable.MAX_WEARABLES; + + for (int i = 0; i < count; i++) m_wearables[i] = new AvatarWearable((OSDArray)wears[i]); } else @@ -1453,7 +1601,58 @@ namespace OpenSim.Framework SHAPE_EYELID_INNER_CORNER_UP = 214, SKIRT_SKIRT_RED = 215, SKIRT_SKIRT_GREEN = 216, - SKIRT_SKIRT_BLUE = 217 + SKIRT_SKIRT_BLUE = 217, + + /// + /// Avatar Physics section. These are 0 type visual params which get transmitted. + /// + + /// + /// Breast Part 1 + /// + BREAST_PHYSICS_MASS = 218, + BREAST_PHYSICS_GRAVITY = 219, + BREAST_PHYSICS_DRAG = 220, + BREAST_PHYSICS_UPDOWN_MAX_EFFECT = 221, + BREAST_PHYSICS_UPDOWN_SPRING = 222, + BREAST_PHYSICS_UPDOWN_GAIN = 223, + BREAST_PHYSICS_UPDOWN_DAMPING = 224, + BREAST_PHYSICS_INOUT_MAX_EFFECT = 225, + BREAST_PHYSICS_INOUT_SPRING = 226, + BREAST_PHYSICS_INOUT_GAIN = 227, + BREAST_PHYSICS_INOUT_DAMPING = 228, + /// + /// Belly + /// + BELLY_PHYISCS_MASS = 229, + BELLY_PHYSICS_GRAVITY = 230, + BELLY_PHYSICS_DRAG = 231, + BELLY_PHYISCS_UPDOWN_MAX_EFFECT = 232, + BELLY_PHYSICS_UPDOWN_SPRING = 233, + BELLY_PHYSICS_UPDOWN_GAIN = 234, + BELLY_PHYSICS_UPDOWN_DAMPING = 235, + + /// + /// Butt + /// + BUTT_PHYSICS_MASS = 236, + BUTT_PHYSICS_GRAVITY = 237, + BUTT_PHYSICS_DRAG = 238, + BUTT_PHYSICS_UPDOWN_MAX_EFFECT = 239, + BUTT_PHYSICS_UPDOWN_SPRING = 240, + BUTT_PHYSICS_UPDOWN_GAIN = 241, + BUTT_PHYSICS_UPDOWN_DAMPING = 242, + BUTT_PHYSICS_LEFTRIGHT_MAX_EFFECT = 243, + BUTT_PHYSICS_LEFTRIGHT_SPRING = 244, + BUTT_PHYSICS_LEFTRIGHT_GAIN = 245, + BUTT_PHYSICS_LEFTRIGHT_DAMPING = 246, + /// + /// Breast Part 2 + /// + BREAST_PHYSICS_LEFTRIGHT_MAX_EFFECT = 247, + BREAST_PHYSICS_LEFTRIGHT_SPRING= 248, + BREAST_PHYSICS_LEFTRIGHT_GAIN = 249, + BREAST_PHYSICS_LEFTRIGHT_DAMPING = 250 } #endregion } diff --git a/OpenSim/Framework/AvatarWearable.cs b/OpenSim/Framework/AvatarWearable.cs index 8e27596..e7615f2 100644 --- a/OpenSim/Framework/AvatarWearable.cs +++ b/OpenSim/Framework/AvatarWearable.cs @@ -65,7 +65,9 @@ namespace OpenSim.Framework public static readonly int ALPHA = 13; public static readonly int TATTOO = 14; - public static readonly int MAX_WEARABLES = 15; + public static readonly int LEGACY_VERSION_MAX_WEARABLES = 15; + public static readonly int PHYSICS = 15; + public static readonly int MAX_WEARABLES = 16; public static readonly UUID DEFAULT_BODY_ITEM = new UUID("66c41e39-38f9-f75a-024e-585989bfaba9"); public static readonly UUID DEFAULT_BODY_ASSET = new UUID("66c41e39-38f9-f75a-024e-585989bfab73"); diff --git a/OpenSim/Framework/BasicDOSProtector.cs b/OpenSim/Framework/BasicDOSProtector.cs new file mode 100644 index 0000000..89bfa94 --- /dev/null +++ b/OpenSim/Framework/BasicDOSProtector.cs @@ -0,0 +1,275 @@ +/* + * 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 OpenSimulator 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.Reflection; +using log4net; + +namespace OpenSim.Framework +{ + + public class BasicDOSProtector + { + public enum ThrottleAction + { + DoThrottledMethod, + DoThrow + } + private readonly CircularBuffer _generalRequestTimes; // General request checker + private readonly BasicDosProtectorOptions _options; + private readonly Dictionary> _deeperInspection; // per client request checker + private readonly Dictionary _tempBlocked; // blocked list + private readonly Dictionary _sessions; + private readonly System.Timers.Timer _forgetTimer; // Cleanup timer + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private readonly System.Threading.ReaderWriterLockSlim _blockLockSlim = new System.Threading.ReaderWriterLockSlim(); + private readonly System.Threading.ReaderWriterLockSlim _sessionLockSlim = new System.Threading.ReaderWriterLockSlim(); + public BasicDOSProtector(BasicDosProtectorOptions options) + { + _generalRequestTimes = new CircularBuffer(options.MaxRequestsInTimeframe + 1, true); + _generalRequestTimes.Put(0); + _options = options; + _deeperInspection = new Dictionary>(); + _tempBlocked = new Dictionary(); + _sessions = new Dictionary(); + _forgetTimer = new System.Timers.Timer(); + _forgetTimer.Elapsed += delegate + { + _forgetTimer.Enabled = false; + + List removes = new List(); + _blockLockSlim.EnterReadLock(); + foreach (string str in _tempBlocked.Keys) + { + if ( + Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), + _tempBlocked[str]) > 0) + removes.Add(str); + } + _blockLockSlim.ExitReadLock(); + lock (_deeperInspection) + { + _blockLockSlim.EnterWriteLock(); + for (int i = 0; i < removes.Count; i++) + { + _tempBlocked.Remove(removes[i]); + _deeperInspection.Remove(removes[i]); + _sessions.Remove(removes[i]); + } + _blockLockSlim.ExitWriteLock(); + } + foreach (string str in removes) + { + m_log.InfoFormat("[{0}] client: {1} is no longer blocked.", + _options.ReportingName, str); + } + _blockLockSlim.EnterReadLock(); + if (_tempBlocked.Count > 0) + _forgetTimer.Enabled = true; + _blockLockSlim.ExitReadLock(); + }; + + _forgetTimer.Interval = _options.ForgetTimeSpan.TotalMilliseconds; + } + + /// + /// Given a string Key, Returns if that context is blocked + /// + /// A Key identifying the context + /// bool Yes or No, True or False for blocked + public bool IsBlocked(string key) + { + bool ret = false; + _blockLockSlim.EnterReadLock(); + ret = _tempBlocked.ContainsKey(key); + _blockLockSlim.ExitReadLock(); + return ret; + } + + /// + /// Process the velocity of this context + /// + /// + /// + /// + public bool Process(string key, string endpoint) + { + if (_options.MaxRequestsInTimeframe < 1 || _options.RequestTimeSpan.TotalMilliseconds < 1) + return true; + + string clientstring = key; + + _blockLockSlim.EnterReadLock(); + if (_tempBlocked.ContainsKey(clientstring)) + { + _blockLockSlim.ExitReadLock(); + + if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod) + return false; + else + throw new System.Security.SecurityException("Throttled"); + } + + _blockLockSlim.ExitReadLock(); + + lock (_generalRequestTimes) + _generalRequestTimes.Put(Util.EnvironmentTickCount()); + + if (_options.MaxConcurrentSessions > 0) + { + int sessionscount = 0; + + _sessionLockSlim.EnterReadLock(); + if (_sessions.ContainsKey(key)) + sessionscount = _sessions[key]; + _sessionLockSlim.ExitReadLock(); + + if (sessionscount > _options.MaxConcurrentSessions) + { + // Add to blocking and cleanup methods + lock (_deeperInspection) + { + _blockLockSlim.EnterWriteLock(); + if (!_tempBlocked.ContainsKey(clientstring)) + { + _tempBlocked.Add(clientstring, + Util.EnvironmentTickCount() + + (int) _options.ForgetTimeSpan.TotalMilliseconds); + _forgetTimer.Enabled = true; + m_log.WarnFormat("[{0}]: client: {1} is blocked for {2} milliseconds based on concurrency, X-ForwardedForAllowed status is {3}, endpoint:{4}", _options.ReportingName, clientstring, _options.ForgetTimeSpan.TotalMilliseconds, _options.AllowXForwardedFor, endpoint); + + } + else + _tempBlocked[clientstring] = Util.EnvironmentTickCount() + + (int) _options.ForgetTimeSpan.TotalMilliseconds; + _blockLockSlim.ExitWriteLock(); + + } + + + } + else + ProcessConcurrency(key, endpoint); + } + if (_generalRequestTimes.Size == _generalRequestTimes.Capacity && + (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _generalRequestTimes.Get()) < + _options.RequestTimeSpan.TotalMilliseconds)) + { + //Trigger deeper inspection + if (DeeperInspection(key, endpoint)) + return true; + if (_options.ThrottledAction == ThrottleAction.DoThrottledMethod) + return false; + else + throw new System.Security.SecurityException("Throttled"); + } + return true; + } + private void ProcessConcurrency(string key, string endpoint) + { + _sessionLockSlim.EnterWriteLock(); + if (_sessions.ContainsKey(key)) + _sessions[key] = _sessions[key] + 1; + else + _sessions.Add(key,1); + _sessionLockSlim.ExitWriteLock(); + } + public void ProcessEnd(string key, string endpoint) + { + _sessionLockSlim.EnterWriteLock(); + if (_sessions.ContainsKey(key)) + { + _sessions[key]--; + if (_sessions[key] <= 0) + _sessions.Remove(key); + } + else + _sessions.Add(key, 1); + + _sessionLockSlim.ExitWriteLock(); + } + + /// + /// At this point, the rate limiting code needs to track 'per user' velocity. + /// + /// Context Key, string representing a rate limiting context + /// + /// + private bool DeeperInspection(string key, string endpoint) + { + lock (_deeperInspection) + { + string clientstring = key; + + + if (_deeperInspection.ContainsKey(clientstring)) + { + _deeperInspection[clientstring].Put(Util.EnvironmentTickCount()); + if (_deeperInspection[clientstring].Size == _deeperInspection[clientstring].Capacity && + (Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(), _deeperInspection[clientstring].Get()) < + _options.RequestTimeSpan.TotalMilliseconds)) + { + //Looks like we're over the limit + _blockLockSlim.EnterWriteLock(); + if (!_tempBlocked.ContainsKey(clientstring)) + _tempBlocked.Add(clientstring, Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds); + else + _tempBlocked[clientstring] = Util.EnvironmentTickCount() + (int)_options.ForgetTimeSpan.TotalMilliseconds; + _blockLockSlim.ExitWriteLock(); + + m_log.WarnFormat("[{0}]: client: {1} is blocked for {2} milliseconds, X-ForwardedForAllowed status is {3}, endpoint:{4}", _options.ReportingName, clientstring, _options.ForgetTimeSpan.TotalMilliseconds, _options.AllowXForwardedFor, endpoint); + + return false; + } + //else + // return true; + } + else + { + _deeperInspection.Add(clientstring, new CircularBuffer(_options.MaxRequestsInTimeframe + 1, true)); + _deeperInspection[clientstring].Put(Util.EnvironmentTickCount()); + _forgetTimer.Enabled = true; + } + + } + return true; + } + + } + + + public class BasicDosProtectorOptions + { + public int MaxRequestsInTimeframe; + public TimeSpan RequestTimeSpan; + public TimeSpan ForgetTimeSpan; + public bool AllowXForwardedFor; + public string ReportingName = "BASICDOSPROTECTOR"; + public BasicDOSProtector.ThrottleAction ThrottledAction = BasicDOSProtector.ThrottleAction.DoThrottledMethod; + public int MaxConcurrentSessions; + } +} diff --git a/OpenSim/Framework/BlockingQueue.cs b/OpenSim/Framework/BlockingQueue.cs index 3658161..d11ad11 100644 --- a/OpenSim/Framework/BlockingQueue.cs +++ b/OpenSim/Framework/BlockingQueue.cs @@ -58,7 +58,7 @@ namespace OpenSim.Framework { lock (m_queueSync) { - if (m_queue.Count < 1 && m_pqueue.Count < 1) + while (m_queue.Count < 1 && m_pqueue.Count < 1) { Monitor.Wait(m_queueSync); } @@ -76,9 +76,10 @@ namespace OpenSim.Framework { lock (m_queueSync) { - if (m_queue.Count < 1 && m_pqueue.Count < 1) + bool success = true; + while (m_queue.Count < 1 && m_pqueue.Count < 1 && success) { - Monitor.Wait(m_queueSync, msTimeout); + success = Monitor.Wait(m_queueSync, msTimeout); } if (m_pqueue.Count > 0) @@ -89,28 +90,47 @@ namespace OpenSim.Framework } } + /// + /// Indicate whether this queue contains the given item. + /// + /// + /// This method is not thread-safe. Do not rely on the result without consistent external locking. + /// public bool Contains(T item) { lock (m_queueSync) { + if (m_queue.Count < 1 && m_pqueue.Count < 1) + return false; + if (m_pqueue.Contains(item)) return true; return m_queue.Contains(item); } } + /// + /// Return a count of the number of requests on this queue. + /// public int Count() { lock (m_queueSync) - { - return m_queue.Count+m_pqueue.Count; - } + return m_queue.Count + m_pqueue.Count; } + /// + /// Return the array of items on this queue. + /// + /// + /// This method is not thread-safe. Do not rely on the result without consistent external locking. + /// public T[] GetQueueArray() { lock (m_queueSync) { + if (m_queue.Count < 1 && m_pqueue.Count < 1) + return new T[0]; + return m_queue.ToArray(); } } diff --git a/OpenSim/Framework/CachedTextureEventArg.cs b/OpenSim/Framework/CachedTextureEventArg.cs new file mode 100644 index 0000000..239fc56 --- /dev/null +++ b/OpenSim/Framework/CachedTextureEventArg.cs @@ -0,0 +1,46 @@ +/* + * 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 OpenSimulator 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.Text; +using OpenMetaverse; + +namespace OpenSim.Framework +{ + public class CachedTextureRequestArg + { + public int BakedTextureIndex; + public UUID WearableHashID; + } + + public class CachedTextureResponseArg + { + public int BakedTextureIndex; + public UUID BakedTextureID; + public String HostName; + } +} diff --git a/OpenSim/Framework/ChildAgentDataUpdate.cs b/OpenSim/Framework/ChildAgentDataUpdate.cs index dfe60aa..1504f21 100644 --- a/OpenSim/Framework/ChildAgentDataUpdate.cs +++ b/OpenSim/Framework/ChildAgentDataUpdate.cs @@ -171,9 +171,10 @@ namespace OpenSim.Framework /// Soon to be decommissioned /// /// - public void CopyFrom(ChildAgentDataUpdate cAgent) + public void CopyFrom(ChildAgentDataUpdate cAgent, UUID sid) { AgentID = new UUID(cAgent.AgentID); + SessionID = sid; // next: ??? Size = new Vector3(); @@ -229,12 +230,14 @@ namespace OpenSim.Framework public class ControllerData { + public UUID ObjectID; public UUID ItemID; public uint IgnoreControls; public uint EventControls; - public ControllerData(UUID item, uint ignore, uint ev) + public ControllerData(UUID obj, UUID item, uint ignore, uint ev) { + ObjectID = obj; ItemID = item; IgnoreControls = ignore; EventControls = ev; @@ -248,6 +251,7 @@ namespace OpenSim.Framework public OSDMap PackUpdateMessage() { OSDMap controldata = new OSDMap(); + controldata["object"] = OSD.FromUUID(ObjectID); controldata["item"] = OSD.FromUUID(ItemID); controldata["ignore"] = OSD.FromInteger(IgnoreControls); controldata["event"] = OSD.FromInteger(EventControls); @@ -258,6 +262,8 @@ namespace OpenSim.Framework public void UnpackUpdateMessage(OSDMap args) { + if (args["object"] != null) + ObjectID = args["object"].AsUUID(); if (args["item"] != null) ItemID = args["item"].AsUUID(); if (args["ignore"] != null) @@ -286,7 +292,13 @@ namespace OpenSim.Framework public Vector3 AtAxis; public Vector3 LeftAxis; public Vector3 UpAxis; - public bool ChangedGrid; + + /// + /// Signal on a V2 teleport that Scene.IncomingChildAgentDataUpdate(AgentData ad) should wait for the + /// scene presence to become root (triggered when the viewer sends a CompleteAgentMovement UDP packet after + /// establishing the connection triggered by it's receipt of a TeleportFinish EQ message). + /// + public bool SenderWantsToWaitForRoot; public float Far; public float Aspect; @@ -310,6 +322,8 @@ namespace OpenSim.Framework public Animation AnimState = null; public UUID GranterID; + public UUID ParentPart; + public Vector3 SitOffset; // Appearance public AvatarAppearance Appearance; @@ -355,8 +369,9 @@ namespace OpenSim.Framework args["left_axis"] = OSD.FromString(LeftAxis.ToString()); args["up_axis"] = OSD.FromString(UpAxis.ToString()); - - args["changed_grid"] = OSD.FromBoolean(ChangedGrid); + //backwards compatibility + args["changed_grid"] = OSD.FromBoolean(SenderWantsToWaitForRoot); + args["wait_for_root"] = OSD.FromBoolean(SenderWantsToWaitForRoot); args["far"] = OSD.FromReal(Far); args["aspect"] = OSD.FromReal(Aspect); @@ -428,9 +443,18 @@ namespace OpenSim.Framework // We might not pass this in all cases... if ((Appearance.Wearables != null) && (Appearance.Wearables.Length > 0)) { - OSDArray wears = new OSDArray(Appearance.Wearables.Length); - foreach (AvatarWearable awear in Appearance.Wearables) - wears.Add(awear.Pack()); + int wearsCount; + if(Appearance.PackLegacyWearables) + wearsCount = AvatarWearable.LEGACY_VERSION_MAX_WEARABLES; + else + wearsCount = AvatarWearable.MAX_WEARABLES; + + if(wearsCount > Appearance.Wearables.Length) + wearsCount = Appearance.Wearables.Length; + + OSDArray wears = new OSDArray(wearsCount); + for(int i = 0; i < wearsCount ; i++) + wears.Add(Appearance.Wearables[i].Pack()); args["wearables"] = wears; } @@ -480,6 +504,10 @@ namespace OpenSim.Framework } args["attach_objects"] = attObjs; } + + args["parent_part"] = OSD.FromUUID(ParentPart); + args["sit_offset"] = OSD.FromString(SitOffset.ToString()); + return args; } @@ -525,8 +553,8 @@ namespace OpenSim.Framework if (args["up_axis"] != null) Vector3.TryParse(args["up_axis"].AsString(), out AtAxis); - if (args["changed_grid"] != null) - ChangedGrid = args["changed_grid"].AsBoolean(); + if (args.ContainsKey("wait_for_root") && args["wait_for_root"] != null) + SenderWantsToWaitForRoot = args["wait_for_root"].AsBoolean(); if (args["far"] != null) Far = (float)(args["far"].AsReal()); @@ -646,7 +674,12 @@ namespace OpenSim.Framework if ((args["wearables"] != null) && (args["wearables"]).Type == OSDType.Array) { OSDArray wears = (OSDArray)(args["wearables"]); - for (int i = 0; i < wears.Count / 2; i++) + + int count = wears.Count; + if (count > AvatarWearable.MAX_WEARABLES) + count = AvatarWearable.MAX_WEARABLES; + + for (int i = 0; i < count / 2; i++) { AvatarWearable awear = new AvatarWearable((OSDArray)wears[i]); Appearance.SetWearable(i,awear); @@ -711,6 +744,11 @@ namespace OpenSim.Framework } } } + + if (args["parent_part"] != null) + ParentPart = args["parent_part"].AsUUID(); + if (args["sit_offset"] != null) + Vector3.TryParse(args["sit_offset"].AsString(), out SitOffset); } public AgentData() diff --git a/OpenSim/Framework/CircularBuffer.cs b/OpenSim/Framework/CircularBuffer.cs new file mode 100644 index 0000000..e919337 --- /dev/null +++ b/OpenSim/Framework/CircularBuffer.cs @@ -0,0 +1,312 @@ +/* +Copyright (c) 2012, Alex Regueiro +All rights reserved. +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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR 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; +using System.Collections.Generic; +using System.Threading; + +namespace OpenSim.Framework +{ + public class CircularBuffer : ICollection, IEnumerable, ICollection, IEnumerable + { + private int capacity; + private int size; + private int head; + private int tail; + private T[] buffer; + + [NonSerialized()] + private object syncRoot; + + public CircularBuffer(int capacity) + : this(capacity, false) + { + } + + public CircularBuffer(int capacity, bool allowOverflow) + { + if (capacity < 0) + throw new ArgumentException("Needs to have at least 1","capacity"); + + this.capacity = capacity; + size = 0; + head = 0; + tail = 0; + buffer = new T[capacity]; + AllowOverflow = allowOverflow; + } + + public bool AllowOverflow + { + get; + set; + } + + public int Capacity + { + get { return capacity; } + set + { + if (value == capacity) + return; + + if (value < size) + throw new ArgumentOutOfRangeException("value","Capacity is too small."); + + var dst = new T[value]; + if (size > 0) + CopyTo(dst); + buffer = dst; + + capacity = value; + } + } + + public int Size + { + get { return size; } + } + + public bool Contains(T item) + { + int bufferIndex = head; + var comparer = EqualityComparer.Default; + for (int i = 0; i < size; i++, bufferIndex++) + { + if (bufferIndex == capacity) + bufferIndex = 0; + + if (item == null && buffer[bufferIndex] == null) + return true; + else if ((buffer[bufferIndex] != null) && + comparer.Equals(buffer[bufferIndex], item)) + return true; + } + + return false; + } + + public void Clear() + { + size = 0; + head = 0; + tail = 0; + } + + public int Put(T[] src) + { + return Put(src, 0, src.Length); + } + + public int Put(T[] src, int offset, int count) + { + if (!AllowOverflow && count > capacity - size) + throw new InvalidOperationException("Buffer Overflow"); + + int srcIndex = offset; + for (int i = 0; i < count; i++, tail++, srcIndex++) + { + if (tail == capacity) + tail = 0; + buffer[tail] = src[srcIndex]; + } + size = Math.Min(size + count, capacity); + return count; + } + + public void Put(T item) + { + if (!AllowOverflow && size == capacity) + throw new InvalidOperationException("Buffer Overflow"); + + buffer[tail] = item; + if (++tail == capacity) + tail = 0; + size++; + } + + public void Skip(int count) + { + head += count; + if (head >= capacity) + head -= capacity; + } + + public T[] Get(int count) + { + var dst = new T[count]; + Get(dst); + return dst; + } + + public int Get(T[] dst) + { + return Get(dst, 0, dst.Length); + } + + public int Get(T[] dst, int offset, int count) + { + int realCount = Math.Min(count, size); + int dstIndex = offset; + for (int i = 0; i < realCount; i++, head++, dstIndex++) + { + if (head == capacity) + head = 0; + dst[dstIndex] = buffer[head]; + } + size -= realCount; + return realCount; + } + + public T Get() + { + if (size == 0) + throw new InvalidOperationException("Buffer Empty"); + + var item = buffer[head]; + if (++head == capacity) + head = 0; + size--; + return item; + } + + public void CopyTo(T[] array) + { + CopyTo(array, 0); + } + + public void CopyTo(T[] array, int arrayIndex) + { + CopyTo(0, array, arrayIndex, size); + } + + public void CopyTo(int index, T[] array, int arrayIndex, int count) + { + if (count > size) + throw new ArgumentOutOfRangeException("count", "Count Too Large"); + + int bufferIndex = head; + for (int i = 0; i < count; i++, bufferIndex++, arrayIndex++) + { + if (bufferIndex == capacity) + bufferIndex = 0; + array[arrayIndex] = buffer[bufferIndex]; + } + } + + public IEnumerator GetEnumerator() + { + int bufferIndex = head; + for (int i = 0; i < size; i++, bufferIndex++) + { + if (bufferIndex == capacity) + bufferIndex = 0; + + yield return buffer[bufferIndex]; + } + } + + public T[] GetBuffer() + { + return buffer; + } + + public T[] ToArray() + { + var dst = new T[size]; + CopyTo(dst); + return dst; + } + + #region ICollection Members + + int ICollection.Count + { + get { return Size; } + } + + bool ICollection.IsReadOnly + { + get { return false; } + } + + void ICollection.Add(T item) + { + Put(item); + } + + bool ICollection.Remove(T item) + { + if (size == 0) + return false; + + Get(); + return true; + } + + #endregion + + #region IEnumerable Members + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + + #region ICollection Members + + int ICollection.Count + { + get { return Size; } + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get + { + if (syncRoot == null) + Interlocked.CompareExchange(ref syncRoot, new object(), null); + return syncRoot; + } + } + + void ICollection.CopyTo(Array array, int arrayIndex) + { + CopyTo((T[])array, arrayIndex); + } + + #endregion + + #region IEnumerable Members + + IEnumerator IEnumerable.GetEnumerator() + { + return (IEnumerator)GetEnumerator(); + } + + #endregion + } +} diff --git a/OpenSim/Framework/ClientInfo.cs b/OpenSim/Framework/ClientInfo.cs index 62acb70..98e4465 100644 --- a/OpenSim/Framework/ClientInfo.cs +++ b/OpenSim/Framework/ClientInfo.cs @@ -33,12 +33,13 @@ namespace OpenSim.Framework { public class ClientInfo { - public AgentCircuitData agentcircuit; + public readonly DateTime StartedTime = DateTime.Now; + public AgentCircuitData agentcircuit = null; public Dictionary needAck; - public List out_packets; - public Dictionary pendingAcks; + public List out_packets = new List(); + public Dictionary pendingAcks = new Dictionary(); public EndPoint proxyEP; public uint sequence; @@ -53,5 +54,14 @@ namespace OpenSim.Framework public int assetThrottle; public int textureThrottle; public int totalThrottle; + + // Used by adaptive only + public int targetThrottle; + + public int maxThrottle; + + public Dictionary SyncRequests = new Dictionary(); + public Dictionary AsyncRequests = new Dictionary(); + public Dictionary GenericRequests = new Dictionary(); } } diff --git a/OpenSim/Framework/Communications/GenericAsyncResult.cs b/OpenSim/Framework/Communications/GenericAsyncResult.cs deleted file mode 100644 index 8e3f62b..0000000 --- a/OpenSim/Framework/Communications/GenericAsyncResult.cs +++ /dev/null @@ -1,185 +0,0 @@ -/* - * 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 OpenSimulator 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.Threading; - -namespace OpenSim.Framework.Communications -{ - internal class SimpleAsyncResult : IAsyncResult - { - private readonly AsyncCallback m_callback; - - /// - /// Is process completed? - /// - /// Should really be boolean, but VolatileRead has no boolean method - private byte m_completed; - - /// - /// Did process complete synchronously? - /// - /// I have a hard time imagining a scenario where this is the case, again, same issue about - /// booleans and VolatileRead as m_completed - /// - private byte m_completedSynchronously; - - private readonly object m_asyncState; - private ManualResetEvent m_waitHandle; - private Exception m_exception; - - internal SimpleAsyncResult(AsyncCallback cb, object state) - { - m_callback = cb; - m_asyncState = state; - m_completed = 0; - m_completedSynchronously = 1; - } - - #region IAsyncResult Members - - public object AsyncState - { - get { return m_asyncState; } - } - - public WaitHandle AsyncWaitHandle - { - get - { - if (m_waitHandle == null) - { - bool done = IsCompleted; - ManualResetEvent mre = new ManualResetEvent(done); - if (Interlocked.CompareExchange(ref m_waitHandle, mre, null) != null) - { - mre.Close(); - } - else - { - if (!done && IsCompleted) - { - m_waitHandle.Set(); - } - } - } - - return m_waitHandle; - } - } - - - public bool CompletedSynchronously - { - get { return Thread.VolatileRead(ref m_completedSynchronously) == 1; } - } - - - public bool IsCompleted - { - get { return Thread.VolatileRead(ref m_completed) == 1; } - } - - #endregion - - #region class Methods - - internal void SetAsCompleted(bool completedSynchronously) - { - m_completed = 1; - if (completedSynchronously) - m_completedSynchronously = 1; - else - m_completedSynchronously = 0; - - SignalCompletion(); - } - - internal void HandleException(Exception e, bool completedSynchronously) - { - m_completed = 1; - if (completedSynchronously) - m_completedSynchronously = 1; - else - m_completedSynchronously = 0; - m_exception = e; - - SignalCompletion(); - } - - private void SignalCompletion() - { - if (m_waitHandle != null) m_waitHandle.Set(); - - if (m_callback != null) m_callback(this); - } - - public void EndInvoke() - { - // This method assumes that only 1 thread calls EndInvoke - if (!IsCompleted) - { - // If the operation isn't done, wait for it - AsyncWaitHandle.WaitOne(); - AsyncWaitHandle.Close(); - m_waitHandle.Close(); - m_waitHandle = null; // Allow early GC - } - - // Operation is done: if an exception occured, throw it - if (m_exception != null) throw m_exception; - } - - #endregion - } - - internal class AsyncResult : SimpleAsyncResult - { - private T m_result = default(T); - - public AsyncResult(AsyncCallback asyncCallback, Object state) : - base(asyncCallback, state) - { - } - - public void SetAsCompleted(T result, bool completedSynchronously) - { - // Save the asynchronous operation's result - m_result = result; - - // Tell the base class that the operation completed - // sucessfully (no exception) - base.SetAsCompleted(completedSynchronously); - } - - public new T EndInvoke() - { - base.EndInvoke(); - return m_result; - } - } -} \ No newline at end of file diff --git a/OpenSim/Framework/Communications/IUserService.cs b/OpenSim/Framework/Communications/IUserService.cs deleted file mode 100644 index dfa059d..0000000 --- a/OpenSim/Framework/Communications/IUserService.cs +++ /dev/null @@ -1,157 +0,0 @@ -/* - * 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 OpenSimulator 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 OpenMetaverse; -using OpenSim.Services.Interfaces; - -namespace OpenSim.Framework.Communications -{ - public interface IUserService - { - /// - /// Add a temporary user profile. - /// - /// A temporary user profile is one that should exist only for the lifetime of the process. - /// - void AddTemporaryUserProfile(UserProfileData userProfile); - - /// - /// Loads a user profile by name - /// - /// First name - /// Last name - /// A user profile. Returns null if no profile is found - UserProfileData GetUserProfile(string firstName, string lastName); - - /// - /// Loads a user profile from a database by UUID - /// - /// The target UUID - /// A user profile. Returns null if no user profile is found. - UserProfileData GetUserProfile(UUID userId); - - UserProfileData GetUserProfile(Uri uri); - - Uri GetUserUri(UserProfileData userProfile); - - UserAgentData GetAgentByUUID(UUID userId); - - void ClearUserAgent(UUID avatarID); - List GenerateAgentPickerRequestResponse(UUID QueryID, string Query); - - UserProfileData SetupMasterUser(string firstName, string lastName); - UserProfileData SetupMasterUser(string firstName, string lastName, string password); - UserProfileData SetupMasterUser(UUID userId); - - /// - /// Update the user's profile. - /// - /// UserProfileData object with updated data. Should be obtained - /// via a call to GetUserProfile(). - /// true if the update could be applied, false if it could not be applied. - bool UpdateUserProfile(UserProfileData data); - - /// - /// Adds a new friend to the database for XUser - /// - /// The agent that who's friends list is being added to - /// The agent that being added to the friends list of the friends list owner - /// A uint bit vector for set perms that the friend being added has; 0 = none, 1=This friend can see when they sign on, 2 = map, 4 edit objects - void AddNewUserFriend(UUID friendlistowner, UUID friend, uint perms); - - /// - /// Delete friend on friendlistowner's friendlist. - /// - /// The agent that who's friends list is being updated - /// The Ex-friend agent - void RemoveUserFriend(UUID friendlistowner, UUID friend); - - /// - /// Update permissions for friend on friendlistowner's friendlist. - /// - /// The agent that who's friends list is being updated - /// The agent that is getting or loosing permissions - /// A uint bit vector for set perms that the friend being added has; 0 = none, 1=This friend can see when they sign on, 2 = map, 4 edit objects - void UpdateUserFriendPerms(UUID friendlistowner, UUID friend, uint perms); - - /// - /// Logs off a user on the user server - /// - /// UUID of the user - /// UUID of the Region - /// regionhandle - /// final position - /// final lookat - void LogOffUser(UUID userid, UUID regionid, ulong regionhandle, Vector3 position, Vector3 lookat); - - /// - /// Logs off a user on the user server (deprecated as of 2008-08-27) - /// - /// UUID of the user - /// UUID of the Region - /// regionhandle - /// final position x - /// final position y - /// final position z - void LogOffUser(UUID userid, UUID regionid, ulong regionhandle, float posx, float posy, float posz); - - /// - /// Returns a list of FriendsListItems that describe the friends and permissions in the friend relationship - /// for UUID friendslistowner - /// - /// - /// The agent for whom we're retreiving the friends Data. - /// - /// A List of FriendListItems that contains info about the user's friends. - /// Always returns a list even if the user has no friends - /// - List GetUserFriendList(UUID friendlistowner); - - // This probably shouldn't be here, it belongs to IAuthentication - // But since Scenes only have IUserService references, I'm placing it here for now. - bool VerifySession(UUID userID, UUID sessionID); - - /// - /// Authenticate a user by their password. - /// - /// - /// This is used by callers outside the login process that want to - /// verify a user who has given their password. - /// - /// This should probably also be in IAuthentication but is here for the same reasons as VerifySession() is - /// - /// - /// - /// - bool AuthenticateUserByPassword(UUID userID, string password); - - // Temporary Hack until we move everything to the new service model - void SetInventoryService(IInventoryService invService); - } -} diff --git a/OpenSim/Framework/Communications/Limit/IRequestLimitStrategy.cs b/OpenSim/Framework/Communications/Limit/IRequestLimitStrategy.cs deleted file mode 100644 index 070d106..0000000 --- a/OpenSim/Framework/Communications/Limit/IRequestLimitStrategy.cs +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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 OpenSimulator 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. - */ - -namespace OpenSim.Framework.Communications.Limit -{ - /// - /// Interface for strategies that can limit requests from the client. Currently only used in the - /// texture modules to deal with repeated requests for certain textures. However, limiting strategies - /// could be used with other requests. - /// - public interface IRequestLimitStrategy - { - /// - /// Should the request be allowed? If the id is not monitored, then the request is always allowed. - /// Otherwise, the strategy criteria will be applied. - /// - /// - /// - bool AllowRequest(TId id); - - /// - /// Has the request been refused just once? - /// - /// False if the request has not yet been refused, or if the request has been refused more - /// than once. - bool IsFirstRefusal(TId id); - - /// - /// Start monitoring for future AllowRequest calls. If the id is already monitored, then monitoring - /// continues. - /// - /// - void MonitorRequests(TId id); - - /// - /// Is the id being monitored? - /// - /// - /// - bool IsMonitoringRequests(TId id); - } -} diff --git a/OpenSim/Framework/Communications/Limit/NullLimitStrategy.cs b/OpenSim/Framework/Communications/Limit/NullLimitStrategy.cs deleted file mode 100644 index 7672653..0000000 --- a/OpenSim/Framework/Communications/Limit/NullLimitStrategy.cs +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 OpenSimulator 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. - */ - -namespace OpenSim.Framework.Communications.Limit -{ - /// - /// Strategy which polices no limits - /// - public class NullLimitStrategy : IRequestLimitStrategy - { - public bool AllowRequest(TId id) { return true; } - public bool IsFirstRefusal(TId id) { return false; } - public void MonitorRequests(TId id) { /* intentionally blank */ } - public bool IsMonitoringRequests(TId id) { return false; } - } -} diff --git a/OpenSim/Framework/Communications/Limit/RepeatLimitStrategy.cs b/OpenSim/Framework/Communications/Limit/RepeatLimitStrategy.cs deleted file mode 100644 index 44dd592..0000000 --- a/OpenSim/Framework/Communications/Limit/RepeatLimitStrategy.cs +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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 OpenSimulator 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.Collections.Generic; - -namespace OpenSim.Framework.Communications.Limit -{ - /// - /// Limit requests by discarding them after they've been repeated a certain number of times. - /// - public class RepeatLimitStrategy : IRequestLimitStrategy - { - /// - /// Record each asset request that we're notified about. - /// - private readonly Dictionary requestCounts = new Dictionary(); - - /// - /// The maximum number of requests that can be made before we drop subsequent requests. - /// - private readonly int m_maxRequests; - public int MaxRequests - { - get { return m_maxRequests; } - } - - /// - /// The maximum number of requests that may be served before all further - /// requests are dropped. - public RepeatLimitStrategy(int maxRequests) - { - m_maxRequests = maxRequests; - } - - /// - /// - /// - public bool AllowRequest(TId id) - { - if (requestCounts.ContainsKey(id)) - { - requestCounts[id] += 1; - - if (requestCounts[id] > m_maxRequests) - { - return false; - } - } - - return true; - } - - /// - /// - /// - public bool IsFirstRefusal(TId id) - { - if (requestCounts.ContainsKey(id) && m_maxRequests + 1 == requestCounts[id]) - { - return true; - } - - return false; - } - - /// - /// - /// - public void MonitorRequests(TId id) - { - if (!IsMonitoringRequests(id)) - { - requestCounts.Add(id, 1); - } - } - - /// - /// - /// - public bool IsMonitoringRequests(TId id) - { - return requestCounts.ContainsKey(id); - } - } -} diff --git a/OpenSim/Framework/Communications/Limit/TimeLimitStrategy.cs b/OpenSim/Framework/Communications/Limit/TimeLimitStrategy.cs deleted file mode 100644 index 7ac8293..0000000 --- a/OpenSim/Framework/Communications/Limit/TimeLimitStrategy.cs +++ /dev/null @@ -1,140 +0,0 @@ -/* - * 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 OpenSimulator 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; - -namespace OpenSim.Framework.Communications.Limit -{ - /// - /// Limit requests by discarding repeat attempts that occur within a given time period - /// - /// XXX Don't use this for limiting texture downloading, at least not until we better handle multiple requests - /// for the same texture at different resolutions. - /// - public class TimeLimitStrategy : IRequestLimitStrategy - { - /// - /// Record the time at which an asset request occurs. - /// - private readonly Dictionary requests = new Dictionary(); - - /// - /// The minimum time period between which requests for the same data will be serviced. - /// - private readonly TimeSpan m_repeatPeriod; - public TimeSpan RepeatPeriod - { - get { return m_repeatPeriod; } - } - - /// - /// - public TimeLimitStrategy(TimeSpan repeatPeriod) - { - m_repeatPeriod = repeatPeriod; - } - - /// - /// - /// - public bool AllowRequest(TId id) - { - if (IsMonitoringRequests(id)) - { - DateTime now = DateTime.Now; - TimeSpan elapsed = now - requests[id].Time; - - if (elapsed < RepeatPeriod) - { - requests[id].Refusals += 1; - return false; - } - - requests[id].Time = now; - } - - return true; - } - - /// - /// - /// - public bool IsFirstRefusal(TId id) - { - if (IsMonitoringRequests(id)) - { - if (1 == requests[id].Refusals) - { - return true; - } - } - - return false; - } - - /// - /// - /// - public void MonitorRequests(TId id) - { - if (!IsMonitoringRequests(id)) - { - requests.Add(id, new Request(DateTime.Now)); - } - } - - /// - /// - /// - public bool IsMonitoringRequests(TId id) - { - return requests.ContainsKey(id); - } - } - - /// - /// Private request details. - /// - class Request - { - /// - /// Time of last request - /// - public DateTime Time; - - /// - /// Number of refusals associated with this request - /// - public int Refusals; - - public Request(DateTime time) - { - Time = time; - } - } -} diff --git a/OpenSim/Framework/Communications/Properties/AssemblyInfo.cs b/OpenSim/Framework/Communications/Properties/AssemblyInfo.cs deleted file mode 100644 index 6d1c03a..0000000 --- a/OpenSim/Framework/Communications/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 OpenSimulator 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.Reflection; -using System.Runtime.InteropServices; - -// General information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. - -[assembly : AssemblyTitle("OpenSim.Framework.Communications")] -[assembly : AssemblyDescription("")] -[assembly : AssemblyConfiguration("")] -[assembly : AssemblyCompany("http://opensimulator.org")] -[assembly : AssemblyProduct("OpenSim")] -[assembly : AssemblyCopyright("Copyright (c) OpenSimulator.org Developers")] -[assembly : AssemblyTrademark("")] -[assembly : AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. - -[assembly : ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM - -[assembly : Guid("13e7c396-78a9-4a5c-baf2-6f980ea75d95")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Revision and Build Numbers -// by using the '*' as shown below: - -[assembly : AssemblyVersion("0.7.5.*")] -[assembly : AssemblyFileVersion("0.6.5.0")] diff --git a/OpenSim/Framework/Communications/RestClient.cs b/OpenSim/Framework/Communications/RestClient.cs deleted file mode 100644 index 97b3b60..0000000 --- a/OpenSim/Framework/Communications/RestClient.cs +++ /dev/null @@ -1,438 +0,0 @@ -/* - * 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 OpenSimulator 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.Reflection; -using System.Text; -using System.Threading; -using System.Web; -using log4net; - -namespace OpenSim.Framework.Communications -{ - /// - /// Implementation of a generic REST client - /// - /// - /// This class is a generic implementation of a REST (Representational State Transfer) web service. This - /// class is designed to execute both synchronously and asynchronously. - /// - /// Internally the implementation works as a two stage asynchronous web-client. - /// When the request is initiated, RestClient will query asynchronously for for a web-response, - /// sleeping until the initial response is returned by the server. Once the initial response is retrieved - /// the second stage of asynchronous requests will be triggered, in an attempt to read of the response - /// object into a memorystream as a sequence of asynchronous reads. - /// - /// The asynchronisity of RestClient is designed to move as much processing into the back-ground, allowing - /// other threads to execute, while it waits for a response from the web-service. RestClient itself can be - /// invoked by the caller in either synchronous mode or asynchronous modes. - /// - public class RestClient - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - // private string realuri; - - #region member variables - - /// - /// The base Uri of the web-service e.g. http://www.google.com - /// - private string _url; - - /// - /// Path elements of the query - /// - private List _pathElements = new List(); - - /// - /// Parameter elements of the query, e.g. min=34 - /// - private Dictionary _parameterElements = new Dictionary(); - - /// - /// Request method. E.g. GET, POST, PUT or DELETE - /// - private string _method; - - /// - /// Temporary buffer used to store bytes temporarily as they come in from the server - /// - private byte[] _readbuf; - - /// - /// MemoryStream representing the resultiong resource - /// - private Stream _resource; - - /// - /// WebRequest object, held as a member variable - /// - private HttpWebRequest _request; - - /// - /// WebResponse object, held as a member variable, so we can close it - /// - private HttpWebResponse _response; - - /// - /// This flag will help block the main synchroneous method, in case we run in synchroneous mode - /// - //public static ManualResetEvent _allDone = new ManualResetEvent(false); - - /// - /// Default time out period - /// - //private const int DefaultTimeout = 10*1000; // 10 seconds timeout - - /// - /// Default Buffer size of a block requested from the web-server - /// - private const int BufferSize = 4096; // Read blocks of 4 KB. - - - /// - /// if an exception occours during async processing, we need to save it, so it can be - /// rethrown on the primary thread; - /// - private Exception _asyncException; - - #endregion member variables - - #region constructors - - /// - /// Instantiate a new RestClient - /// - /// Web-service to query, e.g. http://osgrid.org:8003 - public RestClient(string url) - { - _url = url; - _readbuf = new byte[BufferSize]; - _resource = new MemoryStream(); - _request = null; - _response = null; - _lock = new object(); - } - - private object _lock; - - #endregion constructors - - /// - /// Add a path element to the query, e.g. assets - /// - /// path entry - public void AddResourcePath(string element) - { - if (isSlashed(element)) - _pathElements.Add(element.Substring(0, element.Length - 1)); - else - _pathElements.Add(element); - } - - /// - /// Add a query parameter to the Url - /// - /// Name of the parameter, e.g. min - /// Value of the parameter, e.g. 42 - public void AddQueryParameter(string name, string value) - { - try - { - _parameterElements.Add(HttpUtility.UrlEncode(name), HttpUtility.UrlEncode(value)); - } - catch (ArgumentException) - { - m_log.Error("[REST]: Query parameter " + name + " is already added."); - } - catch (Exception e) - { - m_log.Error("[REST]: An exception was raised adding query parameter to dictionary. Exception: {0}",e); - } - } - - /// - /// Add a query parameter to the Url - /// - /// Name of the parameter, e.g. min - public void AddQueryParameter(string name) - { - try - { - _parameterElements.Add(HttpUtility.UrlEncode(name), null); - } - catch (ArgumentException) - { - m_log.Error("[REST]: Query parameter " + name + " is already added."); - } - catch (Exception e) - { - m_log.Error("[REST]: An exception was raised adding query parameter to dictionary. Exception: {0}",e); - } - } - - /// - /// Web-Request method, e.g. GET, PUT, POST, DELETE - /// - public string RequestMethod - { - get { return _method; } - set { _method = value; } - } - - /// - /// True if string contains a trailing slash '/' - /// - /// string to be examined - /// true if slash is present - private static bool isSlashed(string s) - { - return s.Substring(s.Length - 1, 1) == "/"; - } - - /// - /// Build a Uri based on the initial Url, path elements and parameters - /// - /// fully constructed Uri - private Uri buildUri() - { - StringBuilder sb = new StringBuilder(); - sb.Append(_url); - - foreach (string e in _pathElements) - { - sb.Append("/"); - sb.Append(e); - } - - bool firstElement = true; - foreach (KeyValuePair kv in _parameterElements) - { - if (firstElement) - { - sb.Append("?"); - firstElement = false; - } - else - sb.Append("&"); - - sb.Append(kv.Key); - if (!string.IsNullOrEmpty(kv.Value)) - { - sb.Append("="); - sb.Append(kv.Value); - } - } - // realuri = sb.ToString(); - //m_log.InfoFormat("[REST CLIENT]: RestURL: {0}", realuri); - return new Uri(sb.ToString()); - } - - #region Async communications with server - - /// - /// Async method, invoked when a block of data has been received from the service - /// - /// - private void StreamIsReadyDelegate(IAsyncResult ar) - { - try - { - Stream s = (Stream) ar.AsyncState; - int read = s.EndRead(ar); - - if (read > 0) - { - _resource.Write(_readbuf, 0, read); - // IAsyncResult asynchronousResult = - // s.BeginRead(_readbuf, 0, BufferSize, new AsyncCallback(StreamIsReadyDelegate), s); - s.BeginRead(_readbuf, 0, BufferSize, new AsyncCallback(StreamIsReadyDelegate), s); - - // TODO! Implement timeout, without killing the server - //ThreadPool.RegisterWaitForSingleObject(asynchronousResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true); - } - else - { - s.Close(); - //_allDone.Set(); - } - } - catch (Exception e) - { - //_allDone.Set(); - _asyncException = e; - } - } - - #endregion Async communications with server - - /// - /// Perform a synchronous request - /// - public Stream Request() - { - lock (_lock) - { - _request = (HttpWebRequest) WebRequest.Create(buildUri()); - _request.KeepAlive = false; - _request.ContentType = "application/xml"; - _request.Timeout = 200000; - _request.Method = RequestMethod; - _asyncException = null; - -// IAsyncResult responseAsyncResult = _request.BeginGetResponse(new AsyncCallback(ResponseIsReadyDelegate), _request); - try - { - _response = (HttpWebResponse) _request.GetResponse(); - } - catch (WebException e) - { - HttpWebResponse errorResponse = e.Response as HttpWebResponse; - if (null != errorResponse && HttpStatusCode.NotFound == errorResponse.StatusCode) - { - m_log.Warn("[REST CLIENT] Resource not found (404)"); - } - else - { - m_log.Error("[REST CLIENT] Error fetching resource from server " + _request.Address.ToString()); - m_log.Debug(e.ToString()); - } - - return null; - } - - Stream src = _response.GetResponseStream(); - int length = src.Read(_readbuf, 0, BufferSize); - while (length > 0) - { - _resource.Write(_readbuf, 0, length); - length = src.Read(_readbuf, 0, BufferSize); - } - - - // TODO! Implement timeout, without killing the server - // this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted - //ThreadPool.RegisterWaitForSingleObject(responseAsyncResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true); - -// _allDone.WaitOne(); - if (_response != null) - _response.Close(); - if (_asyncException != null) - throw _asyncException; - - if (_resource != null) - { - _resource.Flush(); - _resource.Seek(0, SeekOrigin.Begin); - } - - return _resource; - } - } - - public Stream Request(Stream src) - { - _request = (HttpWebRequest) WebRequest.Create(buildUri()); - _request.KeepAlive = false; - _request.ContentType = "application/xml"; - _request.Timeout = 900000; - _request.Method = RequestMethod; - _asyncException = null; - _request.ContentLength = src.Length; - - m_log.InfoFormat("[REST]: Request Length {0}", _request.ContentLength); - m_log.InfoFormat("[REST]: Sending Web Request {0}", buildUri()); - src.Seek(0, SeekOrigin.Begin); - m_log.Info("[REST]: Seek is ok"); - Stream dst = _request.GetRequestStream(); - m_log.Info("[REST]: GetRequestStream is ok"); - - byte[] buf = new byte[1024]; - int length = src.Read(buf, 0, 1024); - m_log.Info("[REST]: First Read is ok"); - while (length > 0) - { - dst.Write(buf, 0, length); - length = src.Read(buf, 0, 1024); - } - - _response = (HttpWebResponse) _request.GetResponse(); - -// IAsyncResult responseAsyncResult = _request.BeginGetResponse(new AsyncCallback(ResponseIsReadyDelegate), _request); - - // TODO! Implement timeout, without killing the server - // this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted - //ThreadPool.RegisterWaitForSingleObject(responseAsyncResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true); - - return null; - } - - #region Async Invocation - - public IAsyncResult BeginRequest(AsyncCallback callback, object state) - { - /// - /// In case, we are invoked asynchroneously this object will keep track of the state - /// - AsyncResult ar = new AsyncResult(callback, state); - Util.FireAndForget(RequestHelper, ar); - return ar; - } - - public Stream EndRequest(IAsyncResult asyncResult) - { - AsyncResult ar = (AsyncResult) asyncResult; - - // Wait for operation to complete, then return result or - // throw exception - return ar.EndInvoke(); - } - - private void RequestHelper(Object asyncResult) - { - // We know that it's really an AsyncResult object - AsyncResult ar = (AsyncResult) asyncResult; - try - { - // Perform the operation; if sucessful set the result - Stream s = Request(); - ar.SetAsCompleted(s, false); - } - catch (Exception e) - { - // If operation fails, set the exception - ar.HandleException(e, false); - } - } - - #endregion Async Invocation - } -} diff --git a/OpenSim/Framework/Communications/XMPP/XmppError.cs b/OpenSim/Framework/Communications/XMPP/XmppError.cs deleted file mode 100644 index 3d36e9c..0000000 --- a/OpenSim/Framework/Communications/XMPP/XmppError.cs +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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 OpenSimulator 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.Xml.Serialization; - -namespace OpenSim.Framework.Communications.XMPP -{ - [XmlRoot("error")] - public class XmppErrorStanza - { - public XmppErrorStanza() - { - } - } -} diff --git a/OpenSim/Framework/Communications/XMPP/XmppIqStanza.cs b/OpenSim/Framework/Communications/XMPP/XmppIqStanza.cs deleted file mode 100644 index 12263f4..0000000 --- a/OpenSim/Framework/Communications/XMPP/XmppIqStanza.cs +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 OpenSimulator 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.Xml.Serialization; - -namespace OpenSim.Framework.Communications.XMPP -{ - /// - /// An IQ needs to have one of the follow types set. - /// - public enum XmppIqType - { - [XmlEnum("set")] set, - [XmlEnum("get")] get, - [XmlEnum("result")] result, - [XmlEnum("error")] error, - } - - /// - /// XmppIqStanza needs to be subclassed as the query content is - /// specific to the query type. - /// - [XmlRoot("iq")] - public abstract class XmppIqStanza: XmppStanza - { - /// - /// IQ type: one of set, get, result, error - /// - [XmlAttribute("type")] - public XmppIqType Type; - - public XmppIqStanza(): base() - { - } - } -} diff --git a/OpenSim/Framework/Communications/XMPP/XmppMessageStanza.cs b/OpenSim/Framework/Communications/XMPP/XmppMessageStanza.cs deleted file mode 100644 index 1e8c33e..0000000 --- a/OpenSim/Framework/Communications/XMPP/XmppMessageStanza.cs +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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 OpenSimulator 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.Xml.Serialization; - -namespace OpenSim.Framework.Communications.XMPP -{ - /// - /// Message types. - /// - public enum XmppMessageType - { - [XmlEnum("chat")] chat, - [XmlEnum("error")] error, - [XmlEnum("groupchat")] groupchat, - [XmlEnum("headline")] headline, - [XmlEnum("normal")] normal, - } - - /// - /// Message body. - /// - public class XmppMessageBody - { - [XmlText] - public string Text; - - public XmppMessageBody() - { - } - - public XmppMessageBody(string message) - { - Text = message; - } - - new public string ToString() - { - return Text; - } - } - - [XmlRoot("message")] - public class XmppMessageStanza: XmppStanza - { - /// - /// IQ type: one of set, get, result, error - /// - [XmlAttribute("type")] - public XmppMessageType MessageType; - - // [XmlAttribute("error")] - // public XmppError Error; - - [XmlElement("body")] - public XmppMessageBody Body; - - public XmppMessageStanza() : base() - { - } - - public XmppMessageStanza(string fromJid, string toJid, XmppMessageType mType, string message) : - base(fromJid, toJid) - { - MessageType = mType; - Body = new XmppMessageBody(message); - } - } -} diff --git a/OpenSim/Framework/Communications/XMPP/XmppPresenceStanza.cs b/OpenSim/Framework/Communications/XMPP/XmppPresenceStanza.cs deleted file mode 100644 index 4d45ac0..0000000 --- a/OpenSim/Framework/Communications/XMPP/XmppPresenceStanza.cs +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 OpenSimulator 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.Xml.Serialization; - -namespace OpenSim.Framework.Communications.XMPP -{ - /// - /// Message types. - /// - public enum XmppPresenceType - { - [XmlEnum("unavailable")] unavailable, - [XmlEnum("subscribe")] subscribe, - [XmlEnum("subscribed")] subscribed, - [XmlEnum("unsubscribe")] unsubscribe, - [XmlEnum("unsubscribed")] unsubscribed, - [XmlEnum("probe")] probe, - [XmlEnum("error")] error, - } - - - [XmlRoot("message")] - public class XmppPresenceStanza: XmppStanza - { - /// - /// IQ type: one of set, get, result, error - /// - [XmlAttribute("type")] - public XmppPresenceType PresenceType; - - // [XmlAttribute("error")] - // public XmppError Error; - - public XmppPresenceStanza() : base() - { - } - - public XmppPresenceStanza(string fromJid, string toJid, XmppPresenceType pType) : - base(fromJid, toJid) - { - PresenceType = pType; - } - } -} diff --git a/OpenSim/Framework/Communications/XMPP/XmppSerializer.cs b/OpenSim/Framework/Communications/XMPP/XmppSerializer.cs deleted file mode 100644 index e37ef28..0000000 --- a/OpenSim/Framework/Communications/XMPP/XmppSerializer.cs +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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 OpenSimulator 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.Xml; -using System.Xml.Serialization; - -namespace OpenSim.Framework.Communications.XMPP -{ - public class XmppSerializer - { - // private static readonly ILog _log = - // LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - // need to do it this way, as XmlSerializer(type, extratypes) - // does not work on mono (at least). - private Dictionary _serializerForType = new Dictionary(); - private Dictionary _serializerForName = new Dictionary(); - private XmlSerializerNamespaces _xmlNs; - private string _defaultNS; - - public XmppSerializer(bool server) - { - _xmlNs = new XmlSerializerNamespaces(); - _xmlNs.Add(String.Empty, String.Empty); - if (server) - _defaultNS = "jabber:server"; - else - _defaultNS = "jabber:client"; - - // TODO: do this via reflection - _serializerForType[typeof(XmppMessageStanza)] = _serializerForName["message"] = - new XmlSerializer(typeof(XmppMessageStanza), _defaultNS); - } - - public void Serialize(XmlWriter xw, object o) - { - if (!_serializerForType.ContainsKey(o.GetType())) - throw new ArgumentException(String.Format("no serializer available for type {0}", o.GetType())); - - _serializerForType[o.GetType()].Serialize(xw, o, _xmlNs); - } - - public object Deserialize(XmlReader xr) - { - // position on next element - xr.Read(); - if (!_serializerForName.ContainsKey(xr.LocalName)) - throw new ArgumentException(String.Format("no serializer available for name {0}", xr.LocalName)); - - return _serializerForName[xr.LocalName].Deserialize(xr); - } - } -} diff --git a/OpenSim/Framework/Communications/XMPP/XmppStanza.cs b/OpenSim/Framework/Communications/XMPP/XmppStanza.cs deleted file mode 100644 index 5312a31..0000000 --- a/OpenSim/Framework/Communications/XMPP/XmppStanza.cs +++ /dev/null @@ -1,70 +0,0 @@ -/* - * 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 OpenSimulator 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.Xml.Serialization; - -namespace OpenSim.Framework.Communications.XMPP -{ - public abstract class XmppStanza - { - /// - /// counter used for generating ID - /// - [XmlIgnore] - private static ulong _ctr = 0; - - /// - /// recipient JID - /// - [XmlAttribute("to")] - public string ToJid; - - /// - /// sender JID - /// - [XmlAttribute("from")] - public string FromJid; - - /// - /// unique ID. - /// - [XmlAttribute("id")] - public string MessageId; - - public XmppStanza() - { - } - - public XmppStanza(string fromJid, string toJid) - { - ToJid = toJid; - FromJid = fromJid; - MessageId = String.Format("OpenSim_{0}{1}", DateTime.UtcNow.Ticks, _ctr++); - } - } -} diff --git a/OpenSim/Framework/Communications/XMPP/XmppWriter.cs b/OpenSim/Framework/Communications/XMPP/XmppWriter.cs deleted file mode 100644 index 415d808..0000000 --- a/OpenSim/Framework/Communications/XMPP/XmppWriter.cs +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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 OpenSimulator 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.IO; -using System.Text; -using System.Xml; -using IOStream = System.IO.Stream; - -namespace OpenSim.Framework.Communications.XMPP -{ - public class XMPPWriter: XmlTextWriter - { - public XMPPWriter(TextWriter textWriter) : base(textWriter) - { - } - - public XMPPWriter(IOStream stream) : this(stream, Util.UTF8) - { - } - - public XMPPWriter(IOStream stream, Encoding enc) : base(stream, enc) - { - } - - public override void WriteStartDocument() - { - } - - public override void WriteStartDocument(bool standalone) - { - } - } -} diff --git a/OpenSim/Framework/ConfigSettings.cs b/OpenSim/Framework/ConfigSettings.cs index 002a371..108a3e4 100644 --- a/OpenSim/Framework/ConfigSettings.cs +++ b/OpenSim/Framework/ConfigSettings.cs @@ -31,7 +31,6 @@ namespace OpenSim.Framework { public string PhysicsEngine { get; set; } public string MeshEngineName { get; set; } - public string StorageDll { get; set; } public string ClientstackDll { get; set; } public string LibrariesXMLFile { get; set; } diff --git a/OpenSim/Framework/Configuration/HTTP/HTTPConfiguration.cs b/OpenSim/Framework/Configuration/HTTP/HTTPConfiguration.cs deleted file mode 100644 index 3dce578..0000000 --- a/OpenSim/Framework/Configuration/HTTP/HTTPConfiguration.cs +++ /dev/null @@ -1,119 +0,0 @@ -/* - * 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 OpenSimulator 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.IO; -using System.Net; -using System.Reflection; -using System.Text; -using log4net; -using OpenSim.Framework.Configuration.XML; - -namespace OpenSim.Framework.Configuration.HTTP -{ - public class HTTPConfiguration : IGenericConfig - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - private RemoteConfigSettings remoteConfigSettings; - - private XmlConfiguration xmlConfig; - - private string configFileName = String.Empty; - - public HTTPConfiguration() - { - remoteConfigSettings = new RemoteConfigSettings("remoteconfig.xml"); - xmlConfig = new XmlConfiguration(); - } - - public void SetFileName(string fileName) - { - configFileName = fileName; - } - - public void LoadData() - { - try - { - StringBuilder sb = new StringBuilder(); - - byte[] buf = new byte[8192]; - HttpWebRequest request = - (HttpWebRequest) WebRequest.Create(remoteConfigSettings.baseConfigURL + configFileName); - HttpWebResponse response = (HttpWebResponse) request.GetResponse(); - - Stream resStream = response.GetResponseStream(); - - string tempString = null; - int count = 0; - - do - { - count = resStream.Read(buf, 0, buf.Length); - if (count != 0) - { - tempString = Util.UTF8.GetString(buf, 0, count); - sb.Append(tempString); - } - } while (count > 0); - LoadDataFromString(sb.ToString()); - } - catch (WebException) - { - m_log.Warn("Unable to connect to remote configuration file (" + - remoteConfigSettings.baseConfigURL + configFileName + - "). Creating local file instead."); - xmlConfig.SetFileName(configFileName); - xmlConfig.LoadData(); - } - } - - public void LoadDataFromString(string data) - { - xmlConfig.LoadDataFromString(data); - } - - public string GetAttribute(string attributeName) - { - return xmlConfig.GetAttribute(attributeName); - } - - public bool SetAttribute(string attributeName, string attributeValue) - { - return true; - } - - public void Commit() - { - } - - public void Close() - { - } - } -} diff --git a/OpenSim/Framework/Configuration/HTTP/Properties/AssemblyInfo.cs b/OpenSim/Framework/Configuration/HTTP/Properties/AssemblyInfo.cs deleted file mode 100644 index 0674656..0000000 --- a/OpenSim/Framework/Configuration/HTTP/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("OpenSim.Framework.Configuration.HTTP")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("http://opensimulator.org")] -[assembly: AssemblyProduct("OpenSim")] -[assembly: AssemblyCopyright("OpenSimulator develoeprs")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("cb78b672-d000-4f93-88f9-dae151cc0061")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -[assembly: AssemblyVersion("0.7.5.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/OpenSim/Framework/Configuration/HTTP/RemoteConfigSettings.cs b/OpenSim/Framework/Configuration/HTTP/RemoteConfigSettings.cs deleted file mode 100644 index 10bc88a..0000000 --- a/OpenSim/Framework/Configuration/HTTP/RemoteConfigSettings.cs +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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 OpenSimulator 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; - -namespace OpenSim.Framework.Configuration.HTTP -{ - public class RemoteConfigSettings - { - private ConfigurationMember configMember; - - public string baseConfigURL = String.Empty; - - public RemoteConfigSettings(string filename) - { - configMember = - new ConfigurationMember(filename, "REMOTE CONFIG SETTINGS", loadConfigurationOptions, - handleIncomingConfiguration,true); - configMember.forceConfigurationPluginLibrary("OpenSim.Framework.Configuration.XML.dll"); - configMember.performConfigurationRetrieve(); - } - - public void loadConfigurationOptions() - { - configMember.addConfigurationOption("base_config_url", - ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, - "URL Containing Configuration Files", "http://localhost/", false); - } - - public bool handleIncomingConfiguration(string configuration_key, object configuration_result) - { - if (configuration_key == "base_config_url") - { - baseConfigURL = (string) configuration_result; - } - return true; - } - } -} diff --git a/OpenSim/Framework/Configuration/XML/Properties/AssemblyInfo.cs b/OpenSim/Framework/Configuration/XML/Properties/AssemblyInfo.cs deleted file mode 100644 index 1095b23..0000000 --- a/OpenSim/Framework/Configuration/XML/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("OpenSim.Framework.Configuration.XML")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("http://opensimulator.org")] -[assembly: AssemblyProduct("OpenSim")] -[assembly: AssemblyCopyright("OpenSimulator developers")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("eeb880df-0112-4c3d-87ed-b2108d614c55")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -[assembly: AssemblyVersion("0.7.5.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/OpenSim/Framework/Configuration/XML/XmlConfiguration.cs b/OpenSim/Framework/Configuration/XML/XmlConfiguration.cs deleted file mode 100644 index 43162fc..0000000 --- a/OpenSim/Framework/Configuration/XML/XmlConfiguration.cs +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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 OpenSimulator 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.IO; -using System.Xml; - -namespace OpenSim.Framework.Configuration.XML -{ - public class XmlConfiguration : IGenericConfig - { - private XmlDocument doc; - private XmlNode rootNode; - private XmlNode configNode; - private string fileName; - private bool createdFile = false; - - public void SetFileName(string file) - { - fileName = file; - } - - private void LoadDataToClass() - { - rootNode = doc.SelectSingleNode("Root"); - if (null == rootNode) - throw new Exception("Error: Invalid .xml File. Missing "); - - configNode = rootNode.SelectSingleNode("Config"); - if (null == configNode) - throw new Exception("Error: Invalid .xml File. should contain a "); - } - - public void LoadData() - { - lock (this) - { - doc = new XmlDocument(); - if (File.Exists(fileName)) - { - XmlTextReader reader = new XmlTextReader(fileName); - reader.WhitespaceHandling = WhitespaceHandling.None; - doc.Load(reader); - reader.Close(); - } - else - { - createdFile = true; - rootNode = doc.CreateNode(XmlNodeType.Element, "Root", String.Empty); - doc.AppendChild(rootNode); - configNode = doc.CreateNode(XmlNodeType.Element, "Config", String.Empty); - rootNode.AppendChild(configNode); - } - - LoadDataToClass(); - - if (createdFile) - { - Commit(); - } - } - } - - public void LoadDataFromString(string data) - { - doc = new XmlDocument(); - doc.LoadXml(data); - - LoadDataToClass(); - } - - public string GetAttribute(string attributeName) - { - string result = null; - if (configNode.Attributes[attributeName] != null) - { - result = ((XmlAttribute) configNode.Attributes.GetNamedItem(attributeName)).Value; - } - return result; - } - - public bool SetAttribute(string attributeName, string attributeValue) - { - if (configNode.Attributes[attributeName] != null) - { - ((XmlAttribute) configNode.Attributes.GetNamedItem(attributeName)).Value = attributeValue; - } - else - { - XmlAttribute attri; - attri = doc.CreateAttribute(attributeName); - attri.Value = attributeValue; - configNode.Attributes.Append(attri); - } - return true; - } - - public void Commit() - { - if (fileName == null || fileName == String.Empty) - return; - - if (!Directory.Exists(Util.configDir())) - { - Directory.CreateDirectory(Util.configDir()); - } - doc.Save(fileName); - } - - public void Close() - { - configNode = null; - rootNode = null; - doc = null; - } - } -} diff --git a/OpenSim/Framework/ConfigurationMember.cs b/OpenSim/Framework/ConfigurationMember.cs deleted file mode 100644 index 7afa68a..0000000 --- a/OpenSim/Framework/ConfigurationMember.cs +++ /dev/null @@ -1,530 +0,0 @@ -/* - * 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 OpenSimulator 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.Globalization; -using System.Net; -using System.Reflection; -using System.Xml; -using log4net; -using OpenMetaverse; -//using OpenSim.Framework.Console; - -namespace OpenSim.Framework -{ - public class ConfigurationMember - { - #region Delegates - - public delegate bool ConfigurationOptionResult(string configuration_key, object configuration_result); - - public delegate void ConfigurationOptionsLoad(); - - #endregion - - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - private int cE = 0; - - private string configurationDescription = String.Empty; - private string configurationFilename = String.Empty; - private XmlNode configurationFromXMLNode = null; - private List configurationOptions = new List(); - private IGenericConfig configurationPlugin = null; - - /// - /// This is the default configuration DLL loaded - /// - private string configurationPluginFilename = "OpenSim.Framework.Configuration.XML.dll"; - - private ConfigurationOptionsLoad loadFunction; - private ConfigurationOptionResult resultFunction; - - private bool useConsoleToPromptOnError = true; - - public ConfigurationMember(string configuration_filename, string configuration_description, - ConfigurationOptionsLoad load_function, ConfigurationOptionResult result_function, bool use_console_to_prompt_on_error) - { - configurationFilename = configuration_filename; - configurationDescription = configuration_description; - loadFunction = load_function; - resultFunction = result_function; - useConsoleToPromptOnError = use_console_to_prompt_on_error; - } - - public ConfigurationMember(XmlNode configuration_xml, string configuration_description, - ConfigurationOptionsLoad load_function, ConfigurationOptionResult result_function, bool use_console_to_prompt_on_error) - { - configurationFilename = String.Empty; - configurationFromXMLNode = configuration_xml; - configurationDescription = configuration_description; - loadFunction = load_function; - resultFunction = result_function; - useConsoleToPromptOnError = use_console_to_prompt_on_error; - } - - public void setConfigurationFilename(string filename) - { - configurationFilename = filename; - } - - public void setConfigurationDescription(string desc) - { - configurationDescription = desc; - } - - public void setConfigurationResultFunction(ConfigurationOptionResult result) - { - resultFunction = result; - } - - public void forceConfigurationPluginLibrary(string dll_filename) - { - configurationPluginFilename = dll_filename; - } - - private void checkAndAddConfigOption(ConfigurationOption option) - { - if ((option.configurationKey != String.Empty && option.configurationQuestion != String.Empty) || - (option.configurationKey != String.Empty && option.configurationUseDefaultNoPrompt)) - { - if (!configurationOptions.Contains(option)) - { - configurationOptions.Add(option); - } - } - else - { - m_log.Info( - "Required fields for adding a configuration option is invalid. Will not add this option (" + - option.configurationKey + ")"); - } - } - - public void addConfigurationOption(string configuration_key, - ConfigurationOption.ConfigurationTypes configuration_type, - string configuration_question, string configuration_default, - bool use_default_no_prompt) - { - ConfigurationOption configOption = new ConfigurationOption(); - configOption.configurationKey = configuration_key; - configOption.configurationQuestion = configuration_question; - configOption.configurationDefault = configuration_default; - configOption.configurationType = configuration_type; - configOption.configurationUseDefaultNoPrompt = use_default_no_prompt; - configOption.shouldIBeAsked = null; //Assumes true, I can ask whenever - checkAndAddConfigOption(configOption); - } - - public void addConfigurationOption(string configuration_key, - ConfigurationOption.ConfigurationTypes configuration_type, - string configuration_question, string configuration_default, - bool use_default_no_prompt, - ConfigurationOption.ConfigurationOptionShouldBeAsked shouldIBeAskedDelegate) - { - ConfigurationOption configOption = new ConfigurationOption(); - configOption.configurationKey = configuration_key; - configOption.configurationQuestion = configuration_question; - configOption.configurationDefault = configuration_default; - configOption.configurationType = configuration_type; - configOption.configurationUseDefaultNoPrompt = use_default_no_prompt; - configOption.shouldIBeAsked = shouldIBeAskedDelegate; - checkAndAddConfigOption(configOption); - } - - // TEMP - REMOVE - public void performConfigurationRetrieve() - { - if (cE > 1) - m_log.Error("READING CONFIGURATION COUT: " + cE.ToString()); - - - configurationPlugin = LoadConfigDll(configurationPluginFilename); - configurationOptions.Clear(); - if (loadFunction == null) - { - m_log.Error("Load Function for '" + configurationDescription + - "' is null. Refusing to run configuration."); - return; - } - - if (resultFunction == null) - { - m_log.Error("Result Function for '" + configurationDescription + - "' is null. Refusing to run configuration."); - return; - } - - //m_log.Debug("[CONFIG]: Calling Configuration Load Function..."); - loadFunction(); - - if (configurationOptions.Count <= 0) - { - m_log.Error("[CONFIG]: No configuration options were specified for '" + configurationOptions + - "'. Refusing to continue configuration."); - return; - } - - bool useFile = true; - if (configurationPlugin == null) - { - m_log.Error("[CONFIG]: Configuration Plugin NOT LOADED!"); - return; - } - - if (configurationFilename.Trim() != String.Empty) - { - configurationPlugin.SetFileName(configurationFilename); - try - { - configurationPlugin.LoadData(); - useFile = true; - } - catch (XmlException e) - { - m_log.WarnFormat("[CONFIG] Not using {0}: {1}", - configurationFilename, - e.Message.ToString()); - //m_log.Error("Error loading " + configurationFilename + ": " + e.ToString()); - useFile = false; - } - } - else - { - if (configurationFromXMLNode != null) - { - m_log.Info("Loading from XML Node, will not save to the file"); - configurationPlugin.LoadDataFromString(configurationFromXMLNode.OuterXml); - } - - m_log.Info("XML Configuration Filename is not valid; will not save to the file."); - useFile = false; - } - - foreach (ConfigurationOption configOption in configurationOptions) - { - bool convertSuccess = false; - object return_result = null; - string errorMessage = String.Empty; - bool ignoreNextFromConfig = false; - while (convertSuccess == false) - { - string console_result = String.Empty; - string attribute = null; - if (useFile || configurationFromXMLNode != null) - { - if (!ignoreNextFromConfig) - { - attribute = configurationPlugin.GetAttribute(configOption.configurationKey); - } - else - { - ignoreNextFromConfig = false; - } - } - - if (attribute == null) - { - if (configOption.configurationUseDefaultNoPrompt || useConsoleToPromptOnError == false) - { - console_result = configOption.configurationDefault; - } - else - { - if ((configOption.shouldIBeAsked != null && - configOption.shouldIBeAsked(configOption.configurationKey)) || - configOption.shouldIBeAsked == null) - { - if (configurationDescription.Trim() != String.Empty) - { - console_result = - MainConsole.Instance.CmdPrompt( - configurationDescription + ": " + configOption.configurationQuestion, - configOption.configurationDefault); - } - else - { - console_result = - MainConsole.Instance.CmdPrompt(configOption.configurationQuestion, - configOption.configurationDefault); - } - } - else - { - //Dont Ask! Just use default - console_result = configOption.configurationDefault; - } - } - } - else - { - console_result = attribute; - } - - // if the first character is a "$", assume it's the name - // of an environment variable and substitute with the value of that variable - if (console_result.StartsWith("$")) - console_result = Environment.GetEnvironmentVariable(console_result.Substring(1)); - - switch (configOption.configurationType) - { - case ConfigurationOption.ConfigurationTypes.TYPE_STRING: - return_result = console_result; - convertSuccess = true; - break; - case ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY: - if (console_result.Length > 0) - { - return_result = console_result; - convertSuccess = true; - } - errorMessage = "a string that is not empty"; - break; - case ConfigurationOption.ConfigurationTypes.TYPE_BOOLEAN: - bool boolResult; - if (Boolean.TryParse(console_result, out boolResult)) - { - convertSuccess = true; - return_result = boolResult; - } - errorMessage = "'true' or 'false' (Boolean)"; - break; - case ConfigurationOption.ConfigurationTypes.TYPE_BYTE: - byte byteResult; - if (Byte.TryParse(console_result, out byteResult)) - { - convertSuccess = true; - return_result = byteResult; - } - errorMessage = "a byte (Byte)"; - break; - case ConfigurationOption.ConfigurationTypes.TYPE_CHARACTER: - char charResult; - if (Char.TryParse(console_result, out charResult)) - { - convertSuccess = true; - return_result = charResult; - } - errorMessage = "a character (Char)"; - break; - case ConfigurationOption.ConfigurationTypes.TYPE_INT16: - short shortResult; - if (Int16.TryParse(console_result, out shortResult)) - { - convertSuccess = true; - return_result = shortResult; - } - errorMessage = "a signed 32 bit integer (short)"; - break; - case ConfigurationOption.ConfigurationTypes.TYPE_INT32: - int intResult; - if (Int32.TryParse(console_result, out intResult)) - { - convertSuccess = true; - return_result = intResult; - } - errorMessage = "a signed 32 bit integer (int)"; - break; - case ConfigurationOption.ConfigurationTypes.TYPE_INT64: - long longResult; - if (Int64.TryParse(console_result, out longResult)) - { - convertSuccess = true; - return_result = longResult; - } - errorMessage = "a signed 32 bit integer (long)"; - break; - case ConfigurationOption.ConfigurationTypes.TYPE_IP_ADDRESS: - IPAddress ipAddressResult; - if (IPAddress.TryParse(console_result, out ipAddressResult)) - { - convertSuccess = true; - return_result = ipAddressResult; - } - errorMessage = "an IP Address (IPAddress)"; - break; - case ConfigurationOption.ConfigurationTypes.TYPE_UUID: - UUID uuidResult; - if (UUID.TryParse(console_result, out uuidResult)) - { - convertSuccess = true; - return_result = uuidResult; - } - errorMessage = "a UUID (UUID)"; - break; - case ConfigurationOption.ConfigurationTypes.TYPE_UUID_NULL_FREE: - UUID uuidResult2; - if (UUID.TryParse(console_result, out uuidResult2)) - { - convertSuccess = true; - - if (uuidResult2 == UUID.Zero) - uuidResult2 = UUID.Random(); - - return_result = uuidResult2; - } - errorMessage = "a non-null UUID (UUID)"; - break; - case ConfigurationOption.ConfigurationTypes.TYPE_Vector3: - Vector3 vectorResult; - if (Vector3.TryParse(console_result, out vectorResult)) - { - convertSuccess = true; - return_result = vectorResult; - } - errorMessage = "a vector (Vector3)"; - break; - case ConfigurationOption.ConfigurationTypes.TYPE_UINT16: - ushort ushortResult; - if (UInt16.TryParse(console_result, out ushortResult)) - { - convertSuccess = true; - return_result = ushortResult; - } - errorMessage = "an unsigned 16 bit integer (ushort)"; - break; - case ConfigurationOption.ConfigurationTypes.TYPE_UINT32: - uint uintResult; - if (UInt32.TryParse(console_result, out uintResult)) - { - convertSuccess = true; - return_result = uintResult; - } - errorMessage = "an unsigned 32 bit integer (uint)"; - break; - case ConfigurationOption.ConfigurationTypes.TYPE_UINT64: - ulong ulongResult; - if (UInt64.TryParse(console_result, out ulongResult)) - { - convertSuccess = true; - return_result = ulongResult; - } - errorMessage = "an unsigned 64 bit integer (ulong)"; - break; - case ConfigurationOption.ConfigurationTypes.TYPE_FLOAT: - float floatResult; - if ( - float.TryParse(console_result, NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, Culture.NumberFormatInfo, - out floatResult)) - { - convertSuccess = true; - return_result = floatResult; - } - errorMessage = "a single-precision floating point number (float)"; - break; - case ConfigurationOption.ConfigurationTypes.TYPE_DOUBLE: - double doubleResult; - if ( - Double.TryParse(console_result, NumberStyles.AllowDecimalPoint | NumberStyles.AllowLeadingSign, Culture.NumberFormatInfo, - out doubleResult)) - { - convertSuccess = true; - return_result = doubleResult; - } - errorMessage = "an double-precision floating point number (double)"; - break; - } - - if (convertSuccess) - { - if (useFile) - { - configurationPlugin.SetAttribute(configOption.configurationKey, console_result); - } - - if (!resultFunction(configOption.configurationKey, return_result)) - { - m_log.Info( - "The handler for the last configuration option denied that input, please try again."); - convertSuccess = false; - ignoreNextFromConfig = true; - } - } - else - { - if (configOption.configurationUseDefaultNoPrompt) - { - m_log.Error(string.Format( - "[CONFIG]: [{3}]:[{1}] is not valid default for parameter [{0}].\nThe configuration result must be parsable to {2}.\n", - configOption.configurationKey, console_result, errorMessage, - configurationFilename)); - convertSuccess = true; - } - else - { - m_log.Warn(string.Format( - "[CONFIG]: [{3}]:[{1}] is not a valid value [{0}].\nThe configuration result must be parsable to {2}.\n", - configOption.configurationKey, console_result, errorMessage, - configurationFilename)); - ignoreNextFromConfig = true; - } - } - } - } - - if (useFile) - { - configurationPlugin.Commit(); - configurationPlugin.Close(); - } - } - - private static IGenericConfig LoadConfigDll(string dllName) - { - Assembly pluginAssembly = Assembly.LoadFrom(dllName); - IGenericConfig plug = null; - - foreach (Type pluginType in pluginAssembly.GetTypes()) - { - if (pluginType.IsPublic) - { - if (!pluginType.IsAbstract) - { - Type typeInterface = pluginType.GetInterface("IGenericConfig", true); - - if (typeInterface != null) - { - plug = - (IGenericConfig) Activator.CreateInstance(pluginAssembly.GetType(pluginType.ToString())); - } - } - } - } - - pluginAssembly = null; - return plug; - } - - public void forceSetConfigurationOption(string configuration_key, string configuration_value) - { - configurationPlugin.LoadData(); - configurationPlugin.SetAttribute(configuration_key, configuration_value); - configurationPlugin.Commit(); - configurationPlugin.Close(); - } - } -} diff --git a/OpenSim/Framework/Console/AssemblyInfo.cs b/OpenSim/Framework/Console/AssemblyInfo.cs index 37c7304..67af471 100644 --- a/OpenSim/Framework/Console/AssemblyInfo.cs +++ b/OpenSim/Framework/Console/AssemblyInfo.cs @@ -55,4 +55,4 @@ using System.Runtime.InteropServices; // You can specify all values by your own or you can build default build and revision // numbers with the '*' character (the default): -[assembly : AssemblyVersion("0.7.5.*")] +[assembly : AssemblyVersion("0.8.2.*")] diff --git a/OpenSim/Framework/Console/CommandConsole.cs b/OpenSim/Framework/Console/CommandConsole.cs index b9f402a..0f68afe 100644 --- a/OpenSim/Framework/Console/CommandConsole.cs +++ b/OpenSim/Framework/Console/CommandConsole.cs @@ -424,9 +424,9 @@ namespace OpenSim.Framework.Console return new string[] { new List(current.Keys)[0] }; } - public string[] Resolve(string[] cmd) + private CommandInfo ResolveCommand(string[] cmd, out string[] result) { - string[] result = cmd; + result = cmd; int index = -1; Dictionary current = tree; @@ -458,7 +458,7 @@ namespace OpenSim.Framework.Console } else if (found.Count > 0) { - return new string[0]; + return null; } else { @@ -467,21 +467,37 @@ namespace OpenSim.Framework.Console } if (current.ContainsKey(String.Empty)) + return (CommandInfo)current[String.Empty]; + + return null; + } + + public bool HasCommand(string command) + { + string[] result; + return ResolveCommand(Parser.Parse(command), out result) != null; + } + + public string[] Resolve(string[] cmd) + { + string[] result; + CommandInfo ci = ResolveCommand(cmd, out result); + + if (ci == null) + return new string[0]; + + if (ci.fn.Count == 0) + return new string[0]; + + foreach (CommandDelegate fn in ci.fn) { - CommandInfo ci = (CommandInfo)current[String.Empty]; - if (ci.fn.Count == 0) + if (fn != null) + fn(ci.module, result); + else return new string[0]; - foreach (CommandDelegate fn in ci.fn) - { - if (fn != null) - fn(ci.module, result); - else - return new string[0]; - } - return result; } - - return new string[0]; + + return result; } public XmlElement GetXml(XmlDocument doc) diff --git a/OpenSim/Framework/Console/ConsoleDisplayUtil.cs b/OpenSim/Framework/Console/ConsoleDisplayUtil.cs new file mode 100644 index 0000000..6417663 --- /dev/null +++ b/OpenSim/Framework/Console/ConsoleDisplayUtil.cs @@ -0,0 +1,48 @@ +/* + * 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 OpenSimulator 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; + +namespace OpenSim.Framework.Console +{ + /// + /// This will be a set of typical column sizes to allow greater consistency between console commands. + /// + public static class ConsoleDisplayUtil + { + public const int CoordTupleSize = 11; + public const int PortSize = 5; + + public const int EstateNameSize = 20; + public const int ParcelNameSize = 40; + public const int RegionNameSize = 20; + public const int UserNameSize = 35; + + public const int UuidSize = 36; + public const int VectorSize = 15; + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Console/ConsoleUtil.cs b/OpenSim/Framework/Console/ConsoleUtil.cs index dff956a..44f6dc1 100644 --- a/OpenSim/Framework/Console/ConsoleUtil.cs +++ b/OpenSim/Framework/Console/ConsoleUtil.cs @@ -49,14 +49,14 @@ namespace OpenSim.Framework.Console = @"Each component of the coord is comma separated. There must be no spaces between the commas. If you don't care about the z component you can simply omit it. If you don't care about the x or y components then you can leave them blank (though a comma is still required) - If you want to specify the maxmimum value of a component then you can use ~ instead of a number + If you want to specify the maximum value of a component then you can use ~ instead of a number If you want to specify the minimum value of a component then you can use -~ instead of a number e.g. - delete object pos 20,20,20 to 40,40,40 + show object pos 20,20,20 to 40,40,40 delete object pos 20,20 to 40,40 - delete object pos ,20,20 to ,40,40 + show object pos ,20,20 to ,40,40 delete object pos ,,30 to ,,~ - delete object pos ,,-~ to ,,30"; + show object pos ,,-~ to ,,30"; public const string MinRawConsoleVectorValue = "-~"; public const string MaxRawConsoleVectorValue = "~"; @@ -156,12 +156,32 @@ namespace OpenSim.Framework.Console } /// - /// Convert a minimum vector input from the console to an OpenMetaverse.Vector3 + /// Convert a console input to a bool, automatically complaining if a console is given. /// /// Can be null if no console is available. /// /param> /// /// + public static bool TryParseConsoleBool(ICommandConsole console, string rawConsoleString, out bool b) + { + if (!bool.TryParse(rawConsoleString, out b)) + { + if (console != null) + console.OutputFormat("ERROR: {0} is not a true or false value", rawConsoleString); + + return false; + } + + return true; + } + + /// + /// Convert a console input to an int, automatically complaining if a console is given. + /// + /// Can be null if no console is available. + /// /param> + /// + /// public static bool TryParseConsoleInt(ICommandConsole console, string rawConsoleInt, out int i) { if (!int.TryParse(rawConsoleInt, out i)) @@ -174,6 +194,71 @@ namespace OpenSim.Framework.Console return true; } + + /// + /// Convert a console input to a float, automatically complaining if a console is given. + /// + /// Can be null if no console is available. + /// /param> + /// + /// + public static bool TryParseConsoleFloat(ICommandConsole console, string rawConsoleInput, out float i) + { + if (!float.TryParse(rawConsoleInput, out i)) + { + if (console != null) + console.OutputFormat("ERROR: {0} is not a valid float", rawConsoleInput); + + return false; + } + + return true; + } + + /// + /// Convert a console input to a double, automatically complaining if a console is given. + /// + /// Can be null if no console is available. + /// /param> + /// + /// + public static bool TryParseConsoleDouble(ICommandConsole console, string rawConsoleInput, out double i) + { + if (!double.TryParse(rawConsoleInput, out i)) + { + if (console != null) + console.OutputFormat("ERROR: {0} is not a valid double", rawConsoleInput); + + return false; + } + + return true; + } + + /// + /// Convert a console integer to a natural int, automatically complaining if a console is given. + /// + /// Can be null if no console is available. + /// /param> + /// + /// + public static bool TryParseConsoleNaturalInt(ICommandConsole console, string rawConsoleInt, out int i) + { + if (TryParseConsoleInt(console, rawConsoleInt, out i)) + { + if (i < 0) + { + if (console != null) + console.OutputFormat("ERROR: {0} is not a positive integer", rawConsoleInt); + + return false; + } + + return true; + } + + return false; + } /// /// Convert a minimum vector input from the console to an OpenMetaverse.Vector3 @@ -207,24 +292,82 @@ namespace OpenSim.Framework.Console /// The strings "~" and "-~" are valid in components. The first substitutes float.MaxValue whilst the second is float.MinValue /// Other than that, component values must be numeric. /// - /// + /// + /// Behaviour if component is blank. If null then conversion fails on a blank component. + /// /// /// public static bool TryParseConsoleVector( string rawConsoleVector, Func blankComponentFunc, out Vector3 vector) { - List components = rawConsoleVector.Split(VectorSeparatorChars).ToList(); - - if (components.Count < 1 || components.Count > 3) + return Vector3.TryParse(CookVector(rawConsoleVector, 3, blankComponentFunc), out vector); + } + + /// + /// Convert a vector input from the console to an OpenMetaverse.Vector2 + /// + /// + /// A string in the form , where there is no space between values. + /// Any component can be missing (e.g. ,40). blankComponentFunc is invoked to replace the blank with a suitable value + /// Also, if the blank component is at the end, then the comma can be missed off entirely (e.g. 40) + /// The strings "~" and "-~" are valid in components. The first substitutes float.MaxValue whilst the second is float.MinValue + /// Other than that, component values must be numeric. + /// + /// + /// Behaviour if component is blank. If null then conversion fails on a blank component. + /// + /// + /// + public static bool TryParseConsole2DVector( + string rawConsoleVector, Func blankComponentFunc, out Vector2 vector) + { + // We don't use Vector2.TryParse() for now because for some reason it expects an input with 3 components + // rather than 2. + string cookedVector = CookVector(rawConsoleVector, 2, blankComponentFunc); + + if (cookedVector == null) { - vector = Vector3.Zero; + vector = Vector2.Zero; + return false; } + else + { + string[] cookedComponents = cookedVector.Split(VectorSeparatorChars); + + vector = new Vector2(float.Parse(cookedComponents[0]), float.Parse(cookedComponents[1])); + + return true; + } + + //return Vector2.TryParse(CookVector(rawConsoleVector, 2, blankComponentFunc), out vector); + } + + /// + /// Convert a raw console vector into a vector that can be be parsed by the relevant OpenMetaverse.TryParse() + /// + /// + /// + /// + /// null if conversion was not possible + private static string CookVector( + string rawConsoleVector, int dimensions, Func blankComponentFunc) + { + List components = rawConsoleVector.Split(VectorSeparatorChars).ToList(); - for (int i = components.Count; i < 3; i++) - components.Add(""); + if (components.Count < 1 || components.Count > dimensions) + return null; - List semiDigestedComponents + if (components.Count < dimensions) + { + if (blankComponentFunc == null) + return null; + else + for (int i = components.Count; i < dimensions; i++) + components.Add(""); + } + + List cookedComponents = components.ConvertAll( c => { @@ -238,11 +381,7 @@ namespace OpenSim.Framework.Console return c; }); - string semiDigestedConsoleVector = string.Join(VectorSeparator, semiDigestedComponents.ToArray()); - - // m_log.DebugFormat("[CONSOLE UTIL]: Parsing {0} into OpenMetaverse.Vector3", semiDigestedConsoleVector); - - return Vector3.TryParse(semiDigestedConsoleVector, out vector); + return string.Join(VectorSeparator, cookedComponents.ToArray()); } } } \ No newline at end of file diff --git a/OpenSim/Framework/Console/LocalConsole.cs b/OpenSim/Framework/Console/LocalConsole.cs index d41481f..28293c0 100644 --- a/OpenSim/Framework/Console/LocalConsole.cs +++ b/OpenSim/Framework/Console/LocalConsole.cs @@ -32,6 +32,8 @@ using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading; +using System.IO; +using Nini.Config; using log4net; namespace OpenSim.Framework.Console @@ -41,11 +43,18 @@ namespace OpenSim.Framework.Console /// public class LocalConsole : CommandConsole { -// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private string m_historyPath; + private bool m_historyEnable; // private readonly object m_syncRoot = new object(); private const string LOGLEVEL_NONE = "(none)"; + // Used to extract categories for colourization. + private Regex m_categoryRegex + = new Regex( + @"^(?.*?)\[(?[^\]]+)\]:?(?.*)", RegexOptions.Singleline | RegexOptions.Compiled); + private int m_cursorYPosition = -1; private int m_cursorXPosition = 0; private StringBuilder m_commandLine = new StringBuilder(); @@ -74,8 +83,54 @@ namespace OpenSim.Framework.Console return Colors[(Math.Abs(input.ToUpper().GetHashCode()) % Colors.Length)]; } - public LocalConsole(string defaultPrompt) : base(defaultPrompt) + public LocalConsole(string defaultPrompt, IConfig startupConfig = null) : base(defaultPrompt) { + + if (startupConfig == null) return; + + m_historyEnable = startupConfig.GetBoolean("ConsoleHistoryFileEnabled", false); + if (!m_historyEnable) + { + m_log.Info("[LOCAL CONSOLE]: Persistent command line history from file is Disabled"); + return; + } + + string m_historyFile = startupConfig.GetString("ConsoleHistoryFile", "OpenSimConsoleHistory.txt"); + int m_historySize = startupConfig.GetInt("ConsoleHistoryFileLines", 100); + m_historyPath = Path.GetFullPath(Path.Combine(Util.configDir(), m_historyFile)); + m_log.InfoFormat("[LOCAL CONSOLE]: Persistent command line history is Enabled, up to {0} lines from file {1}", m_historySize, m_historyPath); + + if (File.Exists(m_historyPath)) + { + using (StreamReader history_file = new StreamReader(m_historyPath)) + { + string line; + while ((line = history_file.ReadLine()) != null) + { + m_history.Add(line); + } + } + + if (m_history.Count > m_historySize) + { + while (m_history.Count > m_historySize) + m_history.RemoveAt(0); + + using (StreamWriter history_file = new StreamWriter(m_historyPath)) + { + foreach (string line in m_history) + { + history_file.WriteLine(line); + } + } + } + m_log.InfoFormat("[LOCAL CONSOLE]: Read {0} lines of command line history from file {1}", m_history.Count, m_historyPath); + } + else + { + m_log.InfoFormat("[LOCAL CONSOLE]: Creating new empty command line history file {0}", m_historyPath); + File.Create(m_historyPath).Dispose(); + } } private void AddToHistory(string text) @@ -84,6 +139,10 @@ namespace OpenSim.Framework.Console m_history.RemoveAt(0); m_history.Add(text); + if (m_historyEnable) + { + File.AppendAllText(m_historyPath, text + Environment.NewLine); + } } /// @@ -280,11 +339,8 @@ namespace OpenSim.Framework.Console string outText = text; if (level != LOGLEVEL_NONE) - { - string regex = @"^(?.*?)\[(?[^\]]+)\]:?(?.*)"; - - Regex RE = new Regex(regex, RegexOptions.Multiline); - MatchCollection matches = RE.Matches(text); + { + MatchCollection matches = m_categoryRegex.Matches(text); if (matches.Count == 1) { @@ -426,6 +482,21 @@ namespace OpenSim.Framework.Console System.Console.Write("{0}", prompt); break; + case ConsoleKey.Delete: + if (m_cursorXPosition == m_commandLine.Length) + break; + + m_commandLine.Remove(m_cursorXPosition, 1); + + SetCursorLeft(0); + m_cursorYPosition = SetCursorTop(m_cursorYPosition); + + if (m_echo) + System.Console.Write("{0}{1} ", prompt, m_commandLine); + else + System.Console.Write("{0}", prompt); + + break; case ConsoleKey.End: m_cursorXPosition = m_commandLine.Length; break; diff --git a/OpenSim/Framework/Console/MockConsole.cs b/OpenSim/Framework/Console/MockConsole.cs index 8ba58e4..1a142df 100644 --- a/OpenSim/Framework/Console/MockConsole.cs +++ b/OpenSim/Framework/Console/MockConsole.cs @@ -40,7 +40,9 @@ namespace OpenSim.Framework.Console /// public class MockConsole : ICommandConsole { +#pragma warning disable 0067 public event OnOutputDelegate OnOutput; +#pragma warning restore 0067 private MockCommands m_commands = new MockCommands(); @@ -80,6 +82,7 @@ namespace OpenSim.Framework.Console public void AddCommand(string module, bool shared, string command, string help, string longhelp, CommandDelegate fn) {} public void AddCommand(string module, bool shared, string command, string help, string longhelp, string descriptivehelp, CommandDelegate fn) {} public string[] FindNextOption(string[] cmd, bool term) { return null; } + public bool HasCommand(string cmd) { return false; } public string[] Resolve(string[] cmd) { return null; } public XmlElement GetXml(XmlDocument doc) { return null; } } diff --git a/OpenSim/Framework/Console/RemoteConsole.cs b/OpenSim/Framework/Console/RemoteConsole.cs index 27edd4b..8ad7b0d 100644 --- a/OpenSim/Framework/Console/RemoteConsole.cs +++ b/OpenSim/Framework/Console/RemoteConsole.cs @@ -234,7 +234,7 @@ namespace OpenSim.Framework.Console string uri = "/ReadResponses/" + sessionID.ToString() + "/"; m_Server.AddPollServiceHTTPHandler( - uri, new PollServiceEventArgs(null, HasEvents, GetEvents, NoEvents, sessionID)); + uri, new PollServiceEventArgs(null, uri, HasEvents, GetEvents, NoEvents, sessionID,25000)); // 25 secs timeout XmlDocument xmldoc = new XmlDocument(); XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, @@ -425,7 +425,7 @@ namespace OpenSim.Framework.Console return false; } - private Hashtable GetEvents(UUID RequestID, UUID sessionID, string request) + private Hashtable GetEvents(UUID RequestID, UUID sessionID) { ConsoleConnection c = null; diff --git a/OpenSim/Framework/Constants.cs b/OpenSim/Framework/Constants.cs index a2eb5ee..3ba264c 100644 --- a/OpenSim/Framework/Constants.cs +++ b/OpenSim/Framework/Constants.cs @@ -30,9 +30,18 @@ namespace OpenSim.Framework { public class Constants { + // 'RegionSize' is the legacy region size. + // DO NOT USE THIS FOR ANY NEW CODE. Use Scene.RegionInfo.RegionSize[XYZ] as a region might not + // be the legacy region size. public const uint RegionSize = 256; public const uint RegionHeight = 4096; - public const byte TerrainPatchSize = 16; + // This could be a parameters but, really, a region of greater than this is pretty unmanageable + public const uint MaximumRegionSize = 8192; + + // Since terrain is stored in 16x16 heights, regions must be a multiple of this number and that is the minimum + public const int MinRegionSize = 16; + public const int TerrainPatchSize = 16; + public const string DefaultTexture = "89556747-24cb-43ed-920b-47caed15465f"; public enum EstateAccessCodex : uint diff --git a/OpenSim/Framework/DAMap.cs b/OpenSim/Framework/DAMap.cs new file mode 100644 index 0000000..4995a92 --- /dev/null +++ b/OpenSim/Framework/DAMap.cs @@ -0,0 +1,328 @@ +/* + * 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 OpenSimulator 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; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Text; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using log4net; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework +{ + /// + /// This class stores and retrieves dynamic attributes. + /// + /// + /// Modules that want to use dynamic attributes need to do so in a private data store + /// which is accessed using a unique name. DAMap provides access to the data stores, + /// each of which is an OSDMap. Modules are free to store any type of data they want + /// within their data store. However, avoid storing large amounts of data because that + /// would slow down database access. + /// + public class DAMap : IXmlSerializable + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private static readonly int MIN_NAMESPACE_LENGTH = 4; + + private OSDMap m_map = new OSDMap(); + + // WARNING: this is temporary for experimentation only, it will be removed!!!! + public OSDMap TopLevelMap + { + get { return m_map; } + set { m_map = value; } + } + + public XmlSchema GetSchema() { return null; } + + public static DAMap FromXml(string rawXml) + { + DAMap map = new DAMap(); + map.ReadXml(rawXml); + return map; + } + + public void ReadXml(XmlReader reader) + { + ReadXml(reader.ReadInnerXml()); + } + + public void ReadXml(string rawXml) + { + // System.Console.WriteLine("Trying to deserialize [{0}]", rawXml); + + lock (this) + { + m_map = (OSDMap)OSDParser.DeserializeLLSDXml(rawXml); + SanitiseMap(this); + } + } + + public void WriteXml(XmlWriter writer) + { + writer.WriteRaw(ToXml()); + } + + public string ToXml() + { + lock (this) + return OSDParser.SerializeLLSDXmlString(m_map); + } + + public void CopyFrom(DAMap other) + { + // Deep copy + + string data = null; + lock (other) + { + if (other.CountNamespaces > 0) + { + data = OSDParser.SerializeLLSDXmlString(other.m_map); + } + } + + lock (this) + { + if (data == null) + Clear(); + else + m_map = (OSDMap)OSDParser.DeserializeLLSDXml(data); + } + } + + /// + /// Sanitise the map to remove any namespaces or stores that are not OSDMap. + /// + /// + /// + public static void SanitiseMap(DAMap daMap) + { + List keysToRemove = null; + + OSDMap namespacesMap = daMap.m_map; + + foreach (string key in namespacesMap.Keys) + { +// Console.WriteLine("Processing ns {0}", key); + if (!(namespacesMap[key] is OSDMap)) + { + if (keysToRemove == null) + keysToRemove = new List(); + + keysToRemove.Add(key); + } + } + + if (keysToRemove != null) + { + foreach (string key in keysToRemove) + { +// Console.WriteLine ("Removing bad ns {0}", key); + namespacesMap.Remove(key); + } + } + + foreach (OSD nsOsd in namespacesMap.Values) + { + OSDMap nsOsdMap = (OSDMap)nsOsd; + keysToRemove = null; + + foreach (string key in nsOsdMap.Keys) + { + if (!(nsOsdMap[key] is OSDMap)) + { + if (keysToRemove == null) + keysToRemove = new List(); + + keysToRemove.Add(key); + } + } + + if (keysToRemove != null) + foreach (string key in keysToRemove) + nsOsdMap.Remove(key); + } + } + + /// + /// Get the number of namespaces + /// + public int CountNamespaces { get { lock (this) { return m_map.Count; } } } + + /// + /// Get the number of stores. + /// + public int CountStores + { + get + { + int count = 0; + + lock (this) + { + foreach (OSD osdNamespace in m_map) + { + count += ((OSDMap)osdNamespace).Count; + } + } + + return count; + } + } + + /// + /// Retrieve a Dynamic Attribute store + /// + /// namespace for the store - use "OpenSim" for in-core modules + /// name of the store within the namespace + /// an OSDMap representing the stored data, or null if not found + public OSDMap GetStore(string ns, string storeName) + { + OSD namespaceOsd; + + lock (this) + { + if (m_map.TryGetValue(ns, out namespaceOsd)) + { + OSD store; + + if (((OSDMap)namespaceOsd).TryGetValue(storeName, out store)) + return (OSDMap)store; + } + } + + return null; + } + + /// + /// Saves a Dynamic attribute store + /// + /// namespace for the store - use "OpenSim" for in-core modules + /// name of the store within the namespace + /// an OSDMap representing the data to store + public void SetStore(string ns, string storeName, OSDMap store) + { + ValidateNamespace(ns); + OSDMap nsMap; + + lock (this) + { + if (!m_map.ContainsKey(ns)) + { + nsMap = new OSDMap(); + m_map[ns] = nsMap; + } + + nsMap = (OSDMap)m_map[ns]; + +// m_log.DebugFormat("[DA MAP]: Setting store to {0}:{1}", ns, storeName); + nsMap[storeName] = store; + } + } + + /// + /// Validate the key used for storing separate data stores. + /// + /// + public static void ValidateNamespace(string ns) + { + if (ns.Length < MIN_NAMESPACE_LENGTH) + throw new Exception("Minimum namespace length is " + MIN_NAMESPACE_LENGTH); + } + + public bool ContainsStore(string ns, string storeName) + { + OSD namespaceOsd; + + lock (this) + { + if (m_map.TryGetValue(ns, out namespaceOsd)) + { + return ((OSDMap)namespaceOsd).ContainsKey(storeName); + } + } + + return false; + } + + public bool TryGetStore(string ns, string storeName, out OSDMap store) + { + OSD namespaceOsd; + + lock (this) + { + if (m_map.TryGetValue(ns, out namespaceOsd)) + { + OSD storeOsd; + + bool result = ((OSDMap)namespaceOsd).TryGetValue(storeName, out storeOsd); + store = (OSDMap)storeOsd; + + return result; + } + } + + store = null; + return false; + } + + public void Clear() + { + lock (this) + m_map.Clear(); + } + + public bool RemoveStore(string ns, string storeName) + { + OSD namespaceOsd; + + lock (this) + { + if (m_map.TryGetValue(ns, out namespaceOsd)) + { + OSDMap namespaceOsdMap = (OSDMap)namespaceOsd; + namespaceOsdMap.Remove(storeName); + + // Don't keep empty namespaces around + if (namespaceOsdMap.Count <= 0) + m_map.Remove(ns); + } + } + + return false; + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/DOMap.cs b/OpenSim/Framework/DOMap.cs new file mode 100644 index 0000000..f5b650b --- /dev/null +++ b/OpenSim/Framework/DOMap.cs @@ -0,0 +1,98 @@ +/* + * 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 OpenSimulator 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; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Xml; +using System.Xml.Schema; +using System.Xml.Serialization; +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework +{ + /// + /// This class stores and retrieves dynamic objects. + /// + /// + /// Experimental - DO NOT USE. Does not yet have namespace support. + /// + public class DOMap + { + private IDictionary m_map; + + public void Add(string ns, string objName, object dynObj) + { + DAMap.ValidateNamespace(ns); + + lock (this) + { + if (m_map == null) + m_map = new Dictionary(); + + m_map.Add(objName, dynObj); + } + } + + public bool ContainsKey(string key) + { + return Get(key) != null; + } + + /// + /// Get a dynamic object + /// + /// + /// Not providing an index method so that users can't casually overwrite each other's objects. + /// + /// + public object Get(string key) + { + lock (this) + { + if (m_map == null) + return null; + else + return m_map[key]; + } + } + + public bool Remove(string key) + { + lock (this) + { + if (m_map == null) + return false; + else + return m_map.Remove(key); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/EstateBan.cs b/OpenSim/Framework/EstateBan.cs index de9ebf6..ebed794 100644 --- a/OpenSim/Framework/EstateBan.cs +++ b/OpenSim/Framework/EstateBan.cs @@ -25,6 +25,10 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +using System; +using System.Collections.Generic; +using System.Reflection; + using OpenMetaverse; namespace OpenSim.Framework @@ -111,5 +115,50 @@ namespace OpenSim.Framework } } + public EstateBan() { } + + public Dictionary ToMap() + { + Dictionary map = new Dictionary(); + PropertyInfo[] properties = this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); + foreach (PropertyInfo p in properties) + map[p.Name] = p.GetValue(this, null); + + return map; + } + + public EstateBan(Dictionary map) + { + foreach (KeyValuePair kvp in map) + { + PropertyInfo p = this.GetType().GetProperty(kvp.Key, BindingFlags.Public | BindingFlags.Instance); + if (p == null) + continue; + object value = p.GetValue(this, null); + if (value is String) + p.SetValue(this, map[p.Name], null); + else if (value is UInt32) + p.SetValue(this, UInt32.Parse((string)map[p.Name]), null); + else if (value is Boolean) + p.SetValue(this, Boolean.Parse((string)map[p.Name]), null); + else if (value is UUID) + p.SetValue(this, UUID.Parse((string)map[p.Name]), null); + } + } + + + /// + /// For debugging + /// + /// + public override string ToString() + { + Dictionary map = ToMap(); + string result = string.Empty; + foreach (KeyValuePair kvp in map) + result += string.Format("{0}: {1} {2}", kvp.Key, kvp.Value, Environment.NewLine); + + return result; + } } } diff --git a/OpenSim/Framework/EstateSettings.cs b/OpenSim/Framework/EstateSettings.cs index a92abbf..4df7860 100644 --- a/OpenSim/Framework/EstateSettings.cs +++ b/OpenSim/Framework/EstateSettings.cs @@ -28,6 +28,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Reflection; using OpenMetaverse; namespace OpenSim.Framework @@ -58,6 +59,30 @@ namespace OpenSim.Framework set { m_EstateName = value; } } + private bool m_AllowLandmark = true; + + public bool AllowLandmark + { + get { return m_AllowLandmark; } + set { m_AllowLandmark = value; } + } + + private bool m_AllowParcelChanges = true; + + public bool AllowParcelChanges + { + get { return m_AllowParcelChanges; } + set { m_AllowParcelChanges = value; } + } + + private bool m_AllowSetHome = true; + + public bool AllowSetHome + { + get { return m_AllowSetHome; } + set { m_AllowSetHome = value; } + } + private uint m_ParentEstateID = 1; public uint ParentEstateID @@ -374,10 +399,132 @@ namespace OpenSim.Framework return l_EstateAccess.Contains(user); } + public void SetFromFlags(ulong regionFlags) + { + ResetHomeOnTeleport = ((regionFlags & (ulong)OpenMetaverse.RegionFlags.ResetHomeOnTeleport) == (ulong)OpenMetaverse.RegionFlags.ResetHomeOnTeleport); + BlockDwell = ((regionFlags & (ulong)OpenMetaverse.RegionFlags.BlockDwell) == (ulong)OpenMetaverse.RegionFlags.BlockDwell); + AllowLandmark = ((regionFlags & (ulong)OpenMetaverse.RegionFlags.AllowLandmark) == (ulong)OpenMetaverse.RegionFlags.AllowLandmark); + AllowParcelChanges = ((regionFlags & (ulong)OpenMetaverse.RegionFlags.AllowParcelChanges) == (ulong)OpenMetaverse.RegionFlags.AllowParcelChanges); + AllowSetHome = ((regionFlags & (ulong)OpenMetaverse.RegionFlags.AllowSetHome) == (ulong)OpenMetaverse.RegionFlags.AllowSetHome); + } + public bool GroupAccess(UUID groupID) { return l_EstateGroups.Contains(groupID); } + public Dictionary ToMap() + { + Dictionary map = new Dictionary(); + PropertyInfo[] properties = this.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance); + foreach (PropertyInfo p in properties) + { + // EstateBans is a complex type, let's treat it as special + if (p.Name == "EstateBans") + continue; + + object value = p.GetValue(this, null); + if (value != null) + { + if (p.PropertyType.IsArray) // of UUIDs + { + if (((Array)value).Length > 0) + { + string[] args = new string[((Array)value).Length]; + int index = 0; + foreach (object o in (Array)value) + args[index++] = o.ToString(); + map[p.Name] = String.Join(",", args); + } + } + else // simple types + map[p.Name] = value; + } + } + + // EstateBans are special + if (EstateBans.Length > 0) + { + Dictionary bans = new Dictionary(); + int i = 0; + foreach (EstateBan ban in EstateBans) + bans["ban" + i++] = ban.ToMap(); + map["EstateBans"] = bans; + } + + return map; + } + + /// + /// For debugging + /// + /// + public override string ToString() + { + Dictionary map = ToMap(); + String result = String.Empty; + + foreach (KeyValuePair kvp in map) + { + if (kvp.Key == "EstateBans") + { + result += "EstateBans:" + Environment.NewLine; + foreach (KeyValuePair ban in (Dictionary)kvp.Value) + result += ban.Value.ToString(); + } + else + result += string.Format("{0}: {1} {2}", kvp.Key, kvp.Value.ToString(), Environment.NewLine); + } + + return result; + } + + public EstateSettings(Dictionary map) + { + foreach (KeyValuePair kvp in map) + { + PropertyInfo p = this.GetType().GetProperty(kvp.Key, BindingFlags.Public | BindingFlags.Instance); + if (p == null) + continue; + + // EstateBans is a complex type, let's treat it as special + if (p.Name == "EstateBans") + continue; + + if (p.PropertyType.IsArray) + { + string[] elements = ((string)map[p.Name]).Split(new char[] { ',' }); + UUID[] uuids = new UUID[elements.Length]; + int i = 0; + foreach (string e in elements) + uuids[i++] = new UUID(e); + p.SetValue(this, uuids, null); + } + else + { + object value = p.GetValue(this, null); + if (value is String) + p.SetValue(this, map[p.Name], null); + else if (value is UInt32) + p.SetValue(this, UInt32.Parse((string)map[p.Name]), null); + else if (value is Boolean) + p.SetValue(this, Boolean.Parse((string)map[p.Name]), null); + else if (value is UUID) + p.SetValue(this, UUID.Parse((string)map[p.Name]), null); + } + } + + // EstateBans are special + if (map.ContainsKey("EstateBans")) + { + var banData = ((Dictionary)map["EstateBans"]).Values; + EstateBan[] bans = new EstateBan[banData.Count]; + int b = 0; + foreach (Dictionary ban in banData) + bans[b++] = new EstateBan(ban); + PropertyInfo bansProperty = this.GetType().GetProperty("EstateBans", BindingFlags.Public | BindingFlags.Instance); + bansProperty.SetValue(this, bans, null); + } + } } } diff --git a/OpenSim/Framework/ExtraPhysicsData.cs b/OpenSim/Framework/ExtraPhysicsData.cs new file mode 100644 index 0000000..9e7334f --- /dev/null +++ b/OpenSim/Framework/ExtraPhysicsData.cs @@ -0,0 +1,50 @@ +/* + * 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 OpenSimulator 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 OpenMetaverse; + +namespace OpenSim.Framework +{ + public enum PhysShapeType : byte + { + prim = 0, + none = 1, + convex = 2, + + invalid = 255 // use to mark invalid data in ExtraPhysicsData + } + + public struct ExtraPhysicsData + { + public float Density; + public float GravitationModifier; + public float Friction; + public float Bounce; + public PhysShapeType PhysShapeType; + + } +} diff --git a/OpenSim/Framework/ForeignUserProfileData.cs b/OpenSim/Framework/ForeignUserProfileData.cs deleted file mode 100644 index 2beaf80..0000000 --- a/OpenSim/Framework/ForeignUserProfileData.cs +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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 OpenSimulator 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; - -namespace OpenSim.Framework -{ - public class ForeignUserProfileData : UserProfileData - { - /// - /// The address of the users home sim, used for foreigners. - /// - private string _userUserServerURI = String.Empty; - - /// - /// The address of the users home sim, used for foreigners. - /// - private string _userHomeAddress = String.Empty; - - /// - /// The port of the users home sim, used for foreigners. - /// - private string _userHomePort = String.Empty; - /// - /// The remoting port of the users home sim, used for foreigners. - /// - private string _userHomeRemotingPort = String.Empty; - - public string UserServerURI - { - get { return _userUserServerURI; } - set { _userUserServerURI = value; } - } - - public string UserHomeAddress - { - get { return _userHomeAddress; } - set { _userHomeAddress = value; } - } - - public string UserHomePort - { - get { return _userHomePort; } - set { _userHomePort = value; } - } - - public string UserHomeRemotingPort - { - get { return _userHomeRemotingPort; } - set { _userHomeRemotingPort = value; } - } - } -} diff --git a/OpenSim/Framework/GridInstantMessage.cs b/OpenSim/Framework/GridInstantMessage.cs index 6ae0488..da3690c 100644 --- a/OpenSim/Framework/GridInstantMessage.cs +++ b/OpenSim/Framework/GridInstantMessage.cs @@ -53,6 +53,24 @@ namespace OpenSim.Framework binaryBucket = new byte[0]; } + public GridInstantMessage(GridInstantMessage im, bool addTimestamp) + { + fromAgentID = im.fromAgentID; + fromAgentName = im.fromAgentName; + toAgentID = im.toAgentID; + dialog = im.dialog; + fromGroup = im.fromGroup; + message = im.message; + imSessionID = im.imSessionID; + offline = im.offline; + Position = im.Position; + binaryBucket = im.binaryBucket; + RegionID = im.RegionID; + + if (addTimestamp) + timestamp = (uint)Util.UnixTimeSinceEpoch(); + } + public GridInstantMessage(IScene scene, UUID _fromAgentID, string _fromAgentName, UUID _toAgentID, byte _dialog, bool _fromGroup, string _message, diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 87433cc..e36edb2 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -64,7 +64,9 @@ namespace OpenSim.Framework public delegate void NetworkStats(int inPackets, int outPackets, int unAckedBytes); - public delegate void SetAppearance(IClientAPI remoteClient, Primitive.TextureEntry textureEntry, byte[] visualParams); + public delegate void CachedTextureRequest(IClientAPI remoteClient, int serial, List cachedTextureRequest); + + public delegate void SetAppearance(IClientAPI remoteClient, Primitive.TextureEntry textureEntry, byte[] visualParams, Vector3 AvSize, WearableCacheItem[] CacheItems); public delegate void StartAnim(IClientAPI remoteClient, UUID animID); @@ -124,7 +126,7 @@ namespace OpenSim.Framework public delegate void ObjectDrop(uint localID, IClientAPI remoteClient); public delegate void UpdatePrimFlags( - uint localID, bool UsePhysics, bool IsTemporary, bool IsPhantom, IClientAPI remoteClient); + uint localID, bool UsePhysics, bool IsTemporary, bool IsPhantom, ExtraPhysicsData PhysData, IClientAPI remoteClient); public delegate void UpdatePrimTexture(uint localID, byte[] texture, IClientAPI remoteClient); @@ -313,7 +315,7 @@ namespace OpenSim.Framework public delegate void ObjectPermissions( IClientAPI controller, UUID agentID, UUID sessionID, byte field, uint localId, uint mask, byte set); - public delegate void EconomyDataRequest(UUID agentID); + public delegate void EconomyDataRequest(IClientAPI client); public delegate void ObjectIncludeInSearch(IClientAPI remoteClient, bool IncludeInSearch, uint localID); @@ -780,6 +782,7 @@ namespace OpenSim.Framework event EstateChangeInfo OnEstateChangeInfo; event EstateManageTelehub OnEstateManageTelehub; // [Obsolete("LLClientView Specific.")] + event CachedTextureRequest OnCachedTextureRequest; event SetAppearance OnSetAppearance; // [Obsolete("LLClientView Specific - Replace and rename OnAvatarUpdate. Difference from SetAppearance?")] event AvatarNowWearing OnAvatarNowWearing; @@ -822,6 +825,8 @@ namespace OpenSim.Framework /// event UpdateAgent OnAgentUpdate; + event UpdateAgent OnAgentCameraUpdate; + event AgentRequestSit OnAgentRequestSit; event AgentSit OnAgentSit; event AvatarPickerRequest OnAvatarPickerRequest; @@ -1087,14 +1092,15 @@ namespace OpenSim.Framework /// void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry); + void SendCachedTextureResponse(ISceneEntity avatar, int serial, List cachedTextures); + void SendStartPingCheck(byte seq); /// /// Tell the client that an object has been deleted /// - /// /// - void SendKillObject(ulong regionHandle, List localID); + void SendKillObject(List localID); void SendAnimations(UUID[] animID, int[] seqs, UUID sourceAgentId, UUID[] objectIDs); void SendRegionHandshake(RegionInfo regionInfo, RegionHandshakeArgs args); @@ -1116,8 +1122,8 @@ namespace OpenSim.Framework void SendInstantMessage(GridInstantMessage im); - void SendGenericMessage(string method, List message); - void SendGenericMessage(string method, List message); + void SendGenericMessage(string method, UUID invoice, List message); + void SendGenericMessage(string method, UUID invoice, List message); void SendLayerData(float[] map); void SendLayerData(int px, int py, float[] map); @@ -1155,7 +1161,8 @@ namespace OpenSim.Framework void SendTeleportStart(uint flags); void SendTeleportProgress(uint flags, string message); - void SendMoneyBalance(UUID transaction, bool success, byte[] description, int balance); + void SendMoneyBalance(UUID transaction, bool success, byte[] description, int balance, int transactionType, UUID sourceID, bool sourceIsGroup, UUID destID, bool destIsGroup, int amount, string item); + void SendPayPrice(UUID objectID, int[] payPrice); void SendCoarseLocationUpdate(List users, List CoarseLocations); @@ -1250,8 +1257,6 @@ namespace OpenSim.Framework void SendDialog(string objectname, UUID objectID, UUID ownerID, string ownerFirstName, string ownerLastName, string msg, UUID textureID, int ch, string[] buttonlabels); - bool AddMoney(int debit); - /// /// Update the client as to where the sun is currently located. /// @@ -1356,6 +1361,8 @@ namespace OpenSim.Framework void SendObjectPropertiesReply(ISceneEntity Entity); + void SendPartPhysicsProprieties(ISceneEntity Entity); + void SendAgentOffline(UUID[] agentIDs); void SendAgentOnline(UUID[] agentIDs); @@ -1469,7 +1476,7 @@ namespace OpenSim.Framework void SendChangeUserRights(UUID agentID, UUID friendID, int rights); void SendTextBoxRequest(string message, int chatChannel, string objectname, UUID ownerID, string ownerFirstName, string ownerLastName, UUID objectId); - void StopFlying(ISceneEntity presence); + void SendAgentTerseUpdate(ISceneEntity presence); void SendPlacesReply(UUID queryID, UUID transactionID, PlacesReplyData[] data); } diff --git a/OpenSim/Framework/ICommandConsole.cs b/OpenSim/Framework/ICommandConsole.cs index a6573f8..3db2765 100644 --- a/OpenSim/Framework/ICommandConsole.cs +++ b/OpenSim/Framework/ICommandConsole.cs @@ -67,9 +67,16 @@ namespace OpenSim.Framework string help, string longhelp, string descriptivehelp, CommandDelegate fn); - string[] FindNextOption(string[] cmd, bool term); + /// + /// Has the given command already been registered? + /// + /// + /// Command. + bool HasCommand(string command); + + string[] FindNextOption(string[] command, bool term); - string[] Resolve(string[] cmd); + string[] Resolve(string[] command); XmlElement GetXml(XmlDocument doc); } diff --git a/OpenSim/Framework/IImprovedAssetCache.cs b/OpenSim/Framework/IImprovedAssetCache.cs index 251215a..a853e90 100644 --- a/OpenSim/Framework/IImprovedAssetCache.cs +++ b/OpenSim/Framework/IImprovedAssetCache.cs @@ -31,9 +31,34 @@ namespace OpenSim.Framework { public interface IImprovedAssetCache { + /// + /// Cache the specified asset. + /// + /// void Cache(AssetBase asset); + + /// + /// Get an asset by its id. + /// + /// + /// null if the asset does not exist. AssetBase Get(string id); + + /// + /// Check whether an asset with the specified id exists in the cache. + /// + /// + bool Check(string id); + + /// + /// Expire an asset from the cache. + /// + /// void Expire(string id); + + /// + /// Clear the cache. + /// void Clear(); } -} +} \ No newline at end of file diff --git a/OpenSim/Framework/ILandChannel.cs b/OpenSim/Framework/ILandChannel.cs index 869d4c8..c46c03c 100644 --- a/OpenSim/Framework/ILandChannel.cs +++ b/OpenSim/Framework/ILandChannel.cs @@ -56,6 +56,13 @@ namespace OpenSim.Region.Framework.Interfaces ILandObject GetLandObject(float x, float y); /// + /// Get the parcel at the specified point + /// + /// Vector where x and y components are between 0 and 256. z component is ignored. + /// Land object at the point supplied + ILandObject GetLandObject(Vector3 position); + + /// /// Get the parcels near the specified point /// /// diff --git a/OpenSim/Framework/ILandObject.cs b/OpenSim/Framework/ILandObject.cs index 4f98d7b..8465c86 100644 --- a/OpenSim/Framework/ILandObject.cs +++ b/OpenSim/Framework/ILandObject.cs @@ -38,6 +38,7 @@ namespace OpenSim.Framework int GetParcelMaxPrimCount(); int GetSimulatorMaxPrimCount(); int GetPrimsFree(); + Dictionary GetLandObjectOwners(); LandData LandData { get; set; } bool[,] LandBitmap { get; set; } @@ -70,6 +71,7 @@ namespace OpenSim.Framework void UpdateLandProperties(LandUpdateArgs args, IClientAPI remote_client); bool IsEitherBannedOrRestricted(UUID avatar); bool IsBannedFromLand(UUID avatar); + bool CanBeOnThisLand(UUID avatar, float posHeight); bool IsRestrictedFromLand(UUID avatar); bool IsInLandAccessList(UUID avatar); void SendLandUpdateToClient(IClientAPI remote_client); diff --git a/OpenSim/Framework/IMoneyModule.cs b/OpenSim/Framework/IMoneyModule.cs index 1e09728..52f3e83 100644 --- a/OpenSim/Framework/IMoneyModule.cs +++ b/OpenSim/Framework/IMoneyModule.cs @@ -38,7 +38,8 @@ namespace OpenSim.Framework int GetBalance(UUID agentID); bool UploadCovered(UUID agentID, int amount); bool AmountCovered(UUID agentID, int amount); - void ApplyCharge(UUID agentID, int amount, string text); + void ApplyCharge(UUID agentID, int amount, MoneyTransactionType type); + void ApplyCharge(UUID agentID, int amount, MoneyTransactionType type, string extraData); void ApplyUploadCharge(UUID agentID, int amount, string text); int UploadCharge { get; } diff --git a/OpenSim/Framework/IPeople.cs b/OpenSim/Framework/IPeople.cs new file mode 100644 index 0000000..8d274d0 --- /dev/null +++ b/OpenSim/Framework/IPeople.cs @@ -0,0 +1,49 @@ +/* + * 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 OpenSimulator 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 OpenMetaverse; + +namespace OpenSim.Framework +{ + public class UserData + { + public UUID Id { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public string HomeURL { get; set; } + public Dictionary ServerURLs { get; set; } + public bool IsUnknownUser { get; set; } + public bool HasGridUserTried { get; set; } + } + + public interface IPeople + { + List GetUserData(string query, int page_size, int page_number); + } +} \ No newline at end of file diff --git a/OpenSim/Framework/IRegionLoader.cs b/OpenSim/Framework/IRegionLoader.cs deleted file mode 100644 index c566fc7..0000000 --- a/OpenSim/Framework/IRegionLoader.cs +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 OpenSimulator 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 Nini.Config; - -namespace OpenSim.Framework -{ - public interface IRegionLoader - { - void SetIniConfigSource(IConfigSource configSource); - RegionInfo[] LoadRegions(); - } -} \ No newline at end of file diff --git a/OpenSim/Framework/IScene.cs b/OpenSim/Framework/IScene.cs index 87ec99e..e1b6d1e 100644 --- a/OpenSim/Framework/IScene.cs +++ b/OpenSim/Framework/IScene.cs @@ -86,24 +86,26 @@ namespace OpenSim.Framework event restart OnRestart; /// - /// Add a new client and create a presence for it. All clients except initial login clients will starts off as a child agent + /// Add a new agent with an attached client. All agents except initial login clients will starts off as a child agent /// - the later agent crossing will promote it to a root agent. /// /// /// The type of agent to add. /// /// The scene agent if the new client was added or if an agent that already existed. - ISceneAgent AddNewClient(IClientAPI client, PresenceType type); + ISceneAgent AddNewAgent(IClientAPI client, PresenceType type); /// - /// Remove the given client from the scene. + /// Tell a single agent to disconnect from the region. /// /// - /// Close the neighbour child agents associated with this client. - void RemoveClient(UUID agentID, bool closeChildAgents); + /// + /// Force the agent to close even if it might be in the middle of some other operation. You do not want to + /// force unless you are absolutely sure that the agent is dead and a normal close is not working. + /// + bool CloseAgent(UUID agentID, bool force); void Restart(); - //RegionInfo OtherRegionUp(RegionInfo thisRegion); string GetSimulatorVersion(); @@ -136,5 +138,10 @@ namespace OpenSim.Framework ISceneObject DeserializeObject(string representation); bool CheckClient(UUID agentID, System.Net.IPEndPoint ep); + + /// + /// Start the scene and associated scripts within it. + /// + void Start(); } -} +} \ No newline at end of file diff --git a/OpenSim/Framework/ISceneAgent.cs b/OpenSim/Framework/ISceneAgent.cs index 563d906..ca1399c 100644 --- a/OpenSim/Framework/ISceneAgent.cs +++ b/OpenSim/Framework/ISceneAgent.cs @@ -66,12 +66,17 @@ namespace OpenSim.Framework AvatarAppearance Appearance { get; set; } /// + /// Set if initial data about the scene (avatars, objects) has been sent to the client. + /// + bool SentInitialDataToClient { get; } + + /// /// Send initial scene data to the client controlling this agent /// /// /// This includes scene object data and the appearance data of other avatars. /// - void SendInitialDataToMe(); + void SendInitialDataToClient(); /// /// Direction in which the scene presence is looking. diff --git a/OpenSim/Framework/ISceneObject.cs b/OpenSim/Framework/ISceneObject.cs index afac9b8..754b77b 100644 --- a/OpenSim/Framework/ISceneObject.cs +++ b/OpenSim/Framework/ISceneObject.cs @@ -32,6 +32,8 @@ namespace OpenSim.Framework { public interface ISceneObject { + string Name { get; } + UUID UUID { get; } /// diff --git a/OpenSim/Framework/InventoryCollection.cs b/OpenSim/Framework/InventoryCollection.cs index 7049902..59655eb 100644 --- a/OpenSim/Framework/InventoryCollection.cs +++ b/OpenSim/Framework/InventoryCollection.cs @@ -37,6 +37,8 @@ namespace OpenSim.Framework { public List Folders; public List Items; - public UUID UserID; + public UUID OwnerID; + public UUID FolderID; + public int Version; } } diff --git a/OpenSim/Framework/InventoryFolderBase.cs b/OpenSim/Framework/InventoryFolderBase.cs index b3457a6..fb66056 100644 --- a/OpenSim/Framework/InventoryFolderBase.cs +++ b/OpenSim/Framework/InventoryFolderBase.cs @@ -34,6 +34,9 @@ namespace OpenSim.Framework /// public class InventoryFolderBase : InventoryNodeBase { + public static readonly string ROOT_FOLDER_NAME = "My Inventory"; + public static readonly string SUITCASE_FOLDER_NAME = "My Suitcase"; + /// /// The folder this folder is contained in /// diff --git a/OpenSim/Framework/InventoryItemBase.cs b/OpenSim/Framework/InventoryItemBase.cs index 3d45e76..f9fd752 100644 --- a/OpenSim/Framework/InventoryItemBase.cs +++ b/OpenSim/Framework/InventoryItemBase.cs @@ -34,7 +34,7 @@ namespace OpenSim.Framework /// Inventory Item - contains all the properties associated with an individual inventory piece. /// public class InventoryItemBase : InventoryNodeBase, ICloneable - { + { /// /// The inventory type of the item. This is slightly different from the asset type in some situations. /// @@ -82,12 +82,15 @@ namespace OpenSim.Framework set { m_creatorId = value; + + if ((m_creatorId == null) || !UUID.TryParse(m_creatorId, out m_creatorIdAsUuid)) + m_creatorIdAsUuid = UUID.Zero; } } protected string m_creatorId; /// - /// The CreatorId expressed as a UUID.tely + /// The CreatorId expressed as a UUID. /// public UUID CreatorIdAsUuid { @@ -122,7 +125,7 @@ namespace OpenSim.Framework { get { - if (m_creatorData != null && m_creatorData != string.Empty) + if (!string.IsNullOrEmpty(m_creatorData)) return m_creatorId + ';' + m_creatorData; else return m_creatorId; diff --git a/OpenSim/Framework/Location.cs b/OpenSim/Framework/Location.cs index 9504e03..0b88834 100644 --- a/OpenSim/Framework/Location.cs +++ b/OpenSim/Framework/Location.cs @@ -33,10 +33,10 @@ namespace OpenSim.Framework [Serializable] public class Location : ICloneable { - private readonly int m_x; - private readonly int m_y; + private readonly uint m_x; + private readonly uint m_y; - public Location(int x, int y) + public Location(uint x, uint y) { m_x = x; m_y = y; @@ -44,21 +44,21 @@ namespace OpenSim.Framework public Location(ulong regionHandle) { - m_x = (int) regionHandle; - m_y = (int) (regionHandle >> 32); + m_x = (uint)(regionHandle >> 32); + m_y = (uint)(regionHandle & (ulong)uint.MaxValue); } public ulong RegionHandle { - get { return Utils.UIntsToLong((uint)m_x, (uint)m_y); } + get { return Utils.UIntsToLong(m_x, m_y); } } - public int X + public uint X { get { return m_x; } } - public int Y + public uint Y { get { return m_y; } } diff --git a/OpenSim/Framework/LogWriter.cs b/OpenSim/Framework/LogWriter.cs new file mode 100755 index 0000000..2e0bf4a --- /dev/null +++ b/OpenSim/Framework/LogWriter.cs @@ -0,0 +1,181 @@ +/* + * 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 OpenSimulator 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.IO; +using System.Text; +using log4net; + +namespace OpenSim.Framework +{ + /// + /// Class for writing a high performance, high volume log file. + /// Sometimes, to debug, one has a high volume logging to do and the regular + /// log file output is not appropriate. + /// Create a new instance with the parameters needed and + /// call Write() to output a line. Call Close() when finished. + /// If created with no parameters, it will not log anything. + /// + public class LogWriter : IDisposable + { + public bool Enabled { get; private set; } + + private string m_logDirectory = "."; + private int m_logMaxFileTimeMin = 5; // 5 minutes + public String LogFileHeader { get; set; } + + private StreamWriter m_logFile = null; + private TimeSpan m_logFileLife; + private DateTime m_logFileEndTime; + private Object m_logFileWriteLock = new Object(); + private bool m_flushWrite; + + // set externally when debugging. If let 'null', this does not write any error messages. + public ILog ErrorLogger = null; + private string LogHeader = "[LOG WRITER]"; + + /// + /// Create a log writer that will not write anything. Good for when not enabled + /// but the write statements are still in the code. + /// + public LogWriter() + { + Enabled = false; + m_logFile = null; + } + + /// + /// Create a log writer instance. + /// + /// The directory to create the log file in. May be 'null' for default. + /// The characters that begin the log file name. May be 'null' for default. + /// Maximum age of a log file in minutes. If zero, will set default. + /// Whether to do a flush after every log write. Best left off but + /// if one is looking for a crash, this is a good thing to turn on. + public LogWriter(string dir, string headr, int maxFileTime, bool flushWrite) + { + m_logDirectory = dir == null ? "." : dir; + + LogFileHeader = headr == null ? "log-" : headr; + + m_logMaxFileTimeMin = maxFileTime; + if (m_logMaxFileTimeMin < 1) + m_logMaxFileTimeMin = 5; + + m_logFileLife = new TimeSpan(0, m_logMaxFileTimeMin, 0); + m_logFileEndTime = DateTime.Now + m_logFileLife; + + m_flushWrite = flushWrite; + + Enabled = true; + } + // Constructor that assumes flushWrite is off. + public LogWriter(string dir, string headr, int maxFileTime) : this(dir, headr, maxFileTime, false) + { + } + + public void Dispose() + { + this.Close(); + } + + public void Close() + { + Enabled = false; + if (m_logFile != null) + { + m_logFile.Close(); + m_logFile.Dispose(); + m_logFile = null; + } + } + + public void Write(string line, params object[] args) + { + if (!Enabled) return; + Write(String.Format(line, args)); + } + + public void Flush() + { + if (!Enabled) return; + if (m_logFile != null) + { + m_logFile.Flush(); + } + } + + public void Write(string line) + { + if (!Enabled) return; + try + { + lock (m_logFileWriteLock) + { + DateTime now = DateTime.UtcNow; + if (m_logFile == null || now > m_logFileEndTime) + { + if (m_logFile != null) + { + m_logFile.Close(); + m_logFile.Dispose(); + m_logFile = null; + } + + // First log file or time has expired, start writing to a new log file + m_logFileEndTime = now + m_logFileLife; + string path = (m_logDirectory.Length > 0 ? m_logDirectory + + System.IO.Path.DirectorySeparatorChar.ToString() : "") + + String.Format("{0}{1}.log", LogFileHeader, now.ToString("yyyyMMddHHmmss")); + m_logFile = new StreamWriter(File.Open(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite)); + } + if (m_logFile != null) + { + StringBuilder buff = new StringBuilder(line.Length + 25); + buff.Append(now.ToString("yyyyMMddHHmmssfff")); + // buff.Append(now.ToString("yyyyMMddHHmmss")); + buff.Append(","); + buff.Append(line); + buff.Append("\r\n"); + m_logFile.Write(buff.ToString()); + if (m_flushWrite) + m_logFile.Flush(); + } + } + } + catch (Exception e) + { + if (ErrorLogger != null) + { + ErrorLogger.ErrorFormat("{0}: FAILURE WRITING TO LOGFILE: {1}", LogHeader, e); + } + Enabled = false; + } + return; + } + } +} diff --git a/OpenSim/Framework/MapBlockData.cs b/OpenSim/Framework/MapBlockData.cs index 2298ac5..4bee499 100644 --- a/OpenSim/Framework/MapBlockData.cs +++ b/OpenSim/Framework/MapBlockData.cs @@ -27,6 +27,7 @@ using System; using OpenMetaverse; +using OpenMetaverse.StructuredData; namespace OpenSim.Framework { @@ -40,9 +41,26 @@ namespace OpenSim.Framework public byte WaterHeight; public ushort X; public ushort Y; + public ushort SizeX; + public ushort SizeY; public MapBlockData() { } + + public OSDMap ToOSD() + { + OSDMap map = new OSDMap(); + map["X"] = X; + map["Y"] = Y; + map["SizeX"] = SizeX; + map["SizeY"] = SizeY; + map["Name"] = Name; + map["Access"] = Access; + map["RegionFlags"] = RegionFlags; + map["WaterHeight"] = WaterHeight; + map["MapImageID"] = MapImageId; + return map; + } } } diff --git a/OpenSim/Framework/MapItemReplyStruct.cs b/OpenSim/Framework/MapItemReplyStruct.cs index 58011bd..c8693ae 100644 --- a/OpenSim/Framework/MapItemReplyStruct.cs +++ b/OpenSim/Framework/MapItemReplyStruct.cs @@ -26,6 +26,7 @@ */ using OpenMetaverse; +using OpenMetaverse.StructuredData; namespace OpenSim.Framework { @@ -37,5 +38,37 @@ namespace OpenSim.Framework public int Extra; public int Extra2; public string name; + + public mapItemReply(uint pX, uint pY, UUID pId, string pName, int pExt1, int pExt2) + { + x = pX; + y = pY; + id = pId; + name = pName; + Extra = pExt1; + Extra2 = pExt2; + } + + public OSDMap ToOSD() + { + OSDMap map = new OSDMap(); + map["X"] = OSD.FromInteger((int)x); + map["Y"] = OSD.FromInteger((int)y); + map["ID"] = OSD.FromUUID(id); + map["Name"] = OSD.FromString(name); + map["Extra"] = OSD.FromInteger(Extra); + map["Extra2"] = OSD.FromInteger(Extra2); + return map; + } + + public void FromOSD(OSDMap map) + { + x = (uint) map["X"].AsInteger(); + y = (uint) map["Y"].AsInteger(); + id = map["ID"].AsUUID(); + Extra = map["Extra"].AsInteger(); + Extra2 = map["Extra2"].AsInteger(); + name = map["Name"].AsString(); + } } } diff --git a/OpenSim/Framework/MetricsCollector.cs b/OpenSim/Framework/MetricsCollector.cs new file mode 100644 index 0000000..c8f4a33 --- /dev/null +++ b/OpenSim/Framework/MetricsCollector.cs @@ -0,0 +1,223 @@ +using System; +using System.Diagnostics; + +namespace OpenSim.Framework +{ + /// + /// A MetricsCollector for 'long' values. + /// + public class MetricsCollectorLong : MetricsCollector + { + public MetricsCollectorLong(int windowSize, int numBuckets) + : base(windowSize, numBuckets) + { + } + + protected override long GetZero() { return 0; } + + protected override long Add(long a, long b) { return a + b; } + } + + + /// + /// A MetricsCollector for time spans. + /// + public class MetricsCollectorTime : MetricsCollectorLong + { + public MetricsCollectorTime(int windowSize, int numBuckets) + : base(windowSize, numBuckets) + { + } + + public void AddSample(Stopwatch timer) + { + long ticks = timer.ElapsedTicks; + if (ticks > 0) + AddSample(ticks); + } + + public TimeSpan GetSumTime() + { + return TimeSpan.FromMilliseconds((GetSum() * 1000) / Stopwatch.Frequency); + } + } + + + struct MetricsBucket + { + public T value; + public int count; + } + + + /// + /// Collects metrics in a sliding window. + /// + /// + /// MetricsCollector provides the current Sum of the metrics that it collects. It can easily be extended + /// to provide the Average, too. It uses a sliding window to keep these values current. + /// + /// This class is not thread-safe. + /// + /// Subclass MetricsCollector to have it use a concrete value type. Override the abstract methods. + /// + public abstract class MetricsCollector + { + private int bucketSize; // e.g. 3,000 ms + + private MetricsBucket[] buckets; + + private int NumBuckets { get { return buckets.Length; } } + + + // The number of the current bucket, if we had an infinite number of buckets and didn't have to wrap around + long curBucketGlobal; + + // The total of all the buckets + T totalSum; + int totalCount; + + + /// + /// Returns the default (zero) value. + /// + /// + protected abstract T GetZero(); + + /// + /// Adds two values. + /// + protected abstract T Add(T a, T b); + + + /// + /// Creates a MetricsCollector. + /// + /// The period of time over which to collect the metrics, in ms. E.g.: 30,000. + /// The number of buckets to divide the samples into. E.g.: 10. Using more buckets + /// smooths the jarring that occurs whenever we drop an old bucket, but uses more memory. + public MetricsCollector(int windowSize, int numBuckets) + { + bucketSize = windowSize / numBuckets; + buckets = new MetricsBucket[numBuckets]; + Reset(); + } + + public void Reset() + { + ZeroBuckets(0, NumBuckets); + curBucketGlobal = GetNow() / bucketSize; + totalSum = GetZero(); + totalCount = 0; + } + + public void AddSample(T sample) + { + MoveWindow(); + + int curBucket = (int)(curBucketGlobal % NumBuckets); + buckets[curBucket].value = Add(buckets[curBucket].value, sample); + buckets[curBucket].count++; + + totalSum = Add(totalSum, sample); + totalCount++; + } + + /// + /// Returns the total values in the collection window. + /// + public T GetSum() + { + // It might have been a while since we last added a sample, so we may need to adjust the window + MoveWindow(); + + return totalSum; + } + + /// + /// Returns the current time in ms. + /// + private long GetNow() + { + return DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; + } + + /// + /// Clears the values in buckets [offset, offset+num) + /// + private void ZeroBuckets(int offset, int num) + { + for (int i = 0; i < num; i++) + { + buckets[offset + i].value = GetZero(); + buckets[offset + i].count = 0; + } + } + + /// + /// Adjusts the buckets so that the "current bucket" corresponds to the current time. + /// This may require dropping old buckets. + /// + /// + /// This method allows for the possibility that we don't get new samples for each bucket, so the + /// new bucket may be some distance away from the last used bucket. + /// + private void MoveWindow() + { + long newBucketGlobal = GetNow() / bucketSize; + long bucketsDistance = newBucketGlobal - curBucketGlobal; + + if (bucketsDistance == 0) + { + // We're still on the same bucket as before + return; + } + + if (bucketsDistance >= NumBuckets) + { + // Discard everything + Reset(); + return; + } + + int curBucket = (int)(curBucketGlobal % NumBuckets); + int newBucket = (int)(newBucketGlobal % NumBuckets); + + + // Clear all the buckets in this range: (cur, new] + int numToClear = (int)bucketsDistance; + + if (curBucket < NumBuckets - 1) + { + // Clear buckets at the end of the window + int num = Math.Min((int)bucketsDistance, NumBuckets - (curBucket + 1)); + ZeroBuckets(curBucket + 1, num); + numToClear -= num; + } + + if (numToClear > 0) + { + // Clear buckets at the beginning of the window + ZeroBuckets(0, numToClear); + } + + // Move the "current bucket" pointer + curBucketGlobal = newBucketGlobal; + + RecalcTotal(); + } + + private void RecalcTotal() + { + totalSum = GetZero(); + totalCount = 0; + + for (int i = 0; i < NumBuckets; i++) + { + totalSum = Add(totalSum, buckets[i].value); + totalCount += buckets[i].count; + } + } + + } +} diff --git a/OpenSim/Framework/Monitoring/AssetStatsCollector.cs b/OpenSim/Framework/Monitoring/AssetStatsCollector.cs index 2a4d45b..6a0f676 100644 --- a/OpenSim/Framework/Monitoring/AssetStatsCollector.cs +++ b/OpenSim/Framework/Monitoring/AssetStatsCollector.cs @@ -28,6 +28,8 @@ using System; using System.Timers; +using OpenMetaverse.StructuredData; + namespace OpenSim.Framework.Monitoring { /// @@ -100,5 +102,29 @@ Asset requests yesterday : {3} ({4} per hour) of which {5} were not found", AssetRequestsToday, assetRequestsTodayPerHour, AssetRequestsNotFoundToday, AssetRequestsYesterday, assetRequestsYesterdayPerHour, AssetRequestsNotFoundYesterday); } + + public override string XReport(string uptime, string version) + { + return OSDParser.SerializeJsonString(OReport(uptime, version)); + } + + public override OSDMap OReport(string uptime, string version) + { + double elapsedHours = (DateTime.Now - startTime).TotalHours; + if (elapsedHours <= 0) { elapsedHours = 1; } // prevent divide by zero + + long assetRequestsTodayPerHour = (long)Math.Round(AssetRequestsToday / elapsedHours); + long assetRequestsYesterdayPerHour = (long)Math.Round(AssetRequestsYesterday / 24.0); + + OSDMap ret = new OSDMap(); + ret.Add("AssetRequestsToday", OSD.FromLong(AssetRequestsToday)); + ret.Add("AssetRequestsTodayPerHour", OSD.FromLong(assetRequestsTodayPerHour)); + ret.Add("AssetRequestsNotFoundToday", OSD.FromLong(AssetRequestsNotFoundToday)); + ret.Add("AssetRequestsYesterday", OSD.FromLong(AssetRequestsYesterday)); + ret.Add("AssetRequestsYesterdayPerHour", OSD.FromLong(assetRequestsYesterdayPerHour)); + ret.Add("AssetRequestsNotFoundYesterday", OSD.FromLong(assetRequestsNotFoundYesterday)); + + return ret; + } } } diff --git a/OpenSim/Framework/Monitoring/BaseStatsCollector.cs b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs index 2903b6e..20495f6 100644 --- a/OpenSim/Framework/Monitoring/BaseStatsCollector.cs +++ b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * @@ -45,16 +45,16 @@ namespace OpenSim.Framework.Monitoring sb.Append(Environment.NewLine); sb.AppendFormat( - "Allocated to OpenSim objects: {0} MB\n", + "Heap allocated to OpenSim : {0} MB\n", Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0)); sb.AppendFormat( - "OpenSim last object memory churn : {0} MB/s\n", - Math.Round((MemoryWatchdog.LastMemoryChurn * 1000) / 1024.0 / 1024, 3)); + "Last heap allocation rate : {0} MB/s\n", + Math.Round((MemoryWatchdog.LastHeapAllocationRate * 1000) / 1024.0 / 1024, 3)); sb.AppendFormat( - "OpenSim average object memory churn : {0} MB/s\n", - Math.Round((MemoryWatchdog.AverageMemoryChurn * 1000) / 1024.0 / 1024, 3)); + "Average heap allocation rate: {0} MB/s\n", + Math.Round((MemoryWatchdog.AverageHeapAllocationRate * 1000) / 1024.0 / 1024, 3)); sb.AppendFormat( "Process memory : {0} MB\n", @@ -67,5 +67,12 @@ namespace OpenSim.Framework.Monitoring { return (string) Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0).ToString() ; } + + public virtual OSDMap OReport(string uptime, string version) + { + OSDMap ret = new OSDMap(); + ret.Add("TotalMemory", new OSDReal(Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0))); + return ret; + } } } diff --git a/OpenSim/Framework/Monitoring/Checks/Check.cs b/OpenSim/Framework/Monitoring/Checks/Check.cs new file mode 100644 index 0000000..594386a --- /dev/null +++ b/OpenSim/Framework/Monitoring/Checks/Check.cs @@ -0,0 +1,118 @@ +/* + * 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 OpenSimulator 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.Text; + +namespace OpenSim.Framework.Monitoring +{ + public class Check + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static readonly char[] DisallowedShortNameCharacters = { '.' }; + + /// + /// Category of this stat (e.g. cache, scene, etc). + /// + public string Category { get; private set; } + + /// + /// Containing name for this stat. + /// FIXME: In the case of a scene, this is currently the scene name (though this leaves + /// us with a to-be-resolved problem of non-unique region names). + /// + /// + /// The container. + /// + public string Container { get; private set; } + + /// + /// Action used to check whether alert should go off. + /// + /// + /// Should return true if check passes. False otherwise. + /// + public Func CheckFunc { get; private set; } + + /// + /// Message from the last failure, if any. If there is no message or no failure then will be null. + /// + /// + /// Should be set by the CheckFunc when applicable. + /// + public string LastFailureMessage { get; set; } + + public StatVerbosity Verbosity { get; private set; } + public string ShortName { get; private set; } + public string Name { get; private set; } + public string Description { get; private set; } + + public Check( + string shortName, + string name, + string description, + string category, + string container, + Func checkFunc, + StatVerbosity verbosity) + { + if (ChecksManager.SubCommands.Contains(category)) + throw new Exception( + string.Format("Alert cannot be in category '{0}' since this is reserved for a subcommand", category)); + + foreach (char c in DisallowedShortNameCharacters) + { + if (shortName.IndexOf(c) != -1) + throw new Exception(string.Format("Alert name {0} cannot contain character {1}", shortName, c)); + } + + ShortName = shortName; + Name = name; + Description = description; + Category = category; + Container = container; + CheckFunc = checkFunc; + Verbosity = verbosity; + } + + public bool CheckIt() + { + return CheckFunc(this); + } + + public virtual string ToConsoleString() + { + return string.Format( + "{0}.{1}.{2} - {3}", + Category, + Container, + ShortName, + Description); + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Monitoring/ChecksManager.cs b/OpenSim/Framework/Monitoring/ChecksManager.cs new file mode 100644 index 0000000..e4a7f8c --- /dev/null +++ b/OpenSim/Framework/Monitoring/ChecksManager.cs @@ -0,0 +1,262 @@ +/* + * 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 OpenSimulator 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.Linq; +using System.Reflection; +using System.Text; +using log4net; + +namespace OpenSim.Framework.Monitoring +{ + /// + /// Static class used to register/deregister checks on runtime conditions. + /// + public static class ChecksManager + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + // Subcommand used to list other stats. + public const string ListSubCommand = "list"; + + // All subcommands + public static HashSet SubCommands = new HashSet { ListSubCommand }; + + /// + /// Checks categorized by category/container/shortname + /// + /// + /// Do not add or remove directly from this dictionary. + /// + public static SortedDictionary>> RegisteredChecks + = new SortedDictionary>>(); + + public static void RegisterConsoleCommands(ICommandConsole console) + { + console.Commands.AddCommand( + "General", + false, + "show checks", + "show checks", + "Show checks configured for this server", + "If no argument is specified then info on all checks will be shown.\n" + + "'list' argument will show check categories.\n" + + "THIS FACILITY IS EXPERIMENTAL", + HandleShowchecksCommand); + } + + public static void HandleShowchecksCommand(string module, string[] cmd) + { + ICommandConsole con = MainConsole.Instance; + + if (cmd.Length > 2) + { + foreach (string name in cmd.Skip(2)) + { + string[] components = name.Split('.'); + + string categoryName = components[0]; +// string containerName = components.Length > 1 ? components[1] : null; + + if (categoryName == ListSubCommand) + { + con.Output("check categories available are:"); + + foreach (string category in RegisteredChecks.Keys) + con.OutputFormat(" {0}", category); + } +// else +// { +// SortedDictionary> category; +// if (!Registeredchecks.TryGetValue(categoryName, out category)) +// { +// con.OutputFormat("No such category as {0}", categoryName); +// } +// else +// { +// if (String.IsNullOrEmpty(containerName)) +// { +// OutputConfiguredToConsole(con, category); +// } +// else +// { +// SortedDictionary container; +// if (category.TryGetValue(containerName, out container)) +// { +// OutputContainerChecksToConsole(con, container); +// } +// else +// { +// con.OutputFormat("No such container {0} in category {1}", containerName, categoryName); +// } +// } +// } +// } + } + } + else + { + OutputAllChecksToConsole(con); + } + } + + /// + /// Registers a statistic. + /// + /// + /// + public static bool RegisterCheck(Check check) + { + SortedDictionary> category = null, newCategory; + SortedDictionary container = null, newContainer; + + lock (RegisteredChecks) + { + // Check name is not unique across category/container/shortname key. + // XXX: For now just return false. This is to avoid problems in regression tests where all tests + // in a class are run in the same instance of the VM. + if (TryGetCheckParents(check, out category, out container)) + return false; + + // We take a copy-on-write approach here of replacing dictionaries when keys are added or removed. + // This means that we don't need to lock or copy them on iteration, which will be a much more + // common operation after startup. + if (container != null) + newContainer = new SortedDictionary(container); + else + newContainer = new SortedDictionary(); + + if (category != null) + newCategory = new SortedDictionary>(category); + else + newCategory = new SortedDictionary>(); + + newContainer[check.ShortName] = check; + newCategory[check.Container] = newContainer; + RegisteredChecks[check.Category] = newCategory; + } + + return true; + } + + /// + /// Deregister an check + /// > + /// + /// + public static bool DeregisterCheck(Check check) + { + SortedDictionary> category = null, newCategory; + SortedDictionary container = null, newContainer; + + lock (RegisteredChecks) + { + if (!TryGetCheckParents(check, out category, out container)) + return false; + + newContainer = new SortedDictionary(container); + newContainer.Remove(check.ShortName); + + newCategory = new SortedDictionary>(category); + newCategory.Remove(check.Container); + + newCategory[check.Container] = newContainer; + RegisteredChecks[check.Category] = newCategory; + + return true; + } + } + + public static bool TryGetCheckParents( + Check check, + out SortedDictionary> category, + out SortedDictionary container) + { + category = null; + container = null; + + lock (RegisteredChecks) + { + if (RegisteredChecks.TryGetValue(check.Category, out category)) + { + if (category.TryGetValue(check.Container, out container)) + { + if (container.ContainsKey(check.ShortName)) + return true; + } + } + } + + return false; + } + + public static void CheckChecks() + { + lock (RegisteredChecks) + { + foreach (SortedDictionary> category in RegisteredChecks.Values) + { + foreach (SortedDictionary container in category.Values) + { + foreach (Check check in container.Values) + { + if (!check.CheckIt()) + m_log.WarnFormat( + "[CHECKS MANAGER]: Check {0}.{1}.{2} failed with message {3}", check.Category, check.Container, check.ShortName, check.LastFailureMessage); + } + } + } + } + } + + private static void OutputAllChecksToConsole(ICommandConsole con) + { + foreach (var category in RegisteredChecks.Values) + { + OutputCategoryChecksToConsole(con, category); + } + } + + private static void OutputCategoryChecksToConsole( + ICommandConsole con, SortedDictionary> category) + { + foreach (var container in category.Values) + { + OutputContainerChecksToConsole(con, container); + } + } + + private static void OutputContainerChecksToConsole(ICommandConsole con, SortedDictionary container) + { + foreach (Check check in container.Values) + { + con.Output(check.ToConsoleString()); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs b/OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs index 99f75e3..40df562 100644 --- a/OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs +++ b/OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs @@ -25,6 +25,8 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +using OpenMetaverse.StructuredData; + namespace OpenSim.Framework.Monitoring { /// @@ -45,5 +47,12 @@ namespace OpenSim.Framework.Monitoring /// A /// string XReport(string uptime, string version); + + /// + /// Report back collected statistical information as an OSDMap of key/values + /// + /// + /// + OSDMap OReport(string uptime, string version); } } diff --git a/OpenSim/Framework/Monitoring/JobEngine.cs b/OpenSim/Framework/Monitoring/JobEngine.cs new file mode 100644 index 0000000..6db9a67 --- /dev/null +++ b/OpenSim/Framework/Monitoring/JobEngine.cs @@ -0,0 +1,341 @@ +/* + * 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 OpenSimulator 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.Concurrent; +using System.Reflection; +using System.Threading; +using log4net; +using OpenSim.Framework; + +namespace OpenSim.Framework.Monitoring +{ + public class JobEngine + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public int LogLevel { get; set; } + + public string Name { get; private set; } + + public string LoggingName { get; private set; } + + /// + /// Is this engine running? + /// + public bool IsRunning { get; private set; } + + /// + /// The current job that the engine is running. + /// + /// + /// Will be null if no job is currently running. + /// + public Job CurrentJob { get; private set; } + + /// + /// Number of jobs waiting to be processed. + /// + public int JobsWaiting { get { return m_jobQueue.Count; } } + + /// + /// The timeout in milliseconds to wait for at least one event to be written when the recorder is stopping. + /// + public int RequestProcessTimeoutOnStop { get; set; } + + /// + /// Controls whether we need to warn in the log about exceeding the max queue size. + /// + /// + /// This is flipped to false once queue max has been exceeded and back to true when it falls below max, in + /// order to avoid spamming the log with lots of warnings. + /// + private bool m_warnOverMaxQueue = true; + + private BlockingCollection m_jobQueue; + + private CancellationTokenSource m_cancelSource; + + /// + /// Used to signal that we are ready to complete stop. + /// + private ManualResetEvent m_finishedProcessingAfterStop = new ManualResetEvent(false); + + public JobEngine(string name, string loggingName) + { + Name = name; + LoggingName = loggingName; + + RequestProcessTimeoutOnStop = 5000; + } + + public void Start() + { + lock (this) + { + if (IsRunning) + return; + + IsRunning = true; + + m_finishedProcessingAfterStop.Reset(); + + m_jobQueue = new BlockingCollection(new ConcurrentQueue(), 5000); + m_cancelSource = new CancellationTokenSource(); + + WorkManager.StartThread( + ProcessRequests, + Name, + ThreadPriority.Normal, + false, + true, + null, + int.MaxValue); + } + } + + public void Stop() + { + lock (this) + { + try + { + if (!IsRunning) + return; + + IsRunning = false; + + int requestsLeft = m_jobQueue.Count; + + if (requestsLeft <= 0) + { + m_cancelSource.Cancel(); + } + else + { + m_log.InfoFormat("[{0}]: Waiting to write {1} events after stop.", LoggingName, requestsLeft); + + while (requestsLeft > 0) + { + if (!m_finishedProcessingAfterStop.WaitOne(RequestProcessTimeoutOnStop)) + { + // After timeout no events have been written + if (requestsLeft == m_jobQueue.Count) + { + m_log.WarnFormat( + "[{0}]: No requests processed after {1} ms wait. Discarding remaining {2} requests", + LoggingName, RequestProcessTimeoutOnStop, requestsLeft); + + break; + } + } + + requestsLeft = m_jobQueue.Count; + } + } + } + finally + { + m_cancelSource.Dispose(); + } + } + } + + /// + /// Make a job. + /// + /// + /// We provide this method to replace the constructor so that we can later pool job objects if necessary to + /// reduce memory churn. Normally one would directly call QueueJob() with parameters anyway. + /// + /// + /// Name. + /// Action. + /// Common identifier. + public static Job MakeJob(string name, Action action, string commonId = null) + { + return Job.MakeJob(name, action, commonId); + } + + /// + /// Remove the next job queued for processing. + /// + /// + /// Returns null if there is no next job. + /// Will not remove a job currently being performed. + /// + public Job RemoveNextJob() + { + Job nextJob; + m_jobQueue.TryTake(out nextJob); + + return nextJob; + } + + /// + /// Queue the job for processing. + /// + /// true, if job was queued, false otherwise. + /// Name of job. This appears on the console and in logging. + /// Action to perform. + /// + /// Common identifier for a set of jobs. This is allows a set of jobs to be removed + /// if required (e.g. all jobs for a given agent. Optional. + /// + public bool QueueJob(string name, Action action, string commonId = null) + { + return QueueJob(MakeJob(name, action, commonId)); + } + + /// + /// Queue the job for processing. + /// + /// true, if job was queued, false otherwise. + /// The job + /// + public bool QueueJob(Job job) + { + if (m_jobQueue.Count < m_jobQueue.BoundedCapacity) + { + m_jobQueue.Add(job); + + if (!m_warnOverMaxQueue) + m_warnOverMaxQueue = true; + + return true; + } + else + { + if (m_warnOverMaxQueue) + { + m_log.WarnFormat( + "[{0}]: Job queue at maximum capacity, not recording job from {1} in {2}", + LoggingName, job.Name, Name); + + m_warnOverMaxQueue = false; + } + + return false; + } + } + + private void ProcessRequests() + { + try + { + while (IsRunning || m_jobQueue.Count > 0) + { + try + { + CurrentJob = m_jobQueue.Take(m_cancelSource.Token); + } + catch (ObjectDisposedException e) + { + // If we see this whilst not running then it may be due to a race where this thread checks + // IsRunning after the stopping thread sets it to false and disposes of the cancellation source. + if (IsRunning) + throw e; + else + break; + } + + if (LogLevel >= 1) + m_log.DebugFormat("[{0}]: Processing job {1}", LoggingName, CurrentJob.Name); + + try + { + CurrentJob.Action(); + } + catch (Exception e) + { + m_log.Error( + string.Format( + "[{0}]: Job {1} failed, continuing. Exception ", LoggingName, CurrentJob.Name), e); + } + + if (LogLevel >= 1) + m_log.DebugFormat("[{0}]: Processed job {1}", LoggingName, CurrentJob.Name); + + CurrentJob = null; + } + } + catch (OperationCanceledException) + { + } + + m_finishedProcessingAfterStop.Set(); + } + + public class Job + { + /// + /// Name of the job. + /// + /// + /// This appears on console and debug output. + /// + public string Name { get; private set; } + + /// + /// Common ID for this job. + /// + /// + /// This allows all jobs with a certain common ID (e.g. a client UUID) to be removed en-masse if required. + /// Can be null if this is not required. + /// + public string CommonId { get; private set; } + + /// + /// Action to perform when this job is processed. + /// + public Action Action { get; private set; } + + private Job(string name, string commonId, Action action) + { + Name = name; + CommonId = commonId; + Action = action; + } + + /// + /// Make a job. It needs to be separately queued. + /// + /// + /// We provide this method to replace the constructor so that we can pool job objects if necessary to + /// to reduce memory churn. Normally one would directly call JobEngine.QueueJob() with parameters anyway. + /// + /// + /// Name. + /// Action. + /// Common identifier. + public static Job MakeJob(string name, Action action, string commonId = null) + { + return new Job(name, commonId, action); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Monitoring/MemoryWatchdog.cs b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs index c6010cd..c474622 100644 --- a/OpenSim/Framework/Monitoring/MemoryWatchdog.cs +++ b/OpenSim/Framework/Monitoring/MemoryWatchdog.cs @@ -60,17 +60,17 @@ namespace OpenSim.Framework.Monitoring private static bool m_enabled; /// - /// Last memory churn in bytes per millisecond. + /// Average heap allocation rate in bytes per millisecond. /// - public static double AverageMemoryChurn + public static double AverageHeapAllocationRate { get { if (m_samples.Count > 0) return m_samples.Average(); else return 0; } } /// - /// Average memory churn in bytes per millisecond. + /// Last heap allocation in bytes /// - public static double LastMemoryChurn + public static double LastHeapAllocationRate { get { if (m_samples.Count > 0) return m_samples.Last(); else return 0; } } diff --git a/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs b/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs index 1f2bb40..a617b93 100644 --- a/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("0.7.5.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("0.8.3.*")] + diff --git a/OpenSim/Framework/Monitoring/ServerStatsCollector.cs b/OpenSim/Framework/Monitoring/ServerStatsCollector.cs new file mode 100644 index 0000000..77315bb --- /dev/null +++ b/OpenSim/Framework/Monitoring/ServerStatsCollector.cs @@ -0,0 +1,346 @@ +/* + * 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 OpenSimulator 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.Diagnostics; +using System.Linq; +using System.Net.NetworkInformation; +using System.Text; +using System.Threading; +using log4net; +using Nini.Config; +using OpenMetaverse.StructuredData; +using OpenSim.Framework; + +namespace OpenSim.Framework.Monitoring +{ + public class ServerStatsCollector + { + private readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private readonly string LogHeader = "[SERVER STATS]"; + + public bool Enabled = false; + private static Dictionary RegisteredStats = new Dictionary(); + + public readonly string CategoryServer = "server"; + + public readonly string ContainerThreadpool = "threadpool"; + public readonly string ContainerProcessor = "processor"; + public readonly string ContainerMemory = "memory"; + public readonly string ContainerNetwork = "network"; + public readonly string ContainerProcess = "process"; + + public string NetworkInterfaceTypes = "Ethernet"; + + readonly int performanceCounterSampleInterval = 500; +// int lastperformanceCounterSampleTime = 0; + + private class PerfCounterControl + { + public PerformanceCounter perfCounter; + public int lastFetch; + public string name; + public PerfCounterControl(PerformanceCounter pPc) + : this(pPc, String.Empty) + { + } + public PerfCounterControl(PerformanceCounter pPc, string pName) + { + perfCounter = pPc; + lastFetch = 0; + name = pName; + } + } + + PerfCounterControl processorPercentPerfCounter = null; + + // IRegionModuleBase.Initialize + public void Initialise(IConfigSource source) + { + if (source == null) + return; + + IConfig cfg = source.Configs["Monitoring"]; + + if (cfg != null) + Enabled = cfg.GetBoolean("ServerStatsEnabled", true); + + if (Enabled) + { + NetworkInterfaceTypes = cfg.GetString("NetworkInterfaceTypes", "Ethernet"); + } + } + + public void Start() + { + if (RegisteredStats.Count == 0) + RegisterServerStats(); + } + + public void Close() + { + if (RegisteredStats.Count > 0) + { + foreach (Stat stat in RegisteredStats.Values) + { + StatsManager.DeregisterStat(stat); + stat.Dispose(); + } + RegisteredStats.Clear(); + } + } + + private void MakeStat(string pName, string pDesc, string pUnit, string pContainer, Action act) + { + MakeStat(pName, pDesc, pUnit, pContainer, act, MeasuresOfInterest.None); + } + + private void MakeStat(string pName, string pDesc, string pUnit, string pContainer, Action act, MeasuresOfInterest moi) + { + string desc = pDesc; + if (desc == null) + desc = pName; + Stat stat = new Stat(pName, pName, desc, pUnit, CategoryServer, pContainer, StatType.Pull, moi, act, StatVerbosity.Debug); + StatsManager.RegisterStat(stat); + RegisteredStats.Add(pName, stat); + } + + public void RegisterServerStats() + { +// lastperformanceCounterSampleTime = Util.EnvironmentTickCount(); + PerformanceCounter tempPC; + Stat tempStat; + string tempName; + + try + { + tempName = "CPUPercent"; + tempPC = new PerformanceCounter("Processor", "% Processor Time", "_Total"); + processorPercentPerfCounter = new PerfCounterControl(tempPC); + // A long time bug in mono is that CPU percent is reported as CPU percent idle. Windows reports CPU percent busy. + tempStat = new Stat(tempName, tempName, "", "percent", CategoryServer, ContainerProcessor, + StatType.Pull, (s) => { GetNextValue(s, processorPercentPerfCounter); }, + StatVerbosity.Info); + StatsManager.RegisterStat(tempStat); + RegisteredStats.Add(tempName, tempStat); + + MakeStat("TotalProcessorTime", null, "sec", ContainerProcessor, + (s) => { s.Value = Math.Round(Process.GetCurrentProcess().TotalProcessorTime.TotalSeconds, 3); }); + + MakeStat("UserProcessorTime", null, "sec", ContainerProcessor, + (s) => { s.Value = Math.Round(Process.GetCurrentProcess().UserProcessorTime.TotalSeconds, 3); }); + + MakeStat("PrivilegedProcessorTime", null, "sec", ContainerProcessor, + (s) => { s.Value = Math.Round(Process.GetCurrentProcess().PrivilegedProcessorTime.TotalSeconds, 3); }); + + MakeStat("Threads", null, "threads", ContainerProcessor, + (s) => { s.Value = Process.GetCurrentProcess().Threads.Count; }); + } + catch (Exception e) + { + m_log.ErrorFormat("{0} Exception creating 'Process': {1}", LogHeader, e); + } + + MakeStat("BuiltinThreadpoolWorkerThreadsAvailable", null, "threads", ContainerThreadpool, + s => + { + int workerThreads, iocpThreads; + ThreadPool.GetAvailableThreads(out workerThreads, out iocpThreads); + s.Value = workerThreads; + }); + + MakeStat("BuiltinThreadpoolIOCPThreadsAvailable", null, "threads", ContainerThreadpool, + s => + { + int workerThreads, iocpThreads; + ThreadPool.GetAvailableThreads(out workerThreads, out iocpThreads); + s.Value = iocpThreads; + }); + + if (Util.FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool && Util.GetSmartThreadPoolInfo() != null) + { + MakeStat("STPMaxThreads", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().MaxThreads); + MakeStat("STPMinThreads", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().MinThreads); + MakeStat("STPConcurrency", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().MaxConcurrentWorkItems); + MakeStat("STPActiveThreads", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().ActiveThreads); + MakeStat("STPInUseThreads", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().InUseThreads); + MakeStat("STPWorkItemsWaiting", null, "threads", ContainerThreadpool, s => s.Value = Util.GetSmartThreadPoolInfo().WaitingCallbacks); + } + + MakeStat( + "HTTPRequestsMade", + "Number of outbound HTTP requests made", + "requests", + ContainerNetwork, + s => s.Value = WebUtil.RequestNumber, + MeasuresOfInterest.AverageChangeOverTime); + + try + { + List okInterfaceTypes = new List(NetworkInterfaceTypes.Split(',')); + + IEnumerable nics = NetworkInterface.GetAllNetworkInterfaces(); + foreach (NetworkInterface nic in nics) + { + if (nic.OperationalStatus != OperationalStatus.Up) + continue; + + string nicInterfaceType = nic.NetworkInterfaceType.ToString(); + if (!okInterfaceTypes.Contains(nicInterfaceType)) + { + m_log.DebugFormat("{0} Not including stats for network interface '{1}' of type '{2}'.", + LogHeader, nic.Name, nicInterfaceType); + m_log.DebugFormat("{0} To include, add to comma separated list in [Monitoring]NetworkInterfaceTypes={1}", + LogHeader, NetworkInterfaceTypes); + continue; + } + + if (nic.Supports(NetworkInterfaceComponent.IPv4)) + { + IPv4InterfaceStatistics nicStats = nic.GetIPv4Statistics(); + if (nicStats != null) + { + MakeStat("BytesRcvd/" + nic.Name, nic.Name, "KB", ContainerNetwork, + (s) => { LookupNic(s, (ns) => { return ns.BytesReceived; }, 1024.0); }); + MakeStat("BytesSent/" + nic.Name, nic.Name, "KB", ContainerNetwork, + (s) => { LookupNic(s, (ns) => { return ns.BytesSent; }, 1024.0); }); + MakeStat("TotalBytes/" + nic.Name, nic.Name, "KB", ContainerNetwork, + (s) => { LookupNic(s, (ns) => { return ns.BytesSent + ns.BytesReceived; }, 1024.0); }); + } + } + // TODO: add IPv6 (it may actually happen someday) + } + } + catch (Exception e) + { + m_log.ErrorFormat("{0} Exception creating 'Network Interface': {1}", LogHeader, e); + } + + MakeStat("ProcessMemory", null, "MB", ContainerMemory, + (s) => { s.Value = Math.Round(Process.GetCurrentProcess().WorkingSet64 / 1024d / 1024d, 3); }); + MakeStat("HeapMemory", null, "MB", ContainerMemory, + (s) => { s.Value = Math.Round(GC.GetTotalMemory(false) / 1024d / 1024d, 3); }); + MakeStat("LastHeapAllocationRate", null, "MB/sec", ContainerMemory, + (s) => { s.Value = Math.Round(MemoryWatchdog.LastHeapAllocationRate * 1000d / 1024d / 1024d, 3); }); + MakeStat("AverageHeapAllocationRate", null, "MB/sec", ContainerMemory, + (s) => { s.Value = Math.Round(MemoryWatchdog.AverageHeapAllocationRate * 1000d / 1024d / 1024d, 3); }); + } + + // Notes on performance counters: + // "How To Read Performance Counters": http://blogs.msdn.com/b/bclteam/archive/2006/06/02/618156.aspx + // "How to get the CPU Usage in C#": http://stackoverflow.com/questions/278071/how-to-get-the-cpu-usage-in-c + // "Mono Performance Counters": http://www.mono-project.com/Mono_Performance_Counters + private delegate double PerfCounterNextValue(); + + private void GetNextValue(Stat stat, PerfCounterControl perfControl) + { + if (Util.EnvironmentTickCountSubtract(perfControl.lastFetch) > performanceCounterSampleInterval) + { + if (perfControl != null && perfControl.perfCounter != null) + { + try + { + stat.Value = Math.Round(perfControl.perfCounter.NextValue(), 3); + } + catch (Exception e) + { + m_log.ErrorFormat("{0} Exception on NextValue fetching {1}: {2}", LogHeader, stat.Name, e); + } + + perfControl.lastFetch = Util.EnvironmentTickCount(); + } + } + } + + // Lookup the nic that goes with this stat and set the value by using a fetch action. + // Not sure about closure with delegates inside delegates. + private delegate double GetIPv4StatValue(IPv4InterfaceStatistics interfaceStat); + private void LookupNic(Stat stat, GetIPv4StatValue getter, double factor) + { + // Get the one nic that has the name of this stat + IEnumerable nics = NetworkInterface.GetAllNetworkInterfaces().Where( + (network) => network.Name == stat.Description); + try + { + foreach (NetworkInterface nic in nics) + { + IPv4InterfaceStatistics intrStats = nic.GetIPv4Statistics(); + if (intrStats != null) + { + double newVal = Math.Round(getter(intrStats) / factor, 3); + stat.Value = newVal; + } + break; + } + } + catch + { + // There are times interfaces go away so we just won't update the stat for this + m_log.ErrorFormat("{0} Exception fetching stat on interface '{1}'", LogHeader, stat.Description); + } + } + } + + public class ServerStatsAggregator : Stat + { + public ServerStatsAggregator( + string shortName, + string name, + string description, + string unitName, + string category, + string container + ) + : base( + shortName, + name, + description, + unitName, + category, + container, + StatType.Push, + MeasuresOfInterest.None, + null, + StatVerbosity.Info) + { + } + public override string ToConsoleString() + { + StringBuilder sb = new StringBuilder(); + + return sb.ToString(); + } + + public override OSDMap ToOSDMap() + { + OSDMap ret = new OSDMap(); + + return ret; + } + } +} diff --git a/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs old mode 100644 new mode 100755 index aa86202..e4df7ee --- a/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs +++ b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs @@ -27,6 +27,8 @@ using System; using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; using System.Text; using OpenMetaverse; using OpenMetaverse.StructuredData; @@ -39,8 +41,6 @@ namespace OpenSim.Framework.Monitoring /// public class SimExtraStatsCollector : BaseStatsCollector { - private long abnormalClientThreadTerminations; - // private long assetsInCache; // private long texturesInCache; // private long assetCacheMemoryUsage; @@ -72,11 +72,11 @@ namespace OpenSim.Framework.Monitoring private volatile float pendingUploads; private volatile float activeScripts; private volatile float scriptLinesPerSecond; - - /// - /// Number of times that a client thread terminated because of an exception - /// - public long AbnormalClientThreadTerminations { get { return abnormalClientThreadTerminations; } } + private volatile float m_frameDilation; + private volatile float m_usersLoggingIn; + private volatile float m_totalGeoPrims; + private volatile float m_totalMeshes; + private volatile float m_inUseThreads; // /// // /// These statistics are being collected by push rather than pull. Pull would be simpler, but I had the @@ -166,11 +166,6 @@ namespace OpenSim.Framework.Monitoring private IDictionary packetQueueStatsCollectors = new Dictionary(); - public void AddAbnormalClientThreadTermination() - { - abnormalClientThreadTerminations++; - } - // public void AddAsset(AssetBase asset) // { // assetsInCache++; @@ -260,6 +255,10 @@ namespace OpenSim.Framework.Monitoring { // FIXME: SimStats shouldn't allow an arbitrary stat packing order (which is inherited from the original // SimStatsPacket that was being used). + + // For an unknown reason the original designers decided not to + // include the spare MS statistic inside of this class, this is + // located inside the StatsBlock at location 21, thus it is skipped timeDilation = stats.StatsBlock[0].StatValue; simFps = stats.StatsBlock[1].StatValue; physicsFps = stats.StatsBlock[2].StatValue; @@ -281,6 +280,11 @@ namespace OpenSim.Framework.Monitoring pendingUploads = stats.StatsBlock[18].StatValue; activeScripts = stats.StatsBlock[19].StatValue; scriptLinesPerSecond = stats.StatsBlock[20].StatValue; + m_frameDilation = stats.StatsBlock[22].StatValue; + m_usersLoggingIn = stats.StatsBlock[23].StatValue; + m_totalGeoPrims = stats.StatsBlock[24].StatValue; + m_totalMeshes = stats.StatsBlock[25].StatValue; + m_inUseThreads = stats.StatsBlock[26].StatValue; } /// @@ -324,10 +328,12 @@ Asset service request failures: {3}" + Environment.NewLine, sb.Append(Environment.NewLine); sb.Append("CONNECTION STATISTICS"); sb.Append(Environment.NewLine); - sb.Append( - string.Format( - "Abnormal client thread terminations: {0}" + Environment.NewLine, - abnormalClientThreadTerminations)); + + List stats = StatsManager.GetStatsFromEachContainer("clientstack", "ClientLogoutsDueToNoReceives"); + + sb.AppendFormat( + "Client logouts due to no data receive timeout: {0}\n\n", + stats != null ? stats.Sum(s => s.Value).ToString() : "unknown"); // sb.Append(Environment.NewLine); // sb.Append("INVENTORY STATISTICS"); @@ -338,7 +344,7 @@ Asset service request failures: {3}" + Environment.NewLine, // InventoryServiceRetrievalFailures)); sb.Append(Environment.NewLine); - sb.Append("FRAME STATISTICS"); + sb.Append("SAMPLE FRAME STATISTICS"); sb.Append(Environment.NewLine); sb.Append("Dilatn SimFPS PhyFPS AgntUp RootAg ChldAg Prims AtvPrm AtvScr ScrLPS"); sb.Append(Environment.NewLine); @@ -359,11 +365,12 @@ Asset service request failures: {3}" + Environment.NewLine, inPacketsPerSecond, outPacketsPerSecond, pendingDownloads, pendingUploads, unackedBytes, totalFrameTime, netFrameTime, physicsFrameTime, otherFrameTime, agentFrameTime, imageFrameTime)); - Dictionary> sceneStats; - + /* 20130319 RA: For the moment, disable the dump of 'scene' catagory as they are mostly output by + * the two formatted printouts above. + SortedDictionary> sceneStats; if (StatsManager.TryGetStats("scene", out sceneStats)) { - foreach (KeyValuePair> kvp in sceneStats) + foreach (KeyValuePair> kvp in sceneStats) { foreach (Stat stat in kvp.Value.Values) { @@ -374,6 +381,7 @@ Asset service request failures: {3}" + Environment.NewLine, } } } + */ /* sb.Append(Environment.NewLine); @@ -405,6 +413,36 @@ Asset service request failures: {3}" + Environment.NewLine, /// public override string XReport(string uptime, string version) { + return OSDParser.SerializeJsonString(OReport(uptime, version)); + } + + /// + /// Report back collected statistical information as an OSDMap + /// + /// + public override OSDMap OReport(string uptime, string version) + { + // Get the amount of physical memory, allocated with the instance of this program, in kilobytes; + // the working set is the set of memory pages currently visible to this program in physical RAM + // memory and includes both shared (e.g. system libraries) and private data + double memUsage = Process.GetCurrentProcess().WorkingSet64 / 1024.0; + + // Get the number of threads from the system that are currently + // running + int numberThreadsRunning = 0; + foreach (ProcessThread currentThread in + Process.GetCurrentProcess().Threads) + { + // A known issue with the current process .Threads property is + // that it can return null threads, thus don't count those as + // running threads and prevent the program function from failing + if (currentThread != null && + currentThread.ThreadState == ThreadState.Running) + { + numberThreadsRunning++; + } + } + OSDMap args = new OSDMap(30); // args["AssetsInCache"] = OSD.FromString (String.Format ("{0:0.##}", AssetsInCache)); // args["TimeAfterCacheMiss"] = OSD.FromString (String.Format ("{0:0.##}", @@ -441,14 +479,28 @@ Asset service request failures: {3}" + Environment.NewLine, args["Memory"] = OSD.FromString (base.XReport (uptime, version)); args["Uptime"] = OSD.FromString (uptime); args["Version"] = OSD.FromString (version); - - string strBuffer = ""; - strBuffer = OSDParser.SerializeJsonString(args); - return strBuffer; + args["FrameDilatn"] = OSD.FromString(String.Format("{0:0.##}", m_frameDilation)); + args["Logging in Users"] = OSD.FromString(String.Format("{0:0.##}", + m_usersLoggingIn)); + args["GeoPrims"] = OSD.FromString(String.Format("{0:0.##}", + m_totalGeoPrims)); + args["Mesh Objects"] = OSD.FromString(String.Format("{0:0.##}", + m_totalMeshes)); + args["XEngine Thread Count"] = OSD.FromString(String.Format("{0:0.##}", + m_inUseThreads)); + args["Util Thread Count"] = OSD.FromString(String.Format("{0:0.##}", + Util.GetSmartThreadPoolInfo().InUseThreads)); + args["System Thread Count"] = OSD.FromString(String.Format( + "{0:0.##}", numberThreadsRunning)); + args["ProcMem"] = OSD.FromString(String.Format("{0:#,###,###.##}", + memUsage)); + + return args; } } + /// /// Pull packet queue stats from packet queues and report /// @@ -474,5 +526,11 @@ Asset service request failures: {3}" + Environment.NewLine, { return ""; } + + public OSDMap OReport(string uptime, string version) + { + OSDMap ret = new OSDMap(); + return ret; + } } } diff --git a/OpenSim/Framework/Monitoring/Stats/CounterStat.cs b/OpenSim/Framework/Monitoring/Stats/CounterStat.cs new file mode 100755 index 0000000..318cf1c --- /dev/null +++ b/OpenSim/Framework/Monitoring/Stats/CounterStat.cs @@ -0,0 +1,119 @@ +/* + * 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 OpenSimulator 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.Linq; +using System.Text; + +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework.Monitoring +{ +// A statistic that wraps a counter. +// Built this way mostly so histograms and history can be created. +public class CounterStat : Stat +{ + private SortedDictionary m_histograms; + private object counterLock = new object(); + + public CounterStat( + string shortName, + string name, + string description, + string unitName, + string category, + string container, + StatVerbosity verbosity) + : base(shortName, name, description, unitName, category, container, StatType.Push, null, verbosity) + { + m_histograms = new SortedDictionary(); + } + + // Histograms are presumably added at intialization time and the list does not change thereafter. + // Thus no locking of the histogram list. + public void AddHistogram(string histoName, EventHistogram histo) + { + m_histograms.Add(histoName, histo); + } + + public delegate void ProcessHistogram(string name, EventHistogram histo); + public void ForEachHistogram(ProcessHistogram process) + { + foreach (KeyValuePair kvp in m_histograms) + { + process(kvp.Key, kvp.Value); + } + } + + public void Event() + { + this.Event(1); + } + + // Count the underlying counter. + public void Event(int cnt) + { + lock (counterLock) + { + base.Value += cnt; + + foreach (EventHistogram histo in m_histograms.Values) + { + histo.Event(cnt); + } + } + } + + // CounterStat is a basic stat plus histograms + public override OSDMap ToOSDMap() + { + // Get the foundational instance + OSDMap map = base.ToOSDMap(); + + map["StatType"] = "CounterStat"; + + // If there are any histograms, add a new field that is an array of histograms as OSDMaps + if (m_histograms.Count > 0) + { + lock (counterLock) + { + if (m_histograms.Count > 0) + { + OSDArray histos = new OSDArray(); + foreach (EventHistogram histo in m_histograms.Values) + { + histos.Add(histo.GetHistogramAsOSDMap()); + } + map.Add("Histograms", histos); + } + } + } + return map; + } +} +} diff --git a/OpenSim/Framework/Monitoring/Stats/EventHistogram.cs b/OpenSim/Framework/Monitoring/Stats/EventHistogram.cs new file mode 100755 index 0000000..f51f322 --- /dev/null +++ b/OpenSim/Framework/Monitoring/Stats/EventHistogram.cs @@ -0,0 +1,173 @@ +/* + * 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 OpenSimulator 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.Linq; +using System.Text; + +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework.Monitoring +{ +// Create a time histogram of events. The histogram is built in a wrap-around +// array of equally distributed buckets. +// For instance, a minute long histogram of second sized buckets would be: +// new EventHistogram(60, 1000) +public class EventHistogram +{ + private int m_timeBase; + private int m_numBuckets; + private int m_bucketMilliseconds; + private int m_lastBucket; + private int m_totalHistogramMilliseconds; + private long[] m_histogram; + private object histoLock = new object(); + + public EventHistogram(int numberOfBuckets, int millisecondsPerBucket) + { + m_numBuckets = numberOfBuckets; + m_bucketMilliseconds = millisecondsPerBucket; + m_totalHistogramMilliseconds = m_numBuckets * m_bucketMilliseconds; + + m_histogram = new long[m_numBuckets]; + Zero(); + m_lastBucket = 0; + m_timeBase = Util.EnvironmentTickCount(); + } + + public void Event() + { + this.Event(1); + } + + // Record an event at time 'now' in the histogram. + public void Event(int cnt) + { + lock (histoLock) + { + // The time as displaced from the base of the histogram + int bucketTime = Util.EnvironmentTickCountSubtract(m_timeBase); + + // If more than the total time of the histogram, we just start over + if (bucketTime > m_totalHistogramMilliseconds) + { + Zero(); + m_lastBucket = 0; + m_timeBase = Util.EnvironmentTickCount(); + } + else + { + // To which bucket should we add this event? + int bucket = bucketTime / m_bucketMilliseconds; + + // Advance m_lastBucket to the new bucket. Zero any buckets skipped over. + while (bucket != m_lastBucket) + { + // Zero from just after the last bucket to the new bucket or the end + for (int jj = m_lastBucket + 1; jj <= Math.Min(bucket, m_numBuckets - 1); jj++) + { + m_histogram[jj] = 0; + } + m_lastBucket = bucket; + // If the new bucket is off the end, wrap around to the beginning + if (bucket > m_numBuckets) + { + bucket -= m_numBuckets; + m_lastBucket = 0; + m_histogram[m_lastBucket] = 0; + m_timeBase += m_totalHistogramMilliseconds; + } + } + } + m_histogram[m_lastBucket] += cnt; + } + } + + // Get a copy of the current histogram + public long[] GetHistogram() + { + long[] ret = new long[m_numBuckets]; + lock (histoLock) + { + int indx = m_lastBucket + 1; + for (int ii = 0; ii < m_numBuckets; ii++, indx++) + { + if (indx >= m_numBuckets) + indx = 0; + ret[ii] = m_histogram[indx]; + } + } + return ret; + } + + public OSDMap GetHistogramAsOSDMap() + { + OSDMap ret = new OSDMap(); + + ret.Add("Buckets", OSD.FromInteger(m_numBuckets)); + ret.Add("BucketMilliseconds", OSD.FromInteger(m_bucketMilliseconds)); + ret.Add("TotalMilliseconds", OSD.FromInteger(m_totalHistogramMilliseconds)); + + // Compute a number for the first bucket in the histogram. + // This will allow readers to know how this histogram relates to any previously read histogram. + int baseBucketNum = (m_timeBase / m_bucketMilliseconds) + m_lastBucket + 1; + ret.Add("BaseNumber", OSD.FromInteger(baseBucketNum)); + + ret.Add("Values", GetHistogramAsOSDArray()); + + return ret; + } + // Get a copy of the current histogram + public OSDArray GetHistogramAsOSDArray() + { + OSDArray ret = new OSDArray(m_numBuckets); + lock (histoLock) + { + int indx = m_lastBucket + 1; + for (int ii = 0; ii < m_numBuckets; ii++, indx++) + { + if (indx >= m_numBuckets) + indx = 0; + ret[ii] = OSD.FromLong(m_histogram[indx]); + } + } + return ret; + } + + // Zero out the histogram + public void Zero() + { + lock (histoLock) + { + for (int ii = 0; ii < m_numBuckets; ii++) + m_histogram[ii] = 0; + } + } +} + +} diff --git a/OpenSim/Framework/Monitoring/Stats/PercentageStat.cs b/OpenSim/Framework/Monitoring/Stats/PercentageStat.cs index 60bed55..55ddf06 100644 --- a/OpenSim/Framework/Monitoring/Stats/PercentageStat.cs +++ b/OpenSim/Framework/Monitoring/Stats/PercentageStat.cs @@ -29,6 +29,8 @@ using System; using System.Collections.Generic; using System.Text; +using OpenMetaverse.StructuredData; + namespace OpenSim.Framework.Monitoring { public class PercentageStat : Stat @@ -84,5 +86,19 @@ namespace OpenSim.Framework.Monitoring return sb.ToString(); } + + // PercentageStat is a basic stat plus percent calc + public override OSDMap ToOSDMap() + { + // Get the foundational instance + OSDMap map = base.ToOSDMap(); + + map["StatType"] = "PercentageStat"; + + map.Add("Antecedent", OSD.FromLong(Antecedent)); + map.Add("Consequent", OSD.FromLong(Consequent)); + + return map; + } } } \ No newline at end of file diff --git a/OpenSim/Framework/Monitoring/Stats/Stat.cs b/OpenSim/Framework/Monitoring/Stats/Stat.cs index f91251b..a7cb2a6 100644 --- a/OpenSim/Framework/Monitoring/Stats/Stat.cs +++ b/OpenSim/Framework/Monitoring/Stats/Stat.cs @@ -27,15 +27,23 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Reflection; using System.Text; +using log4net; +using OpenMetaverse.StructuredData; namespace OpenSim.Framework.Monitoring { /// /// Holds individual statistic details /// - public class Stat + public class Stat : IDisposable { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static readonly char[] DisallowedShortNameCharacters = { '.' }; + /// /// Category of this stat (e.g. cache, scene, etc). /// @@ -93,7 +101,7 @@ namespace OpenSim.Framework.Monitoring /// /// Will be null if no measures of interest require samples. /// - private static Queue m_samples; + private Queue m_samples; /// /// Maximum number of statistical samples. @@ -160,6 +168,13 @@ namespace OpenSim.Framework.Monitoring throw new Exception( string.Format("Stat cannot be in category '{0}' since this is reserved for a subcommand", category)); + foreach (char c in DisallowedShortNameCharacters) + { + if (shortName.IndexOf(c) != -1) + shortName = shortName.Replace(c, '#'); +// throw new Exception(string.Format("Stat name {0} cannot contain character {1}", shortName, c)); + } + ShortName = shortName; Name = name; Description = description; @@ -181,6 +196,12 @@ namespace OpenSim.Framework.Monitoring Verbosity = verbosity; } + // IDisposable.Dispose() + public virtual void Dispose() + { + return; + } + /// /// Record a value in the sample set. /// @@ -196,6 +217,8 @@ namespace OpenSim.Framework.Monitoring if (m_samples.Count >= m_maxSamples) m_samples.Dequeue(); +// m_log.DebugFormat("[STAT]: Recording value {0} for {1}", newValue, Name); + m_samples.Enqueue(newValue); } } @@ -203,35 +226,100 @@ namespace OpenSim.Framework.Monitoring public virtual string ToConsoleString() { StringBuilder sb = new StringBuilder(); - sb.AppendFormat("{0}.{1}.{2} : {3}{4}", Category, Container, ShortName, Value, UnitName); + sb.AppendFormat( + "{0}.{1}.{2} : {3}{4}", + Category, + Container, + ShortName, + Value, + string.IsNullOrEmpty(UnitName) ? "" : string.Format(" {0}", UnitName)); AppendMeasuresOfInterest(sb); return sb.ToString(); } - protected void AppendMeasuresOfInterest(StringBuilder sb) + public virtual OSDMap ToOSDMap() { - if ((MeasuresOfInterest & MeasuresOfInterest.AverageChangeOverTime) - == MeasuresOfInterest.AverageChangeOverTime) + OSDMap ret = new OSDMap(); + ret.Add("StatType", "Stat"); // used by overloading classes to denote type of stat + + ret.Add("Category", OSD.FromString(Category)); + ret.Add("Container", OSD.FromString(Container)); + ret.Add("ShortName", OSD.FromString(ShortName)); + ret.Add("Name", OSD.FromString(Name)); + ret.Add("Description", OSD.FromString(Description)); + ret.Add("UnitName", OSD.FromString(UnitName)); + ret.Add("Value", OSD.FromReal(Value)); + + double lastChangeOverTime, averageChangeOverTime; + if (ComputeMeasuresOfInterest(out lastChangeOverTime, out averageChangeOverTime)) + { + ret.Add("LastChangeOverTime", OSD.FromReal(lastChangeOverTime)); + ret.Add("AverageChangeOverTime", OSD.FromReal(averageChangeOverTime)); + } + + return ret; + } + + // Compute the averages over time and return same. + // Return 'true' if averages were actually computed. 'false' if no average info. + public bool ComputeMeasuresOfInterest(out double lastChangeOverTime, out double averageChangeOverTime) + { + bool ret = false; + lastChangeOverTime = 0; + averageChangeOverTime = 0; + + if ((MeasuresOfInterest & MeasuresOfInterest.AverageChangeOverTime) == MeasuresOfInterest.AverageChangeOverTime) { double totalChange = 0; + double? penultimateSample = null; double? lastSample = null; lock (m_samples) { + // m_log.DebugFormat( + // "[STAT]: Samples for {0} are {1}", + // Name, string.Join(",", m_samples.Select(s => s.ToString()).ToArray())); + foreach (double s in m_samples) { if (lastSample != null) totalChange += s - (double)lastSample; + penultimateSample = lastSample; lastSample = s; } } + if (lastSample != null && penultimateSample != null) + { + lastChangeOverTime + = ((double)lastSample - (double)penultimateSample) / (Watchdog.WATCHDOG_INTERVAL_MS / 1000); + } + int divisor = m_samples.Count <= 1 ? 1 : m_samples.Count - 1; - sb.AppendFormat(", {0:0.##}{1}/s", totalChange / divisor / (Watchdog.WATCHDOG_INTERVAL_MS / 1000), UnitName); + averageChangeOverTime = totalChange / divisor / (Watchdog.WATCHDOG_INTERVAL_MS / 1000); + ret = true; + } + + return ret; + } + + protected void AppendMeasuresOfInterest(StringBuilder sb) + { + double lastChangeOverTime = 0; + double averageChangeOverTime = 0; + + if (ComputeMeasuresOfInterest(out lastChangeOverTime, out averageChangeOverTime)) + { + sb.AppendFormat( + ", {0:0.##}{1}/s, {2:0.##}{3}/s", + lastChangeOverTime, + string.IsNullOrEmpty(UnitName) ? "" : string.Format(" {0}", UnitName), + averageChangeOverTime, + string.IsNullOrEmpty(UnitName) ? "" : string.Format(" {0}", UnitName)); } } } diff --git a/OpenSim/Framework/Monitoring/StatsLogger.cs b/OpenSim/Framework/Monitoring/StatsLogger.cs new file mode 100644 index 0000000..15a37aa --- /dev/null +++ b/OpenSim/Framework/Monitoring/StatsLogger.cs @@ -0,0 +1,151 @@ +/* + * 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 OpenSimulator 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.Reflection; +using System.Text; +using System.Timers; +using log4net; + +namespace OpenSim.Framework.Monitoring +{ + /// + /// Provides a means to continuously log stats for debugging purposes. + /// + public static class StatsLogger + { + private static readonly ILog m_statsLog = LogManager.GetLogger("special.StatsLogger"); + + private static Timer m_loggingTimer; + private static int m_statsLogIntervalMs = 5000; + + public static void RegisterConsoleCommands(ICommandConsole console) + { + console.Commands.AddCommand( + "General", + false, + "stats record", + "stats record start|stop", + "Control whether stats are being regularly recorded to a separate file.", + "For debug purposes. Experimental.", + HandleStatsRecordCommand); + + console.Commands.AddCommand( + "General", + false, + "stats save", + "stats save ", + "Save stats snapshot to a file. If the file already exists, then the report is appended.", + "For debug purposes. Experimental.", + HandleStatsSaveCommand); + } + + public static void HandleStatsRecordCommand(string module, string[] cmd) + { + ICommandConsole con = MainConsole.Instance; + + if (cmd.Length != 3) + { + con.Output("Usage: stats record start|stop"); + return; + } + + if (cmd[2] == "start") + { + Start(); + con.OutputFormat("Now recording all stats to file every {0}ms", m_statsLogIntervalMs); + } + else if (cmd[2] == "stop") + { + Stop(); + con.Output("Stopped recording stats to file."); + } + } + + public static void HandleStatsSaveCommand(string module, string[] cmd) + { + ICommandConsole con = MainConsole.Instance; + + if (cmd.Length != 3) + { + con.Output("Usage: stats save "); + return; + } + + string path = cmd[2]; + + using (StreamWriter sw = new StreamWriter(path, true)) + { + foreach (string line in GetReport()) + sw.WriteLine(line); + } + + MainConsole.Instance.OutputFormat("Stats saved to file {0}", path); + } + + public static void Start() + { + if (m_loggingTimer != null) + Stop(); + + m_loggingTimer = new Timer(m_statsLogIntervalMs); + m_loggingTimer.AutoReset = false; + m_loggingTimer.Elapsed += Log; + m_loggingTimer.Start(); + } + + public static void Stop() + { + if (m_loggingTimer != null) + { + m_loggingTimer.Stop(); + } + } + + private static void Log(object sender, ElapsedEventArgs e) + { + foreach (string line in GetReport()) + m_statsLog.Info(line); + + m_loggingTimer.Start(); + } + + private static List GetReport() + { + List lines = new List(); + + lines.Add(string.Format("*** STATS REPORT AT {0} ***", DateTime.Now)); + + foreach (string report in StatsManager.GetAllStatsReports()) + lines.Add(report); + + return lines; + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Monitoring/StatsManager.cs b/OpenSim/Framework/Monitoring/StatsManager.cs index 0762b01..3136ee8 100644 --- a/OpenSim/Framework/Monitoring/StatsManager.cs +++ b/OpenSim/Framework/Monitoring/StatsManager.cs @@ -26,15 +26,20 @@ */ using System; +using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Text; +using OpenSim.Framework; +using OpenMetaverse.StructuredData; + namespace OpenSim.Framework.Monitoring { /// - /// Singleton used to provide access to statistics reporters + /// Static class used to register/deregister/fetch statistics /// - public class StatsManager + public static class StatsManager { // Subcommand used to list other stats. public const string AllSubCommand = "all"; @@ -51,31 +56,43 @@ namespace OpenSim.Framework.Monitoring /// /// Do not add or remove directly from this dictionary. /// - public static Dictionary>> RegisteredStats - = new Dictionary>>(); + public static SortedDictionary>> RegisteredStats + = new SortedDictionary>>(); - private static AssetStatsCollector assetStats; - private static UserStatsCollector userStats; - private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector(); +// private static AssetStatsCollector assetStats; +// private static UserStatsCollector userStats; +// private static SimExtraStatsCollector simExtraStats = new SimExtraStatsCollector(); - public static AssetStatsCollector AssetStats { get { return assetStats; } } - public static UserStatsCollector UserStats { get { return userStats; } } - public static SimExtraStatsCollector SimExtraStats { get { return simExtraStats; } } +// public static AssetStatsCollector AssetStats { get { return assetStats; } } +// public static UserStatsCollector UserStats { get { return userStats; } } + public static SimExtraStatsCollector SimExtraStats { get; set; } public static void RegisterConsoleCommands(ICommandConsole console) { console.Commands.AddCommand( "General", false, - "show stats", - "show stats [list|all|]", + "stats show", + "stats show [list|all|([.])+", "Show statistical information for this server", "If no final argument is specified then legacy statistics information is currently shown.\n" - + "If list is specified then statistic categories are shown.\n" - + "If all is specified then all registered statistics are shown.\n" - + "If a category name is specified then only statistics from that category are shown.\n" + + "'list' argument will show statistic categories.\n" + + "'all' will show all statistics.\n" + + "A name will show statistics from that category.\n" + + "A . name will show statistics from that category in that container.\n" + + "More than one name can be given separated by spaces.\n" + "THIS STATS FACILITY IS EXPERIMENTAL AND DOES NOT YET CONTAIN ALL STATS", HandleShowStatsCommand); + + console.Commands.AddCommand( + "General", + false, + "show stats", + "show stats [list|all|([.])+", + "Alias for 'stats show' command", + HandleShowStatsCommand); + + StatsLogger.RegisterConsoleCommands(console); } public static void HandleShowStatsCommand(string module, string[] cmd) @@ -84,105 +101,286 @@ namespace OpenSim.Framework.Monitoring if (cmd.Length > 2) { - var categoryName = cmd[2]; - - if (categoryName == AllSubCommand) + foreach (string name in cmd.Skip(2)) { - foreach (var category in RegisteredStats.Values) + string[] components = name.Split('.'); + + string categoryName = components[0]; + string containerName = components.Length > 1 ? components[1] : null; + string statName = components.Length > 2 ? components[2] : null; + + if (categoryName == AllSubCommand) { - OutputCategoryStatsToConsole(con, category); + OutputAllStatsToConsole(con); } - } - else if (categoryName == ListSubCommand) - { - con.Output("Statistic categories available are:"); - foreach (string category in RegisteredStats.Keys) - con.OutputFormat(" {0}", category); - } - else - { - Dictionary> category; - if (!RegisteredStats.TryGetValue(categoryName, out category)) + else if (categoryName == ListSubCommand) { - con.OutputFormat("No such category as {0}", categoryName); + con.Output("Statistic categories available are:"); + foreach (string category in RegisteredStats.Keys) + con.OutputFormat(" {0}", category); } else { - OutputCategoryStatsToConsole(con, category); + SortedDictionary> category; + if (!RegisteredStats.TryGetValue(categoryName, out category)) + { + con.OutputFormat("No such category as {0}", categoryName); + } + else + { + if (String.IsNullOrEmpty(containerName)) + { + OutputCategoryStatsToConsole(con, category); + } + else + { + SortedDictionary container; + if (category.TryGetValue(containerName, out container)) + { + if (String.IsNullOrEmpty(statName)) + { + OutputContainerStatsToConsole(con, container); + } + else + { + Stat stat; + if (container.TryGetValue(statName, out stat)) + { + OutputStatToConsole(con, stat); + } + else + { + con.OutputFormat( + "No such stat {0} in {1}.{2}", statName, categoryName, containerName); + } + } + } + else + { + con.OutputFormat("No such container {0} in category {1}", containerName, categoryName); + } + } + } } } } else { // Legacy - con.Output(SimExtraStats.Report()); + if (SimExtraStats != null) + con.Output(SimExtraStats.Report()); + else + OutputAllStatsToConsole(con); } } - private static void OutputCategoryStatsToConsole( - ICommandConsole con, Dictionary> category) + public static List GetAllStatsReports() + { + List reports = new List(); + + foreach (var category in RegisteredStats.Values) + reports.AddRange(GetCategoryStatsReports(category)); + + return reports; + } + + private static void OutputAllStatsToConsole(ICommandConsole con) { + foreach (string report in GetAllStatsReports()) + con.Output(report); + } + + private static List GetCategoryStatsReports( + SortedDictionary> category) + { + List reports = new List(); + foreach (var container in category.Values) + reports.AddRange(GetContainerStatsReports(container)); + + return reports; + } + + private static void OutputCategoryStatsToConsole( + ICommandConsole con, SortedDictionary> category) + { + foreach (string report in GetCategoryStatsReports(category)) + con.Output(report); + } + + private static List GetContainerStatsReports(SortedDictionary container) + { + List reports = new List(); + + foreach (Stat stat in container.Values) + reports.Add(stat.ToConsoleString()); + + return reports; + } + + private static void OutputContainerStatsToConsole( + ICommandConsole con, SortedDictionary container) + { + foreach (string report in GetContainerStatsReports(container)) + con.Output(report); + } + + private static void OutputStatToConsole(ICommandConsole con, Stat stat) + { + con.Output(stat.ToConsoleString()); + } + + // Creates an OSDMap of the format: + // { categoryName: { + // containerName: { + // statName: { + // "Name": name, + // "ShortName": shortName, + // ... + // }, + // statName: { + // "Name": name, + // "ShortName": shortName, + // ... + // }, + // ... + // }, + // containerName: { + // ... + // }, + // ... + // }, + // categoryName: { + // ... + // }, + // ... + // } + // The passed in parameters will filter the categories, containers and stats returned. If any of the + // parameters are either EmptyOrNull or the AllSubCommand value, all of that type will be returned. + // Case matters. + public static OSDMap GetStatsAsOSDMap(string pCategoryName, string pContainerName, string pStatName) + { + OSDMap map = new OSDMap(); + + foreach (string catName in RegisteredStats.Keys) { - foreach (Stat stat in container.Values) + // Do this category if null spec, "all" subcommand or category name matches passed parameter. + // Skip category if none of the above. + if (!(String.IsNullOrEmpty(pCategoryName) || pCategoryName == AllSubCommand || pCategoryName == catName)) + continue; + + OSDMap contMap = new OSDMap(); + foreach (string contName in RegisteredStats[catName].Keys) { - con.Output(stat.ToConsoleString()); + if (!(string.IsNullOrEmpty(pContainerName) || pContainerName == AllSubCommand || pContainerName == contName)) + continue; + + OSDMap statMap = new OSDMap(); + + SortedDictionary theStats = RegisteredStats[catName][contName]; + foreach (string statName in theStats.Keys) + { + if (!(String.IsNullOrEmpty(pStatName) || pStatName == AllSubCommand || pStatName == statName)) + continue; + + statMap.Add(statName, theStats[statName].ToOSDMap()); + } + + contMap.Add(contName, statMap); } + map.Add(catName, contMap); } + + return map; } - /// - /// Start collecting statistics related to assets. - /// Should only be called once. - /// - public static AssetStatsCollector StartCollectingAssetStats() + public static Hashtable HandleStatsRequest(Hashtable request) { - assetStats = new AssetStatsCollector(); + Hashtable responsedata = new Hashtable(); +// string regpath = request["uri"].ToString(); + int response_code = 200; + string contenttype = "text/json"; - return assetStats; - } + string pCategoryName = StatsManager.AllSubCommand; + string pContainerName = StatsManager.AllSubCommand; + string pStatName = StatsManager.AllSubCommand; - /// - /// Start collecting statistics related to users. - /// Should only be called once. - /// - public static UserStatsCollector StartCollectingUserStats() - { - userStats = new UserStatsCollector(); + if (request.ContainsKey("cat")) pCategoryName = request["cat"].ToString(); + if (request.ContainsKey("cont")) pContainerName = request["cat"].ToString(); + if (request.ContainsKey("stat")) pStatName = request["stat"].ToString(); - return userStats; + string strOut = StatsManager.GetStatsAsOSDMap(pCategoryName, pContainerName, pStatName).ToString(); + + // If requestor wants it as a callback function, build response as a function rather than just the JSON string. + if (request.ContainsKey("callback")) + { + strOut = request["callback"].ToString() + "(" + strOut + ");"; + } + + // m_log.DebugFormat("{0} StatFetch: uri={1}, cat={2}, cont={3}, stat={4}, resp={5}", + // LogHeader, regpath, pCategoryName, pContainerName, pStatName, strOut); + + responsedata["int_response_code"] = response_code; + responsedata["content_type"] = contenttype; + responsedata["keepalive"] = false; + responsedata["str_response_string"] = strOut; + responsedata["access_control_allow_origin"] = "*"; + + return responsedata; } +// /// +// /// Start collecting statistics related to assets. +// /// Should only be called once. +// /// +// public static AssetStatsCollector StartCollectingAssetStats() +// { +// assetStats = new AssetStatsCollector(); +// +// return assetStats; +// } +// +// /// +// /// Start collecting statistics related to users. +// /// Should only be called once. +// /// +// public static UserStatsCollector StartCollectingUserStats() +// { +// userStats = new UserStatsCollector(); +// +// return userStats; +// } + /// - /// Registers a statistic. + /// Register a statistic. /// /// /// public static bool RegisterStat(Stat stat) { - Dictionary> category = null, newCategory; - Dictionary container = null, newContainer; + SortedDictionary> category = null, newCategory; + SortedDictionary container = null, newContainer; lock (RegisteredStats) { // Stat name is not unique across category/container/shortname key. // XXX: For now just return false. This is to avoid problems in regression tests where all tests // in a class are run in the same instance of the VM. - if (TryGetStat(stat, out category, out container)) + if (TryGetStatParents(stat, out category, out container)) return false; // We take a copy-on-write approach here of replacing dictionaries when keys are added or removed. // This means that we don't need to lock or copy them on iteration, which will be a much more // common operation after startup. if (container != null) - newContainer = new Dictionary(container); + newContainer = new SortedDictionary(container); else - newContainer = new Dictionary(); + newContainer = new SortedDictionary(); if (category != null) - newCategory = new Dictionary>(category); + newCategory = new SortedDictionary>(category); else - newCategory = new Dictionary>(); + newCategory = new SortedDictionary>(); newContainer[stat.ShortName] = stat; newCategory[stat.Container] = newContainer; @@ -196,21 +394,21 @@ namespace OpenSim.Framework.Monitoring /// Deregister a statistic /// > /// - /// public static bool DeregisterStat(Stat stat) { - Dictionary> category = null, newCategory; - Dictionary container = null, newContainer; + SortedDictionary> category = null, newCategory; + SortedDictionary container = null, newContainer; lock (RegisteredStats) { - if (!TryGetStat(stat, out category, out container)) + if (!TryGetStatParents(stat, out category, out container)) return false; - newContainer = new Dictionary(container); + newContainer = new SortedDictionary(container); newContainer.Remove(stat.ShortName); - newCategory = new Dictionary>(category); + newCategory = new SortedDictionary>(category); newCategory.Remove(stat.Container); newCategory[stat.Container] = newContainer; @@ -220,15 +418,70 @@ namespace OpenSim.Framework.Monitoring } } - public static bool TryGetStats(string category, out Dictionary> stats) + public static bool TryGetStat(string category, string container, string statShortName, out Stat stat) { - return RegisteredStats.TryGetValue(category, out stats); + stat = null; + SortedDictionary> categoryStats; + + lock (RegisteredStats) + { + if (!TryGetStatsForCategory(category, out categoryStats)) + return false; + + SortedDictionary containerStats; + + if (!categoryStats.TryGetValue(container, out containerStats)) + return false; + + return containerStats.TryGetValue(statShortName, out stat); + } + } + + public static bool TryGetStatsForCategory( + string category, out SortedDictionary> stats) + { + lock (RegisteredStats) + return RegisteredStats.TryGetValue(category, out stats); + } + + /// + /// Get the same stat for each container in a given category. + /// + /// + /// The stats if there were any to fetch. Otherwise null. + /// + /// + /// + public static List GetStatsFromEachContainer(string category, string statShortName) + { + SortedDictionary> categoryStats; + + lock (RegisteredStats) + { + if (!RegisteredStats.TryGetValue(category, out categoryStats)) + return null; + + List stats = null; + + foreach (SortedDictionary containerStats in categoryStats.Values) + { + if (containerStats.ContainsKey(statShortName)) + { + if (stats == null) + stats = new List(); + + stats.Add(containerStats[statShortName]); + } + } + + return stats; + } } - public static bool TryGetStat( + public static bool TryGetStatParents( Stat stat, - out Dictionary> category, - out Dictionary container) + out SortedDictionary> category, + out SortedDictionary container) { category = null; container = null; @@ -252,9 +505,9 @@ namespace OpenSim.Framework.Monitoring { lock (RegisteredStats) { - foreach (Dictionary> category in RegisteredStats.Values) + foreach (SortedDictionary> category in RegisteredStats.Values) { - foreach (Dictionary container in category.Values) + foreach (SortedDictionary container in category.Values) { foreach (Stat stat in container.Values) { diff --git a/OpenSim/Framework/Monitoring/UserStatsCollector.cs b/OpenSim/Framework/Monitoring/UserStatsCollector.cs index e89c8e6..81e0fa4 100644 --- a/OpenSim/Framework/Monitoring/UserStatsCollector.cs +++ b/OpenSim/Framework/Monitoring/UserStatsCollector.cs @@ -27,6 +27,8 @@ using System.Timers; +using OpenMetaverse.StructuredData; + namespace OpenSim.Framework.Monitoring { /// @@ -88,5 +90,21 @@ namespace OpenSim.Framework.Monitoring Logouts total : {3}", SuccessfulLogins, SuccessfulLoginsToday, SuccessfulLoginsYesterday, Logouts); } + + public override string XReport(string uptime, string version) + { + return OSDParser.SerializeJsonString(OReport(uptime, version)); + } + + public override OSDMap OReport(string uptime, string version) + { + OSDMap ret = new OSDMap(); + ret.Add("SuccessfulLogins", OSD.FromInteger(SuccessfulLogins)); + ret.Add("SuccessfulLoginsToday", OSD.FromInteger(SuccessfulLoginsToday)); + ret.Add("SuccessfulLoginsYesterday", OSD.FromInteger(SuccessfulLoginsYesterday)); + ret.Add("Logouts", OSD.FromInteger(Logouts)); + + return ret; + } } } diff --git a/OpenSim/Framework/Monitoring/Watchdog.cs b/OpenSim/Framework/Monitoring/Watchdog.cs index 3f992b1..a644fa5 100644 --- a/OpenSim/Framework/Monitoring/Watchdog.cs +++ b/OpenSim/Framework/Monitoring/Watchdog.cs @@ -38,6 +38,8 @@ namespace OpenSim.Framework.Monitoring /// public static class Watchdog { + private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + /// Timer interval in milliseconds for the watchdog timer public const double WATCHDOG_INTERVAL_MS = 2500.0d; @@ -82,12 +84,32 @@ namespace OpenSim.Framework.Monitoring /// public Func AlarmMethod { get; set; } - public ThreadWatchdogInfo(Thread thread, int timeout) + /// + /// Stat structure associated with this thread. + /// + public Stat Stat { get; set; } + + public ThreadWatchdogInfo(Thread thread, int timeout, string name) { Thread = thread; Timeout = timeout; FirstTick = Environment.TickCount & Int32.MaxValue; LastTick = FirstTick; + + Stat + = new Stat( + name, + string.Format("Last update of thread {0}", name), + "", + "ms", + "server", + "thread", + StatType.Pull, + MeasuresOfInterest.None, + stat => stat.Value = Environment.TickCount & Int32.MaxValue - LastTick, + StatVerbosity.Debug); + + StatsManager.RegisterStat(Stat); } public ThreadWatchdogInfo(ThreadWatchdogInfo previousTwi) @@ -100,6 +122,11 @@ namespace OpenSim.Framework.Monitoring AlarmIfTimeout = previousTwi.AlarmIfTimeout; AlarmMethod = previousTwi.AlarmMethod; } + + public void Cleanup() + { + StatsManager.DeregisterStat(Stat); + } } /// @@ -116,7 +143,7 @@ namespace OpenSim.Framework.Monitoring get { return m_enabled; } set { -// m_log.DebugFormat("[MEMORY WATCHDOG]: Setting MemoryWatchdog.Enabled to {0}", value); + // m_log.DebugFormat("[MEMORY WATCHDOG]: Setting MemoryWatchdog.Enabled to {0}", value); if (value == m_enabled) return; @@ -132,9 +159,8 @@ namespace OpenSim.Framework.Monitoring m_watchdogTimer.Enabled = m_enabled; } } - private static bool m_enabled; - private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + private static bool m_enabled; private static Dictionary m_threads; private static System.Timers.Timer m_watchdogTimer; @@ -155,57 +181,19 @@ namespace OpenSim.Framework.Monitoring } /// - /// Start a new thread that is tracked by the watchdog timer. - /// - /// The method that will be executed in a new thread - /// A name to give to the new thread - /// Priority to run the thread at - /// True to run this thread as a background thread, otherwise false - /// Trigger an alarm function is we have timed out - /// The newly created Thread object - public static Thread StartThread( - ThreadStart start, string name, ThreadPriority priority, bool isBackground, bool alarmIfTimeout) - { - return StartThread(start, name, priority, isBackground, alarmIfTimeout, null, DEFAULT_WATCHDOG_TIMEOUT_MS); - } - - /// - /// Start a new thread that is tracked by the watchdog timer + /// Add a thread to the watchdog tracker. /// - /// The method that will be executed in a new thread - /// A name to give to the new thread - /// Priority to run the thread at - /// True to run this thread as a background - /// thread, otherwise false - /// Trigger an alarm function is we have timed out - /// - /// Alarm method to call if alarmIfTimeout is true and there is a timeout. - /// Normally, this will just return some useful debugging information. - /// - /// Number of milliseconds to wait until we issue a warning about timeout. - /// The newly created Thread object - public static Thread StartThread( - ThreadStart start, string name, ThreadPriority priority, bool isBackground, - bool alarmIfTimeout, Func alarmMethod, int timeout) + /// Information about the thread. + /// Name of the thread. + /// If true then creation of thread is logged. + public static void AddThread(ThreadWatchdogInfo info, string name, bool log = true) { - Thread thread = new Thread(start); - thread.Name = name; - thread.Priority = priority; - thread.IsBackground = isBackground; - - ThreadWatchdogInfo twi - = new ThreadWatchdogInfo(thread, timeout) - { AlarmIfTimeout = alarmIfTimeout, AlarmMethod = alarmMethod }; - - m_log.DebugFormat( - "[WATCHDOG]: Started tracking thread {0}, ID {1}", twi.Thread.Name, twi.Thread.ManagedThreadId); + if (log) + m_log.DebugFormat( + "[WATCHDOG]: Started tracking thread {0}, ID {1}", name, info.Thread.ManagedThreadId); lock (m_threads) - m_threads.Add(twi.Thread.ManagedThreadId, twi); - - thread.Start(); - - return thread; + m_threads.Add(info.Thread.ManagedThreadId, info); } /// @@ -219,25 +207,28 @@ namespace OpenSim.Framework.Monitoring /// /// Stops watchdog tracking on the current thread /// + /// If true then normal events in thread removal are not logged. /// /// True if the thread was removed from the list of tracked /// threads, otherwise false /// - public static bool RemoveThread() + public static bool RemoveThread(bool log = true) { - return RemoveThread(Thread.CurrentThread.ManagedThreadId); + return RemoveThread(Thread.CurrentThread.ManagedThreadId, log); } - private static bool RemoveThread(int threadID) + private static bool RemoveThread(int threadID, bool log = true) { lock (m_threads) { ThreadWatchdogInfo twi; if (m_threads.TryGetValue(threadID, out twi)) { - m_log.DebugFormat( - "[WATCHDOG]: Removing thread {0}, ID {1}", twi.Thread.Name, twi.Thread.ManagedThreadId); + if (log) + m_log.DebugFormat( + "[WATCHDOG]: Removing thread {0}, ID {1}", twi.Thread.Name, twi.Thread.ManagedThreadId); + twi.Cleanup(); m_threads.Remove(threadID); return true; @@ -293,7 +284,7 @@ namespace OpenSim.Framework.Monitoring } catch { } } - + /// /// Get currently watched threads for diagnostic purposes /// @@ -380,6 +371,7 @@ namespace OpenSim.Framework.Monitoring if (MemoryWatchdog.Enabled) MemoryWatchdog.Update(); + ChecksManager.CheckChecks(); StatsManager.RecordStats(); m_watchdogTimer.Start(); diff --git a/OpenSim/Framework/Monitoring/WorkManager.cs b/OpenSim/Framework/Monitoring/WorkManager.cs new file mode 100644 index 0000000..d1a74ce --- /dev/null +++ b/OpenSim/Framework/Monitoring/WorkManager.cs @@ -0,0 +1,290 @@ +/* + * 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 OpenSimulator 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.Reflection; +using System.Threading; +using log4net; + +namespace OpenSim.Framework.Monitoring +{ + /// + /// Manages various work items in the simulator. + /// + /// + /// Currently, here work can be started + /// * As a long-running and monitored thread. + /// * In a thread that will never timeout but where the job is expected to eventually complete. + /// * In a threadpool thread that will timeout if it takes a very long time to complete (> 10 mins). + /// * As a job which will be run in a single-threaded job engine. Such jobs must not incorporate delays (sleeps, + /// network waits, etc.). + /// + /// This is an evolving approach to better manage the work that OpenSimulator is asked to do from a very diverse + /// range of sources (client actions, incoming network, outgoing network calls, etc.). + /// + /// Util.FireAndForget is still available to insert jobs in the threadpool, though this is equivalent to + /// WorkManager.RunInThreadPool(). + /// + public static class WorkManager + { + private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); + + public static JobEngine JobEngine { get; private set; } + + static WorkManager() + { + JobEngine = new JobEngine("Non-blocking non-critical job engine", "JOB ENGINE"); + + StatsManager.RegisterStat( + new Stat( + "JobsWaiting", + "Number of jobs waiting for processing.", + "", + "", + "server", + "jobengine", + StatType.Pull, + MeasuresOfInterest.None, + stat => stat.Value = JobEngine.JobsWaiting, + StatVerbosity.Debug)); + + MainConsole.Instance.Commands.AddCommand( + "Debug", + false, + "debug jobengine", + "debug jobengine ", + "Start, stop, get status or set logging level of the job engine.", + "If stopped then all outstanding jobs are processed immediately.", + HandleControlCommand); + } + + /// + /// Start a new long-lived thread. + /// + /// The method that will be executed in a new thread + /// A name to give to the new thread + /// Priority to run the thread at + /// True to run this thread as a background thread, otherwise false + /// Trigger an alarm function is we have timed out + /// If true then creation of thread is logged. + /// The newly created Thread object + public static Thread StartThread( + ThreadStart start, string name, ThreadPriority priority, bool isBackground, bool alarmIfTimeout, bool log = true) + { + return StartThread(start, name, priority, isBackground, alarmIfTimeout, null, Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS, log); + } + + /// + /// Start a new thread that is tracked by the watchdog + /// + /// The method that will be executed in a new thread + /// A name to give to the new thread + /// Priority to run the thread at + /// True to run this thread as a background + /// thread, otherwise false + /// Trigger an alarm function is we have timed out + /// + /// Alarm method to call if alarmIfTimeout is true and there is a timeout. + /// Normally, this will just return some useful debugging information. + /// + /// Number of milliseconds to wait until we issue a warning about timeout. + /// If true then creation of thread is logged. + /// The newly created Thread object + public static Thread StartThread( + ThreadStart start, string name, ThreadPriority priority, bool isBackground, + bool alarmIfTimeout, Func alarmMethod, int timeout, bool log = true) + { + Thread thread = new Thread(start); + thread.Priority = priority; + thread.IsBackground = isBackground; + + Watchdog.ThreadWatchdogInfo twi + = new Watchdog.ThreadWatchdogInfo(thread, timeout, name) + { AlarmIfTimeout = alarmIfTimeout, AlarmMethod = alarmMethod }; + + Watchdog.AddThread(twi, name, log:log); + + thread.Start(); + thread.Name = name; + + return thread; + } + + /// + /// Run the callback in a new thread immediately. If the thread exits with an exception log it but do + /// not propogate it. + /// + /// Code for the thread to execute. + /// Object to pass to the thread. + /// Name of the thread + public static void RunInThread(WaitCallback callback, object obj, string name, bool log = false) + { + if (Util.FireAndForgetMethod == FireAndForgetMethod.RegressionTest) + { + Culture.SetCurrentCulture(); + callback(obj); + return; + } + + ThreadStart ts = new ThreadStart(delegate() + { + try + { + Culture.SetCurrentCulture(); + callback(obj); + Watchdog.RemoveThread(log:false); + } + catch (Exception e) + { + m_log.Error(string.Format("[WATCHDOG]: Exception in thread {0}.", name), e); + } + }); + + StartThread(ts, name, ThreadPriority.Normal, true, false, log:log); + } + + /// + /// Run the callback via a threadpool thread. + /// + /// + /// Such jobs may run after some delay but must always complete. + /// + /// + /// + /// The name of the job. This is used in monitoring and debugging. + public static void RunInThreadPool(System.Threading.WaitCallback callback, object obj, string name) + { + Util.FireAndForget(callback, obj, name); + } + + /// + /// Run a job. + /// + /// + /// This differs from direct scheduling (e.g. Util.FireAndForget) in that a job can be run in the job + /// engine if it is running, where all jobs are currently performed in sequence on a single thread. This is + /// to prevent observed overload and server freeze problems when there are hundreds of connections which all attempt to + /// perform work at once (e.g. in conference situations). With lower numbers of connections, the small + /// delay in performing jobs in sequence rather than concurrently has not been notiecable in testing, though a future more + /// sophisticated implementation could perform jobs concurrently when the server is under low load. + /// + /// However, be advised that some callers of this function rely on all jobs being performed in sequence if any + /// jobs are performed in sequence (i.e. if jobengine is active or not). Therefore, expanding the jobengine + /// beyond a single thread will require considerable thought. + /// + /// Also, any jobs submitted must be guaranteed to complete within a reasonable timeframe (e.g. they cannot + /// incorporate a network delay with a long timeout). At the moment, work that could suffer such issues + /// should still be run directly with RunInThread(), Util.FireAndForget(), etc. This is another area where + /// the job engine could be improved and so CPU utilization improved by better management of concurrency within + /// OpenSimulator. + /// + /// General classification for the job (e.g. "RezAttachments"). + /// Callback for job. + /// Object to pass to callback when run + /// Specific name of job (e.g. "RezAttachments for Joe Bloggs" + /// If set to true then the job may be run in ths calling thread. + /// If the true then the job must never timeout. + /// If set to true then extra logging is performed. + public static void RunJob( + string jobType, WaitCallback callback, object obj, string name, + bool canRunInThisThread = false, bool mustNotTimeout = false, + bool log = false) + { + if (Util.FireAndForgetMethod == FireAndForgetMethod.RegressionTest) + { + Culture.SetCurrentCulture(); + callback(obj); + return; + } + + if (JobEngine.IsRunning) + JobEngine.QueueJob(name, () => callback(obj)); + else if (canRunInThisThread) + callback(obj); + else if (mustNotTimeout) + RunInThread(callback, obj, name, log); + else + Util.FireAndForget(callback, obj, name); + } + + private static void HandleControlCommand(string module, string[] args) + { + // if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) + // return; + + if (args.Length < 3) + { + MainConsole.Instance.Output("Usage: debug jobengine "); + return; + } + + string subCommand = args[2]; + + if (subCommand == "stop") + { + JobEngine.Stop(); + MainConsole.Instance.OutputFormat("Stopped job engine."); + } + else if (subCommand == "start") + { + JobEngine.Start(); + MainConsole.Instance.OutputFormat("Started job engine."); + } + else if (subCommand == "status") + { + MainConsole.Instance.OutputFormat("Job engine running: {0}", JobEngine.IsRunning); + + JobEngine.Job job = JobEngine.CurrentJob; + MainConsole.Instance.OutputFormat("Current job {0}", job != null ? job.Name : "none"); + + MainConsole.Instance.OutputFormat( + "Jobs waiting: {0}", JobEngine.IsRunning ? JobEngine.JobsWaiting.ToString() : "n/a"); + MainConsole.Instance.OutputFormat("Log Level: {0}", JobEngine.LogLevel); + } + else if (subCommand == "log") + { + if (args.Length < 4) + { + MainConsole.Instance.Output("Usage: debug jobengine log "); + return; + } + + // int logLevel; + int logLevel = int.Parse(args[3]); + // if (ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out logLevel)) + // { + JobEngine.LogLevel = logLevel; + MainConsole.Instance.OutputFormat("Set debug log level to {0}", JobEngine.LogLevel); + // } + } + else + { + MainConsole.Instance.OutputFormat("Unrecognized job engine subcommand {0}", subCommand); + } + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/OutboundUrlFilter.cs b/OpenSim/Framework/OutboundUrlFilter.cs new file mode 100644 index 0000000..baa3647 --- /dev/null +++ b/OpenSim/Framework/OutboundUrlFilter.cs @@ -0,0 +1,256 @@ +/* + * 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 OpenSimulator 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.Linq; +using System.Net; +using System.Reflection; +using log4net; +using LukeSkywalker.IPNetwork; +using Nini.Config; + +namespace OpenSim.Framework +{ + public class OutboundUrlFilter + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public string Name { get; private set; } + + private List m_blacklistNetworks; + private List m_blacklistEndPoints; + + private List m_blacklistExceptionNetworks; + private List m_blacklistExceptionEndPoints; + + public OutboundUrlFilter( + string name, + List blacklistNetworks, List blacklistEndPoints, + List blacklistExceptionNetworks, List blacklistExceptionEndPoints) + { + Name = name; + + m_blacklistNetworks = blacklistNetworks; + m_blacklistEndPoints = blacklistEndPoints; + m_blacklistExceptionNetworks = blacklistExceptionNetworks; + m_blacklistExceptionEndPoints = blacklistExceptionEndPoints; + } + + /// + /// Initializes a new instance of the class. + /// + /// Name of the filter for logging purposes. + /// Filter configuration + public OutboundUrlFilter(string name, IConfigSource config) + { + Name = name; + + string configBlacklist + = "0.0.0.0/8|10.0.0.0/8|100.64.0.0/10|127.0.0.0/8|169.254.0.0/16|172.16.0.0/12|192.0.0.0/24|192.0.2.0/24|192.88.99.0/24|192.168.0.0/16|198.18.0.0/15|198.51.100.0/24|203.0.113.0/24|224.0.0.0/4|240.0.0.0/4|255.255.255.255/32"; + string configBlacklistExceptions = ""; + + IConfig networkConfig = config.Configs["Network"]; + + if (networkConfig != null) + { + configBlacklist = networkConfig.GetString("OutboundDisallowForUserScripts", configBlacklist); + configBlacklistExceptions + = networkConfig.GetString("OutboundDisallowForUserScriptsExcept", configBlacklistExceptions); + } + + m_log.DebugFormat( + "[OUTBOUND URL FILTER]: OutboundDisallowForUserScripts for {0} is [{1}]", Name, configBlacklist); + m_log.DebugFormat( + "[OUTBOUND URL FILTER]: OutboundDisallowForUserScriptsExcept for {0} is [{1}]", Name, configBlacklistExceptions); + + OutboundUrlFilter.ParseConfigList( + configBlacklist, Name, out m_blacklistNetworks, out m_blacklistEndPoints); + OutboundUrlFilter.ParseConfigList( + configBlacklistExceptions, Name, out m_blacklistExceptionNetworks, out m_blacklistExceptionEndPoints); + } + + private static void ParseConfigList( + string fullConfigEntry, string filterName, out List networks, out List endPoints) + { + // Parse blacklist + string[] configBlacklistEntries + = fullConfigEntry.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + + configBlacklistEntries = configBlacklistEntries.Select(e => e.Trim()).ToArray(); + + networks = new List(); + endPoints = new List(); + + foreach (string configEntry in configBlacklistEntries) + { + if (configEntry.Contains("/")) + { + IPNetwork network; + + if (!IPNetwork.TryParse(configEntry, out network)) + { + m_log.ErrorFormat( + "[OUTBOUND URL FILTER]: Entry [{0}] is invalid network for {1}", configEntry, filterName); + + continue; + } + + networks.Add(network); + } + else + { + Uri configEntryUri; + + if (!Uri.TryCreate("http://" + configEntry, UriKind.Absolute, out configEntryUri)) + { + m_log.ErrorFormat( + "[OUTBOUND URL FILTER]: EndPoint entry [{0}] is invalid endpoint for {1}", + configEntry, filterName); + + continue; + } + + IPAddress[] addresses = Dns.GetHostAddresses(configEntryUri.Host); + + foreach (IPAddress addr in addresses) + { + if (addr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) + { + // m_log.DebugFormat("[OUTBOUND URL FILTER]: Found address [{0}] in config", addr); + + IPEndPoint configEntryEp = new IPEndPoint(addr, configEntryUri.Port); + endPoints.Add(configEntryEp); + + // m_log.DebugFormat("[OUTBOUND URL FILTER]: Added blacklist exception [{0}]", configEntryEp); + } + } + } + } + } + + /// + /// Determines if an url is in a list of networks and endpoints. + /// + /// + /// IP address + /// + /// Networks. + /// End points. + /// Filter name. + private static bool IsInNetwork( + IPAddress addr, int port, List networks, List endPoints, string filterName) + { + foreach (IPNetwork ipn in networks) + { +// m_log.DebugFormat( +// "[OUTBOUND URL FILTER]: Checking [{0}] against network [{1}]", addr, ipn); + + if (IPNetwork.Contains(ipn, addr)) + { +// m_log.DebugFormat( +// "[OUTBOUND URL FILTER]: Found [{0}] in network [{1}]", addr, ipn); + + return true; + } + } + + // m_log.DebugFormat("[OUTBOUND URL FILTER]: Found address [{0}]", addr); + + foreach (IPEndPoint ep in endPoints) + { +// m_log.DebugFormat( +// "[OUTBOUND URL FILTER]: Checking [{0}:{1}] against endpoint [{2}]", +// addr, port, ep); + + if (addr.Equals(ep.Address) && port == ep.Port) + { +// m_log.DebugFormat( +// "[OUTBOUND URL FILTER]: Found [{0}:{1}] in endpoint [{2}]", addr, port, ep); + + return true; + } + } + +// m_log.DebugFormat("[OUTBOUND URL FILTER]: Did not find [{0}:{1}] in list", addr, port); + + return false; + } + + /// + /// Checks whether the given url is allowed by the filter. + /// + /// + public bool CheckAllowed(Uri url) + { + bool allowed = true; + + // Check that we are permitted to make calls to this endpoint. + bool foundIpv4Address = false; + + IPAddress[] addresses = Dns.GetHostAddresses(url.Host); + + foreach (IPAddress addr in addresses) + { + if (addr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) + { +// m_log.DebugFormat("[OUTBOUND URL FILTER]: Found address [{0}]", addr); + + foundIpv4Address = true; + + // Check blacklist + if (OutboundUrlFilter.IsInNetwork(addr, url.Port, m_blacklistNetworks, m_blacklistEndPoints, Name)) + { +// m_log.DebugFormat("[OUTBOUND URL FILTER]: Found [{0}] in blacklist for {1}", url, Name); + + // Check blacklist exceptions + allowed + = OutboundUrlFilter.IsInNetwork( + addr, url.Port, m_blacklistExceptionNetworks, m_blacklistExceptionEndPoints, Name); + +// if (allowed) +// m_log.DebugFormat("[OUTBOUND URL FILTER]: Found [{0}] in whitelist for {1}", url, Name); + } + } + + // Found at least one address in a blacklist and not a blacklist exception + if (!allowed) + return false; +// else +// m_log.DebugFormat("[OUTBOUND URL FILTER]: URL [{0}] not in blacklist for {1}", url, Name); + } + + // We do not know how to handle IPv6 securely yet. + if (!foundIpv4Address) + return false; + +// m_log.DebugFormat("[OUTBOUND URL FILTER]: Allowing request [{0}]", url); + + return allowed; + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/PermissionsUtil.cs b/OpenSim/Framework/PermissionsUtil.cs new file mode 100644 index 0000000..d785a78 --- /dev/null +++ b/OpenSim/Framework/PermissionsUtil.cs @@ -0,0 +1,87 @@ +/* + * 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 OpenSimulator 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.Reflection; +using System.Text; +using log4net; + +namespace OpenSim.Framework +{ + public static class PermissionsUtil + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Logs permissions flags. Useful when debugging permission problems. + /// + /// + public static void LogPermissions(String name, String message, uint basePerm, uint curPerm, uint nextPerm) + { + m_log.DebugFormat("Permissions of \"{0}\" at \"{1}\": Base {2} ({3:X4}), Current {4} ({5:X4}), NextOwner {6} ({7:X4})", + name, message, + PermissionsToString(basePerm), basePerm, PermissionsToString(curPerm), curPerm, PermissionsToString(nextPerm), nextPerm); + } + + /// + /// Converts a permissions bit-mask to a string (e.g., "MCT"). + /// + private static string PermissionsToString(uint perms) + { + string str = ""; + if ((perms & (int)PermissionMask.Modify) != 0) + str += "M"; + if ((perms & (int)PermissionMask.Copy) != 0) + str += "C"; + if ((perms & (int)PermissionMask.Transfer) != 0) + str += "T"; + if (str == "") + str = "."; + return str; + } + + /// + /// Applies an object's folded permissions to its regular permissions. + /// + /// The folded permissions. Only the lowest 7 bits are examined. + /// The permissions variable to modify. + public static void ApplyFoldedPermissions(uint foldedPerms, ref uint mainPerms) + { + if ((foldedPerms & 7) == 0) + return; // assume that if the folded permissions are 0 then this means that they weren't actually recorded + + if ((foldedPerms & ((uint)PermissionMask.Copy >> 13)) == 0) + mainPerms &= ~(uint)PermissionMask.Copy; + if ((foldedPerms & ((uint)PermissionMask.Transfer >> 13)) == 0) + mainPerms &= ~(uint)PermissionMask.Transfer; + if ((foldedPerms & ((uint)PermissionMask.Modify >> 13)) == 0) + mainPerms &= ~(uint)PermissionMask.Modify; + } + + } +} diff --git a/OpenSim/Framework/PluginLoader.cs b/OpenSim/Framework/PluginLoader.cs index 819cb7b..d12aa61 100644 --- a/OpenSim/Framework/PluginLoader.cs +++ b/OpenSim/Framework/PluginLoader.cs @@ -215,12 +215,12 @@ namespace OpenSim.Framework AddinManager.AddinLoadError += on_addinloaderror_; AddinManager.AddinLoaded += on_addinloaded_; - clear_registry_(); + //clear_registry_(dir); - suppress_console_output_(true); + //suppress_console_output_(true); AddinManager.Initialize(dir); AddinManager.Registry.Update(null); - suppress_console_output_(false); + //suppress_console_output_(false); } private void on_addinloaded_(object sender, AddinEventArgs args) @@ -239,18 +239,19 @@ namespace OpenSim.Framework + args.Exception.StackTrace); } - private void clear_registry_() + private void clear_registry_(string dir) { // The Mono addin manager (in Mono.Addins.dll version 0.2.0.0) // occasionally seems to corrupt its addin cache // Hence, as a temporary solution we'll remove it before each startup + try { - if (Directory.Exists("addin-db-000")) - Directory.Delete("addin-db-000", true); + if (Directory.Exists(dir + "/addin-db-000")) + Directory.Delete(dir + "/addin-db-000", true); - if (Directory.Exists("addin-db-001")) - Directory.Delete("addin-db-001", true); + if (Directory.Exists(dir + "/addin-db-001")) + Directory.Delete(dir + "/addin-db-001", true); } catch (IOException) { diff --git a/OpenSim/Framework/PluginManager.cs b/OpenSim/Framework/PluginManager.cs index 00263f5..0117096 100644 --- a/OpenSim/Framework/PluginManager.cs +++ b/OpenSim/Framework/PluginManager.cs @@ -218,7 +218,7 @@ namespace OpenSim.Framework Console.WriteLine ("Looking for updates..."); Repositories.UpdateAllRepositories (ps); Console.WriteLine ("Available add-in updates:"); - bool found = false; + AddinRepositoryEntry[] entries = Repositories.GetAvailableUpdates(); foreach (AddinRepositoryEntry entry in entries) @@ -541,7 +541,7 @@ namespace OpenSim.Framework { list.AddRange(PluginRegistry.GetAddins()); } - catch(Exception e) + catch (Exception) { Addin[] x = xlist.ToArray(typeof(Addin)) as Addin[]; return x; diff --git a/OpenSim/Framework/PrimitiveBaseShape.cs b/OpenSim/Framework/PrimitiveBaseShape.cs index 4c36819..c8a5376 100644 --- a/OpenSim/Framework/PrimitiveBaseShape.cs +++ b/OpenSim/Framework/PrimitiveBaseShape.cs @@ -105,6 +105,7 @@ namespace OpenSim.Framework private ushort _profileHollow; private Vector3 _scale; private byte _state; + private byte _lastattach; private ProfileShape _profileShape; private HollowShape _hollowShape; @@ -207,6 +208,7 @@ namespace OpenSim.Framework PCode = (byte)prim.PrimData.PCode; State = prim.PrimData.State; + LastAttachPoint = prim.PrimData.State; PathBegin = Primitive.PackBeginCut(prim.PrimData.PathBegin); PathEnd = Primitive.PackEndCut(prim.PrimData.PathEnd); PathScaleX = Primitive.PackPathScale(prim.PrimData.PathScaleX); @@ -583,6 +585,15 @@ namespace OpenSim.Framework } } + public byte LastAttachPoint { + get { + return _lastattach; + } + set { + _lastattach = value; + } + } + public ProfileShape ProfileShape { get { return _profileShape; @@ -622,6 +633,8 @@ namespace OpenSim.Framework } } + // This is only used at runtime. For sculpties this holds the texture data, and for meshes + // the mesh data. public byte[] SculptData { get @@ -1147,14 +1160,13 @@ namespace OpenSim.Framework public void ReadSculptData(byte[] data, int pos) { - byte[] SculptTextureUUID = new byte[16]; - UUID SculptUUID = UUID.Zero; - byte SculptTypel = data[16+pos]; + UUID SculptUUID; + byte SculptTypel; - if (data.Length+pos >= 17) + if (data.Length-pos >= 17) { _sculptEntry = true; - SculptTextureUUID = new byte[16]; + byte[] SculptTextureUUID = new byte[16]; SculptTypel = data[16 + pos]; Array.Copy(data, pos, SculptTextureUUID,0, 16); SculptUUID = new UUID(SculptTextureUUID, 0); diff --git a/OpenSim/Framework/RegionFlags.cs b/OpenSim/Framework/RegionFlags.cs index a3089b0..7c6569e 100644 --- a/OpenSim/Framework/RegionFlags.cs +++ b/OpenSim/Framework/RegionFlags.cs @@ -48,6 +48,7 @@ namespace OpenSim.Framework NoMove = 64, // Don't allow moving this region Reservation = 128, // This is an inactive reservation Authenticate = 256, // Require authentication - Hyperlink = 512 // Record represents a HG link + Hyperlink = 512, // Record represents a HG link + DefaultHGRegion = 1024 // Record represents a default region for hypergrid teleports only. } } \ No newline at end of file diff --git a/OpenSim/Framework/RegionInfo.cs b/OpenSim/Framework/RegionInfo.cs index 016f2a6..79fbd96 100644 --- a/OpenSim/Framework/RegionInfo.cs +++ b/OpenSim/Framework/RegionInfo.cs @@ -99,9 +99,8 @@ namespace OpenSim.Framework public class RegionInfo { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly string LogHeader = "[REGION INFO]"; - public bool commFailTF = false; - public ConfigurationMember configMember; public string RegionFile = String.Empty; public bool isSandbox = false; public bool Persistent = true; @@ -126,8 +125,8 @@ namespace OpenSim.Framework private int m_physPrimMax = 0; private bool m_clampPrimSize = false; private int m_objectCapacity = 0; + private int m_maxPrimsPerUser = -1; private int m_linksetCapacity = 0; - private int m_agentCapacity = 0; private string m_regionType = String.Empty; private RegionLightShareData m_windlight = new RegionLightShareData(); protected uint m_httpPort; @@ -137,15 +136,41 @@ namespace OpenSim.Framework public bool m_allow_alternate_ports; protected string m_externalHostName; protected IPEndPoint m_internalEndPoint; - protected uint? m_regionLocX; - protected uint? m_regionLocY; protected uint m_remotingPort; public UUID RegionID = UUID.Zero; public string RemotingAddress; public UUID ScopeID = UUID.Zero; + private UUID m_maptileStaticUUID = UUID.Zero; - private Dictionary m_otherSettings = new Dictionary(); + public uint WorldLocX = 0; + public uint WorldLocY = 0; + public uint WorldLocZ = 0; + /// + /// X dimension of the region. + /// + /// + /// If this is a varregion then the default size set here will be replaced when we load the region config. + /// + public uint RegionSizeX = Constants.RegionSize; + + /// + /// X dimension of the region. + /// + /// + /// If this is a varregion then the default size set here will be replaced when we load the region config. + /// + public uint RegionSizeY = Constants.RegionSize; + + /// + /// Z dimension of the region. + /// + /// + /// XXX: Unknown if this accounts for regions with negative Z. + /// + public uint RegionSizeZ = Constants.RegionHeight; + + private Dictionary m_extraSettings = new Dictionary(); // Apparently, we're applying the same estatesettings regardless of whether it's local or remote. @@ -197,7 +222,6 @@ namespace OpenSim.Framework try { // This will throw if it's not legal Nini XML format - // and thereby toss it to the legacy loader // IConfigSource xmlsource = new XmlConfigSource(filename); @@ -210,29 +234,27 @@ namespace OpenSim.Framework catch (Exception) { } - - configMember = - new ConfigurationMember(filename, description, loadConfigurationOptions, handleIncomingConfiguration, !skipConsoleConfig); - configMember.performConfigurationRetrieve(); - RegionFile = filename; } // The web loader uses this // public RegionInfo(string description, XmlNode xmlNode, bool skipConsoleConfig, IConfigSource configSource) { - // m_configSource = configSource; - configMember = - new ConfigurationMember(xmlNode, description, loadConfigurationOptions, handleIncomingConfiguration, !skipConsoleConfig); - configMember.performConfigurationRetrieve(); + XmlElement elem = (XmlElement)xmlNode; + string name = elem.GetAttribute("Name"); + string xmlstr = "" + xmlNode.OuterXml + ""; + XmlConfigSource source = new XmlConfigSource(XmlReader.Create(new StringReader(xmlstr))); + ReadNiniConfig(source, name); + m_serverURI = string.Empty; } - public RegionInfo(uint regionLocX, uint regionLocY, IPEndPoint internalEndPoint, string externalUri) + public RegionInfo(uint legacyRegionLocX, uint legacyRegionLocY, IPEndPoint internalEndPoint, string externalUri) { - m_regionLocX = regionLocX; - m_regionLocY = regionLocY; - + RegionLocX = legacyRegionLocX; + RegionLocY = legacyRegionLocY; + RegionSizeX = Constants.RegionSize; + RegionSizeY = Constants.RegionSize; m_internalEndPoint = internalEndPoint; m_externalHostName = externalUri; m_serverURI = string.Empty; @@ -318,16 +340,18 @@ namespace OpenSim.Framework get { return m_objectCapacity; } } - public int LinksetCapacity + public int MaxPrimsPerUser { - get { return m_linksetCapacity; } + get { return m_maxPrimsPerUser; } } - public int AgentCapacity + public int LinksetCapacity { - get { return m_agentCapacity; } + get { return m_linksetCapacity; } } + public int AgentCapacity { get; set; } + public byte AccessLevel { get { return (byte)Util.ConvertMaturityToAccessLevel((uint)RegionSettings.Maturity); } @@ -338,6 +362,13 @@ namespace OpenSim.Framework get { return m_regionType; } } + public UUID MaptileStaticUUID + { + get { return m_maptileStaticUUID; } + } + + public string MaptileStaticFile { get; private set; } + /// /// The port by which http communication occurs with the region (most noticeably, CAPS communication) /// @@ -441,25 +472,42 @@ namespace OpenSim.Framework /// /// The x co-ordinate of this region in map tiles (e.g. 1000). + /// Coordinate is scaled as world coordinates divided by the legacy region size + /// and is thus is the number of legacy regions. /// public uint RegionLocX { - get { return m_regionLocX.Value; } - set { m_regionLocX = value; } + get { return WorldLocX / Constants.RegionSize; } + set { WorldLocX = value * Constants.RegionSize; } } /// /// The y co-ordinate of this region in map tiles (e.g. 1000). + /// Coordinate is scaled as world coordinates divided by the legacy region size + /// and is thus is the number of legacy regions. /// public uint RegionLocY { - get { return m_regionLocY.Value; } - set { m_regionLocY = value; } + get { return WorldLocY / Constants.RegionSize; } + set { WorldLocY = value * Constants.RegionSize; } + } + + public void SetDefaultRegionSize() + { + WorldLocX = 0; + WorldLocY = 0; + WorldLocZ = 0; + RegionSizeX = Constants.RegionSize; + RegionSizeY = Constants.RegionSize; + RegionSizeZ = Constants.RegionHeight; } + // A unique region handle is created from the region's world coordinates. + // This cannot be changed because some code expects to receive the region handle and then + // compute the region coordinates from it. public ulong RegionHandle { - get { return Util.UIntsToLong((RegionLocX * (uint) Constants.RegionSize), (RegionLocY * (uint) Constants.RegionSize)); } + get { return Util.UIntsToLong(WorldLocX, WorldLocY); } } public void SetEndPoint(string ipaddr, int port) @@ -469,20 +517,20 @@ namespace OpenSim.Framework m_internalEndPoint = tmpEPE; } - public string GetOtherSetting(string key) + public string GetSetting(string key) { string val; string keylower = key.ToLower(); - if (m_otherSettings.TryGetValue(keylower, out val)) + if (m_extraSettings.TryGetValue(keylower, out val)) return val; m_log.DebugFormat("[RegionInfo] Could not locate value for parameter {0}", key); return null; } - public void SetOtherSetting(string key, string value) + private void SetExtraSetting(string key, string value) { string keylower = key.ToLower(); - m_otherSettings[keylower] = value; + m_extraSettings[keylower] = value; } private void ReadNiniConfig(IConfigSource source, string name) @@ -566,8 +614,25 @@ namespace OpenSim.Framework string[] locationElements = location.Split(new char[] {','}); - m_regionLocX = Convert.ToUInt32(locationElements[0]); - m_regionLocY = Convert.ToUInt32(locationElements[1]); + RegionLocX = Convert.ToUInt32(locationElements[0]); + RegionLocY = Convert.ToUInt32(locationElements[1]); + + // Region size + // Default to legacy region size if not specified. + allKeys.Remove("SizeX"); + string configSizeX = config.GetString("SizeX", Constants.RegionSize.ToString()); + config.Set("SizeX", configSizeX); + RegionSizeX = Convert.ToUInt32(configSizeX); + allKeys.Remove("SizeY"); + string configSizeY = config.GetString("SizeY", Constants.RegionSize.ToString()); + config.Set("SizeY", configSizeX); + RegionSizeY = Convert.ToUInt32(configSizeY); + allKeys.Remove("SizeZ"); + string configSizeZ = config.GetString("SizeZ", Constants.RegionHeight.ToString()); + config.Set("SizeZ", configSizeX); + RegionSizeZ = Convert.ToUInt32(configSizeZ); + + DoRegionSizeSanityChecks(); // InternalAddress // @@ -641,7 +706,7 @@ namespace OpenSim.Framework m_regionType = config.GetString("RegionType", String.Empty); allKeys.Remove("RegionType"); - #region Prim stuff + #region Prim and map stuff m_nonphysPrimMin = config.GetFloat("NonPhysicalPrimMin", 0); allKeys.Remove("NonPhysicalPrimMin"); @@ -661,12 +726,25 @@ namespace OpenSim.Framework m_objectCapacity = config.GetInt("MaxPrims", 15000); allKeys.Remove("MaxPrims"); + m_maxPrimsPerUser = config.GetInt("MaxPrimsPerUser", -1); + allKeys.Remove("MaxPrimsPerUser"); + m_linksetCapacity = config.GetInt("LinksetPrims", 0); allKeys.Remove("LinksetPrims"); + + allKeys.Remove("MaptileStaticUUID"); + string mapTileStaticUUID = config.GetString("MaptileStaticUUID", UUID.Zero.ToString()); + if (UUID.TryParse(mapTileStaticUUID.Trim(), out m_maptileStaticUUID)) + { + config.Set("MaptileStaticUUID", m_maptileStaticUUID.ToString()); + } + + MaptileStaticFile = config.GetString("MaptileStaticFile", String.Empty); + allKeys.Remove("MaptileStaticFile"); #endregion - m_agentCapacity = config.GetInt("MaxAgents", 100); + AgentCapacity = config.GetInt("MaxAgents", 100); allKeys.Remove("MaxAgents"); // Multi-tenancy @@ -676,7 +754,58 @@ namespace OpenSim.Framework foreach (String s in allKeys) { - SetOtherSetting(s, config.GetString(s)); + SetExtraSetting(s, config.GetString(s)); + } + } + + // Make sure user specified region sizes are sane. + // Must be multiples of legacy region size (256). + private void DoRegionSizeSanityChecks() + { + if (RegionSizeX != Constants.RegionSize || RegionSizeY != Constants.RegionSize) + { + // Doing non-legacy region sizes. + // Enforce region size to be multiples of the legacy region size (256) + uint partial = RegionSizeX % Constants.RegionSize; + if (partial != 0) + { + RegionSizeX -= partial; + if (RegionSizeX == 0) + RegionSizeX = Constants.RegionSize; + m_log.ErrorFormat("{0} Region size must be multiple of {1}. Enforcing {2}.RegionSizeX={3} instead of specified {4}", + LogHeader, Constants.RegionSize, m_regionName, RegionSizeX, RegionSizeX + partial); + } + partial = RegionSizeY % Constants.RegionSize; + if (partial != 0) + { + RegionSizeY -= partial; + if (RegionSizeY == 0) + RegionSizeY = Constants.RegionSize; + m_log.ErrorFormat("{0} Region size must be multiple of {1}. Enforcing {2}.RegionSizeY={3} instead of specified {4}", + LogHeader, Constants.RegionSize, m_regionName, RegionSizeY, RegionSizeY + partial); + } + + // Because of things in the viewer, regions MUST be square. + // Remove this check when viewers have been updated. + if (RegionSizeX != RegionSizeY) + { + uint minSize = Math.Min(RegionSizeX, RegionSizeY); + RegionSizeX = minSize; + RegionSizeY = minSize; + m_log.ErrorFormat("{0} Regions must be square until viewers are updated. Forcing region {1} size to <{2},{3}>", + LogHeader, m_regionName, RegionSizeX, RegionSizeY); + } + + // There is a practical limit to region size. + if (RegionSizeX > Constants.MaximumRegionSize || RegionSizeY > Constants.MaximumRegionSize) + { + RegionSizeX = Util.Clamp(RegionSizeX, Constants.RegionSize, Constants.MaximumRegionSize); + RegionSizeY = Util.Clamp(RegionSizeY, Constants.RegionSize, Constants.MaximumRegionSize); + m_log.ErrorFormat("{0} Region dimensions must be less than {1}. Clamping {2}'s size to <{3},{4}>", + LogHeader, Constants.MaximumRegionSize, m_regionName, RegionSizeX, RegionSizeY); + } + + m_log.InfoFormat("{0} Region {1} size set to <{2},{3}>", LogHeader, m_regionName, RegionSizeX, RegionSizeY); } } @@ -691,9 +820,18 @@ namespace OpenSim.Framework config.Set("RegionUUID", RegionID.ToString()); - string location = String.Format("{0},{1}", m_regionLocX, m_regionLocY); + string location = String.Format("{0},{1}", RegionLocX, RegionLocY); config.Set("Location", location); + if (RegionSizeX > 0) + config.Set("SizeX", RegionSizeX); + + if (RegionSizeY > 0) + config.Set("SizeY", RegionSizeY); + +// if (RegionSizeZ > 0) +// config.Set("SizeZ", RegionSizeZ); + config.Set("InternalAddress", m_internalEndPoint.Address.ToString()); config.Set("InternalPort", m_internalEndPoint.Port); @@ -718,22 +856,26 @@ namespace OpenSim.Framework if (m_objectCapacity > 0) config.Set("MaxPrims", m_objectCapacity); + if (m_maxPrimsPerUser > -1) + config.Set("MaxPrimsPerUser", m_maxPrimsPerUser); + if (m_linksetCapacity > 0) config.Set("LinksetPrims", m_linksetCapacity); - if (m_agentCapacity > 0) - config.Set("MaxAgents", m_agentCapacity); + if (AgentCapacity > 0) + config.Set("MaxAgents", AgentCapacity); if (ScopeID != UUID.Zero) config.Set("ScopeID", ScopeID.ToString()); if (RegionType != String.Empty) config.Set("RegionType", RegionType); - } - public bool ignoreIncomingConfiguration(string configuration_key, object configuration_result) - { - return true; + if (m_maptileStaticUUID != UUID.Zero) + config.Set("MaptileStaticUUID", m_maptileStaticUUID.ToString()); + + if (MaptileStaticFile != null && MaptileStaticFile != String.Empty) + config.Set("MaptileStaticFile", MaptileStaticFile); } public void SaveRegionToFile(string description, string filename) @@ -755,215 +897,14 @@ namespace OpenSim.Framework return; } - else if (filename.ToLower().EndsWith(".xml")) - { - configMember = new ConfigurationMember(filename, description, loadConfigurationOptionsFromMe, - ignoreIncomingConfiguration, false); - configMember.performConfigurationRetrieve(); - RegionFile = filename; - } else throw new Exception("Invalid file type for region persistence."); } - public void loadConfigurationOptionsFromMe() - { - configMember.addConfigurationOption("sim_UUID", ConfigurationOption.ConfigurationTypes.TYPE_UUID_NULL_FREE, - "UUID of Region (Default is recommended, random UUID)", - RegionID.ToString(), true); - configMember.addConfigurationOption("sim_name", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, - "Region Name", RegionName, true); - configMember.addConfigurationOption("sim_location_x", ConfigurationOption.ConfigurationTypes.TYPE_UINT32, - "Grid Location (X Axis)", m_regionLocX.ToString(), true); - configMember.addConfigurationOption("sim_location_y", ConfigurationOption.ConfigurationTypes.TYPE_UINT32, - "Grid Location (Y Axis)", m_regionLocY.ToString(), true); - //m_configMember.addConfigurationOption("datastore", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, "Filename for local storage", "OpenSim.db", false); - configMember.addConfigurationOption("internal_ip_address", - ConfigurationOption.ConfigurationTypes.TYPE_IP_ADDRESS, - "Internal IP Address for incoming UDP client connections", - m_internalEndPoint.Address.ToString(), - true); - configMember.addConfigurationOption("internal_ip_port", ConfigurationOption.ConfigurationTypes.TYPE_INT32, - "Internal IP Port for incoming UDP client connections", - m_internalEndPoint.Port.ToString(), true); - configMember.addConfigurationOption("allow_alternate_ports", - ConfigurationOption.ConfigurationTypes.TYPE_BOOLEAN, - "Allow sim to find alternate UDP ports when ports are in use?", - m_allow_alternate_ports.ToString(), true); - configMember.addConfigurationOption("external_host_name", - ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, - "External Host Name", m_externalHostName, true); - configMember.addConfigurationOption("lastmap_uuid", ConfigurationOption.ConfigurationTypes.TYPE_UUID, - "Last Map UUID", lastMapUUID.ToString(), true); - configMember.addConfigurationOption("lastmap_refresh", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, - "Last Map Refresh", Util.UnixTimeSinceEpoch().ToString(), true); - - configMember.addConfigurationOption("nonphysical_prim_min", ConfigurationOption.ConfigurationTypes.TYPE_FLOAT, - "Minimum size for nonphysical prims", m_nonphysPrimMin.ToString(), true); - - configMember.addConfigurationOption("nonphysical_prim_max", ConfigurationOption.ConfigurationTypes.TYPE_INT32, - "Maximum size for nonphysical prims", m_nonphysPrimMax.ToString(), true); - - configMember.addConfigurationOption("physical_prim_min", ConfigurationOption.ConfigurationTypes.TYPE_FLOAT, - "Minimum size for nonphysical prims", m_physPrimMin.ToString(), true); - - configMember.addConfigurationOption("physical_prim_max", ConfigurationOption.ConfigurationTypes.TYPE_INT32, - "Maximum size for physical prims", m_physPrimMax.ToString(), true); - - configMember.addConfigurationOption("clamp_prim_size", ConfigurationOption.ConfigurationTypes.TYPE_BOOLEAN, - "Clamp prims to max size", m_clampPrimSize.ToString(), true); - - configMember.addConfigurationOption("object_capacity", ConfigurationOption.ConfigurationTypes.TYPE_INT32, - "Max objects this sim will hold", m_objectCapacity.ToString(), true); - - configMember.addConfigurationOption("linkset_capacity", ConfigurationOption.ConfigurationTypes.TYPE_INT32, - "Max prims an object will hold", m_linksetCapacity.ToString(), true); - - configMember.addConfigurationOption("agent_capacity", ConfigurationOption.ConfigurationTypes.TYPE_INT32, - "Max avatars this sim will hold", m_agentCapacity.ToString(), true); - - configMember.addConfigurationOption("scope_id", ConfigurationOption.ConfigurationTypes.TYPE_UUID, - "Scope ID for this region", ScopeID.ToString(), true); - - configMember.addConfigurationOption("region_type", ConfigurationOption.ConfigurationTypes.TYPE_STRING, - "Free form string describing the type of region", String.Empty, true); - } - - public void loadConfigurationOptions() - { - configMember.addConfigurationOption("sim_UUID", ConfigurationOption.ConfigurationTypes.TYPE_UUID, - "UUID of Region (Default is recommended, random UUID)", - UUID.Random().ToString(), true); - configMember.addConfigurationOption("sim_name", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, - "Region Name", "OpenSim Test", false); - configMember.addConfigurationOption("sim_location_x", ConfigurationOption.ConfigurationTypes.TYPE_UINT32, - "Grid Location (X Axis)", "1000", false); - configMember.addConfigurationOption("sim_location_y", ConfigurationOption.ConfigurationTypes.TYPE_UINT32, - "Grid Location (Y Axis)", "1000", false); - //m_configMember.addConfigurationOption("datastore", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, "Filename for local storage", "OpenSim.db", false); - configMember.addConfigurationOption("internal_ip_address", - ConfigurationOption.ConfigurationTypes.TYPE_IP_ADDRESS, - "Internal IP Address for incoming UDP client connections", "0.0.0.0", - false); - configMember.addConfigurationOption("internal_ip_port", ConfigurationOption.ConfigurationTypes.TYPE_INT32, - "Internal IP Port for incoming UDP client connections", - ConfigSettings.DefaultRegionHttpPort.ToString(), false); - configMember.addConfigurationOption("allow_alternate_ports", ConfigurationOption.ConfigurationTypes.TYPE_BOOLEAN, - "Allow sim to find alternate UDP ports when ports are in use?", - "false", true); - configMember.addConfigurationOption("external_host_name", - ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, - "External Host Name", "127.0.0.1", false); - configMember.addConfigurationOption("lastmap_uuid", ConfigurationOption.ConfigurationTypes.TYPE_UUID, - "Last Map UUID", lastMapUUID.ToString(), true); - - configMember.addConfigurationOption("lastmap_refresh", ConfigurationOption.ConfigurationTypes.TYPE_STRING_NOT_EMPTY, - "Last Map Refresh", Util.UnixTimeSinceEpoch().ToString(), true); - - configMember.addConfigurationOption("nonphysical_prim_max", ConfigurationOption.ConfigurationTypes.TYPE_INT32, - "Maximum size for nonphysical prims", "0", true); - - configMember.addConfigurationOption("physical_prim_max", ConfigurationOption.ConfigurationTypes.TYPE_INT32, - "Maximum size for physical prims", "0", true); - - configMember.addConfigurationOption("clamp_prim_size", ConfigurationOption.ConfigurationTypes.TYPE_BOOLEAN, - "Clamp prims to max size", "false", true); - - configMember.addConfigurationOption("object_capacity", ConfigurationOption.ConfigurationTypes.TYPE_INT32, - "Max objects this sim will hold", "15000", true); - - configMember.addConfigurationOption("agent_capacity", ConfigurationOption.ConfigurationTypes.TYPE_INT32, - "Max avatars this sim will hold", "100", true); - - configMember.addConfigurationOption("scope_id", ConfigurationOption.ConfigurationTypes.TYPE_UUID, - "Scope ID for this region", UUID.Zero.ToString(), true); - - configMember.addConfigurationOption("region_type", ConfigurationOption.ConfigurationTypes.TYPE_STRING, - "Region Type", String.Empty, true); - } - - public bool handleIncomingConfiguration(string configuration_key, object configuration_result) - { - switch (configuration_key) - { - case "sim_UUID": - RegionID = (UUID) configuration_result; - originRegionID = (UUID) configuration_result; - break; - case "sim_name": - RegionName = (string) configuration_result; - break; - case "sim_location_x": - m_regionLocX = (uint) configuration_result; - break; - case "sim_location_y": - m_regionLocY = (uint) configuration_result; - break; - case "internal_ip_address": - IPAddress address = (IPAddress) configuration_result; - m_internalEndPoint = new IPEndPoint(address, 0); - break; - case "internal_ip_port": - m_internalEndPoint.Port = (int) configuration_result; - break; - case "allow_alternate_ports": - m_allow_alternate_ports = (bool) configuration_result; - break; - case "external_host_name": - if ((string) configuration_result != "SYSTEMIP") - { - m_externalHostName = (string) configuration_result; - } - else - { - m_externalHostName = Util.GetLocalHost().ToString(); - } - break; - case "lastmap_uuid": - lastMapUUID = (UUID)configuration_result; - break; - case "lastmap_refresh": - lastMapRefresh = (string)configuration_result; - break; - case "nonphysical_prim_max": - m_nonphysPrimMax = (int)configuration_result; - break; - case "physical_prim_max": - m_physPrimMax = (int)configuration_result; - break; - case "clamp_prim_size": - m_clampPrimSize = (bool)configuration_result; - break; - case "object_capacity": - m_objectCapacity = (int)configuration_result; - break; - case "linkset_capacity": - m_linksetCapacity = (int)configuration_result; - break; - case "agent_capacity": - m_agentCapacity = (int)configuration_result; - break; - case "scope_id": - ScopeID = (UUID)configuration_result; - break; - case "region_type": - m_regionType = (string)configuration_result; - break; - } - - return true; - } - public void SaveLastMapUUID(UUID mapUUID) { lastMapUUID = mapUUID; lastMapRefresh = Util.UnixTimeSinceEpoch().ToString(); - - if (configMember == null) - return; - - configMember.forceSetConfigurationOption("lastmap_uuid", mapUUID.ToString()); - configMember.forceSetConfigurationOption("lastmap_refresh", lastMapRefresh); } public OSDMap PackRegionInfoData() @@ -975,8 +916,13 @@ namespace OpenSim.Framework args["external_host_name"] = OSD.FromString(ExternalHostName); args["http_port"] = OSD.FromString(HttpPort.ToString()); args["server_uri"] = OSD.FromString(ServerURI); + args["region_xloc"] = OSD.FromString(RegionLocX.ToString()); args["region_yloc"] = OSD.FromString(RegionLocY.ToString()); + args["region_size_x"] = OSD.FromString(RegionSizeX.ToString()); + args["region_size_y"] = OSD.FromString(RegionSizeY.ToString()); + args["region_size_z"] = OSD.FromString(RegionSizeZ.ToString()); + args["internal_ep_address"] = OSD.FromString(InternalEndPoint.Address.ToString()); args["internal_ep_port"] = OSD.FromString(InternalEndPoint.Port.ToString()); if ((RemotingAddress != null) && !RemotingAddress.Equals("")) @@ -1015,6 +961,13 @@ namespace OpenSim.Framework UInt32.TryParse(args["region_yloc"].AsString(), out locy); RegionLocY = locy; } + if (args.ContainsKey("region_size_x")) + RegionSizeX = (uint)args["region_size_x"].AsInteger(); + if (args.ContainsKey("region_size_y")) + RegionSizeY = (uint)args["region_size_y"].AsInteger(); + if (args.ContainsKey("region_size_z")) + RegionSizeZ = (uint)args["region_size_z"].AsInteger(); + IPAddress ip_addr = null; if (args["internal_ep_address"] != null) { @@ -1051,23 +1004,5 @@ namespace OpenSim.Framework regionInfo.ServerURI = serverURI; return regionInfo; } - - public Dictionary ToKeyValuePairs() - { - Dictionary kvp = new Dictionary(); - kvp["uuid"] = RegionID.ToString(); - kvp["locX"] = RegionLocX.ToString(); - kvp["locY"] = RegionLocY.ToString(); - kvp["external_ip_address"] = ExternalEndPoint.Address.ToString(); - kvp["external_port"] = ExternalEndPoint.Port.ToString(); - kvp["external_host_name"] = ExternalHostName; - kvp["http_port"] = HttpPort.ToString(); - kvp["internal_ip_address"] = InternalEndPoint.Address.ToString(); - kvp["internal_port"] = InternalEndPoint.Port.ToString(); - kvp["alternate_ports"] = m_allow_alternate_ports.ToString(); - kvp["server_uri"] = ServerURI; - - return kvp; - } } } diff --git a/OpenSim/Framework/RegionLoader/Filesystem/Properties/AssemblyInfo.cs b/OpenSim/Framework/RegionLoader/Filesystem/Properties/AssemblyInfo.cs deleted file mode 100644 index d670f2f..0000000 --- a/OpenSim/Framework/RegionLoader/Filesystem/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("OpenSim.Framework.RegionLoader.Filesystem")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("http://opensimulator.org")] -[assembly: AssemblyProduct("OpenSim")] -[assembly: AssemblyCopyright("OpenSimulator developers")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("4ab5c74b-e886-40a1-b67d-a04df285e706")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -[assembly: AssemblyVersion("0.7.5.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/OpenSim/Framework/RegionLoader/Filesystem/RegionLoaderFileSystem.cs b/OpenSim/Framework/RegionLoader/Filesystem/RegionLoaderFileSystem.cs deleted file mode 100644 index 8332c14..0000000 --- a/OpenSim/Framework/RegionLoader/Filesystem/RegionLoaderFileSystem.cs +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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 OpenSimulator 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 log4net; -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using Nini.Config; - -namespace OpenSim.Framework.RegionLoader.Filesystem -{ - public class RegionLoaderFileSystem : IRegionLoader - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - private IConfigSource m_configSource; - - public void SetIniConfigSource(IConfigSource configSource) - { - m_configSource = configSource; - } - - public RegionInfo[] LoadRegions() - { - string regionConfigPath = Path.Combine(Util.configDir(), "Regions"); - bool allowRegionless = false; - - try - { - IConfig startupConfig = (IConfig)m_configSource.Configs["Startup"]; - regionConfigPath = startupConfig.GetString("regionload_regionsdir", regionConfigPath).Trim(); - allowRegionless = startupConfig.GetBoolean("allow_regionless", false); - } - catch (Exception) - { - // No INI setting recorded. - } - - if (!Directory.Exists(regionConfigPath)) - { - Directory.CreateDirectory(regionConfigPath); - } - - string[] configFiles = Directory.GetFiles(regionConfigPath, "*.xml"); - string[] iniFiles = Directory.GetFiles(regionConfigPath, "*.ini"); - - // Create an empty Regions.ini if there are no existing config files. - if (!allowRegionless && configFiles.Length == 0 && iniFiles.Length == 0) - { - new RegionInfo("DEFAULT REGION CONFIG", Path.Combine(regionConfigPath, "Regions.ini"), false, m_configSource); - iniFiles = Directory.GetFiles(regionConfigPath, "*.ini"); - } - - m_log.InfoFormat("[REGION LOADER FILE SYSTEM]: Loading config files from {0}", regionConfigPath); - - List regionInfos = new List(); - - int i = 0; - foreach (string file in iniFiles) - { - m_log.InfoFormat("[REGION LOADER FILE SYSTEM]: Loading config file {0}", file); - - IConfigSource source = new IniConfigSource(file); - - foreach (IConfig config in source.Configs) - { - RegionInfo regionInfo = new RegionInfo("REGION CONFIG #" + (i + 1), file, false, m_configSource, config.Name); - regionInfos.Add(regionInfo); - - m_log.InfoFormat("[REGION LOADER FILE SYSTEM]: Loaded config for region {0}", regionInfo.RegionName); - - i++; - } - } - - foreach (string file in configFiles) - { - m_log.InfoFormat("[REGION LOADER FILE SYSTEM]: Loading config file {0}", file); - - RegionInfo regionInfo = new RegionInfo("REGION CONFIG #" + (i + 1), file, false, m_configSource); - regionInfos.Add(regionInfo); - - m_log.InfoFormat("[REGION LOADER FILE SYSTEM]: Loaded config for region {0}", regionInfo.RegionName); - - i++; - } - - return regionInfos.ToArray(); - } - } -} \ No newline at end of file diff --git a/OpenSim/Framework/RegionLoader/Web/Properties/AssemblyInfo.cs b/OpenSim/Framework/RegionLoader/Web/Properties/AssemblyInfo.cs deleted file mode 100644 index 7309a12..0000000 --- a/OpenSim/Framework/RegionLoader/Web/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("OpenSim.Framework.RegionLoader.Web")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("http://opensimulator.org")] -[assembly: AssemblyProduct("OpenSim")] -[assembly: AssemblyCopyright("OpenSimulator developers")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("985afff8-e7ed-4056-acce-39abf7a43d33")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -[assembly: AssemblyVersion("0.7.5.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/OpenSim/Framework/RegionLoader/Web/RegionLoaderWebServer.cs b/OpenSim/Framework/RegionLoader/Web/RegionLoaderWebServer.cs deleted file mode 100644 index a2f5d9c..0000000 --- a/OpenSim/Framework/RegionLoader/Web/RegionLoaderWebServer.cs +++ /dev/null @@ -1,130 +0,0 @@ -/* - * 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 OpenSimulator 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.IO; -using System.Net; -using System.Reflection; -using System.Xml; -using log4net; -using Nini.Config; - -namespace OpenSim.Framework.RegionLoader.Web -{ - public class RegionLoaderWebServer : IRegionLoader - { - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - private IConfigSource m_configSource; - - public void SetIniConfigSource(IConfigSource configSource) - { - m_configSource = configSource; - } - - public RegionInfo[] LoadRegions() - { - if (m_configSource == null) - { - m_log.Error("[WEBLOADER]: Unable to load configuration source!"); - return null; - } - else - { - IConfig startupConfig = (IConfig) m_configSource.Configs["Startup"]; - string url = startupConfig.GetString("regionload_webserver_url", String.Empty).Trim(); - bool allowRegionless = startupConfig.GetBoolean("allow_regionless", false); - - if (url == String.Empty) - { - m_log.Error("[WEBLOADER]: Unable to load webserver URL - URL was empty."); - return null; - } - else - { - RegionInfo[] regionInfos = new RegionInfo[] {}; - int regionCount = 0; - HttpWebRequest webRequest = (HttpWebRequest) WebRequest.Create(url); - webRequest.Timeout = 30000; //30 Second Timeout - m_log.DebugFormat("[WEBLOADER]: Sending download request to {0}", url); - - try - { - HttpWebResponse webResponse = (HttpWebResponse) webRequest.GetResponse(); - m_log.Debug("[WEBLOADER]: Downloading region information..."); - StreamReader reader = new StreamReader(webResponse.GetResponseStream()); - string xmlSource = String.Empty; - string tempStr = reader.ReadLine(); - while (tempStr != null) - { - xmlSource = xmlSource + tempStr; - tempStr = reader.ReadLine(); - } - m_log.Debug("[WEBLOADER]: Done downloading region information from server. Total Bytes: " + - xmlSource.Length); - XmlDocument xmlDoc = new XmlDocument(); - xmlDoc.LoadXml(xmlSource); - if (xmlDoc.FirstChild.Name == "Regions") - { - regionCount = xmlDoc.FirstChild.ChildNodes.Count; - - if (regionCount > 0) - { - regionInfos = new RegionInfo[regionCount]; - int i; - for (i = 0; i < xmlDoc.FirstChild.ChildNodes.Count; i++) - { - m_log.Debug(xmlDoc.FirstChild.ChildNodes[i].OuterXml); - regionInfos[i] = - new RegionInfo("REGION CONFIG #" + (i + 1), xmlDoc.FirstChild.ChildNodes[i],false,m_configSource); - } - } - } - } - catch (WebException ex) - { - if (((HttpWebResponse)ex.Response).StatusCode == HttpStatusCode.NotFound) - { - if (!allowRegionless) - throw ex; - } - else - throw ex; - } - - if (regionCount > 0 | allowRegionless) - return regionInfos; - else - { - m_log.Error("[WEBLOADER]: No region configs were available."); - return null; - } - } - } - } - } -} diff --git a/OpenSim/Framework/RegionSettings.cs b/OpenSim/Framework/RegionSettings.cs index 47dbcec..a895c40 100644 --- a/OpenSim/Framework/RegionSettings.cs +++ b/OpenSim/Framework/RegionSettings.cs @@ -200,7 +200,7 @@ namespace OpenSim.Framework set { m_ObjectBonus = value; } } - private int m_Maturity = 1; + private int m_Maturity = 0; public int Maturity { @@ -482,21 +482,14 @@ namespace OpenSim.Framework set { m_LoadedCreationID = value; } } - // Connected Telehub object - private UUID m_TelehubObject = UUID.Zero; - public UUID TelehubObject - { - get - { - return m_TelehubObject; - } - set - { - m_TelehubObject = value; - } - } + /// + /// Connected Telehub object + /// + public UUID TelehubObject { get; set; } - // Our Connected Telehub's SpawnPoints + /// + /// Our connected Telehub's SpawnPoints + /// public List l_SpawnPoints = new List(); // Add a SpawnPoint diff --git a/OpenSim/Framework/RestClient.cs b/OpenSim/Framework/RestClient.cs new file mode 100644 index 0000000..7080ca5 --- /dev/null +++ b/OpenSim/Framework/RestClient.cs @@ -0,0 +1,676 @@ +/* + * 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 OpenSimulator 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.Reflection; +using System.Text; +using System.Threading; +using System.Web; +using log4net; + +using OpenSim.Framework.ServiceAuth; + +namespace OpenSim.Framework +{ + /// + /// Implementation of a generic REST client + /// + /// + /// This class is a generic implementation of a REST (Representational State Transfer) web service. This + /// class is designed to execute both synchronously and asynchronously. + /// + /// Internally the implementation works as a two stage asynchronous web-client. + /// When the request is initiated, RestClient will query asynchronously for for a web-response, + /// sleeping until the initial response is returned by the server. Once the initial response is retrieved + /// the second stage of asynchronous requests will be triggered, in an attempt to read of the response + /// object into a memorystream as a sequence of asynchronous reads. + /// + /// The asynchronisity of RestClient is designed to move as much processing into the back-ground, allowing + /// other threads to execute, while it waits for a response from the web-service. RestClient itself can be + /// invoked by the caller in either synchronous mode or asynchronous modes. + /// + public class RestClient : IDisposable + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + // private string realuri; + + #region member variables + + /// + /// The base Uri of the web-service e.g. http://www.google.com + /// + private string _url; + + /// + /// Path elements of the query + /// + private List _pathElements = new List(); + + /// + /// Parameter elements of the query, e.g. min=34 + /// + private Dictionary _parameterElements = new Dictionary(); + + /// + /// Request method. E.g. GET, POST, PUT or DELETE + /// + private string _method; + + /// + /// Temporary buffer used to store bytes temporarily as they come in from the server + /// + private byte[] _readbuf; + + /// + /// MemoryStream representing the resulting resource + /// + private Stream _resource; + + /// + /// WebRequest object, held as a member variable + /// + private HttpWebRequest _request; + + /// + /// WebResponse object, held as a member variable, so we can close it + /// + private HttpWebResponse _response; + + /// + /// This flag will help block the main synchroneous method, in case we run in synchroneous mode + /// + //public static ManualResetEvent _allDone = new ManualResetEvent(false); + + /// + /// Default time out period + /// + //private const int DefaultTimeout = 10*1000; // 10 seconds timeout + + /// + /// Default Buffer size of a block requested from the web-server + /// + private const int BufferSize = 4096; // Read blocks of 4 KB. + + + /// + /// if an exception occours during async processing, we need to save it, so it can be + /// rethrown on the primary thread; + /// + private Exception _asyncException; + + #endregion member variables + + #region constructors + + /// + /// Instantiate a new RestClient + /// + /// Web-service to query, e.g. http://osgrid.org:8003 + public RestClient(string url) + { + _url = url; + _readbuf = new byte[BufferSize]; + _resource = new MemoryStream(); + _request = null; + _response = null; + _lock = new object(); + } + + private object _lock; + + #endregion constructors + + + #region Dispose + + private bool disposed = false; + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposed) + return; + + if (disposing) + { + _resource.Dispose(); + } + + disposed = true; + } + + #endregion Dispose + + + /// + /// Add a path element to the query, e.g. assets + /// + /// path entry + public void AddResourcePath(string element) + { + if (isSlashed(element)) + _pathElements.Add(element.Substring(0, element.Length - 1)); + else + _pathElements.Add(element); + } + + /// + /// Add a query parameter to the Url + /// + /// Name of the parameter, e.g. min + /// Value of the parameter, e.g. 42 + public void AddQueryParameter(string name, string value) + { + try + { + _parameterElements.Add(HttpUtility.UrlEncode(name), HttpUtility.UrlEncode(value)); + } + catch (ArgumentException) + { + m_log.Error("[REST]: Query parameter " + name + " is already added."); + } + catch (Exception e) + { + m_log.Error("[REST]: An exception was raised adding query parameter to dictionary. Exception: {0}",e); + } + } + + /// + /// Add a query parameter to the Url + /// + /// Name of the parameter, e.g. min + public void AddQueryParameter(string name) + { + try + { + _parameterElements.Add(HttpUtility.UrlEncode(name), null); + } + catch (ArgumentException) + { + m_log.Error("[REST]: Query parameter " + name + " is already added."); + } + catch (Exception e) + { + m_log.Error("[REST]: An exception was raised adding query parameter to dictionary. Exception: {0}",e); + } + } + + /// + /// Web-Request method, e.g. GET, PUT, POST, DELETE + /// + public string RequestMethod + { + get { return _method; } + set { _method = value; } + } + + /// + /// True if string contains a trailing slash '/' + /// + /// string to be examined + /// true if slash is present + private static bool isSlashed(string s) + { + return s.Substring(s.Length - 1, 1) == "/"; + } + + /// + /// Build a Uri based on the initial Url, path elements and parameters + /// + /// fully constructed Uri + private Uri buildUri() + { + StringBuilder sb = new StringBuilder(); + sb.Append(_url); + + foreach (string e in _pathElements) + { + sb.Append("/"); + sb.Append(e); + } + + bool firstElement = true; + foreach (KeyValuePair kv in _parameterElements) + { + if (firstElement) + { + sb.Append("?"); + firstElement = false; + } + else + sb.Append("&"); + + sb.Append(kv.Key); + if (!string.IsNullOrEmpty(kv.Value)) + { + sb.Append("="); + sb.Append(kv.Value); + } + } + // realuri = sb.ToString(); + //m_log.InfoFormat("[REST CLIENT]: RestURL: {0}", realuri); + return new Uri(sb.ToString()); + } + + #region Async communications with server + + /// + /// Async method, invoked when a block of data has been received from the service + /// + /// + private void StreamIsReadyDelegate(IAsyncResult ar) + { + try + { + Stream s = (Stream) ar.AsyncState; + int read = s.EndRead(ar); + + if (read > 0) + { + _resource.Write(_readbuf, 0, read); + // IAsyncResult asynchronousResult = + // s.BeginRead(_readbuf, 0, BufferSize, new AsyncCallback(StreamIsReadyDelegate), s); + s.BeginRead(_readbuf, 0, BufferSize, new AsyncCallback(StreamIsReadyDelegate), s); + + // TODO! Implement timeout, without killing the server + //ThreadPool.RegisterWaitForSingleObject(asynchronousResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true); + } + else + { + s.Close(); + //_allDone.Set(); + } + } + catch (Exception e) + { + //_allDone.Set(); + _asyncException = e; + } + } + + #endregion Async communications with server + + /// + /// Perform a synchronous request + /// + public Stream Request() + { + return Request(null); + } + + /// + /// Perform a synchronous request + /// + public Stream Request(IServiceAuth auth) + { + lock (_lock) + { + _request = (HttpWebRequest) WebRequest.Create(buildUri()); + _request.KeepAlive = false; + _request.ContentType = "application/xml"; + _request.Timeout = 200000; + _request.Method = RequestMethod; + _asyncException = null; + if (auth != null) + auth.AddAuthorization(_request.Headers); + + int reqnum = WebUtil.RequestNumber++; + if (WebUtil.DebugLevel >= 3) + m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} REST {1} to {2}", reqnum, _request.Method, _request.RequestUri); + +// IAsyncResult responseAsyncResult = _request.BeginGetResponse(new AsyncCallback(ResponseIsReadyDelegate), _request); + + try + { + using (_response = (HttpWebResponse) _request.GetResponse()) + { + using (Stream src = _response.GetResponseStream()) + { + int length = src.Read(_readbuf, 0, BufferSize); + while (length > 0) + { + _resource.Write(_readbuf, 0, length); + length = src.Read(_readbuf, 0, BufferSize); + } + + // TODO! Implement timeout, without killing the server + // this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted + //ThreadPool.RegisterWaitForSingleObject(responseAsyncResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true); + + // _allDone.WaitOne(); + } + } + } + catch (WebException e) + { + using (HttpWebResponse errorResponse = e.Response as HttpWebResponse) + { + if (null != errorResponse && HttpStatusCode.NotFound == errorResponse.StatusCode) + { + // This is often benign. E.g., requesting a missing asset will return 404. + m_log.DebugFormat("[REST CLIENT] Resource not found (404): {0}", _request.Address.ToString()); + } + else + { + m_log.Error(string.Format("[REST CLIENT] Error fetching resource from server: {0} ", _request.Address.ToString()), e); + } + } + + return null; + } + + if (_asyncException != null) + throw _asyncException; + + if (_resource != null) + { + _resource.Flush(); + _resource.Seek(0, SeekOrigin.Begin); + } + + if (WebUtil.DebugLevel >= 5) + WebUtil.LogResponseDetail(reqnum, _resource); + + return _resource; + } + } + + public Stream Request(Stream src, IServiceAuth auth) + { + _request = (HttpWebRequest) WebRequest.Create(buildUri()); + _request.KeepAlive = false; + _request.ContentType = "application/xml"; + _request.Timeout = 900000; + _request.Method = RequestMethod; + _asyncException = null; + _request.ContentLength = src.Length; + if (auth != null) + auth.AddAuthorization(_request.Headers); + + src.Seek(0, SeekOrigin.Begin); + + int reqnum = WebUtil.RequestNumber++; + if (WebUtil.DebugLevel >= 3) + m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} REST {1} to {2}", reqnum, _request.Method, _request.RequestUri); + if (WebUtil.DebugLevel >= 5) + WebUtil.LogOutgoingDetail(string.Format("SEND {0}: ", reqnum), src); + + Stream dst = _request.GetRequestStream(); + + byte[] buf = new byte[1024]; + int length = src.Read(buf, 0, 1024); + while (length > 0) + { + dst.Write(buf, 0, length); + length = src.Read(buf, 0, 1024); + } + + try + { + _response = (HttpWebResponse)_request.GetResponse(); + } + catch (WebException e) + { + m_log.WarnFormat("[REST]: Request {0} {1} failed with status {2} and message {3}", + RequestMethod, _request.RequestUri, e.Status, e.Message); + return null; + } + catch (Exception e) + { + m_log.WarnFormat( + "[REST]: Request {0} {1} failed with exception {2} {3}", + RequestMethod, _request.RequestUri, e.Message, e.StackTrace); + return null; + } + + if (WebUtil.DebugLevel >= 5) + { + using (Stream responseStream = _response.GetResponseStream()) + { + using (StreamReader reader = new StreamReader(responseStream)) + { + string responseStr = reader.ReadToEnd(); + WebUtil.LogResponseDetail(reqnum, responseStr); + } + } + } + + _response.Close(); + +// IAsyncResult responseAsyncResult = _request.BeginGetResponse(new AsyncCallback(ResponseIsReadyDelegate), _request); + + // TODO! Implement timeout, without killing the server + // this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted + //ThreadPool.RegisterWaitForSingleObject(responseAsyncResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), _request, DefaultTimeout, true); + + return null; + } + + #region Async Invocation + + public IAsyncResult BeginRequest(AsyncCallback callback, object state) + { + /// + /// In case, we are invoked asynchroneously this object will keep track of the state + /// + AsyncResult ar = new AsyncResult(callback, state); + Util.FireAndForget(RequestHelper, ar, "RestClient.BeginRequest"); + return ar; + } + + public Stream EndRequest(IAsyncResult asyncResult) + { + AsyncResult ar = (AsyncResult) asyncResult; + + // Wait for operation to complete, then return result or + // throw exception + return ar.EndInvoke(); + } + + private void RequestHelper(Object asyncResult) + { + // We know that it's really an AsyncResult object + AsyncResult ar = (AsyncResult) asyncResult; + try + { + // Perform the operation; if sucessful set the result + Stream s = Request(null); + ar.SetAsCompleted(s, false); + } + catch (Exception e) + { + // If operation fails, set the exception + ar.HandleException(e, false); + } + } + + #endregion Async Invocation + } + + internal class SimpleAsyncResult : IAsyncResult + { + private readonly AsyncCallback m_callback; + + /// + /// Is process completed? + /// + /// Should really be boolean, but VolatileRead has no boolean method + private byte m_completed; + + /// + /// Did process complete synchronously? + /// + /// I have a hard time imagining a scenario where this is the case, again, same issue about + /// booleans and VolatileRead as m_completed + /// + private byte m_completedSynchronously; + + private readonly object m_asyncState; + private ManualResetEvent m_waitHandle; + private Exception m_exception; + + internal SimpleAsyncResult(AsyncCallback cb, object state) + { + m_callback = cb; + m_asyncState = state; + m_completed = 0; + m_completedSynchronously = 1; + } + + #region IAsyncResult Members + + public object AsyncState + { + get { return m_asyncState; } + } + + public WaitHandle AsyncWaitHandle + { + get + { + if (m_waitHandle == null) + { + bool done = IsCompleted; + ManualResetEvent mre = new ManualResetEvent(done); + if (Interlocked.CompareExchange(ref m_waitHandle, mre, null) != null) + { + mre.Close(); + } + else + { + if (!done && IsCompleted) + { + m_waitHandle.Set(); + } + } + } + + return m_waitHandle; + } + } + + + public bool CompletedSynchronously + { + get { return Thread.VolatileRead(ref m_completedSynchronously) == 1; } + } + + + public bool IsCompleted + { + get { return Thread.VolatileRead(ref m_completed) == 1; } + } + + #endregion + + #region class Methods + + internal void SetAsCompleted(bool completedSynchronously) + { + m_completed = 1; + if (completedSynchronously) + m_completedSynchronously = 1; + else + m_completedSynchronously = 0; + + SignalCompletion(); + } + + internal void HandleException(Exception e, bool completedSynchronously) + { + m_completed = 1; + if (completedSynchronously) + m_completedSynchronously = 1; + else + m_completedSynchronously = 0; + m_exception = e; + + SignalCompletion(); + } + + private void SignalCompletion() + { + if (m_waitHandle != null) m_waitHandle.Set(); + + if (m_callback != null) m_callback(this); + } + + public void EndInvoke() + { + // This method assumes that only 1 thread calls EndInvoke + if (!IsCompleted) + { + // If the operation isn't done, wait for it + AsyncWaitHandle.WaitOne(); + AsyncWaitHandle.Close(); + m_waitHandle.Close(); + m_waitHandle = null; // Allow early GC + } + + // Operation is done: if an exception occured, throw it + if (m_exception != null) throw m_exception; + } + + #endregion + } + + internal class AsyncResult : SimpleAsyncResult + { + private T m_result = default(T); + + public AsyncResult(AsyncCallback asyncCallback, Object state) : + base(asyncCallback, state) + { + } + + public void SetAsCompleted(T result, bool completedSynchronously) + { + // Save the asynchronous operation's result + m_result = result; + + // Tell the base class that the operation completed + // sucessfully (no exception) + base.SetAsCompleted(completedSynchronously); + } + + public new T EndInvoke() + { + base.EndInvoke(); + return m_result; + } + } + +} \ No newline at end of file diff --git a/OpenSim/Framework/SLUtil.cs b/OpenSim/Framework/SLUtil.cs index 537de7a..e66d5be 100644 --- a/OpenSim/Framework/SLUtil.cs +++ b/OpenSim/Framework/SLUtil.cs @@ -25,13 +25,9 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +using OpenMetaverse; using System; using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Xml; -using log4net; -using OpenMetaverse; namespace OpenSim.Framework { @@ -39,12 +35,36 @@ namespace OpenSim.Framework { // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + /// + /// Asset types used only in OpenSim. + /// To avoid clashing with the code numbers used in Second Life, use only negative numbers here. + /// + public enum OpenSimAssetType : sbyte + { + Material = -2 + } + + #region SL / file extension / content-type conversions + /// + /// Returns the Enum entry corresponding to the given code, regardless of whether it belongs + /// to the AssetType or OpenSimAssetType enums. + /// + public static object AssetTypeFromCode(sbyte assetType) + { + if (Enum.IsDefined(typeof(OpenMetaverse.AssetType), assetType)) + return (OpenMetaverse.AssetType)assetType; + else if (Enum.IsDefined(typeof(OpenSimAssetType), assetType)) + return (OpenSimAssetType)assetType; + else + return OpenMetaverse.AssetType.Unknown; + } + private class TypeMapping { private sbyte assetType; - private InventoryType inventoryType; + private sbyte inventoryType; private string contentType; private string contentType2; private string extension; @@ -56,15 +76,10 @@ namespace OpenSim.Framework public object AssetType { - get { - if (Enum.IsDefined(typeof(OpenMetaverse.AssetType), assetType)) - return (OpenMetaverse.AssetType)assetType; - else - return OpenMetaverse.AssetType.Unknown; - } + get { return AssetTypeFromCode(assetType); } } - public InventoryType InventoryType + public sbyte InventoryType { get { return inventoryType; } } @@ -84,7 +99,7 @@ namespace OpenSim.Framework get { return extension; } } - private TypeMapping(sbyte assetType, InventoryType inventoryType, string contentType, string contentType2, string extension) + private TypeMapping(sbyte assetType, sbyte inventoryType, string contentType, string contentType2, string extension) { this.assetType = assetType; this.inventoryType = inventoryType; @@ -93,13 +108,28 @@ namespace OpenSim.Framework this.extension = extension; } - public TypeMapping(AssetType assetType, InventoryType inventoryType, string contentType, string contentType2, string extension) + public TypeMapping(AssetType assetType, sbyte inventoryType, string contentType, string contentType2, string extension) : this((sbyte)assetType, inventoryType, contentType, contentType2, extension) { } + public TypeMapping(AssetType assetType, InventoryType inventoryType, string contentType, string contentType2, string extension) + : this((sbyte)assetType, (sbyte)inventoryType, contentType, contentType2, extension) + { + } + public TypeMapping(AssetType assetType, InventoryType inventoryType, string contentType, string extension) - : this((sbyte)assetType, inventoryType, contentType, null, extension) + : this((sbyte)assetType, (sbyte)inventoryType, contentType, null, extension) + { + } + + public TypeMapping(AssetType assetType, FolderType inventoryType, string contentType, string extension) + : this((sbyte)assetType, (sbyte)inventoryType, contentType, null, extension) + { + } + + public TypeMapping(OpenSimAssetType assetType, InventoryType inventoryType, string contentType, string extension) + : this((sbyte)assetType, (sbyte)inventoryType, contentType, null, extension) { } } @@ -125,51 +155,65 @@ namespace OpenSim.Framework new TypeMapping(AssetType.Object, InventoryType.Object, "application/vnd.ll.primitive", "application/x-metaverse-primitive", "primitive"), new TypeMapping(AssetType.Object, InventoryType.Attachment, "application/vnd.ll.primitive", "application/x-metaverse-primitive", "primitive"), new TypeMapping(AssetType.Notecard, InventoryType.Notecard, "application/vnd.ll.notecard", "application/x-metaverse-notecard", "notecard"), - new TypeMapping(AssetType.Folder, InventoryType.Folder, "application/vnd.ll.folder", "folder"), - new TypeMapping(AssetType.RootFolder, InventoryType.RootCategory, "application/vnd.ll.rootfolder", "rootfolder"), new TypeMapping(AssetType.LSLText, InventoryType.LSL, "application/vnd.ll.lsltext", "application/x-metaverse-lsl", "lsl"), new TypeMapping(AssetType.LSLBytecode, InventoryType.LSL, "application/vnd.ll.lslbyte", "application/x-metaverse-lso", "lso"), new TypeMapping(AssetType.Bodypart, InventoryType.Wearable, "application/vnd.ll.bodypart", "application/x-metaverse-bodypart", "bodypart"), - new TypeMapping(AssetType.TrashFolder, InventoryType.Folder, "application/vnd.ll.trashfolder", "trashfolder"), - new TypeMapping(AssetType.SnapshotFolder, InventoryType.Folder, "application/vnd.ll.snapshotfolder", "snapshotfolder"), - new TypeMapping(AssetType.LostAndFoundFolder, InventoryType.Folder, "application/vnd.ll.lostandfoundfolder", "lostandfoundfolder"), new TypeMapping(AssetType.Animation, InventoryType.Animation, "application/vnd.ll.animation", "application/x-metaverse-animation", "animation"), new TypeMapping(AssetType.Gesture, InventoryType.Gesture, "application/vnd.ll.gesture", "application/x-metaverse-gesture", "gesture"), new TypeMapping(AssetType.Simstate, InventoryType.Snapshot, "application/x-metaverse-simstate", "simstate"), - new TypeMapping(AssetType.FavoriteFolder, InventoryType.Unknown, "application/vnd.ll.favoritefolder", "favoritefolder"), new TypeMapping(AssetType.Link, InventoryType.Unknown, "application/vnd.ll.link", "link"), new TypeMapping(AssetType.LinkFolder, InventoryType.Unknown, "application/vnd.ll.linkfolder", "linkfolder"), - new TypeMapping(AssetType.CurrentOutfitFolder, InventoryType.Unknown, "application/vnd.ll.currentoutfitfolder", "currentoutfitfolder"), - new TypeMapping(AssetType.OutfitFolder, InventoryType.Unknown, "application/vnd.ll.outfitfolder", "outfitfolder"), - new TypeMapping(AssetType.MyOutfitsFolder, InventoryType.Unknown, "application/vnd.ll.myoutfitsfolder", "myoutfitsfolder"), - new TypeMapping(AssetType.Mesh, InventoryType.Mesh, "application/vnd.ll.mesh", "llm") + new TypeMapping(AssetType.Mesh, InventoryType.Mesh, "application/vnd.ll.mesh", "llm"), + + // The next few items are about inventory folders + new TypeMapping(AssetType.Folder, FolderType.None, "application/vnd.ll.folder", "folder"), + new TypeMapping(AssetType.Folder, FolderType.Root, "application/vnd.ll.rootfolder", "rootfolder"), + new TypeMapping(AssetType.Folder, FolderType.Trash, "application/vnd.ll.trashfolder", "trashfolder"), + new TypeMapping(AssetType.Folder, FolderType.Snapshot, "application/vnd.ll.snapshotfolder", "snapshotfolder"), + new TypeMapping(AssetType.Folder, FolderType.LostAndFound, "application/vnd.ll.lostandfoundfolder", "lostandfoundfolder"), + new TypeMapping(AssetType.Folder, FolderType.Favorites, "application/vnd.ll.favoritefolder", "favoritefolder"), + new TypeMapping(AssetType.Folder, FolderType.CurrentOutfit, "application/vnd.ll.currentoutfitfolder", "currentoutfitfolder"), + new TypeMapping(AssetType.Folder, FolderType.Outfit, "application/vnd.ll.outfitfolder", "outfitfolder"), + new TypeMapping(AssetType.Folder, FolderType.MyOutfits, "application/vnd.ll.myoutfitsfolder", "myoutfitsfolder"), + + // This next mappping is an asset to inventory item mapping. + // Note: LL stores folders as assets of type Folder = 8, and it has a corresponding InventoryType = 8 + // OpenSim doesn't store folders as assets, so this mapping should only be used when parsing things from the viewer to the server + new TypeMapping(AssetType.Folder, InventoryType.Folder, "application/vnd.ll.folder", "folder"), + + // OpenSim specific + new TypeMapping(OpenSimAssetType.Material, InventoryType.Unknown, "application/llsd+xml", "material") }; private static Dictionary asset2Content; private static Dictionary asset2Extension; - private static Dictionary inventory2Content; + private static Dictionary inventory2Content; private static Dictionary content2Asset; - private static Dictionary content2Inventory; + private static Dictionary content2Inventory; static SLUtil() { asset2Content = new Dictionary(); asset2Extension = new Dictionary(); - inventory2Content = new Dictionary(); + inventory2Content = new Dictionary(); content2Asset = new Dictionary(); - content2Inventory = new Dictionary(); + content2Inventory = new Dictionary(); foreach (TypeMapping mapping in MAPPINGS) { sbyte assetType = mapping.AssetTypeCode; if (!asset2Content.ContainsKey(assetType)) asset2Content.Add(assetType, mapping.ContentType); + if (!asset2Extension.ContainsKey(assetType)) asset2Extension.Add(assetType, mapping.Extension); + if (!inventory2Content.ContainsKey(mapping.InventoryType)) inventory2Content.Add(mapping.InventoryType, mapping.ContentType); + if (!content2Asset.ContainsKey(mapping.ContentType)) content2Asset.Add(mapping.ContentType, assetType); + if (!content2Inventory.ContainsKey(mapping.ContentType)) content2Inventory.Add(mapping.ContentType, mapping.InventoryType); @@ -194,8 +238,8 @@ namespace OpenSim.Framework public static string SLInvTypeToContentType(int invType) { string contentType; - if (!inventory2Content.TryGetValue((InventoryType)invType, out contentType)) - contentType = inventory2Content[InventoryType.Unknown]; + if (!inventory2Content.TryGetValue((sbyte)invType, out contentType)) + contentType = inventory2Content[(sbyte)InventoryType.Unknown]; return contentType; } @@ -209,9 +253,9 @@ namespace OpenSim.Framework public static sbyte ContentTypeToSLInvType(string contentType) { - InventoryType invType; + sbyte invType; if (!content2Inventory.TryGetValue(contentType, out invType)) - invType = InventoryType.Unknown; + invType = (sbyte)InventoryType.Unknown; return (sbyte)invType; } @@ -225,106 +269,270 @@ namespace OpenSim.Framework #endregion SL / file extension / content-type conversions - /// - /// Parse a notecard in Linden format to a string of ordinary text. - /// - /// - /// - public static string ParseNotecardToString(string rawInput) + private class NotecardReader { - string[] output = ParseNotecardToList(rawInput).ToArray(); + private string rawInput; + private int lineNumber; -// foreach (string line in output) -// m_log.DebugFormat("[PARSE NOTECARD]: ParseNotecardToString got line {0}", line); - - return string.Join("\n", output); + public int LineNumber + { + get + { + return lineNumber; + } + } + + public NotecardReader(string _rawInput) + { + rawInput = (string)_rawInput.Clone(); + lineNumber = 0; + } + + public string getLine() + { + if(rawInput.Length == 0) + { + throw new NotANotecardFormatException(lineNumber + 1); + } + + int pos = rawInput.IndexOf('\n'); + if(pos < 0) + { + pos = rawInput.Length; + } + + /* cut line from rest */ + ++lineNumber; + string line = rawInput.Substring(0, pos); + if (pos + 1 >= rawInput.Length) + { + rawInput = string.Empty; + } + else + { + rawInput = rawInput.Substring(pos + 1); + } + /* clean up line from double spaces and tabs */ + line = line.Replace("\t", " "); + while(line.IndexOf(" ") >= 0) + { + line = line.Replace(" ", " "); + } + return line.Replace("\r", "").Trim(); + } + + public string getBlock(int length) + { + /* cut line from rest */ + if(length > rawInput.Length) + { + throw new NotANotecardFormatException(lineNumber); + } + string line = rawInput.Substring(0, length); + rawInput = rawInput.Substring(length); + return line; + } } - - /// - /// Parse a notecard in Linden format to a list of ordinary lines. - /// - /// - /// - public static List ParseNotecardToList(string rawInput) + + public class NotANotecardFormatException : Exception + { + public int lineNumber; + public NotANotecardFormatException(int _lineNumber) + : base() + { + lineNumber = _lineNumber; + } + } + + private static void skipSection(NotecardReader reader) { - string[] input = rawInput.Replace("\r", "").Split('\n'); - int idx = 0; - int level = 0; - List output = new List(); - string[] words; + if (reader.getLine() != "{") + throw new NotANotecardFormatException(reader.LineNumber); - while (idx < input.Length) + string line; + while ((line = reader.getLine()) != "}") { - if (input[idx] == "{") + if(line.IndexOf('{')>=0) + { + throw new NotANotecardFormatException(reader.LineNumber); + } + } + } + + private static void skipInventoryItem(NotecardReader reader) + { + if (reader.getLine() != "{") + throw new NotANotecardFormatException(reader.LineNumber); + + string line; + while((line = reader.getLine()) != "}") + { + string[] data = line.Split(' '); + if(data.Length == 0) { - level++; - idx++; continue; } + if(data[0] == "permissions") + { + skipSection(reader); + } + else if(data[0] == "sale_info") + { + skipSection(reader); + } + else if (line.IndexOf('{') >= 0) + { + throw new NotANotecardFormatException(reader.LineNumber); + } + } + } + + private static void skipInventoryItems(NotecardReader reader) + { + if(reader.getLine() != "{") + { + throw new NotANotecardFormatException(reader.LineNumber); + } - if (input[idx]== "}") + string line; + while((line = reader.getLine()) != "}") + { + string[] data = line.Split(' '); + if(data.Length == 0) { - level--; - idx++; continue; } - switch (level) + if(data[0] == "inv_item") + { + skipInventoryItem(reader); + } + else if (line.IndexOf('{') >= 0) + { + throw new NotANotecardFormatException(reader.LineNumber); + } + + } + } + + private static void skipInventory(NotecardReader reader) + { + if (reader.getLine() != "{") + throw new NotANotecardFormatException(reader.LineNumber); + + string line; + while((line = reader.getLine()) != "}") + { + string[] data = line.Split(' '); + if(data[0] == "count") { - case 0: - words = input[idx].Split(' '); // Linden text ver - // Notecards are created *really* empty. Treat that as "no text" (just like after saving an empty notecard) - if (words.Length < 3) - return output; - - int version = int.Parse(words[3]); - if (version != 2) - return output; - break; - case 1: - words = input[idx].Split(' '); - if (words[0] == "LLEmbeddedItems") - break; - if (words[0] == "Text") + int count = Int32.Parse(data[1]); + for(int i = 0; i < count; ++i) { - int len = int.Parse(words[2]); - idx++; + skipInventoryItems(reader); + } + } + else if (line.IndexOf('{') >= 0) + { + throw new NotANotecardFormatException(reader.LineNumber); + } + } + } + + private static string readNotecardText(NotecardReader reader) + { + if (reader.getLine() != "{") + throw new NotANotecardFormatException(reader.LineNumber); + + string notecardString = string.Empty; + string line; + while((line = reader.getLine()) != "}") + { + string[] data = line.Split(' '); + if (data.Length == 0) + { + continue; + } - int count = -1; + if (data[0] == "LLEmbeddedItems") + { + skipInventory(reader); + } + else if(data[0] == "Text" && data.Length == 3) + { + int length = Int32.Parse(data[2]); + notecardString = reader.getBlock(length); + } + else if (line.IndexOf('{') >= 0) + { + throw new NotANotecardFormatException(reader.LineNumber); + } - while (count < len && idx < input.Length) - { - // int l = input[idx].Length; - string ln = input[idx]; + } + return notecardString; + } - int need = len-count-1; - if (ln.Length > need) - ln = ln.Substring(0, need); + private static string readNotecard(byte[] rawInput) + { + string rawIntermedInput = string.Empty; -// m_log.DebugFormat("[PARSE NOTECARD]: Adding line {0}", ln); - output.Add(ln); - count += ln.Length + 1; - idx++; - } + /* make up a Raw Encoding here */ + foreach(byte c in rawInput) + { + char d = (char)c; + rawIntermedInput += d; + } - return output; - } - break; - case 2: - words = input[idx].Split(' '); // count - if (words[0] == "count") - { - int c = int.Parse(words[1]); - if (c > 0) - return output; - break; - } - break; + NotecardReader reader = new NotecardReader(rawIntermedInput); + string line; + try + { + line = reader.getLine(); + } + catch(Exception) + { + return System.Text.Encoding.UTF8.GetString(rawInput); + } + string[] versioninfo = line.Split(' '); + if(versioninfo.Length < 3) + { + return System.Text.Encoding.UTF8.GetString(rawInput); + } + else if(versioninfo[0] != "Linden" || versioninfo[1] != "text") + { + return System.Text.Encoding.UTF8.GetString(rawInput); + } + else + { + /* now we actually decode the Encoding, before we needed it in raw */ + string o = readNotecardText(reader); + byte[] a = new byte[o.Length]; + for(int i = 0; i < o.Length; ++i) + { + a[i] = (byte)o[i]; } - idx++; + return System.Text.Encoding.UTF8.GetString(a); } - - return output; + } + + /// + /// Parse a notecard in Linden format to a string of ordinary text. + /// + /// + /// + public static string ParseNotecardToString(byte[] rawInput) + { + return readNotecard(rawInput); + } + + /// + /// Parse a notecard in Linden format to a list of ordinary lines. + /// + /// + /// + public static string[] ParseNotecardToArray(byte[] rawInput) + { + return readNotecard(rawInput).Replace("\r", "").Split('\n'); } } } diff --git a/OpenSim/Framework/Serialization/ArchiveConstants.cs b/OpenSim/Framework/Serialization/ArchiveConstants.cs index 0c12787..ab3c285 100644 --- a/OpenSim/Framework/Serialization/ArchiveConstants.cs +++ b/OpenSim/Framework/Serialization/ArchiveConstants.cs @@ -29,6 +29,7 @@ using System; using System.Collections.Generic; using System.Text; using OpenMetaverse; +using OpenSimAssetType = OpenSim.Framework.SLUtil.OpenSimAssetType; namespace OpenSim.Framework.Serialization { @@ -114,20 +115,17 @@ namespace OpenSim.Framework.Serialization ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.ImageJPEG] = ASSET_EXTENSION_SEPARATOR + "image.jpg"; ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.ImageTGA] = ASSET_EXTENSION_SEPARATOR + "image.tga"; ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Landmark] = ASSET_EXTENSION_SEPARATOR + "landmark.txt"; - ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.LostAndFoundFolder] = ASSET_EXTENSION_SEPARATOR + "lostandfoundfolder.txt"; // Not sure if we'll ever see this ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.LSLBytecode] = ASSET_EXTENSION_SEPARATOR + "bytecode.lso"; ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.LSLText] = ASSET_EXTENSION_SEPARATOR + "script.lsl"; ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Mesh] = ASSET_EXTENSION_SEPARATOR + "mesh.llmesh"; ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Notecard] = ASSET_EXTENSION_SEPARATOR + "notecard.txt"; ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Object] = ASSET_EXTENSION_SEPARATOR + "object.xml"; - ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.RootFolder] = ASSET_EXTENSION_SEPARATOR + "rootfolder.txt"; // Not sure if we'll ever see this ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Simstate] = ASSET_EXTENSION_SEPARATOR + "simstate.bin"; // Not sure if we'll ever see this - ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.SnapshotFolder] = ASSET_EXTENSION_SEPARATOR + "snapshotfolder.txt"; // Not sure if we'll ever see this ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Sound] = ASSET_EXTENSION_SEPARATOR + "sound.ogg"; ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.SoundWAV] = ASSET_EXTENSION_SEPARATOR + "sound.wav"; ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.Texture] = ASSET_EXTENSION_SEPARATOR + "texture.jp2"; ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.TextureTGA] = ASSET_EXTENSION_SEPARATOR + "texture.tga"; - ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.TrashFolder] = ASSET_EXTENSION_SEPARATOR + "trashfolder.txt"; // Not sure if we'll ever see this + ASSET_TYPE_TO_EXTENSION[(sbyte)OpenSimAssetType.Material] = ASSET_EXTENSION_SEPARATOR + "material.xml"; // Not sure if we'll ever see this EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "animation.bvh"] = (sbyte)AssetType.Animation; EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "bodypart.txt"] = (sbyte)AssetType.Bodypart; @@ -138,20 +136,17 @@ namespace OpenSim.Framework.Serialization EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "image.jpg"] = (sbyte)AssetType.ImageJPEG; EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "image.tga"] = (sbyte)AssetType.ImageTGA; EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "landmark.txt"] = (sbyte)AssetType.Landmark; - EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "lostandfoundfolder.txt"] = (sbyte)AssetType.LostAndFoundFolder; EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "bytecode.lso"] = (sbyte)AssetType.LSLBytecode; EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "script.lsl"] = (sbyte)AssetType.LSLText; EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "mesh.llmesh"] = (sbyte)AssetType.Mesh; EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "notecard.txt"] = (sbyte)AssetType.Notecard; EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "object.xml"] = (sbyte)AssetType.Object; - EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "rootfolder.txt"] = (sbyte)AssetType.RootFolder; EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "simstate.bin"] = (sbyte)AssetType.Simstate; - EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "snapshotfolder.txt"] = (sbyte)AssetType.SnapshotFolder; EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "sound.ogg"] = (sbyte)AssetType.Sound; EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "sound.wav"] = (sbyte)AssetType.SoundWAV; EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "texture.jp2"] = (sbyte)AssetType.Texture; EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "texture.tga"] = (sbyte)AssetType.TextureTGA; - EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "trashfolder.txt"] = (sbyte)AssetType.TrashFolder; + EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "material.xml"] = (sbyte)OpenSimAssetType.Material; } public static string CreateOarLandDataPath(LandData ld) diff --git a/OpenSim/Framework/Serialization/External/ExternalRepresentationUtils.cs b/OpenSim/Framework/Serialization/External/ExternalRepresentationUtils.cs index c56f213..55640ac 100644 --- a/OpenSim/Framework/Serialization/External/ExternalRepresentationUtils.cs +++ b/OpenSim/Framework/Serialization/External/ExternalRepresentationUtils.cs @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * @@ -27,6 +27,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Reflection; using System.Xml; @@ -47,20 +48,20 @@ namespace OpenSim.Framework.Serialization.External /// Populate a node with data read from xml using a dictinoary of processors /// /// - /// /param> + /// /// /// true on successful, false if there were any processing failures public static bool ExecuteReadProcessors( - NodeType nodeToFill, Dictionary> processors, XmlTextReader xtr) + NodeType nodeToFill, Dictionary> processors, XmlReader xtr) { return ExecuteReadProcessors( nodeToFill, processors, xtr, - (o, name, e) - => m_log.DebugFormat( - "[ExternalRepresentationUtils]: Exception while parsing element {0}, continuing. Exception {1}{2}", - name, e.Message, e.StackTrace)); + (o, nodeName, e) => { + m_log.Debug(string.Format("[ExternalRepresentationUtils]: Error while parsing element {0} ", + nodeName), e); + }); } /// @@ -75,23 +76,27 @@ namespace OpenSim.Framework.Serialization.External /// true on successful, false if there were any processing failures public static bool ExecuteReadProcessors( NodeType nodeToFill, - Dictionary> processors, - XmlTextReader xtr, + Dictionary> processors, + XmlReader xtr, Action parseExceptionAction) { bool errors = false; + int numErrors = 0; + + Stopwatch timer = new Stopwatch(); + timer.Start(); string nodeName = string.Empty; while (xtr.NodeType != XmlNodeType.EndElement) { nodeName = xtr.Name; -// m_log.DebugFormat("[ExternalRepresentationUtils]: Processing: {0}", nodeName); + // m_log.DebugFormat("[ExternalRepresentationUtils]: Processing node: {0}", nodeName); - Action p = null; + Action p = null; if (processors.TryGetValue(xtr.Name, out p)) { -// m_log.DebugFormat("[ExternalRepresentationUtils]: Found {0} processor, nodeName); + // m_log.DebugFormat("[ExternalRepresentationUtils]: Found processor for {0}", nodeName); try { @@ -101,6 +106,18 @@ namespace OpenSim.Framework.Serialization.External { errors = true; parseExceptionAction(nodeToFill, nodeName, e); + + if (xtr.EOF) + { + m_log.Debug("[ExternalRepresentationUtils]: Aborting ExecuteReadProcessors due to unexpected end of XML"); + break; + } + + if (++numErrors == 10) + { + m_log.Debug("[ExternalRepresentationUtils]: Aborting ExecuteReadProcessors due to too many parsing errors"); + break; + } if (xtr.NodeType == XmlNodeType.EndElement) xtr.Read(); @@ -108,9 +125,16 @@ namespace OpenSim.Framework.Serialization.External } else { - // m_log.DebugFormat("[LandDataSerializer]: caught unknown element {0}", nodeName); + // m_log.DebugFormat("[ExternalRepresentationUtils]: found unknown element \"{0}\"", nodeName); xtr.ReadOuterXml(); // ignore } + + if (timer.Elapsed.TotalSeconds >= 60) + { + m_log.Debug("[ExternalRepresentationUtils]: Aborting ExecuteReadProcessors due to timeout"); + errors = true; + break; + } } return errors; @@ -125,7 +149,8 @@ namespace OpenSim.Framework.Serialization.External /// The service for retrieving user account information /// The scope of the user account information (Grid ID) /// The SceneObjectPart represented in XML2 - public static string RewriteSOP(string xml, string homeURL, IUserAccountService userService, UUID scopeID) + [Obsolete("This method is deprecated. Use RewriteSOP instead.")] + public static string RewriteSOP_Old(string xml, string homeURL, IUserAccountService userService, UUID scopeID) { if (xml == string.Empty || homeURL == string.Empty || userService == null) return xml; @@ -161,7 +186,7 @@ namespace OpenSim.Framework.Serialization.External if (!hasCreatorData && creator != null) { XmlElement creatorData = doc.CreateElement("CreatorData"); - creatorData.InnerText = homeURL + ";" + creator.FirstName + " " + creator.LastName; + creatorData.InnerText = CalcCreatorData(homeURL, creator.FirstName + " " + creator.LastName); sop.AppendChild(creatorData); } } @@ -172,5 +197,215 @@ namespace OpenSim.Framework.Serialization.External return wr.ToString(); } } + + /// + /// Takes a XML representation of a SceneObjectPart and returns another XML representation + /// with creator data added to it. + /// + /// The SceneObjectPart represented in XML2 + /// An identifier for the component that's calling this function + /// The URL of the user agents service (home) for the creator + /// The service for retrieving user account information + /// The scope of the user account information (Grid ID) + /// The SceneObjectPart represented in XML2 + public static string RewriteSOP(string xmlData, string sceneName, string homeURL, IUserAccountService userService, UUID scopeID) + { + // Console.WriteLine("Input XML [{0}]", xmlData); + if (xmlData == string.Empty || homeURL == string.Empty || userService == null) + return xmlData; + + // Deal with bug + xmlData = ExternalRepresentationUtils.SanitizeXml(xmlData); + + using (StringWriter sw = new StringWriter()) + using (XmlTextWriter writer = new XmlTextWriter(sw)) + using (XmlTextReader wrappedReader = new XmlTextReader(xmlData, XmlNodeType.Element, null)) + using (XmlReader reader = XmlReader.Create(wrappedReader, new XmlReaderSettings() { IgnoreWhitespace = true, ConformanceLevel = ConformanceLevel.Fragment })) + { + TransformXml(reader, writer, sceneName, homeURL, userService, scopeID); + + // Console.WriteLine("Output: [{0}]", sw.ToString()); + + return sw.ToString(); + } + } + + protected static void TransformXml(XmlReader reader, XmlWriter writer, string sceneName, string homeURI, IUserAccountService userAccountService, UUID scopeID) + { + // m_log.DebugFormat("[HG ASSET MAPPER]: Transforming XML"); + + int sopDepth = -1; + UserAccount creator = null; + bool hasCreatorData = false; + + while (reader.Read()) + { + // Console.WriteLine("Depth: {0}, name {1}", reader.Depth, reader.Name); + + switch (reader.NodeType) + { + case XmlNodeType.Attribute: + // Console.WriteLine("FOUND ATTRIBUTE {0}", reader.Name); + writer.WriteAttributeString(reader.Name, reader.Value); + break; + + case XmlNodeType.CDATA: + writer.WriteCData(reader.Value); + break; + + case XmlNodeType.Comment: + writer.WriteComment(reader.Value); + break; + + case XmlNodeType.DocumentType: + writer.WriteDocType(reader.Name, reader.Value, null, null); + break; + + case XmlNodeType.Element: + // m_log.DebugFormat("Depth {0} at element {1}", reader.Depth, reader.Name); + + writer.WriteStartElement(reader.Prefix, reader.LocalName, reader.NamespaceURI); + + if (reader.HasAttributes) + { + while (reader.MoveToNextAttribute()) + writer.WriteAttributeString(reader.Name, reader.Value); + + reader.MoveToElement(); + } + + if (reader.LocalName == "SceneObjectPart") + { + if (sopDepth < 0) + { + sopDepth = reader.Depth; + // m_log.DebugFormat("[HG ASSET MAPPER]: Set sopDepth to {0}", sopDepth); + } + } + else + { + if (sopDepth >= 0 && reader.Depth == sopDepth + 1) + { + if (reader.Name == "CreatorID") + { + reader.Read(); + if (reader.NodeType == XmlNodeType.Element && reader.Name == "Guid" || reader.Name == "UUID") + { + reader.Read(); + + if (reader.NodeType == XmlNodeType.Text) + { + UUID uuid = UUID.Zero; + UUID.TryParse(reader.Value, out uuid); + creator = userAccountService.GetUserAccount(scopeID, uuid); + writer.WriteElementString("UUID", reader.Value); + reader.Read(); + } + else + { + // If we unexpected run across mixed content in this node, still carry on + // transforming the subtree (this replicates earlier behaviour). + TransformXml(reader, writer, sceneName, homeURI, userAccountService, scopeID); + } + } + else + { + // If we unexpected run across mixed content in this node, still carry on + // transforming the subtree (this replicates earlier behaviour). + TransformXml(reader, writer, sceneName, homeURI, userAccountService, scopeID); + } + } + else if (reader.Name == "CreatorData") + { + reader.Read(); + if (reader.NodeType == XmlNodeType.Text) + { + hasCreatorData = true; + writer.WriteString(reader.Value); + } + else + { + // If we unexpected run across mixed content in this node, still carry on + // transforming the subtree (this replicates earlier behaviour). + TransformXml(reader, writer, sceneName, homeURI, userAccountService, scopeID); + } + } + } + } + + if (reader.IsEmptyElement) + { + // m_log.DebugFormat("[HG ASSET MAPPER]: Writing end for empty element {0}", reader.Name); + writer.WriteEndElement(); + } + + break; + + case XmlNodeType.EndElement: + // m_log.DebugFormat("Depth {0} at EndElement", reader.Depth); + if (sopDepth == reader.Depth) + { + if (!hasCreatorData && creator != null) + writer.WriteElementString(reader.Prefix, "CreatorData", reader.NamespaceURI, string.Format("{0};{1} {2}", homeURI, creator.FirstName, creator.LastName)); + + // m_log.DebugFormat("[HG ASSET MAPPER]: Reset sopDepth"); + sopDepth = -1; + creator = null; + hasCreatorData = false; + } + writer.WriteEndElement(); + break; + + case XmlNodeType.EntityReference: + writer.WriteEntityRef(reader.Name); + break; + + case XmlNodeType.ProcessingInstruction: + writer.WriteProcessingInstruction(reader.Name, reader.Value); + break; + + case XmlNodeType.Text: + writer.WriteString(reader.Value); + break; + + case XmlNodeType.XmlDeclaration: + // For various reasons, not all serializations have xml declarations (or consistent ones) + // and as it's embedded inside a byte stream we don't need it anyway, so ignore. + break; + + default: + m_log.WarnFormat( + "[HG ASSET MAPPER]: Unrecognized node {0} in asset XML transform in {1}", + reader.NodeType, sceneName); + break; + } + } + } + + public static string CalcCreatorData(string homeURL, string name) + { + return homeURL + ";" + name; + } + + internal static string CalcCreatorData(string homeURL, UUID uuid, string name) + { + return homeURL + "/" + uuid + ";" + name; + } + + /// + /// Sanitation for bug introduced in Oct. 20 (1eb3e6cc43e2a7b4053bc1185c7c88e22356c5e8) + /// + /// + /// + public static string SanitizeXml(string xmlData) + { + string fixedData = xmlData; + if (fixedData != null) + // Loop, because it may contain multiple + while (fixedData.Contains("xmlns:xmlns:")) + fixedData = fixedData.Replace("xmlns:xmlns:", "xmlns:"); + return fixedData; + } + } -} \ No newline at end of file +} diff --git a/OpenSim/Framework/Serialization/External/LandDataSerializer.cs b/OpenSim/Framework/Serialization/External/LandDataSerializer.cs index 709b516..e42d56f 100644 --- a/OpenSim/Framework/Serialization/External/LandDataSerializer.cs +++ b/OpenSim/Framework/Serialization/External/LandDataSerializer.cs @@ -44,11 +44,11 @@ namespace OpenSim.Framework.Serialization.External { // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - private static Dictionary> m_ldProcessors - = new Dictionary>(); + private static Dictionary> m_ldProcessors + = new Dictionary>(); - private static Dictionary> m_laeProcessors - = new Dictionary>(); + private static Dictionary> m_laeProcessors + = new Dictionary>(); static LandDataSerializer() { @@ -134,7 +134,7 @@ namespace OpenSim.Framework.Serialization.External "AccessList", (lae, xtr) => lae.Flags = (AccessList)Convert.ToUInt32(xtr.ReadElementString("AccessList"))); } - public static void ProcessParcelAccessList(LandData ld, XmlTextReader xtr) + public static void ProcessParcelAccessList(LandData ld, XmlReader xtr) { if (!xtr.IsEmptyElement) { @@ -213,8 +213,13 @@ namespace OpenSim.Framework.Serialization.External xtw.WriteElementString("ClaimDate", Convert.ToString(landData.ClaimDate)); xtw.WriteElementString("ClaimPrice", Convert.ToString(landData.ClaimPrice)); xtw.WriteElementString("GlobalID", landData.GlobalID.ToString()); - xtw.WriteElementString("GroupID", landData.GroupID.ToString()); - xtw.WriteElementString("IsGroupOwned", Convert.ToString(landData.IsGroupOwned)); + + UUID groupID = options.ContainsKey("wipe-owners") ? UUID.Zero : landData.GroupID; + xtw.WriteElementString("GroupID", groupID.ToString()); + + bool isGroupOwned = options.ContainsKey("wipe-owners") ? false : landData.IsGroupOwned; + xtw.WriteElementString("IsGroupOwned", Convert.ToString(isGroupOwned)); + xtw.WriteElementString("Bitmap", Convert.ToBase64String(landData.Bitmap)); xtw.WriteElementString("Description", landData.Description); xtw.WriteElementString("Flags", Convert.ToString((uint)landData.Flags)); @@ -227,13 +232,8 @@ namespace OpenSim.Framework.Serialization.External xtw.WriteElementString("MediaURL", landData.MediaURL); xtw.WriteElementString("MusicURL", landData.MusicURL); - UUID ownerIdToWrite; - if (options != null && options.ContainsKey("wipe-owners")) - ownerIdToWrite = UUID.Zero; - else - ownerIdToWrite = landData.OwnerID; - - xtw.WriteElementString("OwnerID", ownerIdToWrite.ToString()); + UUID ownerID = options.ContainsKey("wipe-owners") ? UUID.Zero : landData.OwnerID; + xtw.WriteElementString("OwnerID", ownerID.ToString()); xtw.WriteStartElement("ParcelAccessList"); foreach (LandAccessEntry pal in landData.ParcelAccessList) diff --git a/OpenSim/Framework/Serialization/External/UserInventoryItemSerializer.cs b/OpenSim/Framework/Serialization/External/UserInventoryItemSerializer.cs index 88f9581..994cede 100644 --- a/OpenSim/Framework/Serialization/External/UserInventoryItemSerializer.cs +++ b/OpenSim/Framework/Serialization/External/UserInventoryItemSerializer.cs @@ -46,8 +46,8 @@ namespace OpenSim.Framework.Serialization.External { // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - private static Dictionary> m_InventoryItemXmlProcessors - = new Dictionary>(); + private static Dictionary> m_InventoryItemXmlProcessors + = new Dictionary>(); #region InventoryItemBase Processor initialization static UserInventoryItemSerializer() @@ -76,103 +76,103 @@ namespace OpenSim.Framework.Serialization.External #endregion #region InventoryItemBase Processors - private static void ProcessName(InventoryItemBase item, XmlTextReader reader) + private static void ProcessName(InventoryItemBase item, XmlReader reader) { item.Name = reader.ReadElementContentAsString("Name", String.Empty); } - private static void ProcessID(InventoryItemBase item, XmlTextReader reader) + private static void ProcessID(InventoryItemBase item, XmlReader reader) { item.ID = Util.ReadUUID(reader, "ID"); } - private static void ProcessInvType(InventoryItemBase item, XmlTextReader reader) + private static void ProcessInvType(InventoryItemBase item, XmlReader reader) { item.InvType = reader.ReadElementContentAsInt("InvType", String.Empty); } - private static void ProcessCreatorUUID(InventoryItemBase item, XmlTextReader reader) + private static void ProcessCreatorUUID(InventoryItemBase item, XmlReader reader) { item.CreatorId = reader.ReadElementContentAsString("CreatorUUID", String.Empty); } - private static void ProcessCreatorID(InventoryItemBase item, XmlTextReader reader) + private static void ProcessCreatorID(InventoryItemBase item, XmlReader reader) { // when it exists, this overrides the previous item.CreatorId = reader.ReadElementContentAsString("CreatorID", String.Empty); } - private static void ProcessCreationDate(InventoryItemBase item, XmlTextReader reader) + private static void ProcessCreationDate(InventoryItemBase item, XmlReader reader) { item.CreationDate = reader.ReadElementContentAsInt("CreationDate", String.Empty); } - private static void ProcessOwner(InventoryItemBase item, XmlTextReader reader) + private static void ProcessOwner(InventoryItemBase item, XmlReader reader) { item.Owner = Util.ReadUUID(reader, "Owner"); } - private static void ProcessDescription(InventoryItemBase item, XmlTextReader reader) + private static void ProcessDescription(InventoryItemBase item, XmlReader reader) { item.Description = reader.ReadElementContentAsString("Description", String.Empty); } - private static void ProcessAssetType(InventoryItemBase item, XmlTextReader reader) + private static void ProcessAssetType(InventoryItemBase item, XmlReader reader) { item.AssetType = reader.ReadElementContentAsInt("AssetType", String.Empty); } - private static void ProcessAssetID(InventoryItemBase item, XmlTextReader reader) + private static void ProcessAssetID(InventoryItemBase item, XmlReader reader) { item.AssetID = Util.ReadUUID(reader, "AssetID"); } - private static void ProcessSaleType(InventoryItemBase item, XmlTextReader reader) + private static void ProcessSaleType(InventoryItemBase item, XmlReader reader) { item.SaleType = (byte)reader.ReadElementContentAsInt("SaleType", String.Empty); } - private static void ProcessSalePrice(InventoryItemBase item, XmlTextReader reader) + private static void ProcessSalePrice(InventoryItemBase item, XmlReader reader) { item.SalePrice = reader.ReadElementContentAsInt("SalePrice", String.Empty); } - private static void ProcessBasePermissions(InventoryItemBase item, XmlTextReader reader) + private static void ProcessBasePermissions(InventoryItemBase item, XmlReader reader) { item.BasePermissions = (uint)reader.ReadElementContentAsInt("BasePermissions", String.Empty); } - private static void ProcessCurrentPermissions(InventoryItemBase item, XmlTextReader reader) + private static void ProcessCurrentPermissions(InventoryItemBase item, XmlReader reader) { item.CurrentPermissions = (uint)reader.ReadElementContentAsInt("CurrentPermissions", String.Empty); } - private static void ProcessEveryOnePermissions(InventoryItemBase item, XmlTextReader reader) + private static void ProcessEveryOnePermissions(InventoryItemBase item, XmlReader reader) { item.EveryOnePermissions = (uint)reader.ReadElementContentAsInt("EveryOnePermissions", String.Empty); } - private static void ProcessNextPermissions(InventoryItemBase item, XmlTextReader reader) + private static void ProcessNextPermissions(InventoryItemBase item, XmlReader reader) { item.NextPermissions = (uint)reader.ReadElementContentAsInt("NextPermissions", String.Empty); } - private static void ProcessFlags(InventoryItemBase item, XmlTextReader reader) + private static void ProcessFlags(InventoryItemBase item, XmlReader reader) { item.Flags = (uint)reader.ReadElementContentAsInt("Flags", String.Empty); } - private static void ProcessGroupID(InventoryItemBase item, XmlTextReader reader) + private static void ProcessGroupID(InventoryItemBase item, XmlReader reader) { item.GroupID = Util.ReadUUID(reader, "GroupID"); } - private static void ProcessGroupOwned(InventoryItemBase item, XmlTextReader reader) + private static void ProcessGroupOwned(InventoryItemBase item, XmlReader reader) { item.GroupOwned = Util.ReadBoolean(reader); } - private static void ProcessCreatorData(InventoryItemBase item, XmlTextReader reader) + private static void ProcessCreatorData(InventoryItemBase item, XmlReader reader) { item.CreatorData = reader.ReadElementContentAsString("CreatorData", String.Empty); } @@ -277,7 +277,7 @@ namespace OpenSim.Framework.Serialization.External writer.WriteStartElement("GroupOwned"); writer.WriteString(inventoryItem.GroupOwned.ToString()); writer.WriteEndElement(); - if (options.ContainsKey("creators") && inventoryItem.CreatorData != null && inventoryItem.CreatorData != string.Empty) + if (options.ContainsKey("creators") && !string.IsNullOrEmpty(inventoryItem.CreatorData)) writer.WriteElementString("CreatorData", inventoryItem.CreatorData); else if (options.ContainsKey("home")) { @@ -286,7 +286,8 @@ namespace OpenSim.Framework.Serialization.External UserAccount account = userAccountService.GetUserAccount(UUID.Zero, inventoryItem.CreatorIdAsUuid); if (account != null) { - writer.WriteElementString("CreatorData", (string)options["home"] + "/" + inventoryItem.CreatorIdAsUuid + ";" + account.FirstName + " " + account.LastName); + string creatorData = ExternalRepresentationUtils.CalcCreatorData((string)options["home"], inventoryItem.CreatorIdAsUuid, account.FirstName + " " + account.LastName); + writer.WriteElementString("CreatorData", creatorData); } writer.WriteElementString("CreatorID", inventoryItem.CreatorId); } diff --git a/OpenSim/Framework/Serialization/Properties/AssemblyInfo.cs b/OpenSim/Framework/Serialization/Properties/AssemblyInfo.cs index 11efa4b..acec20f 100644 --- a/OpenSim/Framework/Serialization/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/Serialization/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("0.7.5.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("0.8.3.*")] + diff --git a/OpenSim/Framework/Serialization/Tests/LandDataSerializerTests.cs b/OpenSim/Framework/Serialization/Tests/LandDataSerializerTests.cs index ea100ee..e81cb78 100644 --- a/OpenSim/Framework/Serialization/Tests/LandDataSerializerTests.cs +++ b/OpenSim/Framework/Serialization/Tests/LandDataSerializerTests.cs @@ -121,7 +121,8 @@ namespace OpenSim.Framework.Serialization.Tests TestHelpers.InMethod(); // log4net.Config.XmlConfigurator.Configure(); - LandData ld = LandDataSerializer.Deserialize(LandDataSerializer.Serialize(this.land, null)); + Dictionary options = new Dictionary(); + LandData ld = LandDataSerializer.Deserialize(LandDataSerializer.Serialize(this.land, options)); Assert.That(ld, Is.Not.Null, "Deserialize(string) returned null"); // Assert.That(ld.AABBMax, Is.EqualTo(land.AABBMax)); // Assert.That(ld.AABBMin, Is.EqualTo(land.AABBMin)); diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs index c0dc907..828a852 100644 --- a/OpenSim/Framework/Servers/BaseOpenSimServer.cs +++ b/OpenSim/Framework/Servers/BaseOpenSimServer.cs @@ -45,6 +45,7 @@ using OpenSim.Framework.Monitoring; using OpenSim.Framework.Servers; using OpenSim.Framework.Servers.HttpServer; using Timer=System.Timers.Timer; +using Nini.Config; namespace OpenSim.Framework.Servers { @@ -56,9 +57,15 @@ namespace OpenSim.Framework.Servers private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); /// + /// Used by tests to suppress Environment.Exit(0) so that post-run operations are possible. + /// + public bool SuppressExit { get; set; } + + /// /// This will control a periodic log printout of the current 'show stats' (if they are active) for this /// server. /// + private int m_periodDiagnosticTimerMS = 60 * 60 * 1000; private Timer m_periodicDiagnosticsTimer = new Timer(60 * 60 * 1000); /// @@ -77,8 +84,6 @@ namespace OpenSim.Framework.Servers // Random uuid for private data m_osSecret = UUID.Random().ToString(); - m_periodicDiagnosticsTimer.Elapsed += new ElapsedEventHandler(LogDiagnostics); - m_periodicDiagnosticsTimer.Enabled = true; } /// @@ -86,26 +91,34 @@ namespace OpenSim.Framework.Servers /// protected virtual void StartupSpecific() { - if (m_console == null) - return; - + StatsManager.SimExtraStats = new SimExtraStatsCollector(); RegisterCommonCommands(); - - m_console.Commands.AddCommand("General", false, "quit", - "quit", - "Quit the application", HandleQuit); + RegisterCommonComponents(Config); + + IConfig startupConfig = Config.Configs["Startup"]; + int logShowStatsSeconds = startupConfig.GetInt("LogShowStatsSeconds", m_periodDiagnosticTimerMS / 1000); + m_periodDiagnosticTimerMS = logShowStatsSeconds * 1000; + m_periodicDiagnosticsTimer.Elapsed += new ElapsedEventHandler(LogDiagnostics); + if (m_periodDiagnosticTimerMS != 0) + { + m_periodicDiagnosticsTimer.Interval = m_periodDiagnosticTimerMS; + m_periodicDiagnosticsTimer.Enabled = true; + } + } + + protected override void ShutdownSpecific() + { + m_log.Info("[SHUTDOWN]: Shutdown processing on main thread complete. Exiting..."); - m_console.Commands.AddCommand("General", false, "shutdown", - "shutdown", - "Quit the application", HandleQuit); + RemovePIDFile(); + + base.ShutdownSpecific(); + + if (!SuppressExit) + Environment.Exit(0); } /// - /// Should be overriden and referenced by descendents if they need to perform extra shutdown processing - /// - public virtual void ShutdownSpecific() {} - - /// /// Provides a list of help topics that are available. Overriding classes should append their topics to the /// information returned when the base method is called. /// @@ -133,45 +146,18 @@ namespace OpenSim.Framework.Servers /// Performs initialisation of the scene, such as loading configuration from disk. /// public virtual void Startup() - { - m_log.Info("[STARTUP]: Beginning startup processing"); - - m_log.Info("[STARTUP]: OpenSimulator version: " + m_version + Environment.NewLine); - // clr version potentially is more confusing than helpful, since it doesn't tell us if we're running under Mono/MS .NET and - // the clr version number doesn't match the project version number under Mono. - //m_log.Info("[STARTUP]: Virtual machine runtime version: " + Environment.Version + Environment.NewLine); - m_log.InfoFormat( - "[STARTUP]: Operating system version: {0}, .NET platform {1}, {2}-bit\n", - Environment.OSVersion, Environment.OSVersion.Platform, Util.Is64BitProcess() ? "64" : "32"); - + { StartupSpecific(); TimeSpan timeTaken = DateTime.Now - m_startuptime; - m_log.InfoFormat( - "[STARTUP]: Non-script portion of startup took {0}m {1}s. PLEASE WAIT FOR LOGINS TO BE ENABLED ON REGIONS ONCE SCRIPTS HAVE STARTED.", + MainConsole.Instance.OutputFormat( + "PLEASE WAIT FOR LOGINS TO BE ENABLED ON REGIONS ONCE SCRIPTS HAVE STARTED. Non-script portion of startup took {0}m {1}s.", timeTaken.Minutes, timeTaken.Seconds); } - /// - /// Should be overriden and referenced by descendents if they need to perform extra shutdown processing - /// - public virtual void Shutdown() + public string osSecret { - ShutdownSpecific(); - - m_log.Info("[SHUTDOWN]: Shutdown processing on main thread complete. Exiting..."); - RemovePIDFile(); - - Environment.Exit(0); - } - - private void HandleQuit(string module, string[] args) - { - Shutdown(); - } - - public string osSecret { // Secret uuid for the simulator get { return m_osSecret; } } diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs index aa49343..f252bd5 100644 --- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs +++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs @@ -46,6 +46,7 @@ using CoolHTTPListener = HttpServer.HttpListener; using HttpListener=System.Net.HttpListener; using LogPrio=HttpServer.LogPrio; using OpenSim.Framework.Monitoring; +using System.IO.Compression; namespace OpenSim.Framework.Servers.HttpServer { @@ -53,6 +54,16 @@ namespace OpenSim.Framework.Servers.HttpServer { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private HttpServerLogWriter httpserverlog = new HttpServerLogWriter(); + private static Encoding UTF8NoBOM = new System.Text.UTF8Encoding(false); + + /// + /// This is a pending websocket request before it got an sucessful upgrade response. + /// The consumer must call handler.HandshakeAndUpgrade() to signal to the handler to + /// start the connection and optionally provide an origin authentication method. + /// + /// + /// + public delegate void WebSocketRequestDelegate(string servicepath, WebSocketHttpServerHandler handler); /// /// Gets or sets the debug level. @@ -71,12 +82,18 @@ namespace OpenSim.Framework.Servers.HttpServer /// public int RequestNumber { get; private set; } + /// + /// Statistic for holding number of requests processed. + /// + private Stat m_requestsProcessedStat; + private volatile int NotSocketErrors = 0; public volatile bool HTTPDRunning = false; // protected HttpListener m_httpListener; protected CoolHTTPListener m_httpListener2; protected Dictionary m_rpcHandlers = new Dictionary(); + protected Dictionary jsonRpcHandlers = new Dictionary(); protected Dictionary m_rpcHandlersKeepAlive = new Dictionary(); protected DefaultLLSDMethod m_defaultLlsdHandler = null; // <-- Moving away from the monolithic.. and going to /registered/ protected Dictionary m_llsdHandlers = new Dictionary(); @@ -86,6 +103,9 @@ namespace OpenSim.Framework.Servers.HttpServer protected Dictionary m_pollHandlers = new Dictionary(); + protected Dictionary m_WebSocketHandlers = + new Dictionary(); + protected uint m_port; protected uint m_sslport; protected bool m_ssl; @@ -95,7 +115,7 @@ namespace OpenSim.Framework.Servers.HttpServer protected IPAddress m_listenIPAddress = IPAddress.Any; - private PollServiceRequestManager m_PollServiceManager; + public PollServiceRequestManager PollServiceRequestManager { get; private set; } public uint SSLPort { @@ -169,6 +189,22 @@ namespace OpenSim.Framework.Servers.HttpServer } } + public void AddWebSocketHandler(string servicepath, WebSocketRequestDelegate handler) + { + lock (m_WebSocketHandlers) + { + if (!m_WebSocketHandlers.ContainsKey(servicepath)) + m_WebSocketHandlers.Add(servicepath, handler); + } + } + + public void RemoveWebSocketHandler(string servicepath) + { + lock (m_WebSocketHandlers) + if (m_WebSocketHandlers.ContainsKey(servicepath)) + m_WebSocketHandlers.Remove(servicepath); + } + public List GetStreamHandlerKeys() { lock (m_streamHandlers) @@ -217,6 +253,37 @@ namespace OpenSim.Framework.Servers.HttpServer return new List(m_rpcHandlers.Keys); } + // JsonRPC + public bool AddJsonRPCHandler(string method, JsonRPCMethod handler) + { + lock(jsonRpcHandlers) + { + jsonRpcHandlers.Add(method, handler); + } + return true; + } + + public JsonRPCMethod GetJsonRPCHandler(string method) + { + lock (jsonRpcHandlers) + { + if (jsonRpcHandlers.ContainsKey(method)) + { + return jsonRpcHandlers[method]; + } + else + { + return null; + } + } + } + + public List GetJsonRpcHandlerKeys() + { + lock (jsonRpcHandlers) + return new List(jsonRpcHandlers.Keys); + } + public bool AddHTTPHandler(string methodName, GenericHTTPMethod handler) { //m_log.DebugFormat("[BASE HTTP SERVER]: Registering {0}", methodName); @@ -309,7 +376,7 @@ namespace OpenSim.Framework.Servers.HttpServer return true; } - private void OnRequest(object source, RequestEventArgs args) + public void OnRequest(object source, RequestEventArgs args) { RequestNumber++; @@ -322,6 +389,8 @@ namespace OpenSim.Framework.Servers.HttpServer if (TryGetPollServiceHTTPHandler(request.UriPath.ToString(), out psEvArgs)) { + psEvArgs.RequestsReceived++; + PollServiceHttpRequest psreq = new PollServiceHttpRequest(psEvArgs, context, request); if (psEvArgs.Request != null) @@ -362,7 +431,7 @@ namespace OpenSim.Framework.Servers.HttpServer psEvArgs.Request(psreq.RequestID, keysvals); } - m_PollServiceManager.Enqueue(psreq); + PollServiceRequestManager.Enqueue(psreq); } else { @@ -375,11 +444,24 @@ namespace OpenSim.Framework.Servers.HttpServer } } - public void OnHandleRequestIOThread(IHttpClientContext context, IHttpRequest request) + private void OnHandleRequestIOThread(IHttpClientContext context, IHttpRequest request) { OSHttpRequest req = new OSHttpRequest(context, request); + WebSocketRequestDelegate dWebSocketRequestDelegate = null; + lock (m_WebSocketHandlers) + { + if (m_WebSocketHandlers.ContainsKey(req.RawUrl)) + dWebSocketRequestDelegate = m_WebSocketHandlers[req.RawUrl]; + } + if (dWebSocketRequestDelegate != null) + { + dWebSocketRequestDelegate(req.Url.AbsolutePath, new WebSocketHttpServerHandler(req, context, 8192)); + return; + } + OSHttpResponse resp = new OSHttpResponse(new HttpResponse(context, request),context); - HandleRequest(req, resp); + resp.ReuseContext = true; + HandleRequest(req, resp); // !!!HACK ALERT!!! // There seems to be a bug in the underlying http code that makes subsequent requests @@ -410,7 +492,9 @@ namespace OpenSim.Framework.Servers.HttpServer { try { - SendHTML500(response); + byte[] buffer500 = SendHTML500(response); + response.OutputStream.Write(buffer500, 0, buffer500.Length); + response.Send(); } catch { @@ -468,7 +552,7 @@ namespace OpenSim.Framework.Servers.HttpServer LogIncomingToStreamHandler(request, requestHandler); response.ContentType = requestHandler.ContentType; // Lets do this defaulting before in case handler has varying content type. - + if (requestHandler is IStreamedRequestHandler) { IStreamedRequestHandler streamedRequestHandler = requestHandler as IStreamedRequestHandler; @@ -556,10 +640,18 @@ namespace OpenSim.Framework.Servers.HttpServer buffer = HandleLLSDRequests(request, response); break; + + case "application/json-rpc": + if (DebugLevel >= 3) + LogIncomingToContentTypeHandler(request); + + buffer = HandleJsonRpcRequests(request, response); + break; case "text/xml": case "application/xml": case "application/json": + default: //m_log.Info("[Debug BASE HTTP SERVER]: in default handler"); // Point of note.. the DoWeHaveA methods check for an EXACT path @@ -600,7 +692,24 @@ namespace OpenSim.Framework.Servers.HttpServer if (buffer != null) { - if (!response.SendChunked) + if (WebUtil.DebugLevel >= 5) + { + string output = System.Text.Encoding.UTF8.GetString(buffer); + + if (WebUtil.DebugLevel >= 6) + { + // Always truncate binary blobs. We don't have a ContentType, so detect them using the request name. + if ((requestHandler != null && requestHandler.Name == "GetMesh")) + { + if (output.Length > WebUtil.MaxRequestDiagLength) + output = output.Substring(0, WebUtil.MaxRequestDiagLength) + "..."; + } + } + + WebUtil.LogResponseDetail(RequestNumber, output); + } + + if (!response.SendChunked && response.ContentLength64 <= 0) response.ContentLength64 = buffer.LongLength; response.OutputStream.Write(buffer, 0, buffer.Length); @@ -630,12 +739,20 @@ namespace OpenSim.Framework.Servers.HttpServer } catch (IOException e) { - m_log.Error(String.Format("[BASE HTTP SERVER]: HandleRequest() threw {0} ", e.StackTrace), e); + m_log.Error("[BASE HTTP SERVER]: HandleRequest() threw exception ", e); } catch (Exception e) { - m_log.Error(String.Format("[BASE HTTP SERVER]: HandleRequest() threw {0} ", e.StackTrace), e); - SendHTML500(response); + m_log.Error("[BASE HTTP SERVER]: HandleRequest() threw exception ", e); + try + { + byte[] buffer500 = SendHTML500(response); + response.OutputStream.Write(buffer500, 0, buffer500.Length); + response.Send(); + } + catch + { + } } finally { @@ -645,7 +762,7 @@ namespace OpenSim.Framework.Servers.HttpServer if (tickdiff > 3000 && requestHandler != null && requestHandler.Name != "GetTexture") { m_log.InfoFormat( - "[BASE HTTP SERVER]: Slow handling of {0} {1} {2} {3} {4} from {5} took {6}ms", + "[LOGHTTP] Slow handling of {0} {1} {2} {3} {4} from {5} took {6}ms", RequestNumber, requestMethod, uriString, @@ -657,7 +774,7 @@ namespace OpenSim.Framework.Servers.HttpServer else if (DebugLevel >= 4) { m_log.DebugFormat( - "[BASE HTTP SERVER]: HTTP IN {0} :{1} took {2}ms", + "[LOGHTTP] HTTP IN {0} :{1} took {2}ms", RequestNumber, Port, tickdiff); @@ -668,7 +785,7 @@ namespace OpenSim.Framework.Servers.HttpServer private void LogIncomingToStreamHandler(OSHttpRequest request, IRequestHandler requestHandler) { m_log.DebugFormat( - "[BASE HTTP SERVER]: HTTP IN {0} :{1} stream handler {2} {3} {4} {5} from {6}", + "[LOGHTTP] HTTP IN {0} :{1} stream handler {2} {3} {4} {5} from {6}", RequestNumber, Port, request.HttpMethod, @@ -684,10 +801,10 @@ namespace OpenSim.Framework.Servers.HttpServer private void LogIncomingToContentTypeHandler(OSHttpRequest request) { m_log.DebugFormat( - "[BASE HTTP SERVER]: HTTP IN {0} :{1} {2} content type handler {3} {4} from {5}", + "[LOGHTTP] HTTP IN {0} :{1} {2} content type handler {3} {4} from {5}", RequestNumber, Port, - (request.ContentType == null || request.ContentType == "") ? "not set" : request.ContentType, + string.IsNullOrEmpty(request.ContentType) ? "not set" : request.ContentType, request.HttpMethod, request.Url.PathAndQuery, request.RemoteIPEndPoint); @@ -699,7 +816,7 @@ namespace OpenSim.Framework.Servers.HttpServer private void LogIncomingToXmlRpcHandler(OSHttpRequest request) { m_log.DebugFormat( - "[BASE HTTP SERVER]: HTTP IN {0} :{1} assumed generic XMLRPC request {2} {3} from {4}", + "[LOGHTTP] HTTP IN {0} :{1} assumed generic XMLRPC request {2} {3} from {4}", RequestNumber, Port, request.HttpMethod, @@ -712,29 +829,49 @@ namespace OpenSim.Framework.Servers.HttpServer private void LogIncomingInDetail(OSHttpRequest request) { - using (StreamReader reader = new StreamReader(Util.Copy(request.InputStream), Encoding.UTF8)) - { - string output; + if (request.ContentType == "application/octet-stream") + return; // never log these; they're just binary data - if (DebugLevel == 5) + Stream inputStream = Util.Copy(request.InputStream); + Stream innerStream = null; + try + { + if ((request.Headers["Content-Encoding"] == "gzip") || (request.Headers["X-Content-Encoding"] == "gzip")) { - const int sampleLength = 80; - char[] sampleChars = new char[sampleLength + 3]; - reader.Read(sampleChars, 0, sampleLength); - sampleChars[80] = '.'; - sampleChars[81] = '.'; - sampleChars[82] = '.'; - output = new string(sampleChars); + innerStream = inputStream; + inputStream = new GZipStream(innerStream, System.IO.Compression.CompressionMode.Decompress); } - else + + using (StreamReader reader = new StreamReader(inputStream, Encoding.UTF8)) { - output = reader.ReadToEnd(); - } + string output; - m_log.DebugFormat("[BASE HTTP SERVER]: {0}", output.Replace("\n", @"\n")); + if (DebugLevel == 5) + { + char[] chars = new char[WebUtil.MaxRequestDiagLength + 1]; // +1 so we know to add "..." only if needed + int len = reader.Read(chars, 0, WebUtil.MaxRequestDiagLength + 1); + output = new string(chars, 0, Math.Min(len, WebUtil.MaxRequestDiagLength)); + if (len > WebUtil.MaxRequestDiagLength) + output += "..."; + } + else + { + output = reader.ReadToEnd(); + } + + m_log.DebugFormat("[LOGHTTP] {0}", Util.BinaryToASCII(output)); + } + } + finally + { + if (innerStream != null) + innerStream.Dispose(); + inputStream.Dispose(); } } + private readonly string HANDLER_SEPARATORS = "/?&#-"; + private bool TryGetStreamHandler(string handlerKey, out IRequestHandler streamHandler) { string bestMatch = null; @@ -743,7 +880,8 @@ namespace OpenSim.Framework.Servers.HttpServer { foreach (string pattern in m_streamHandlers.Keys) { - if (handlerKey.StartsWith(pattern)) + if ((handlerKey == pattern) + || (handlerKey.StartsWith(pattern) && (HANDLER_SEPARATORS.IndexOf(handlerKey[pattern.Length]) >= 0))) { if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length) { @@ -773,7 +911,8 @@ namespace OpenSim.Framework.Servers.HttpServer { foreach (string pattern in m_pollHandlers.Keys) { - if (handlerKey.StartsWith(pattern)) + if ((handlerKey == pattern) + || (handlerKey.StartsWith(pattern) && (HANDLER_SEPARATORS.IndexOf(handlerKey[pattern.Length]) >= 0))) { if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length) { @@ -805,7 +944,8 @@ namespace OpenSim.Framework.Servers.HttpServer { foreach (string pattern in m_HTTPHandlers.Keys) { - if (handlerKey.StartsWith(pattern)) + if ((handlerKey == pattern) + || (handlerKey.StartsWith(pattern) && (HANDLER_SEPARATORS.IndexOf(handlerKey[pattern.Length]) >= 0))) { if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length) { @@ -854,16 +994,33 @@ namespace OpenSim.Framework.Servers.HttpServer /// private byte[] HandleXmlRpcRequests(OSHttpRequest request, OSHttpResponse response) { + String requestBody; + Stream requestStream = request.InputStream; + Stream innerStream = null; + try + { + if ((request.Headers["Content-Encoding"] == "gzip") || (request.Headers["X-Content-Encoding"] == "gzip")) + { + innerStream = requestStream; + requestStream = new GZipStream(innerStream, System.IO.Compression.CompressionMode.Decompress); + } - Encoding encoding = Encoding.UTF8; - StreamReader reader = new StreamReader(requestStream, encoding); + using (StreamReader reader = new StreamReader(requestStream, Encoding.UTF8)) + { + requestBody = reader.ReadToEnd(); + } + } + finally + { + if (innerStream != null) + innerStream.Dispose(); + requestStream.Dispose(); + } - string requestBody = reader.ReadToEnd(); - reader.Close(); - requestStream.Close(); //m_log.Debug(requestBody); requestBody = requestBody.Replace("", ""); + string responseString = String.Empty; XmlRpcRequest xmlRprcRequest = null; @@ -958,7 +1115,19 @@ namespace OpenSim.Framework.Servers.HttpServer } response.ContentType = "text/xml"; - responseString = XmlRpcResponseSerializer.Singleton.Serialize(xmlRpcResponse); + using (MemoryStream outs = new MemoryStream()) + using (XmlTextWriter writer = new XmlTextWriter(outs, UTF8NoBOM)) + { + writer.Formatting = Formatting.None; + XmlRpcResponseSerializer.Singleton.Serialize(writer, xmlRpcResponse); + writer.Flush(); + outs.Flush(); + outs.Position = 0; + using (StreamReader sr = new StreamReader(outs)) + { + responseString = sr.ReadToEnd(); + } + } } else { @@ -985,6 +1154,93 @@ namespace OpenSim.Framework.Servers.HttpServer return buffer; } + // JsonRpc (v2.0 only) + // Batch requests not yet supported + private byte[] HandleJsonRpcRequests(OSHttpRequest request, OSHttpResponse response) + { + Stream requestStream = request.InputStream; + JsonRpcResponse jsonRpcResponse = new JsonRpcResponse(); + OSDMap jsonRpcRequest = null; + + try + { + jsonRpcRequest = (OSDMap)OSDParser.DeserializeJson(requestStream); + } + catch (LitJson.JsonException e) + { + jsonRpcResponse.Error.Code = ErrorCode.InternalError; + jsonRpcResponse.Error.Message = e.Message; + } + + requestStream.Close(); + + if (jsonRpcRequest != null) + { + if (jsonRpcRequest.ContainsKey("jsonrpc") || jsonRpcRequest["jsonrpc"].AsString() == "2.0") + { + jsonRpcResponse.JsonRpc = "2.0"; + + // If we have no id, then it's a "notification" + if (jsonRpcRequest.ContainsKey("id")) + { + jsonRpcResponse.Id = jsonRpcRequest["id"].AsString(); + } + + string methodname = jsonRpcRequest["method"]; + JsonRPCMethod method; + + if (jsonRpcHandlers.ContainsKey(methodname)) + { + lock(jsonRpcHandlers) + { + jsonRpcHandlers.TryGetValue(methodname, out method); + } + bool res = false; + try + { + res = method(jsonRpcRequest, ref jsonRpcResponse); + if(!res) + { + // The handler sent back an unspecified error + if(jsonRpcResponse.Error.Code == 0) + { + jsonRpcResponse.Error.Code = ErrorCode.InternalError; + } + } + } + catch (Exception e) + { + string ErrorMessage = string.Format("[BASE HTTP SERVER]: Json-Rpc Handler Error method {0} - {1}", methodname, e.Message); + m_log.Error(ErrorMessage); + jsonRpcResponse.Error.Code = ErrorCode.InternalError; + jsonRpcResponse.Error.Message = ErrorMessage; + } + } + else // Error no hanlder defined for requested method + { + jsonRpcResponse.Error.Code = ErrorCode.InvalidRequest; + jsonRpcResponse.Error.Message = string.Format ("No handler defined for {0}", methodname); + } + } + else // not json-rpc 2.0 could be v1 + { + jsonRpcResponse.Error.Code = ErrorCode.InvalidRequest; + jsonRpcResponse.Error.Message = "Must be valid json-rpc 2.0 see: http://www.jsonrpc.org/specification"; + + if (jsonRpcRequest.ContainsKey("id")) + jsonRpcResponse.Id = jsonRpcRequest["id"].AsString(); + } + } + + response.KeepAlive = true; + string responseData = string.Empty; + + responseData = jsonRpcResponse.Serialize(); + + byte[] buffer = Encoding.UTF8.GetBytes(responseData); + return buffer; + } + private byte[] HandleLLSDRequests(OSHttpRequest request, OSHttpResponse response) { //m_log.Warn("[BASE HTTP SERVER]: We've figured out it's a LLSD Request"); @@ -1575,16 +1831,24 @@ namespace OpenSim.Framework.Servers.HttpServer response.SendChunked = false; response.ContentLength64 = buffer.Length; response.ContentEncoding = Encoding.UTF8; - + + return buffer; } public void Start() { - StartHTTP(); + Start(true); } - private void StartHTTP() + /// + /// Start the http server + /// + /// + /// If true then poll responses are performed asynchronsly. + /// Option exists to allow regression tests to perform processing synchronously. + /// + public void Start(bool performPollResponsesAsync) { m_log.InfoFormat( "[BASE HTTP SERVER]: Starting {0} server on port {1}", UseSSL ? "HTTPS" : "HTTP", Port); @@ -1622,8 +1886,9 @@ namespace OpenSim.Framework.Servers.HttpServer m_httpListener2.Start(64); // Long Poll Service Manager with 3 worker threads a 25 second timeout for no events - m_PollServiceManager = new PollServiceRequestManager(this, 3, 25000); - m_PollServiceManager.Start(); + PollServiceRequestManager = new PollServiceRequestManager(this, performPollResponsesAsync, 3, 25000); + PollServiceRequestManager.Start(); + HTTPDRunning = true; //HttpListenerContext context; @@ -1642,6 +1907,21 @@ namespace OpenSim.Framework.Servers.HttpServer // useful without inbound HTTP. throw e; } + + m_requestsProcessedStat + = new Stat( + "HTTPRequestsServed", + "Number of inbound HTTP requests processed", + "", + "requests", + "httpserver", + Port.ToString(), + StatType.Pull, + MeasuresOfInterest.AverageChangeOverTime, + stat => stat.Value = RequestNumber, + StatVerbosity.Debug); + + StatsManager.RegisterStat(m_requestsProcessedStat); } public void httpServerDisconnectMonitor(IHttpClientContext source, SocketError err) @@ -1672,9 +1952,12 @@ namespace OpenSim.Framework.Servers.HttpServer public void Stop() { HTTPDRunning = false; + + StatsManager.DeregisterStat(m_requestsProcessedStat); + try { - m_PollServiceManager.Stop(); + PollServiceRequestManager.Stop(); m_httpListener2.ExceptionThrown -= httpServerException; //m_httpListener2.DisconnectHandler = null; @@ -1741,6 +2024,12 @@ namespace OpenSim.Framework.Servers.HttpServer m_rpcHandlers.Remove(method); } + public void RemoveJsonRPCHandler(string method) + { + lock(jsonRpcHandlers) + jsonRpcHandlers.Remove(method); + } + public bool RemoveLLSDHandler(string path, LLSDMethod handler) { lock (m_llsdHandlers) diff --git a/OpenSim/Framework/Servers/HttpServer/BaseOutputStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/BaseOutputStreamHandler.cs new file mode 100644 index 0000000..72b3065 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/BaseOutputStreamHandler.cs @@ -0,0 +1,60 @@ +/* + * 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 OpenSimulator 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.IO; + +namespace OpenSim.Framework.Servers.HttpServer +{ + /// + /// Base handler for writing to an output stream + /// + /// + /// Inheriting classes should override ProcessRequest() rather than Handle() + /// + public abstract class BaseOutputStreamHandler : BaseRequestHandler, IRequestHandler + { + protected BaseOutputStreamHandler(string httpMethod, string path) : this(httpMethod, path, null, null) {} + + protected BaseOutputStreamHandler(string httpMethod, string path, string name, string description) + : base(httpMethod, path, name, description) {} + + public virtual void Handle( + string path, Stream request, Stream response, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + RequestsReceived++; + + ProcessRequest(path, request, response, httpRequest, httpResponse); + + RequestsHandled++; + } + + protected virtual void ProcessRequest( + string path, Stream request, Stream response, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Servers/HttpServer/BaseRequestHandler.cs b/OpenSim/Framework/Servers/HttpServer/BaseRequestHandler.cs index ae7aaf2..d4a1ec3 100644 --- a/OpenSim/Framework/Servers/HttpServer/BaseRequestHandler.cs +++ b/OpenSim/Framework/Servers/HttpServer/BaseRequestHandler.cs @@ -26,11 +26,16 @@ */ using System; +using OpenSim.Framework.Monitoring; namespace OpenSim.Framework.Servers.HttpServer { public abstract class BaseRequestHandler { + public int RequestsReceived { get; protected set; } + + public int RequestsHandled { get; protected set; } + public virtual string ContentType { get { return "application/xml"; } @@ -57,6 +62,24 @@ namespace OpenSim.Framework.Servers.HttpServer Description = description; m_httpMethod = httpMethod; m_path = path; + + // FIXME: A very temporary measure to stop the simulator stats being overwhelmed with user CAPS info. + // Needs to be fixed properly in stats display + if (!path.StartsWith("/CAPS/")) + { + StatsManager.RegisterStat( + new Stat( + "requests", + "requests", + "Number of requests received by this service endpoint", + "requests", + "service", + string.Format("{0}:{1}", httpMethod, path), + StatType.Pull, + MeasuresOfInterest.AverageChangeOverTime, + s => s.Value = RequestsReceived, + StatVerbosity.Debug)); + } } public virtual string Path diff --git a/OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs index 6342983..41aa19b 100644 --- a/OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs +++ b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs @@ -26,17 +26,60 @@ */ using System.IO; +using System.Net; +using OpenSim.Framework.ServiceAuth; namespace OpenSim.Framework.Servers.HttpServer { + /// + /// Base streamed request handler. + /// + /// + /// Inheriting classes should override ProcessRequest() rather than Handle() + /// public abstract class BaseStreamHandler : BaseRequestHandler, IStreamedRequestHandler { - public abstract byte[] Handle(string path, Stream request, - IOSHttpRequest httpRequest, IOSHttpResponse httpResponse); + protected IServiceAuth m_Auth; - protected BaseStreamHandler(string httpMethod, string path) : this(httpMethod, path, null, null) {} + protected BaseStreamHandler(string httpMethod, string path) : this(httpMethod, path, null, null) { } protected BaseStreamHandler(string httpMethod, string path, string name, string description) : base(httpMethod, path, name, description) {} + + protected BaseStreamHandler(string httpMethod, string path, IServiceAuth auth) + : base(httpMethod, path, null, null) + { + m_Auth = auth; + } + + public virtual byte[] Handle( + string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + RequestsReceived++; + + if (m_Auth != null) + { + HttpStatusCode statusCode; + + if (!m_Auth.Authenticate(httpRequest.Headers, httpResponse.AddHeader, out statusCode)) + { + httpResponse.StatusCode = (int)statusCode; + httpResponse.ContentType = "text/plain"; + return new byte[0]; + } + } + + byte[] result = ProcessRequest(path, request, httpRequest, httpResponse); + + RequestsHandled++; + + return result; + } + + protected virtual byte[] ProcessRequest( + string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + return null; + } } } \ No newline at end of file diff --git a/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs new file mode 100644 index 0000000..1b88545 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs @@ -0,0 +1,107 @@ +/* + * 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 OpenSimulator 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 OpenSim.Framework; +using System.IO; + +namespace OpenSim.Framework.Servers.HttpServer +{ + /// + /// BaseStreamHandlerBasicDOSProtector Base streamed request handler. + /// + /// + /// Inheriting classes should override ProcessRequest() rather than Handle() + /// + public abstract class BaseStreamHandlerBasicDOSProtector : BaseRequestHandler, IStreamedRequestHandler + { + + private readonly BasicDosProtectorOptions _options; + private readonly BasicDOSProtector _dosProtector; + + protected BaseStreamHandlerBasicDOSProtector(string httpMethod, string path, BasicDosProtectorOptions options) : this(httpMethod, path, null, null, options) {} + + protected BaseStreamHandlerBasicDOSProtector(string httpMethod, string path, string name, string description, BasicDosProtectorOptions options) + : base(httpMethod, path, name, description) + { + _options = options; + _dosProtector = new BasicDOSProtector(_options); + } + + public virtual byte[] Handle( + string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + byte[] result; + RequestsReceived++; + string clientstring = GetClientString(httpRequest); + string endpoint = GetRemoteAddr(httpRequest); + if (_dosProtector.Process(clientstring, endpoint)) + result = ProcessRequest(path, request, httpRequest, httpResponse); + else + result = ThrottledRequest(path, request, httpRequest, httpResponse); + if (_options.MaxConcurrentSessions > 0) + _dosProtector.ProcessEnd(clientstring, endpoint); + + RequestsHandled++; + + return result; + } + + protected virtual byte[] ProcessRequest( + string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + return null; + } + + protected virtual byte[] ThrottledRequest( + string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + { + return new byte[0]; + } + + + private string GetRemoteAddr(IOSHttpRequest httpRequest) + { + string remoteaddr = string.Empty; + if (httpRequest.Headers["remote_addr"] != null) + remoteaddr = httpRequest.Headers["remote_addr"]; + + return remoteaddr; + } + + private string GetClientString(IOSHttpRequest httpRequest) + { + string clientstring = string.Empty; + + if (_options.AllowXForwardedFor && httpRequest.Headers["x-forwarded-for"] != null) + clientstring = httpRequest.Headers["x-forwarded-for"]; + else + clientstring = GetRemoteAddr(httpRequest); + + return clientstring; + + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/BinaryStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/BinaryStreamHandler.cs index b94bfb4..1b03f54 100644 --- a/OpenSim/Framework/Servers/HttpServer/BinaryStreamHandler.cs +++ b/OpenSim/Framework/Servers/HttpServer/BinaryStreamHandler.cs @@ -45,7 +45,7 @@ namespace OpenSim.Framework.Servers.HttpServer m_method = binaryMethod; } - public override byte[] Handle(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { byte[] data = ReadFully(request); string param = GetParam(path); diff --git a/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs new file mode 100644 index 0000000..cd4b8ff --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs @@ -0,0 +1,119 @@ +/* + * 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 OpenSimulator 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.Collections; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public class GenericHTTPDOSProtector + { + private readonly GenericHTTPMethod _normalMethod; + private readonly GenericHTTPMethod _throttledMethod; + + private readonly BasicDosProtectorOptions _options; + private readonly BasicDOSProtector _dosProtector; + + public GenericHTTPDOSProtector(GenericHTTPMethod normalMethod, GenericHTTPMethod throttledMethod, BasicDosProtectorOptions options) + { + _normalMethod = normalMethod; + _throttledMethod = throttledMethod; + + _options = options; + _dosProtector = new BasicDOSProtector(_options); + } + public Hashtable Process(Hashtable request) + { + Hashtable process = null; + string clientstring= GetClientString(request); + string endpoint = GetRemoteAddr(request); + if (_dosProtector.Process(clientstring, endpoint)) + process = _normalMethod(request); + else + process = _throttledMethod(request); + + if (_options.MaxConcurrentSessions>0) + _dosProtector.ProcessEnd(clientstring, endpoint); + + return process; + } + + private string GetRemoteAddr(Hashtable request) + { + string remoteaddr = ""; + if (!request.ContainsKey("headers")) + return remoteaddr; + Hashtable requestinfo = (Hashtable)request["headers"]; + if (!requestinfo.ContainsKey("remote_addr")) + return remoteaddr; + object remote_addrobj = requestinfo["remote_addr"]; + if (remote_addrobj != null) + { + if (!string.IsNullOrEmpty(remote_addrobj.ToString())) + { + remoteaddr = remote_addrobj.ToString(); + } + + } + return remoteaddr; + } + + private string GetClientString(Hashtable request) + { + string clientstring = ""; + if (!request.ContainsKey("headers")) + return clientstring; + + Hashtable requestinfo = (Hashtable)request["headers"]; + if (_options.AllowXForwardedFor && requestinfo.ContainsKey("x-forwarded-for")) + { + object str = requestinfo["x-forwarded-for"]; + if (str != null) + { + if (!string.IsNullOrEmpty(str.ToString())) + { + return str.ToString(); + } + } + } + if (!requestinfo.ContainsKey("remote_addr")) + return clientstring; + + object remote_addrobj = requestinfo["remote_addr"]; + if (remote_addrobj != null) + { + if (!string.IsNullOrEmpty(remote_addrobj.ToString())) + { + clientstring = remote_addrobj.ToString(); + } + } + + return clientstring; + + } + + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs index 0bd3aae..d162bc1 100644 --- a/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs +++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs @@ -97,6 +97,18 @@ namespace OpenSim.Framework.Servers.HttpServer bool AddXmlRPCHandler(string method, XmlRpcMethod handler); bool AddXmlRPCHandler(string method, XmlRpcMethod handler, bool keepAlive); + bool AddJsonRPCHandler(string method, JsonRPCMethod handler); + + /// + /// Websocket HTTP server handlers. + /// + /// + /// + void AddWebSocketHandler(string servicepath, BaseHttpServer.WebSocketRequestDelegate handler); + + + void RemoveWebSocketHandler(string servicepath); + /// /// Gets the XML RPC handler for given method name /// @@ -128,6 +140,8 @@ namespace OpenSim.Framework.Servers.HttpServer void RemoveStreamHandler(string httpMethod, string path); void RemoveXmlRPCHandler(string method); + + void RemoveJsonRPCHandler(string method); string GetHTTP404(string host); diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs index cb5cce5..b8541cb 100644 --- a/OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs +++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs @@ -32,7 +32,6 @@ namespace OpenSim.Framework.Servers.HttpServer { public interface IRequestHandler { - /// /// Name for this handler. /// @@ -59,6 +58,19 @@ namespace OpenSim.Framework.Servers.HttpServer // Return path string Path { get; } + + /// + /// Number of requests received by this handler + /// + int RequestsReceived { get; } + + /// + /// Number of requests handled. + /// + /// + /// Should be equal to RequestsReceived unless requested are being handled slowly or there is deadlock. + /// + int RequestsHandled { get; } } public interface IStreamedRequestHandler : IRequestHandler @@ -69,7 +81,6 @@ namespace OpenSim.Framework.Servers.HttpServer public interface IStreamHandler : IRequestHandler { - // Handle request stream, return byte array void Handle(string path, Stream request, Stream response, IOSHttpRequest httpReqbuest, IOSHttpResponse httpResponse); } diff --git a/OpenSim/Framework/Servers/HttpServer/JsonRPCMethod.cs b/OpenSim/Framework/Servers/HttpServer/JsonRPCMethod.cs new file mode 100644 index 0000000..5bab508 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/JsonRPCMethod.cs @@ -0,0 +1,34 @@ +/* + * 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 OpenSimulator 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.Net; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public delegate bool JsonRPCMethod(OSDMap jsonRpcRequest, ref JsonRpcResponse response); +} diff --git a/OpenSim/Framework/Servers/HttpServer/JsonRpcRequestManager.cs b/OpenSim/Framework/Servers/HttpServer/JsonRpcRequestManager.cs new file mode 100644 index 0000000..2fe1a7d --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/JsonRpcRequestManager.cs @@ -0,0 +1,190 @@ +/* + * 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 OpenSimulator 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.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Text; +using System.IO; +using OpenMetaverse.StructuredData; +using OpenMetaverse; +using log4net; + +namespace OpenSim.Framework.Servers.HttpServer +{ + /// + /// Json rpc request manager. + /// + public class JsonRpcRequestManager + { + static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public JsonRpcRequestManager() + { + } + + /// + /// Sends json-rpc request with a serializable type. + /// + /// + /// OSD Map. + /// + /// + /// Serializable type . + /// + /// + /// Json-rpc method to call. + /// + /// + /// URI of json-rpc service. + /// + /// + /// Id for our call. + /// + public bool JsonRpcRequest(ref object parameters, string method, string uri, string jsonId) + { + if (jsonId == null) + throw new ArgumentNullException("jsonId"); + if (uri == null) + throw new ArgumentNullException("uri"); + if (method == null) + throw new ArgumentNullException("method"); + if (parameters == null) + throw new ArgumentNullException("parameters"); + + OSDMap request = new OSDMap(); + request.Add("jsonrpc", OSD.FromString("2.0")); + request.Add("id", OSD.FromString(jsonId)); + request.Add("method", OSD.FromString(method)); + request.Add("params", OSD.SerializeMembers(parameters)); + + OSDMap response; + try + { + response = WebUtil.PostToService(uri, request, 10000, true); + } + catch (Exception e) + { + m_log.Debug(string.Format("JsonRpc request '{0}' to {1} failed", method, uri), e); + return false; + } + + if (!response.ContainsKey("_Result")) + { + m_log.DebugFormat("JsonRpc request '{0}' to {1} returned an invalid response: {2}", + method, uri, OSDParser.SerializeJsonString(response)); + return false; + } + response = (OSDMap)response["_Result"]; + + OSD data; + + if (response.ContainsKey("error")) + { + data = response["error"]; + m_log.DebugFormat("JsonRpc request '{0}' to {1} returned an error: {2}", + method, uri, OSDParser.SerializeJsonString(data)); + return false; + } + + if (!response.ContainsKey("result")) + { + m_log.DebugFormat("JsonRpc request '{0}' to {1} returned an invalid response: {2}", + method, uri, OSDParser.SerializeJsonString(response)); + return false; + } + + data = response["result"]; + OSD.DeserializeMembers(ref parameters, (OSDMap)data); + + return true; + } + + /// + /// Sends json-rpc request with OSD parameter. + /// + /// + /// The rpc request. + /// + /// + /// data - incoming as parameters, outgoing as result/error + /// + /// + /// Json-rpc method to call. + /// + /// + /// URI of json-rpc service. + /// + /// + /// If set to true json identifier. + /// + public bool JsonRpcRequest(ref OSD data, string method, string uri, string jsonId) + { + if (string.IsNullOrEmpty(jsonId)) + jsonId = UUID.Random().ToString(); + + OSDMap request = new OSDMap(); + request.Add("jsonrpc", OSD.FromString("2.0")); + request.Add("id", OSD.FromString(jsonId)); + request.Add("method", OSD.FromString(method)); + request.Add("params", data); + + OSDMap response; + try + { + response = WebUtil.PostToService(uri, request, 10000, true); + } + catch (Exception e) + { + m_log.Debug(string.Format("JsonRpc request '{0}' to {1} failed", method, uri), e); + return false; + } + + if (!response.ContainsKey("_Result")) + { + m_log.DebugFormat("JsonRpc request '{0}' to {1} returned an invalid response: {2}", + method, uri, OSDParser.SerializeJsonString(response)); + return false; + } + response = (OSDMap)response["_Result"]; + + if (response.ContainsKey("error")) + { + data = response["error"]; + m_log.DebugFormat("JsonRpc request '{0}' to {1} returned an error: {2}", + method, uri, OSDParser.SerializeJsonString(data)); + return false; + } + + data = response; + + return true; + } + + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/JsonRpcResponse.cs b/OpenSim/Framework/Servers/HttpServer/JsonRpcResponse.cs new file mode 100644 index 0000000..2c50587 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/JsonRpcResponse.cs @@ -0,0 +1,150 @@ +/* + * 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 OpenSimulator 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.Net; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework.Servers.HttpServer +{ + public sealed class ErrorCode + { + private ErrorCode() {} + + public const int ParseError = -32700; + public const int InvalidRequest = -32600; + public const int MethodNotFound = -32601; + public const int InvalidParams = -32602; + public const int InternalError = -32604; + + } + + public class JsonRpcError + { + internal OSDMap Error = new OSDMap(); + + public int Code + { + get + { + if (Error.ContainsKey("code")) + return Error["code"].AsInteger(); + else + return 0; + } + set + { + Error["code"] = OSD.FromInteger(value); + } + } + + public string Message + { + get + { + if (Error.ContainsKey("message")) + return Error["message"].AsString(); + else + return null; + } + set + { + Error["message"] = OSD.FromString(value); + } + } + + public OSD Data + { + get; set; + } + } + + public class JsonRpcResponse + { + public string JsonRpc + { + get + { + return Reply["jsonrpc"].AsString(); + } + set + { + Reply["jsonrpc"] = OSD.FromString(value); + } + } + + public string Id + { + get + { + return Reply["id"].AsString(); + } + set + { + Reply["id"] = OSD.FromString(value); + } + } + + public OSD Result + { + get; set; + } + + public JsonRpcError Error + { + get; set; + } + + public OSDMap Reply = new OSDMap(); + + public JsonRpcResponse() + { + Error = new JsonRpcError(); + } + + public string Serialize() + { + if (Result != null) + Reply["result"] = Result; + + if (Error.Code != 0) + { + Reply["error"] = (OSD)Error.Error; + } + + string result = string.Empty; + try + { + result = OSDParser.SerializeJsonString(Reply); + } + catch + { + + } + return result; + } + } +} diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpRequest.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpRequest.cs index 3171759..05ec6dc 100644 --- a/OpenSim/Framework/Servers/HttpServer/OSHttpRequest.cs +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpRequest.cs @@ -182,11 +182,22 @@ namespace OpenSim.Framework.Servers.HttpServer _context = context; if (null != req.Headers["content-encoding"]) - _contentEncoding = Encoding.GetEncoding(_request.Headers["content-encoding"]); + { + try + { + _contentEncoding = Encoding.GetEncoding(_request.Headers["content-encoding"]); + } + catch (Exception) + { + // ignore + } + } + if (null != req.Headers["content-type"]) _contentType = _request.Headers["content-type"]; if (null != req.Headers["user-agent"]) _userAgent = req.Headers["user-agent"]; + if (null != req.Headers["remote_addr"]) { try diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs index 77cfb7e..bdea278 100644 --- a/OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs @@ -70,9 +70,9 @@ namespace OpenSim.Framework.Servers.HttpServer _id = id; _engine = new Thread(new ThreadStart(Engine)); - _engine.Name = EngineID; _engine.IsBackground = true; _engine.Start(); + _engine.Name = string.Format ("Engine:{0}",EngineID); ThreadTracker.Add(_engine); } @@ -91,9 +91,9 @@ namespace OpenSim.Framework.Servers.HttpServer public void Start() { _engine = new Thread(new ThreadStart(Engine)); - _engine.Name = EngineID; _engine.IsBackground = true; _engine.Start(); + _engine.Name = string.Format ("Engine:{0}",EngineID); ThreadTracker.Add(_engine); } diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpServer.cs index 84aa31b..cd62842 100644 --- a/OpenSim/Framework/Servers/HttpServer/OSHttpServer.cs +++ b/OpenSim/Framework/Servers/HttpServer/OSHttpServer.cs @@ -150,9 +150,9 @@ namespace OpenSim.Framework.Servers.HttpServer public void Start() { _engine = new Thread(new ThreadStart(Engine)); - _engine.Name = _engineId; _engine.IsBackground = true; _engine.Start(); + _engine.Name = string.Format ("Engine:{0}",_engineId); ThreadTracker.Add(_engine); diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs index 3089351..9477100 100644 --- a/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs +++ b/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs @@ -34,7 +34,7 @@ namespace OpenSim.Framework.Servers.HttpServer public delegate void RequestMethod(UUID requestID, Hashtable request); public delegate bool HasEventsMethod(UUID requestID, UUID pId); - public delegate Hashtable GetEventsMethod(UUID requestID, UUID pId, string request); + public delegate Hashtable GetEventsMethod(UUID requestID, UUID pId); public delegate Hashtable NoEventsMethod(UUID requestID, UUID pId); @@ -45,17 +45,42 @@ namespace OpenSim.Framework.Servers.HttpServer public NoEventsMethod NoEvents; public RequestMethod Request; public UUID Id; + public int TimeOutms; + public EventType Type; + + public enum EventType : int + { + LongPoll = 0, + LslHttp = 1, + Inventory = 2 + } + + public string Url { get; set; } + + /// + /// Number of requests received for this poll service. + /// + public int RequestsReceived { get; set; } + + /// + /// Number of requests handled by this poll service. + /// + public int RequestsHandled { get; set; } public PollServiceEventArgs( RequestMethod pRequest, + string pUrl, HasEventsMethod pHasEvents, GetEventsMethod pGetEvents, NoEventsMethod pNoEvents, - UUID pId) + UUID pId, int pTimeOutms) { Request = pRequest; + Url = pUrl; HasEvents = pHasEvents; GetEvents = pGetEvents; NoEvents = pNoEvents; Id = pId; + TimeOutms = pTimeOutms; + Type = EventType.LongPoll; } } -} \ No newline at end of file +} diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs index 723530a..caf0e98 100644 --- a/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs +++ b/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs @@ -26,13 +26,19 @@ */ using System; +using System.Collections; +using System.Reflection; +using System.Text; using HttpServer; +using log4net; using OpenMetaverse; namespace OpenSim.Framework.Servers.HttpServer { public class PollServiceHttpRequest { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + public readonly PollServiceEventArgs PollServiceArgs; public readonly IHttpClientContext HttpContext; public readonly IHttpRequest Request; @@ -48,5 +54,44 @@ namespace OpenSim.Framework.Servers.HttpServer RequestTime = System.Environment.TickCount; RequestID = UUID.Random(); } + + internal void DoHTTPGruntWork(BaseHttpServer server, Hashtable responsedata) + { + OSHttpResponse response + = new OSHttpResponse(new HttpResponse(HttpContext, Request), HttpContext); + + byte[] buffer = server.DoHTTPGruntWork(responsedata, response); + + response.SendChunked = false; + response.ContentLength64 = buffer.Length; + response.ContentEncoding = Encoding.UTF8; + + try + { + response.OutputStream.Write(buffer, 0, buffer.Length); + } + catch (Exception ex) + { + m_log.Warn("[POLL SERVICE WORKER THREAD]: Error ", ex); + } + finally + { + //response.OutputStream.Close(); + try + { + response.OutputStream.Flush(); + response.Send(); + + //if (!response.KeepAlive && response.ReuseContext) + // response.FreeContext(); + } + catch (Exception e) + { + m_log.Warn("[POLL SERVICE WORKER THREAD]: Error ", e); + } + + PollServiceArgs.RequestsHandled++; + } + } } } \ No newline at end of file diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs index 3e84c55..28bba70 100644 --- a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs +++ b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs @@ -33,132 +33,298 @@ using log4net; using HttpServer; using OpenSim.Framework; using OpenSim.Framework.Monitoring; +using Amib.Threading; +using System.IO; +using System.Text; +using System.Collections.Generic; namespace OpenSim.Framework.Servers.HttpServer { public class PollServiceRequestManager { -// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + /// + /// Is the poll service request manager running? + /// + /// + /// Can be running either synchronously or asynchronously + /// + public bool IsRunning { get; private set; } + + /// + /// Is the poll service performing responses asynchronously (with its own threads) or synchronously (via + /// external calls)? + /// + public bool PerformResponsesAsync { get; private set; } + + /// + /// Number of responses actually processed and sent to viewer (or aborted due to error). + /// + public int ResponsesProcessed { get; private set; } private readonly BaseHttpServer m_server; - private static Queue m_requests = Queue.Synchronized(new Queue()); + + private BlockingQueue m_requests = new BlockingQueue(); + private static List m_longPollRequests = new List(); + private uint m_WorkerThreadCount = 0; private Thread[] m_workerThreads; - private PollServiceWorkerThread[] m_PollServiceWorkerThreads; - private volatile bool m_running = true; - private int m_pollTimeout; - public PollServiceRequestManager(BaseHttpServer pSrv, uint pWorkerThreadCount, int pTimeout) + private SmartThreadPool m_threadPool = new SmartThreadPool(20000, 12, 2); + +// private int m_timeout = 1000; // increase timeout 250; now use the event one + + public PollServiceRequestManager( + BaseHttpServer pSrv, bool performResponsesAsync, uint pWorkerThreadCount, int pTimeout) { m_server = pSrv; + PerformResponsesAsync = performResponsesAsync; m_WorkerThreadCount = pWorkerThreadCount; - m_pollTimeout = pTimeout; + m_workerThreads = new Thread[m_WorkerThreadCount]; + + StatsManager.RegisterStat( + new Stat( + "QueuedPollResponses", + "Number of poll responses queued for processing.", + "", + "", + "httpserver", + m_server.Port.ToString(), + StatType.Pull, + MeasuresOfInterest.AverageChangeOverTime, + stat => stat.Value = m_requests.Count(), + StatVerbosity.Debug)); + + StatsManager.RegisterStat( + new Stat( + "ProcessedPollResponses", + "Number of poll responses processed.", + "", + "", + "httpserver", + m_server.Port.ToString(), + StatType.Pull, + MeasuresOfInterest.AverageChangeOverTime, + stat => stat.Value = ResponsesProcessed, + StatVerbosity.Debug)); } public void Start() { - m_running = true; - m_workerThreads = new Thread[m_WorkerThreadCount]; - m_PollServiceWorkerThreads = new PollServiceWorkerThread[m_WorkerThreadCount]; + IsRunning = true; - //startup worker threads - for (uint i = 0; i < m_WorkerThreadCount; i++) + if (PerformResponsesAsync) { - m_PollServiceWorkerThreads[i] = new PollServiceWorkerThread(m_server, m_pollTimeout); - m_PollServiceWorkerThreads[i].ReQueue += ReQueueEvent; - - m_workerThreads[i] - = Watchdog.StartThread( - m_PollServiceWorkerThreads[i].ThreadStart, - String.Format("PollServiceWorkerThread{0}", i), - ThreadPriority.Normal, - false, - true, - null, - int.MaxValue); - } + //startup worker threads + for (uint i = 0; i < m_WorkerThreadCount; i++) + { + m_workerThreads[i] + = WorkManager.StartThread( + PoolWorkerJob, + string.Format("PollServiceWorkerThread{0}:{1}", i, m_server.Port), + ThreadPriority.Normal, + false, + false, + null, + int.MaxValue); + } - Watchdog.StartThread( - this.ThreadStart, - "PollServiceWatcherThread", - ThreadPriority.Normal, - false, - true, - null, - 1000 * 60 * 10); + WorkManager.StartThread( + this.CheckLongPollThreads, + string.Format("LongPollServiceWatcherThread:{0}", m_server.Port), + ThreadPriority.Normal, + false, + true, + null, + 1000 * 60 * 10); + } } - internal void ReQueueEvent(PollServiceHttpRequest req) + private void ReQueueEvent(PollServiceHttpRequest req) { - // Do accounting stuff here - Enqueue(req); - } + if (IsRunning) + { + // delay the enqueueing for 100ms. There's no need to have the event + // actively on the queue + Timer t = new Timer(self => { + ((Timer)self).Dispose(); + m_requests.Enqueue(req); + }); - public void Enqueue(PollServiceHttpRequest req) - { - lock (m_requests) - m_requests.Enqueue(req); + t.Change(100, Timeout.Infinite); + + } } - public void ThreadStart() + public void Enqueue(PollServiceHttpRequest req) { - while (m_running) + if (IsRunning) { - Watchdog.UpdateThread(); - ProcessQueuedRequests(); - Thread.Sleep(1000); + if (req.PollServiceArgs.Type == PollServiceEventArgs.EventType.LongPoll) + { + lock (m_longPollRequests) + m_longPollRequests.Add(req); + } + else + m_requests.Enqueue(req); } } - private void ProcessQueuedRequests() + private void CheckLongPollThreads() { - lock (m_requests) + // The only purpose of this thread is to check the EQs for events. + // If there are events, that thread will be placed in the "ready-to-serve" queue, m_requests. + // If there are no events, that thread will be back to its "waiting" queue, m_longPollRequests. + // All other types of tasks (Inventory handlers, http-in, etc) don't have the long-poll nature, + // so if they aren't ready to be served by a worker thread (no events), they are placed + // directly back in the "ready-to-serve" queue by the worker thread. + while (IsRunning) { - if (m_requests.Count == 0) - return; - -// m_log.DebugFormat("[POLL SERVICE REQUEST MANAGER]: Processing {0} requests", m_requests.Count); - - int reqperthread = (int) (m_requests.Count/m_WorkerThreadCount) + 1; + Thread.Sleep(500); + Watchdog.UpdateThread(); - // For Each WorkerThread - for (int tc = 0; tc < m_WorkerThreadCount && m_requests.Count > 0; tc++) +// List not_ready = new List(); + lock (m_longPollRequests) { - //Loop over number of requests each thread handles. - for (int i = 0; i < reqperthread && m_requests.Count > 0; i++) + if (m_longPollRequests.Count > 0 && IsRunning) { - try - { - m_PollServiceWorkerThreads[tc].Enqueue((PollServiceHttpRequest)m_requests.Dequeue()); - } - catch (InvalidOperationException) - { - // The queue is empty, we did our calculations wrong! - return; - } - + List ready = m_longPollRequests.FindAll(req => + (req.PollServiceArgs.HasEvents(req.RequestID, req.PollServiceArgs.Id) || // there are events in this EQ + (Environment.TickCount - req.RequestTime) > req.PollServiceArgs.TimeOutms) // no events, but timeout + ); + + ready.ForEach(req => + { + m_requests.Enqueue(req); + m_longPollRequests.Remove(req); + }); + } + } } - } public void Stop() { - m_running = false; + IsRunning = false; +// m_timeout = -10000; // cause all to expire + Thread.Sleep(1000); // let the world move + + foreach (Thread t in m_workerThreads) + Watchdog.AbortThread(t.ManagedThreadId); + + PollServiceHttpRequest wreq; - foreach (object o in m_requests) + lock (m_longPollRequests) { - PollServiceHttpRequest req = (PollServiceHttpRequest) o; - PollServiceWorkerThread.DoHTTPGruntWork( - m_server, req, req.PollServiceArgs.NoEvents(req.RequestID, req.PollServiceArgs.Id)); + if (m_longPollRequests.Count > 0 && IsRunning) + m_longPollRequests.ForEach(req => m_requests.Enqueue(req)); } + while (m_requests.Count() > 0) + { + try + { + wreq = m_requests.Dequeue(0); + ResponsesProcessed++; + wreq.DoHTTPGruntWork( + m_server, wreq.PollServiceArgs.NoEvents(wreq.RequestID, wreq.PollServiceArgs.Id)); + } + catch + { + } + } + + m_longPollRequests.Clear(); m_requests.Clear(); + } - foreach (Thread t in m_workerThreads) + // work threads + + private void PoolWorkerJob() + { + while (IsRunning) { - t.Abort(); + Watchdog.UpdateThread(); + WaitPerformResponse(); + } + } + + public void WaitPerformResponse() + { + PollServiceHttpRequest req = m_requests.Dequeue(5000); +// m_log.DebugFormat("[YYY]: Dequeued {0}", (req == null ? "null" : req.PollServiceArgs.Type.ToString())); + + if (req != null) + { + try + { + if (req.PollServiceArgs.HasEvents(req.RequestID, req.PollServiceArgs.Id)) + { + Hashtable responsedata = req.PollServiceArgs.GetEvents(req.RequestID, req.PollServiceArgs.Id); + + if (responsedata == null) + return; + + // This is the event queue. + // Even if we're not running we can still perform responses by explicit request. + if (req.PollServiceArgs.Type == PollServiceEventArgs.EventType.LongPoll + || !PerformResponsesAsync) + { + try + { + ResponsesProcessed++; + req.DoHTTPGruntWork(m_server, responsedata); + } + catch (ObjectDisposedException e) // Browser aborted before we could read body, server closed the stream + { + // Ignore it, no need to reply + m_log.Error(e); + } + } + else + { + m_threadPool.QueueWorkItem(x => + { + try + { + ResponsesProcessed++; + req.DoHTTPGruntWork(m_server, responsedata); + } + catch (ObjectDisposedException e) // Browser aborted before we could read body, server closed the stream + { + // Ignore it, no need to reply + m_log.Error(e); + } + catch (Exception e) + { + m_log.Error(e); + } + + return null; + }, null); + } + } + else + { + if ((Environment.TickCount - req.RequestTime) > req.PollServiceArgs.TimeOutms) + { + ResponsesProcessed++; + req.DoHTTPGruntWork( + m_server, req.PollServiceArgs.NoEvents(req.RequestID, req.PollServiceArgs.Id)); + } + else + { + ReQueueEvent(req); + } + } + } + catch (Exception e) + { + m_log.ErrorFormat("Exception in poll service thread: " + e.ToString()); + } } } } diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceWorkerThread.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceWorkerThread.cs deleted file mode 100644 index 5adbcd1..0000000 --- a/OpenSim/Framework/Servers/HttpServer/PollServiceWorkerThread.cs +++ /dev/null @@ -1,165 +0,0 @@ -/* - * 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 OpenSimulator 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; -using System.Collections.Generic; -using System.IO; -using System.Text; -using HttpServer; -using OpenMetaverse; -using System.Reflection; -using log4net; -using OpenSim.Framework.Monitoring; - -namespace OpenSim.Framework.Servers.HttpServer -{ - public delegate void ReQueuePollServiceItem(PollServiceHttpRequest req); - - public class PollServiceWorkerThread - { - private static readonly ILog m_log = - LogManager.GetLogger( - MethodBase.GetCurrentMethod().DeclaringType); - - public event ReQueuePollServiceItem ReQueue; - - private readonly BaseHttpServer m_server; - private BlockingQueue m_request; - private bool m_running = true; - private int m_timeout = 250; - - public PollServiceWorkerThread(BaseHttpServer pSrv, int pTimeout) - { - m_request = new BlockingQueue(); - m_server = pSrv; - m_timeout = pTimeout; - } - - public void ThreadStart() - { - Run(); - } - - public void Run() - { - while (m_running) - { - PollServiceHttpRequest req = m_request.Dequeue(); - - Watchdog.UpdateThread(); - - try - { - if (req.PollServiceArgs.HasEvents(req.RequestID, req.PollServiceArgs.Id)) - { - StreamReader str; - try - { - str = new StreamReader(req.Request.Body); - } - catch (System.ArgumentException) - { - // Stream was not readable means a child agent - // was closed due to logout, leaving the - // Event Queue request orphaned. - continue; - } - - Hashtable responsedata = req.PollServiceArgs.GetEvents(req.RequestID, req.PollServiceArgs.Id, str.ReadToEnd()); - DoHTTPGruntWork(m_server, req, responsedata); - } - else - { - if ((Environment.TickCount - req.RequestTime) > m_timeout) - { - DoHTTPGruntWork( - m_server, - req, - req.PollServiceArgs.NoEvents(req.RequestID, req.PollServiceArgs.Id)); - } - else - { - ReQueuePollServiceItem reQueueItem = ReQueue; - if (reQueueItem != null) - reQueueItem(req); - } - } - } - catch (Exception e) - { - m_log.ErrorFormat("Exception in poll service thread: " + e.ToString()); - } - } - } - - internal void Enqueue(PollServiceHttpRequest pPollServiceHttpRequest) - { - m_request.Enqueue(pPollServiceHttpRequest); - } - - /// - /// FIXME: This should be part of BaseHttpServer - /// - internal static void DoHTTPGruntWork(BaseHttpServer server, PollServiceHttpRequest req, Hashtable responsedata) - { - OSHttpResponse response - = new OSHttpResponse(new HttpResponse(req.HttpContext, req.Request), req.HttpContext); - - byte[] buffer = server.DoHTTPGruntWork(responsedata, response); - - response.SendChunked = false; - response.ContentLength64 = buffer.Length; - response.ContentEncoding = Encoding.UTF8; - - try - { - response.OutputStream.Write(buffer, 0, buffer.Length); - } - catch (Exception ex) - { - m_log.Warn(string.Format("[POLL SERVICE WORKER THREAD]: Error ", ex)); - } - finally - { - //response.OutputStream.Close(); - try - { - response.OutputStream.Flush(); - response.Send(); - - //if (!response.KeepAlive && response.ReuseContext) - // response.FreeContext(); - } - catch (Exception e) - { - m_log.Warn(String.Format("[POLL SERVICE WORKER THREAD]: Error ", e)); - } - } - } - } -} \ No newline at end of file diff --git a/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs b/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs index 02ecc25..5e630dc 100644 --- a/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("0.7.5.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("0.8.3.*")] + diff --git a/OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs b/OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs index 07082a8..bd55657 100644 --- a/OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs +++ b/OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs @@ -33,7 +33,7 @@ namespace OpenSim.Framework.Servers.HttpServer { public delegate TResponse RestDeserialiseMethod(TRequest request); - public class RestDeserialiseHandler : BaseRequestHandler, IStreamHandler + public class RestDeserialiseHandler : BaseOutputStreamHandler, IStreamHandler where TRequest : new() { private RestDeserialiseMethod m_method; @@ -48,7 +48,7 @@ namespace OpenSim.Framework.Servers.HttpServer m_method = method; } - public void Handle(string path, Stream request, Stream responseStream, + protected override void ProcessRequest(string path, Stream request, Stream responseStream, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { TRequest deserial; diff --git a/OpenSim/Framework/Servers/HttpServer/RestObjectPoster.cs b/OpenSim/Framework/Servers/HttpServer/RestObjectPoster.cs index 48ced19..afe052f 100644 --- a/OpenSim/Framework/Servers/HttpServer/RestObjectPoster.cs +++ b/OpenSim/Framework/Servers/HttpServer/RestObjectPoster.cs @@ -52,23 +52,25 @@ namespace OpenSim.Framework.Servers.HttpServer request.Method = verb; request.ContentType = "text/xml"; - MemoryStream buffer = new MemoryStream(); + using (MemoryStream buffer = new MemoryStream()) + { + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Encoding = Encoding.UTF8; - XmlWriterSettings settings = new XmlWriterSettings(); - settings.Encoding = Encoding.UTF8; + using (XmlWriter writer = XmlWriter.Create(buffer, settings)) + { + XmlSerializer serializer = new XmlSerializer(type); + serializer.Serialize(writer, obj); + writer.Flush(); + } - using (XmlWriter writer = XmlWriter.Create(buffer, settings)) - { - XmlSerializer serializer = new XmlSerializer(type); - serializer.Serialize(writer, obj); - writer.Flush(); - } + int length = (int)buffer.Length; + request.ContentLength = length; - int length = (int) buffer.Length; - request.ContentLength = length; + using (Stream requestStream = request.GetRequestStream()) + requestStream.Write(buffer.ToArray(), 0, length); + } - Stream requestStream = request.GetRequestStream(); - requestStream.Write(buffer.ToArray(), 0, length); // IAsyncResult result = request.BeginGetResponse(AsyncCallback, request); request.BeginGetResponse(AsyncCallback, request); } diff --git a/OpenSim/Framework/Servers/HttpServer/RestObjectPosterResponse.cs b/OpenSim/Framework/Servers/HttpServer/RestObjectPosterResponse.cs index 451745c..a911b9f 100644 --- a/OpenSim/Framework/Servers/HttpServer/RestObjectPosterResponse.cs +++ b/OpenSim/Framework/Servers/HttpServer/RestObjectPosterResponse.cs @@ -60,24 +60,25 @@ namespace OpenSim.Framework.Servers.HttpServer request.ContentType = "text/xml"; request.Timeout = 10000; - MemoryStream buffer = new MemoryStream(); + using (MemoryStream buffer = new MemoryStream()) + { + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Encoding = Encoding.UTF8; + + using (XmlWriter writer = XmlWriter.Create(buffer, settings)) + { + XmlSerializer serializer = new XmlSerializer(type); + serializer.Serialize(writer, obj); + writer.Flush(); + } - XmlWriterSettings settings = new XmlWriterSettings(); - settings.Encoding = Encoding.UTF8; + int length = (int)buffer.Length; + request.ContentLength = length; - using (XmlWriter writer = XmlWriter.Create(buffer, settings)) - { - XmlSerializer serializer = new XmlSerializer(type); - serializer.Serialize(writer, obj); - writer.Flush(); + using (Stream requestStream = request.GetRequestStream()) + requestStream.Write(buffer.ToArray(), 0, length); } - int length = (int) buffer.Length; - request.ContentLength = length; - - Stream requestStream = request.GetRequestStream(); - requestStream.Write(buffer.ToArray(), 0, length); - requestStream.Close(); // IAsyncResult result = request.BeginGetResponse(AsyncCallback, request); request.BeginGetResponse(AsyncCallback, request); } diff --git a/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs b/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs index 19c03a8..ad69cd2 100644 --- a/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs +++ b/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs @@ -77,44 +77,34 @@ namespace OpenSim.Framework.Servers.HttpServer request.ContentType = "text/xml"; request.Timeout = 20000; - MemoryStream buffer = new MemoryStream(); - - XmlWriterSettings settings = new XmlWriterSettings(); - settings.Encoding = Encoding.UTF8; - - using (XmlWriter writer = XmlWriter.Create(buffer, settings)) + using (MemoryStream buffer = new MemoryStream()) { - XmlSerializer serializer = new XmlSerializer(type); - serializer.Serialize(writer, sobj); - writer.Flush(); - } + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Encoding = Encoding.UTF8; - int length = (int)buffer.Length; - request.ContentLength = length; + using (XmlWriter writer = XmlWriter.Create(buffer, settings)) + { + XmlSerializer serializer = new XmlSerializer(type); + serializer.Serialize(writer, sobj); + writer.Flush(); + } - Stream requestStream = request.GetRequestStream(); - requestStream.Write(buffer.ToArray(), 0, length); - buffer.Close(); - requestStream.Close(); + int length = (int)buffer.Length; + request.ContentLength = length; + + using (Stream requestStream = request.GetRequestStream()) + requestStream.Write(buffer.ToArray(), 0, length); + } TResponse deserial = default(TResponse); using (WebResponse resp = request.GetResponse()) { XmlSerializer deserializer = new XmlSerializer(typeof(TResponse)); - Stream respStream = null; - try - { - respStream = resp.GetResponseStream(); + + using (Stream respStream = resp.GetResponseStream()) deserial = (TResponse)deserializer.Deserialize(respStream); - } - catch { } - finally - { - if (respStream != null) - respStream.Close(); - resp.Close(); - } } + return deserial; } } @@ -142,25 +132,25 @@ namespace OpenSim.Framework.Servers.HttpServer request.ContentType = "text/xml"; request.Timeout = 10000; - MemoryStream buffer = new MemoryStream(); + using (MemoryStream buffer = new MemoryStream()) + { + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Encoding = Encoding.UTF8; - XmlWriterSettings settings = new XmlWriterSettings(); - settings.Encoding = Encoding.UTF8; + using (XmlWriter writer = XmlWriter.Create(buffer, settings)) + { + XmlSerializer serializer = new XmlSerializer(type); + serializer.Serialize(writer, sobj); + writer.Flush(); + } - using (XmlWriter writer = XmlWriter.Create(buffer, settings)) - { - XmlSerializer serializer = new XmlSerializer(type); - serializer.Serialize(writer, sobj); - writer.Flush(); - } - buffer.Close(); + int length = (int)buffer.Length; + request.ContentLength = length; - int length = (int)buffer.Length; - request.ContentLength = length; + using (Stream requestStream = request.GetRequestStream()) + requestStream.Write(buffer.ToArray(), 0, length); + } - Stream requestStream = request.GetRequestStream(); - requestStream.Write(buffer.ToArray(), 0, length); - requestStream.Close(); // IAsyncResult result = request.BeginGetResponse(AsyncCallback, request); request.BeginGetResponse(AsyncCallback, request); } @@ -192,7 +182,7 @@ namespace OpenSim.Framework.Servers.HttpServer public delegate bool CheckIdentityMethod(string sid, string aid); - public class RestDeserialiseSecureHandler : BaseRequestHandler, IStreamHandler + public class RestDeserialiseSecureHandler : BaseOutputStreamHandler, IStreamHandler where TRequest : new() { private static readonly ILog m_log @@ -210,7 +200,7 @@ namespace OpenSim.Framework.Servers.HttpServer m_method = method; } - public void Handle(string path, Stream request, Stream responseStream, + protected override void ProcessRequest(string path, Stream request, Stream responseStream, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { RestSessionObject deserial = default(RestSessionObject); @@ -246,7 +236,7 @@ namespace OpenSim.Framework.Servers.HttpServer public delegate bool CheckTrustedSourceMethod(IPEndPoint peer); - public class RestDeserialiseTrustedHandler : BaseRequestHandler, IStreamHandler + public class RestDeserialiseTrustedHandler : BaseOutputStreamHandler, IStreamHandler where TRequest : new() { private static readonly ILog m_log @@ -269,7 +259,7 @@ namespace OpenSim.Framework.Servers.HttpServer m_method = method; } - public void Handle(string path, Stream request, Stream responseStream, + protected override void ProcessRequest(string path, Stream request, Stream responseStream, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { TRequest deserial = default(TRequest); @@ -301,6 +291,5 @@ namespace OpenSim.Framework.Servers.HttpServer serializer.Serialize(xmlWriter, response); } } - } - -} + } +} \ No newline at end of file diff --git a/OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs index 1f17fee..0305dee 100644 --- a/OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs +++ b/OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs @@ -48,7 +48,7 @@ namespace OpenSim.Framework.Servers.HttpServer m_restMethod = restMethod; } - public override byte[] Handle(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) + protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { Encoding encoding = Encoding.UTF8; StreamReader streamReader = new StreamReader(request, encoding); diff --git a/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs b/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs new file mode 100644 index 0000000..c2925e3 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs @@ -0,0 +1,1159 @@ +/* + * 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 OpenSimulator 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.Security.Cryptography; +using System.Text; +using System.Threading; +using HttpServer; + +namespace OpenSim.Framework.Servers.HttpServer +{ + // Sealed class. If you're going to unseal it, implement IDisposable. + /// + /// This class implements websockets. It grabs the network context from C#Webserver and utilizes it directly as a tcp streaming service + /// + public sealed class WebSocketHttpServerHandler : BaseRequestHandler + { + + private class WebSocketState + { + public List ReceivedBytes; + public int ExpectedBytes; + public WebsocketFrameHeader Header; + public bool FrameComplete; + public WebSocketFrame ContinuationFrame; + } + + /// + /// Binary Data will trigger this event + /// + public event DataDelegate OnData; + + /// + /// Textual Data will trigger this event + /// + public event TextDelegate OnText; + + /// + /// A ping request form the other side will trigger this event. + /// This class responds to the ping automatically. You shouldn't send a pong. + /// it's informational. + /// + public event PingDelegate OnPing; + + /// + /// This is a response to a ping you sent. + /// + public event PongDelegate OnPong; + + /// + /// This is a regular HTTP Request... This may be removed in the future. + /// +// public event RegularHttpRequestDelegate OnRegularHttpRequest; + + /// + /// When the upgrade from a HTTP request to a Websocket is completed, this will be fired + /// + public event UpgradeCompletedDelegate OnUpgradeCompleted; + + /// + /// If the upgrade failed, this will be fired + /// + public event UpgradeFailedDelegate OnUpgradeFailed; + + /// + /// When the websocket is closed, this will be fired. + /// + public event CloseDelegate OnClose; + + /// + /// Set this delegate to allow your module to validate the origin of the + /// Websocket request. Primary line of defense against cross site scripting + /// + public ValidateHandshake HandshakeValidateMethodOverride = null; + + private ManualResetEvent _receiveDone = new ManualResetEvent(false); + + private OSHttpRequest _request; + private HTTPNetworkContext _networkContext; + private IHttpClientContext _clientContext; + + private int _pingtime = 0; + private byte[] _buffer; + private int _bufferPosition; + private int _bufferLength; + private bool _closing; + private bool _upgraded; + private int _maxPayloadBytes = 41943040; + private int _initialMsgTimeout = 0; + private int _defaultReadTimeout = 10000; + + private const string HandshakeAcceptText = + "HTTP/1.1 101 Switching Protocols\r\n" + + "upgrade: websocket\r\n" + + "Connection: Upgrade\r\n" + + "sec-websocket-accept: {0}\r\n\r\n";// + + //"{1}"; + + private const string HandshakeDeclineText = + "HTTP/1.1 {0} {1}\r\n" + + "Connection: close\r\n\r\n"; + + /// + /// Mysterious constant defined in RFC6455 to append to the client provided security key + /// + private const string WebsocketHandshakeAcceptHashConstant = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + + public WebSocketHttpServerHandler(OSHttpRequest preq, IHttpClientContext pContext, int bufferlen) + : base(preq.HttpMethod, preq.Url.OriginalString) + { + _request = preq; + _networkContext = pContext.GiveMeTheNetworkStreamIKnowWhatImDoing(); + _networkContext.Stream.ReadTimeout = _defaultReadTimeout; + _clientContext = pContext; + _bufferLength = bufferlen; + _buffer = new byte[_bufferLength]; + } + + // Sealed class implments destructor and an internal dispose method. complies with C# unmanaged resource best practices. + ~WebSocketHttpServerHandler() + { + Dispose(); + + } + + /// + /// Sets the length of the stream buffer + /// + /// Byte length. + public void SetChunksize(int pChunk) + { + if (!_upgraded) + { + _buffer = new byte[pChunk]; + } + else + { + throw new InvalidOperationException("You must set the chunksize before the connection is upgraded"); + } + } + + /// + /// This is the famous nagle. + /// + public bool NoDelay_TCP_Nagle + { + get + { + if (_networkContext != null && _networkContext.Socket != null) + { + return _networkContext.Socket.NoDelay; + } + else + { + throw new InvalidOperationException("The socket has been shutdown"); + } + } + set + { + if (_networkContext != null && _networkContext.Socket != null) + _networkContext.Socket.NoDelay = value; + else + { + throw new InvalidOperationException("The socket has been shutdown"); + } + } + } + + /// + /// This triggers the websocket to start the upgrade process... + /// This is a Generalized Networking 'common sense' helper method. Some people expect to call Start() instead + /// of the more context appropriate HandshakeAndUpgrade() + /// + public void Start() + { + HandshakeAndUpgrade(); + } + + /// + /// Max Payload Size in bytes. Defaults to 40MB, but could be set upon connection before calling handshake and upgrade. + /// + public int MaxPayloadSize + { + get { return _maxPayloadBytes; } + set { _maxPayloadBytes = value; } + } + + /// + /// Set this to the maximum amount of milliseconds to wait for the first complete message to be sent or received on the websocket after upgrading + /// Default, it waits forever. Use this when your Websocket consuming code opens a connection and waits for a message from the other side to avoid a Denial of Service vector. + /// + public int InitialMsgTimeout + { + get { return _initialMsgTimeout; } + set { _initialMsgTimeout = value; } + } + + /// + /// This triggers the websocket start the upgrade process + /// + public void HandshakeAndUpgrade() + { + string webOrigin = string.Empty; + string websocketKey = string.Empty; + string acceptKey = string.Empty; + string accepthost = string.Empty; + if (!string.IsNullOrEmpty(_request.Headers["origin"])) + webOrigin = _request.Headers["origin"]; + + if (!string.IsNullOrEmpty(_request.Headers["sec-websocket-key"])) + websocketKey = _request.Headers["sec-websocket-key"]; + + if (!string.IsNullOrEmpty(_request.Headers["host"])) + accepthost = _request.Headers["host"]; + + if (string.IsNullOrEmpty(_request.Headers["upgrade"])) + { + FailUpgrade(OSHttpStatusCode.ClientErrorBadRequest, "no upgrade request submitted"); + } + + string connectionheader = _request.Headers["upgrade"]; + if (connectionheader.ToLower() != "websocket") + { + FailUpgrade(OSHttpStatusCode.ClientErrorBadRequest, "no connection upgrade request submitted"); + } + + // If the object consumer provided a method to validate the origin, we should call it and give the client a success or fail. + // If not.. we should accept any. The assumption here is that there would be no Websocket handlers registered in baseHTTPServer unless + // Something asked for it... + if (HandshakeValidateMethodOverride != null) + { + if (HandshakeValidateMethodOverride(webOrigin, websocketKey, accepthost)) + { + acceptKey = GenerateAcceptKey(websocketKey); + string rawaccept = string.Format(HandshakeAcceptText, acceptKey); + SendUpgradeSuccess(rawaccept); + + + } + else + { + FailUpgrade(OSHttpStatusCode.ClientErrorForbidden, "Origin Validation Failed"); + } + } + else + { + acceptKey = GenerateAcceptKey(websocketKey); + string rawaccept = string.Format(HandshakeAcceptText, acceptKey); + SendUpgradeSuccess(rawaccept); + } + } + public IPEndPoint GetRemoteIPEndpoint() + { + return _request.RemoteIPEndPoint; + } + + /// + /// Generates a handshake response key string based on the client's + /// provided key to prove to the client that we're allowing the Websocket + /// upgrade of our own free will and we were not coerced into doing it. + /// + /// Client provided security key + /// + private static string GenerateAcceptKey(string key) + { + if (string.IsNullOrEmpty(key)) + return string.Empty; + + string acceptkey = key + WebsocketHandshakeAcceptHashConstant; + + SHA1 hashobj = SHA1.Create(); + string ret = Convert.ToBase64String(hashobj.ComputeHash(Encoding.UTF8.GetBytes(acceptkey))); + hashobj.Clear(); + + return ret; + } + + /// + /// Informs the otherside that we accepted their upgrade request + /// + /// The HTTP 1.1 101 response that says Yay \o/ + private void SendUpgradeSuccess(string pHandshakeResponse) + { + // Create a new websocket state so we can keep track of data in between network reads. + WebSocketState socketState = new WebSocketState() { ReceivedBytes = new List(), Header = WebsocketFrameHeader.HeaderDefault(), FrameComplete = true}; + + byte[] bhandshakeResponse = Encoding.UTF8.GetBytes(pHandshakeResponse); + + + + + try + { + if (_initialMsgTimeout > 0) + { + _receiveDone.Reset(); + } + // Begin reading the TCP stream before writing the Upgrade success message to the other side of the stream. + _networkContext.Stream.BeginRead(_buffer, 0, _bufferLength, OnReceive, socketState); + + // Write the upgrade handshake success message + _networkContext.Stream.Write(bhandshakeResponse, 0, bhandshakeResponse.Length); + _networkContext.Stream.Flush(); + _upgraded = true; + UpgradeCompletedDelegate d = OnUpgradeCompleted; + if (d != null) + d(this, new UpgradeCompletedEventArgs()); + if (_initialMsgTimeout > 0) + { + if (!_receiveDone.WaitOne(TimeSpan.FromMilliseconds(_initialMsgTimeout))) + Close(string.Empty); + } + } + catch (IOException) + { + Close(string.Empty); + } + catch (ObjectDisposedException) + { + Close(string.Empty); + } + } + + /// + /// The server has decided not to allow the upgrade to a websocket for some reason. The Http 1.1 response that says Nay >:( + /// + /// HTTP Status reflecting the reason why + /// Textual reason for the upgrade fail + private void FailUpgrade(OSHttpStatusCode pCode, string pMessage ) + { + string handshakeResponse = string.Format(HandshakeDeclineText, (int)pCode, pMessage.Replace("\n", string.Empty).Replace("\r", string.Empty)); + byte[] bhandshakeResponse = Encoding.UTF8.GetBytes(handshakeResponse); + _networkContext.Stream.Write(bhandshakeResponse, 0, bhandshakeResponse.Length); + _networkContext.Stream.Flush(); + _networkContext.Stream.Dispose(); + + UpgradeFailedDelegate d = OnUpgradeFailed; + if (d != null) + d(this,new UpgradeFailedEventArgs()); + } + + + /// + /// This is our ugly Async OnReceive event handler. + /// This chunks the input stream based on the length of the provided buffer and processes out + /// as many frames as it can. It then moves the unprocessed data to the beginning of the buffer. + /// + /// Our Async State from beginread + private void OnReceive(IAsyncResult ar) + { + WebSocketState _socketState = ar.AsyncState as WebSocketState; + try + { + int bytesRead = _networkContext.Stream.EndRead(ar); + if (bytesRead == 0) + { + // Do Disconnect + _networkContext.Stream.Dispose(); + _networkContext = null; + return; + } + _bufferPosition += bytesRead; + + if (_bufferPosition > _bufferLength) + { + // Message too big for chunksize.. not sure how this happened... + //Close(string.Empty); + } + + int offset = 0; + bool headerread = true; + int headerforwardposition = 0; + while (headerread && offset < bytesRead) + { + if (_socketState.FrameComplete) + { + WebsocketFrameHeader pheader = WebsocketFrameHeader.ZeroHeader; + + headerread = WebSocketReader.TryReadHeader(_buffer, offset, _bufferPosition - offset, out pheader, + out headerforwardposition); + offset += headerforwardposition; + + if (headerread) + { + _socketState.FrameComplete = false; + if (pheader.PayloadLen > (ulong) _maxPayloadBytes) + { + Close("Invalid Payload size"); + + return; + } + if (pheader.PayloadLen > 0) + { + if ((int) pheader.PayloadLen > _bufferPosition - offset) + { + byte[] writebytes = new byte[_bufferPosition - offset]; + + Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) _bufferPosition - offset); + _socketState.ExpectedBytes = (int) pheader.PayloadLen; + _socketState.ReceivedBytes.AddRange(writebytes); + _socketState.Header = pheader; // We need to add the header so that we can unmask it + offset += (int) _bufferPosition - offset; + } + else + { + byte[] writebytes = new byte[pheader.PayloadLen]; + Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) pheader.PayloadLen); + WebSocketReader.Mask(pheader.Mask, writebytes); + pheader.IsMasked = false; + _socketState.FrameComplete = true; + _socketState.ReceivedBytes.AddRange(writebytes); + _socketState.Header = pheader; + offset += (int) pheader.PayloadLen; + } + } + else + { + pheader.Mask = 0; + _socketState.FrameComplete = true; + _socketState.Header = pheader; + } + + if (_socketState.FrameComplete) + { + ProcessFrame(_socketState); + _socketState.Header.SetDefault(); + _socketState.ReceivedBytes.Clear(); + _socketState.ExpectedBytes = 0; + + } + } + } + else + { + WebsocketFrameHeader frameHeader = _socketState.Header; + int bytesleft = _socketState.ExpectedBytes - _socketState.ReceivedBytes.Count; + + if (bytesleft > _bufferPosition) + { + byte[] writebytes = new byte[_bufferPosition]; + + Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) _bufferPosition); + _socketState.ReceivedBytes.AddRange(writebytes); + _socketState.Header = frameHeader; // We need to add the header so that we can unmask it + offset += (int) _bufferPosition; + } + else + { + byte[] writebytes = new byte[_bufferPosition]; + Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) _bufferPosition); + _socketState.FrameComplete = true; + _socketState.ReceivedBytes.AddRange(writebytes); + _socketState.Header = frameHeader; + offset += (int) _bufferPosition; + } + if (_socketState.FrameComplete) + { + ProcessFrame(_socketState); + _socketState.Header.SetDefault(); + _socketState.ReceivedBytes.Clear(); + _socketState.ExpectedBytes = 0; + // do some processing + } + } + } + if (offset > 0) + { + // If the buffer is maxed out.. we can just move the cursor. Nothing to move to the beginning. + if (offset <_buffer.Length) + Buffer.BlockCopy(_buffer, offset, _buffer, 0, _bufferPosition - offset); + _bufferPosition -= offset; + } + if (_networkContext.Stream != null && _networkContext.Stream.CanRead && !_closing) + { + _networkContext.Stream.BeginRead(_buffer, _bufferPosition, _bufferLength - _bufferPosition, OnReceive, + _socketState); + } + else + { + // We can't read the stream anymore... + } + } + catch (IOException) + { + Close(string.Empty); + } + catch (ObjectDisposedException) + { + Close(string.Empty); + } + } + + /// + /// Sends a string to the other side + /// + /// the string message that is to be sent + public void SendMessage(string message) + { + if (_initialMsgTimeout > 0) + { + _receiveDone.Set(); + _initialMsgTimeout = 0; + } + byte[] messagedata = Encoding.UTF8.GetBytes(message); + WebSocketFrame textMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = messagedata }; + textMessageFrame.Header.Opcode = WebSocketReader.OpCode.Text; + textMessageFrame.Header.IsEnd = true; + SendSocket(textMessageFrame.ToBytes()); + + } + + public void SendData(byte[] data) + { + if (_initialMsgTimeout > 0) + { + _receiveDone.Set(); + _initialMsgTimeout = 0; + } + WebSocketFrame dataMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = data}; + dataMessageFrame.Header.IsEnd = true; + dataMessageFrame.Header.Opcode = WebSocketReader.OpCode.Binary; + SendSocket(dataMessageFrame.ToBytes()); + + } + + /// + /// Writes raw bytes to the websocket. Unframed data will cause disconnection + /// + /// + private void SendSocket(byte[] data) + { + if (!_closing) + { + try + { + + _networkContext.Stream.Write(data, 0, data.Length); + } + catch (IOException) + { + + } + } + } + + /// + /// Sends a Ping check to the other side. The other side SHOULD respond as soon as possible with a pong frame. This interleaves with incoming fragmented frames. + /// + public void SendPingCheck() + { + if (_initialMsgTimeout > 0) + { + _receiveDone.Set(); + _initialMsgTimeout = 0; + } + WebSocketFrame pingFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = new byte[0] }; + pingFrame.Header.Opcode = WebSocketReader.OpCode.Ping; + pingFrame.Header.IsEnd = true; + _pingtime = Util.EnvironmentTickCount(); + SendSocket(pingFrame.ToBytes()); + } + + /// + /// Closes the websocket connection. Sends a close message to the other side if it hasn't already done so. + /// + /// + public void Close(string message) + { + if (_initialMsgTimeout > 0) + { + _receiveDone.Set(); + _initialMsgTimeout = 0; + } + if (_networkContext == null) + return; + if (_networkContext.Stream != null) + { + if (_networkContext.Stream.CanWrite) + { + byte[] messagedata = Encoding.UTF8.GetBytes(message); + WebSocketFrame closeResponseFrame = new WebSocketFrame() + { + Header = WebsocketFrameHeader.HeaderDefault(), + WebSocketPayload = messagedata + }; + closeResponseFrame.Header.Opcode = WebSocketReader.OpCode.Close; + closeResponseFrame.Header.PayloadLen = (ulong) messagedata.Length; + closeResponseFrame.Header.IsEnd = true; + SendSocket(closeResponseFrame.ToBytes()); + } + } + CloseDelegate closeD = OnClose; + if (closeD != null) + { + closeD(this, new CloseEventArgs()); + } + + _closing = true; + } + + /// + /// Processes a websocket frame and triggers consumer events + /// + /// We need to modify the websocket state here depending on the frame + private void ProcessFrame(WebSocketState psocketState) + { + if (psocketState.Header.IsMasked) + { + byte[] unmask = psocketState.ReceivedBytes.ToArray(); + WebSocketReader.Mask(psocketState.Header.Mask, unmask); + psocketState.ReceivedBytes = new List(unmask); + } + if (psocketState.Header.Opcode != WebSocketReader.OpCode.Continue && _initialMsgTimeout > 0) + { + _receiveDone.Set(); + _initialMsgTimeout = 0; + } + switch (psocketState.Header.Opcode) + { + case WebSocketReader.OpCode.Ping: + PingDelegate pingD = OnPing; + if (pingD != null) + { + pingD(this, new PingEventArgs()); + } + + WebSocketFrame pongFrame = new WebSocketFrame(){Header = WebsocketFrameHeader.HeaderDefault(),WebSocketPayload = new byte[0]}; + pongFrame.Header.Opcode = WebSocketReader.OpCode.Pong; + pongFrame.Header.IsEnd = true; + SendSocket(pongFrame.ToBytes()); + break; + case WebSocketReader.OpCode.Pong: + + PongDelegate pongD = OnPong; + if (pongD != null) + { + pongD(this, new PongEventArgs(){PingResponseMS = Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(),_pingtime)}); + } + break; + case WebSocketReader.OpCode.Binary: + if (!psocketState.Header.IsEnd) // Not done, so we need to store this and wait for the end frame. + { + psocketState.ContinuationFrame = new WebSocketFrame + { + Header = psocketState.Header, + WebSocketPayload = + psocketState.ReceivedBytes.ToArray() + }; + } + else + { + // Send Done Event! + DataDelegate dataD = OnData; + if (dataD != null) + { + dataD(this,new WebsocketDataEventArgs(){Data = psocketState.ReceivedBytes.ToArray()}); + } + } + break; + case WebSocketReader.OpCode.Text: + if (!psocketState.Header.IsEnd) // Not done, so we need to store this and wait for the end frame. + { + psocketState.ContinuationFrame = new WebSocketFrame + { + Header = psocketState.Header, + WebSocketPayload = + psocketState.ReceivedBytes.ToArray() + }; + } + else + { + TextDelegate textD = OnText; + if (textD != null) + { + textD(this, new WebsocketTextEventArgs() { Data = Encoding.UTF8.GetString(psocketState.ReceivedBytes.ToArray()) }); + } + + // Send Done Event! + } + break; + case WebSocketReader.OpCode.Continue: // Continuation. Multiple frames worth of data for one message. Only valid when not using Control Opcodes + //Console.WriteLine("currhead " + psocketState.Header.IsEnd); + //Console.WriteLine("Continuation! " + psocketState.ContinuationFrame.Header.IsEnd); + byte[] combineddata = new byte[psocketState.ReceivedBytes.Count+psocketState.ContinuationFrame.WebSocketPayload.Length]; + byte[] newdata = psocketState.ReceivedBytes.ToArray(); + Buffer.BlockCopy(psocketState.ContinuationFrame.WebSocketPayload, 0, combineddata, 0, psocketState.ContinuationFrame.WebSocketPayload.Length); + Buffer.BlockCopy(newdata, 0, combineddata, + psocketState.ContinuationFrame.WebSocketPayload.Length, newdata.Length); + psocketState.ContinuationFrame.WebSocketPayload = combineddata; + psocketState.Header.PayloadLen = (ulong)combineddata.Length; + if (psocketState.Header.IsEnd) + { + if (psocketState.ContinuationFrame.Header.Opcode == WebSocketReader.OpCode.Text) + { + // Send Done event + TextDelegate textD = OnText; + if (textD != null) + { + textD(this, new WebsocketTextEventArgs() { Data = Encoding.UTF8.GetString(combineddata) }); + } + } + else if (psocketState.ContinuationFrame.Header.Opcode == WebSocketReader.OpCode.Binary) + { + // Send Done event + DataDelegate dataD = OnData; + if (dataD != null) + { + dataD(this, new WebsocketDataEventArgs() { Data = combineddata }); + } + } + else + { + // protocol violation + } + psocketState.ContinuationFrame = null; + } + break; + case WebSocketReader.OpCode.Close: + Close(string.Empty); + + break; + + } + psocketState.Header.SetDefault(); + psocketState.ReceivedBytes.Clear(); + psocketState.ExpectedBytes = 0; + } + public void Dispose() + { + if (_initialMsgTimeout > 0) + { + _receiveDone.Set(); + _initialMsgTimeout = 0; + } + if (_networkContext != null && _networkContext.Stream != null) + { + if (_networkContext.Stream.CanWrite) + _networkContext.Stream.Flush(); + _networkContext.Stream.Close(); + _networkContext.Stream.Dispose(); + _networkContext.Stream = null; + } + + if (_request != null && _request.InputStream != null) + { + _request.InputStream.Close(); + _request.InputStream.Dispose(); + _request = null; + } + + if (_clientContext != null) + { + _clientContext.Close(); + _clientContext = null; + } + } + } + + /// + /// Reads a byte stream and returns Websocket frames. + /// + public class WebSocketReader + { + /// + /// Bit to determine if the frame read on the stream is the last frame in a sequence of fragmented frames + /// + private const byte EndBit = 0x80; + + /// + /// These are the Frame Opcodes + /// + public enum OpCode + { + // Data Opcodes + Continue = 0x0, + Text = 0x1, + Binary = 0x2, + + // Control flow Opcodes + Close = 0x8, + Ping = 0x9, + Pong = 0xA + } + + /// + /// Masks and Unmasks data using the frame mask. Mask is applied per octal + /// Note: Frames from clients MUST be masked + /// Note: Frames from servers MUST NOT be masked + /// + /// Int representing 32 bytes of mask data. Mask is applied per octal + /// + public static void Mask(int pMask, byte[] pBuffer) + { + byte[] maskKey = BitConverter.GetBytes(pMask); + int currentMaskIndex = 0; + for (int i = 0; i < pBuffer.Length; i++) + { + pBuffer[i] = (byte)(pBuffer[i] ^ maskKey[currentMaskIndex]); + if (currentMaskIndex == 3) + { + currentMaskIndex = 0; + } + else + { + currentMaskIndex++; + + } + + } + } + + /// + /// Attempts to read a header off the provided buffer. Returns true, exports a WebSocketFrameheader, + /// and an int to move the buffer forward when it reads a header. False when it can't read a header + /// + /// Bytes read from the stream + /// Starting place in the stream to begin trying to read from + /// Lenth in the stream to try and read from. Provided for cases where the + /// buffer's length is larger then the data in it + /// Outputs the read WebSocket frame header + /// Informs the calling stream to move the buffer forward + /// True if it got a header, False if it didn't get a header + public static bool TryReadHeader(byte[] pBuffer, int pOffset, int length, out WebsocketFrameHeader oHeader, + out int moveBuffer) + { + oHeader = WebsocketFrameHeader.ZeroHeader; + int minumheadersize = 2; + if (length > pBuffer.Length - pOffset) + throw new ArgumentOutOfRangeException("The Length specified was larger the byte array supplied"); + if (length < minumheadersize) + { + moveBuffer = 0; + return false; + } + + byte nibble1 = (byte)(pBuffer[pOffset] & 0xF0); //FIN/RSV1/RSV2/RSV3 + byte nibble2 = (byte)(pBuffer[pOffset] & 0x0F); // Opcode block + + oHeader = new WebsocketFrameHeader(); + oHeader.SetDefault(); + + if ((nibble1 & WebSocketReader.EndBit) == WebSocketReader.EndBit) + { + oHeader.IsEnd = true; + } + else + { + oHeader.IsEnd = false; + } + //Opcode + oHeader.Opcode = (WebSocketReader.OpCode)nibble2; + //Mask + oHeader.IsMasked = Convert.ToBoolean((pBuffer[pOffset + 1] & 0x80) >> 7); + + // Payload length + oHeader.PayloadLen = (byte)(pBuffer[pOffset + 1] & 0x7F); + + int index = 2; // LargerPayload length starts at byte 3 + + switch (oHeader.PayloadLen) + { + case 126: + minumheadersize += 2; + if (length < minumheadersize) + { + moveBuffer = 0; + return false; + } + Array.Reverse(pBuffer, pOffset + index, 2); // two bytes + oHeader.PayloadLen = BitConverter.ToUInt16(pBuffer, pOffset + index); + index += 2; + break; + case 127: // we got more this is a bigger frame + // 8 bytes - uint64 - most significant bit 0 network byte order + minumheadersize += 8; + if (length < minumheadersize) + { + moveBuffer = 0; + return false; + } + Array.Reverse(pBuffer, pOffset + index, 8); + oHeader.PayloadLen = BitConverter.ToUInt64(pBuffer, pOffset + index); + index += 8; + break; + + } + //oHeader.PayloadLeft = oHeader.PayloadLen; // Start the count in case it's chunked over the network. This is different then frame fragmentation + if (oHeader.IsMasked) + { + minumheadersize += 4; + if (length < minumheadersize) + { + moveBuffer = 0; + return false; + } + oHeader.Mask = BitConverter.ToInt32(pBuffer, pOffset + index); + index += 4; + } + moveBuffer = index; + return true; + + } + } + + /// + /// RFC6455 Websocket Frame + /// + public class WebSocketFrame + { + /* + * RFC6455 +nib 0 1 2 3 4 5 6 7 +byt 0 1 2 3 +dec 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-------+-+-------------+-------------------------------+ + |F|R|R|R| opcode|M| Payload len | Extended payload length | + |I|S|S|S| (4) |A| (7) | (16/64) + + |N|V|V|V| |S| | (if payload len==126/127) | + | |1|2|3| |K| | + + +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + + | Extended payload length continued, if payload len == 127 | + + - - - - - - - - - - - - - - - +-------------------------------+ + | |Masking-key, if MASK set to 1 | + +-------------------------------+-------------------------------+ + | Masking-key (continued) | Payload Data | + +-------------------------------- - - - - - - - - - - - - - - - + + : Payload Data continued ... : + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + | Payload Data continued ... | + +---------------------------------------------------------------+ + + * When reading these, the frames are possibly fragmented and interleaved with control frames + * the fragmented frames are not interleaved with data frames. Just control frames + */ + public static readonly WebSocketFrame DefaultFrame = new WebSocketFrame(){Header = new WebsocketFrameHeader(),WebSocketPayload = new byte[0]}; + public WebsocketFrameHeader Header; + public byte[] WebSocketPayload; + + public byte[] ToBytes() + { + Header.PayloadLen = (ulong)WebSocketPayload.Length; + return Header.ToBytes(WebSocketPayload); + } + + } + + public struct WebsocketFrameHeader + { + //public byte CurrentMaskIndex; + /// + /// The last frame in a sequence of fragmented frames or the one and only frame for this message. + /// + public bool IsEnd; + + /// + /// Returns whether the payload data is masked or not. Data from Clients MUST be masked, Data from Servers MUST NOT be masked + /// + public bool IsMasked; + + /// + /// A set of cryptologically sound random bytes XoR-ed against the payload octally. Looped + /// + public int Mask; + /* +byt 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +---------------+---------------+---------------+---------------+ + | Octal 1 | Octal 2 | Octal 3 | Octal 4 | + +---------------+---------------+---------------+---------------+ +*/ + + + public WebSocketReader.OpCode Opcode; + + public UInt64 PayloadLen; + //public UInt64 PayloadLeft; + // Payload is X + Y + //public UInt64 ExtensionDataLength; + //public UInt64 ApplicationDataLength; + public static readonly WebsocketFrameHeader ZeroHeader = WebsocketFrameHeader.HeaderDefault(); + + public void SetDefault() + { + + //CurrentMaskIndex = 0; + IsEnd = true; + IsMasked = true; + Mask = 0; + Opcode = WebSocketReader.OpCode.Close; + // PayloadLeft = 0; + PayloadLen = 0; + // ExtensionDataLength = 0; + // ApplicationDataLength = 0; + + } + + /// + /// Returns a byte array representing the Frame header + /// + /// This is the frame data payload. The header describes the size of the payload. + /// If payload is null, a Zero sized payload is assumed + /// Returns a byte array representing the frame header + public byte[] ToBytes(byte[] payload) + { + List result = new List(); + + // Squeeze in our opcode and our ending bit. + result.Add((byte)((byte)Opcode | (IsEnd?0x80:0x00) )); + + // Again with the three different byte interpretations of size.. + + //bytesize + if (PayloadLen <= 125) + { + result.Add((byte) PayloadLen); + } //Uint16 + else if (PayloadLen <= ushort.MaxValue) + { + result.Add(126); + byte[] payloadLengthByte = BitConverter.GetBytes(Convert.ToUInt16(PayloadLen)); + Array.Reverse(payloadLengthByte); + result.AddRange(payloadLengthByte); + } //UInt64 + else + { + result.Add(127); + byte[] payloadLengthByte = BitConverter.GetBytes(PayloadLen); + Array.Reverse(payloadLengthByte); + result.AddRange(payloadLengthByte); + } + + // Only add a payload if it's not null + if (payload != null) + { + result.AddRange(payload); + } + return result.ToArray(); + } + + /// + /// A Helper method to define the defaults + /// + /// + + public static WebsocketFrameHeader HeaderDefault() + { + return new WebsocketFrameHeader + { + //CurrentMaskIndex = 0, + IsEnd = false, + IsMasked = true, + Mask = 0, + Opcode = WebSocketReader.OpCode.Close, + //PayloadLeft = 0, + PayloadLen = 0, + // ExtensionDataLength = 0, + // ApplicationDataLength = 0 + }; + } + } + + public delegate void DataDelegate(object sender, WebsocketDataEventArgs data); + + public delegate void TextDelegate(object sender, WebsocketTextEventArgs text); + + public delegate void PingDelegate(object sender, PingEventArgs pingdata); + + public delegate void PongDelegate(object sender, PongEventArgs pongdata); + + public delegate void RegularHttpRequestDelegate(object sender, RegularHttpRequestEvnetArgs request); + + public delegate void UpgradeCompletedDelegate(object sender, UpgradeCompletedEventArgs completeddata); + + public delegate void UpgradeFailedDelegate(object sender, UpgradeFailedEventArgs faileddata); + + public delegate void CloseDelegate(object sender, CloseEventArgs closedata); + + public delegate bool ValidateHandshake(string pWebOrigin, string pWebSocketKey, string pHost); + + + public class WebsocketDataEventArgs : EventArgs + { + public byte[] Data; + } + + public class WebsocketTextEventArgs : EventArgs + { + public string Data; + } + + public class PingEventArgs : EventArgs + { + /// + /// The ping event can arbitrarily contain data + /// + public byte[] Data; + } + + public class PongEventArgs : EventArgs + { + /// + /// The pong event can arbitrarily contain data + /// + public byte[] Data; + + public int PingResponseMS; + + } + + public class RegularHttpRequestEvnetArgs : EventArgs + { + + } + + public class UpgradeCompletedEventArgs : EventArgs + { + + } + + public class UpgradeFailedEventArgs : EventArgs + { + + } + + public class CloseEventArgs : EventArgs + { + + } + + +} diff --git a/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs new file mode 100644 index 0000000..f212208 --- /dev/null +++ b/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs @@ -0,0 +1,91 @@ +/* + * 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 OpenSimulator 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.Net; +using Nwc.XmlRpc; +using OpenSim.Framework; + + +namespace OpenSim.Framework.Servers.HttpServer +{ + public class XmlRpcBasicDOSProtector + { + private readonly XmlRpcMethod _normalMethod; + private readonly XmlRpcMethod _throttledMethod; + + private readonly BasicDosProtectorOptions _options; + private readonly BasicDOSProtector _dosProtector; + + public XmlRpcBasicDOSProtector(XmlRpcMethod normalMethod, XmlRpcMethod throttledMethod,BasicDosProtectorOptions options) + { + _normalMethod = normalMethod; + _throttledMethod = throttledMethod; + + _options = options; + _dosProtector = new BasicDOSProtector(_options); + + } + public XmlRpcResponse Process(XmlRpcRequest request, IPEndPoint client) + { + + XmlRpcResponse resp = null; + string clientstring = GetClientString(request, client); + string endpoint = GetEndPoint(request, client); + if (_dosProtector.Process(clientstring, endpoint)) + resp = _normalMethod(request, client); + else + resp = _throttledMethod(request, client); + if (_options.MaxConcurrentSessions > 0) + _dosProtector.ProcessEnd(clientstring, endpoint); + return resp; + } + + private string GetClientString(XmlRpcRequest request, IPEndPoint client) + { + string clientstring; + if (_options.AllowXForwardedFor && request.Params.Count > 3) + { + object headerstr = request.Params[3]; + if (headerstr != null && !string.IsNullOrEmpty(headerstr.ToString())) + clientstring = request.Params[3].ToString(); + else + clientstring = client.Address.ToString(); + } + else + clientstring = client.Address.ToString(); + return clientstring; + } + + private string GetEndPoint(XmlRpcRequest request, IPEndPoint client) + { + return client.Address.ToString(); + } + + } + + +} diff --git a/OpenSim/Framework/Servers/MainServer.cs b/OpenSim/Framework/Servers/MainServer.cs index ae7d515..57931d4 100644 --- a/OpenSim/Framework/Servers/MainServer.cs +++ b/OpenSim/Framework/Servers/MainServer.cs @@ -121,12 +121,14 @@ namespace OpenSim.Framework.Servers + " level >= 2 then long warnings are logged when receiving bad input data.\n" + " level >= 3 then short notices about all incoming non-poll HTTP requests are logged.\n" + " level >= 4 then the time taken to fulfill the request is logged.\n" - + " level >= 5 then a sample from the beginning of the incoming data is logged.\n" - + " level >= 6 then the entire incoming data is logged.\n" + + " level >= 5 then a sample from the beginning of the data is logged.\n" + + " level >= 6 then the entire data is logged.\n" + " no level is specified then the current level is returned.\n\n" + "If out or all and\n" + " level >= 3 then short notices about all outgoing requests going through WebUtil are logged.\n" - + " level >= 4 then the time taken to fulfill the request is logged.\n", + + " level >= 4 then the time taken to fulfill the request is logged.\n" + + " level >= 5 then a sample from the beginning of the data is logged.\n" + + " level >= 6 then the entire data is logged.\n", HandleDebugHttpCommand); } @@ -227,9 +229,16 @@ namespace OpenSim.Framework.Servers handlers.AppendFormat("\t{0}\n", s); handlers.AppendFormat("* HTTP:\n"); - List poll = httpServer.GetPollServiceHandlerKeys(); foreach (String s in httpServer.GetHTTPHandlerKeys()) - handlers.AppendFormat("\t{0} {1}\n", s, (poll.Contains(s) ? "(poll service)" : string.Empty)); + handlers.AppendFormat("\t{0}\n", s); + + handlers.AppendFormat("* HTTP (poll):\n"); + foreach (String s in httpServer.GetPollServiceHandlerKeys()) + handlers.AppendFormat("\t{0}\n", s); + + handlers.AppendFormat("* JSONRPC:\n"); + foreach (String s in httpServer.GetJsonRpcHandlerKeys()) + handlers.AppendFormat("\t{0}\n", s); // handlers.AppendFormat("* Agent:\n"); // foreach (String s in httpServer.GetAgentHandlerKeys()) @@ -276,7 +285,12 @@ namespace OpenSim.Framework.Servers public static bool RemoveHttpServer(uint port) { lock (m_Servers) + { + if (instance != null && instance.Port == port) + instance = null; + return m_Servers.Remove(port); + } } /// diff --git a/OpenSim/Framework/Servers/Properties/AssemblyInfo.cs b/OpenSim/Framework/Servers/Properties/AssemblyInfo.cs index 021f63c..792c62e 100644 --- a/OpenSim/Framework/Servers/Properties/AssemblyInfo.cs +++ b/OpenSim/Framework/Servers/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("0.7.5.*")] +[assembly: AssemblyVersion("0.7.6.*")] [assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/OpenSim/Framework/Servers/ServerBase.cs b/OpenSim/Framework/Servers/ServerBase.cs index 47baac8..07a09e6 100644 --- a/OpenSim/Framework/Servers/ServerBase.cs +++ b/OpenSim/Framework/Servers/ServerBase.cs @@ -29,6 +29,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; +using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; @@ -62,6 +63,8 @@ namespace OpenSim.Framework.Servers protected string m_pidFile = String.Empty; + protected ServerStatsCollector m_serverStatsCollector; + /// /// Server version information. Usually VersionInfo + information about git commit, operating system, etc. /// @@ -76,6 +79,11 @@ namespace OpenSim.Framework.Servers protected void CreatePIDFile(string path) { + if (File.Exists(path)) + m_log.ErrorFormat( + "[SERVER BASE]: Previous pid file {0} still exists on startup. Possibly previously unclean shutdown.", + path); + try { string pidstring = System.Diagnostics.Process.GetCurrentProcess().Id.ToString(); @@ -113,6 +121,26 @@ namespace OpenSim.Framework.Servers } } + /// + /// Log information about the circumstances in which we're running (OpenSimulator version number, CLR details, + /// etc.). + /// + public void LogEnvironmentInformation() + { + // FIXME: This should be done down in ServerBase but we need to sort out and refactor the log4net + // XmlConfigurator calls first accross servers. + m_log.InfoFormat("[SERVER BASE]: Starting in {0}", m_startupDirectory); + + m_log.InfoFormat("[SERVER BASE]: OpenSimulator version: {0}", m_version); + + // clr version potentially is more confusing than helpful, since it doesn't tell us if we're running under Mono/MS .NET and + // the clr version number doesn't match the project version number under Mono. + //m_log.Info("[STARTUP]: Virtual machine runtime version: " + Environment.Version + Environment.NewLine); + m_log.InfoFormat( + "[SERVER BASE]: Operating system version: {0}, .NET platform {1}, {2}-bit", + Environment.OSVersion, Environment.OSVersion.Platform, Util.Is64BitProcess() ? "64" : "32"); + } + public void RegisterCommonAppenders(IConfig startupConfig) { ILoggerRepository repository = LogManager.GetRepository(); @@ -219,7 +247,7 @@ namespace OpenSim.Framework.Servers "Show thread status", HandleShow); m_console.Commands.AddCommand( - "General", false, "threads abort", + "Debug", false, "threads abort", "threads abort ", "Abort a managed thread. Use \"show threads\" to find possible threads.", HandleThreadsAbort); @@ -229,11 +257,281 @@ namespace OpenSim.Framework.Servers "Show thread status. Synonym for \"show threads\"", (string module, string[] args) => Notice(GetThreadsReport())); + m_console.Commands.AddCommand ( + "Debug", false, "debug comms set", + "debug comms set serialosdreq true|false", + "Set comms parameters. For debug purposes.", + HandleDebugCommsSet); + + m_console.Commands.AddCommand ( + "Debug", false, "debug comms status", + "debug comms status", + "Show current debug comms parameters.", + HandleDebugCommsStatus); + + m_console.Commands.AddCommand ( + "Debug", false, "debug threadpool set", + "debug threadpool set worker|iocp min|max ", + "Set threadpool parameters. For debug purposes.", + HandleDebugThreadpoolSet); + + m_console.Commands.AddCommand ( + "Debug", false, "debug threadpool status", + "debug threadpool status", + "Show current debug threadpool parameters.", + HandleDebugThreadpoolStatus); + + m_console.Commands.AddCommand( + "Debug", false, "debug threadpool level", + "debug threadpool level 0.." + Util.MAX_THREADPOOL_LEVEL, + "Turn on logging of activity in the main thread pool.", + "Log levels:\n" + + " 0 = no logging\n" + + " 1 = only first line of stack trace; don't log common threads\n" + + " 2 = full stack trace; don't log common threads\n" + + " 3 = full stack trace, including common threads\n", + HandleDebugThreadpoolLevel); + +// m_console.Commands.AddCommand( +// "Debug", false, "show threadpool calls active", +// "show threadpool calls active", +// "Show details about threadpool calls that are still active (currently waiting or in progress)", +// HandleShowThreadpoolCallsActive); + m_console.Commands.AddCommand( - "General", false, "force gc", + "Debug", false, "show threadpool calls complete", + "show threadpool calls complete", + "Show details about threadpool calls that have been completed.", + HandleShowThreadpoolCallsComplete); + + m_console.Commands.AddCommand( + "Debug", false, "force gc", "force gc", "Manually invoke runtime garbage collection. For debugging purposes", HandleForceGc); + + m_console.Commands.AddCommand( + "General", false, "quit", + "quit", + "Quit the application", (mod, args) => Shutdown()); + + m_console.Commands.AddCommand( + "General", false, "shutdown", + "shutdown", + "Quit the application", (mod, args) => Shutdown()); + + ChecksManager.RegisterConsoleCommands(m_console); + StatsManager.RegisterConsoleCommands(m_console); + } + + public void RegisterCommonComponents(IConfigSource configSource) + { + IConfig networkConfig = configSource.Configs["Network"]; + + if (networkConfig != null) + { + WebUtil.SerializeOSDRequestsPerEndpoint = networkConfig.GetBoolean("SerializeOSDRequests", false); + } + + m_serverStatsCollector = new ServerStatsCollector(); + m_serverStatsCollector.Initialise(configSource); + m_serverStatsCollector.Start(); + } + + private void HandleDebugCommsStatus(string module, string[] args) + { + Notice("serialosdreq is {0}", WebUtil.SerializeOSDRequestsPerEndpoint); + } + + private void HandleDebugCommsSet(string module, string[] args) + { + if (args.Length != 5) + { + Notice("Usage: debug comms set serialosdreq true|false"); + return; + } + + if (args[3] != "serialosdreq") + { + Notice("Usage: debug comms set serialosdreq true|false"); + return; + } + + bool setSerializeOsdRequests; + + if (!ConsoleUtil.TryParseConsoleBool(m_console, args[4], out setSerializeOsdRequests)) + return; + + WebUtil.SerializeOSDRequestsPerEndpoint = setSerializeOsdRequests; + + Notice("serialosdreq is now {0}", setSerializeOsdRequests); + } + + private void HandleShowThreadpoolCallsActive(string module, string[] args) + { + List> calls = Util.GetFireAndForgetCallsInProgress().ToList(); + calls.Sort((kvp1, kvp2) => kvp2.Value.CompareTo(kvp1.Value)); + int namedCalls = 0; + + ConsoleDisplayList cdl = new ConsoleDisplayList(); + foreach (KeyValuePair kvp in calls) + { + if (kvp.Value > 0) + { + cdl.AddRow(kvp.Key, kvp.Value); + namedCalls += kvp.Value; + } + } + + cdl.AddRow("TOTAL NAMED", namedCalls); + + long allQueuedCalls = Util.TotalQueuedFireAndForgetCalls; + long allRunningCalls = Util.TotalRunningFireAndForgetCalls; + + cdl.AddRow("TOTAL QUEUED", allQueuedCalls); + cdl.AddRow("TOTAL RUNNING", allRunningCalls); + cdl.AddRow("TOTAL ANONYMOUS", allQueuedCalls + allRunningCalls - namedCalls); + cdl.AddRow("TOTAL ALL", allQueuedCalls + allRunningCalls); + + MainConsole.Instance.Output(cdl.ToString()); + } + + private void HandleShowThreadpoolCallsComplete(string module, string[] args) + { + List> calls = Util.GetFireAndForgetCallsMade().ToList(); + calls.Sort((kvp1, kvp2) => kvp2.Value.CompareTo(kvp1.Value)); + int namedCallsMade = 0; + + ConsoleDisplayList cdl = new ConsoleDisplayList(); + foreach (KeyValuePair kvp in calls) + { + cdl.AddRow(kvp.Key, kvp.Value); + namedCallsMade += kvp.Value; + } + + cdl.AddRow("TOTAL NAMED", namedCallsMade); + + long allCallsMade = Util.TotalFireAndForgetCallsMade; + cdl.AddRow("TOTAL ANONYMOUS", allCallsMade - namedCallsMade); + cdl.AddRow("TOTAL ALL", allCallsMade); + + MainConsole.Instance.Output(cdl.ToString()); + } + + private void HandleDebugThreadpoolStatus(string module, string[] args) + { + int workerThreads, iocpThreads; + + ThreadPool.GetMinThreads(out workerThreads, out iocpThreads); + Notice("Min worker threads: {0}", workerThreads); + Notice("Min IOCP threads: {0}", iocpThreads); + + ThreadPool.GetMaxThreads(out workerThreads, out iocpThreads); + Notice("Max worker threads: {0}", workerThreads); + Notice("Max IOCP threads: {0}", iocpThreads); + + ThreadPool.GetAvailableThreads(out workerThreads, out iocpThreads); + Notice("Available worker threads: {0}", workerThreads); + Notice("Available IOCP threads: {0}", iocpThreads); + } + + private void HandleDebugThreadpoolSet(string module, string[] args) + { + if (args.Length != 6) + { + Notice("Usage: debug threadpool set worker|iocp min|max "); + return; + } + + int newThreads; + + if (!ConsoleUtil.TryParseConsoleInt(m_console, args[5], out newThreads)) + return; + + string poolType = args[3]; + string bound = args[4]; + + bool fail = false; + int workerThreads, iocpThreads; + + if (poolType == "worker") + { + if (bound == "min") + { + ThreadPool.GetMinThreads(out workerThreads, out iocpThreads); + + if (!ThreadPool.SetMinThreads(newThreads, iocpThreads)) + fail = true; + } + else + { + ThreadPool.GetMaxThreads(out workerThreads, out iocpThreads); + + if (!ThreadPool.SetMaxThreads(newThreads, iocpThreads)) + fail = true; + } + } + else + { + if (bound == "min") + { + ThreadPool.GetMinThreads(out workerThreads, out iocpThreads); + + if (!ThreadPool.SetMinThreads(workerThreads, newThreads)) + fail = true; + } + else + { + ThreadPool.GetMaxThreads(out workerThreads, out iocpThreads); + + if (!ThreadPool.SetMaxThreads(workerThreads, newThreads)) + fail = true; + } + } + + if (fail) + { + Notice("ERROR: Could not set {0} {1} threads to {2}", poolType, bound, newThreads); + } + else + { + int minWorkerThreads, maxWorkerThreads, minIocpThreads, maxIocpThreads; + + ThreadPool.GetMinThreads(out minWorkerThreads, out minIocpThreads); + ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxIocpThreads); + + Notice("Min worker threads now {0}", minWorkerThreads); + Notice("Min IOCP threads now {0}", minIocpThreads); + Notice("Max worker threads now {0}", maxWorkerThreads); + Notice("Max IOCP threads now {0}", maxIocpThreads); + } + } + + private static void HandleDebugThreadpoolLevel(string module, string[] cmdparams) + { + if (cmdparams.Length < 4) + { + MainConsole.Instance.Output("Usage: debug threadpool level 0.." + Util.MAX_THREADPOOL_LEVEL); + return; + } + + string rawLevel = cmdparams[3]; + int newLevel; + + if (!int.TryParse(rawLevel, out newLevel)) + { + MainConsole.Instance.OutputFormat("{0} is not a valid debug level", rawLevel); + return; + } + + if (newLevel < 0 || newLevel > Util.MAX_THREADPOOL_LEVEL) + { + MainConsole.Instance.OutputFormat("{0} is outside the valid debug level range of 0.." + Util.MAX_THREADPOOL_LEVEL, newLevel); + return; + } + + Util.LogThreadPool = newLevel; + MainConsole.Instance.OutputFormat("LogThreadPool set to {0}", newLevel); } private void HandleForceGc(string module, string[] args) @@ -575,7 +873,8 @@ namespace OpenSim.Framework.Servers protected string GetVersionText() { - return String.Format("Version: {0} (interface version {1})", m_version, VersionInfo.MajorInterfaceVersion); + return String.Format("Version: {0} (SIMULATION/{1} - SIMULATION/{2})", + m_version, VersionInfo.SimulationServiceVersionSupportedMin, VersionInfo.SimulationServiceVersionSupportedMax); } /// @@ -621,7 +920,68 @@ namespace OpenSim.Framework.Servers sb.AppendFormat("Total threads active: {0}\n\n", totalThreads); sb.Append("Main threadpool (excluding script engine pools)\n"); - sb.Append(Util.GetThreadPoolReport()); + sb.Append(GetThreadPoolReport()); + + return sb.ToString(); + } + + /// + /// Get a thread pool report. + /// + /// + public static string GetThreadPoolReport() + { + string threadPoolUsed = null; + int maxThreads = 0; + int minThreads = 0; + int allocatedThreads = 0; + int inUseThreads = 0; + int waitingCallbacks = 0; + int completionPortThreads = 0; + + StringBuilder sb = new StringBuilder(); + if (Util.FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool) + { + STPInfo stpi = Util.GetSmartThreadPoolInfo(); + + // ROBUST currently leaves this the FireAndForgetMethod but never actually initializes the threadpool. + if (stpi != null) + { + threadPoolUsed = "SmartThreadPool"; + maxThreads = stpi.MaxThreads; + minThreads = stpi.MinThreads; + inUseThreads = stpi.InUseThreads; + allocatedThreads = stpi.ActiveThreads; + waitingCallbacks = stpi.WaitingCallbacks; + } + } + else if ( + Util.FireAndForgetMethod == FireAndForgetMethod.QueueUserWorkItem + || Util.FireAndForgetMethod == FireAndForgetMethod.UnsafeQueueUserWorkItem) + { + threadPoolUsed = "BuiltInThreadPool"; + ThreadPool.GetMaxThreads(out maxThreads, out completionPortThreads); + ThreadPool.GetMinThreads(out minThreads, out completionPortThreads); + int availableThreads; + ThreadPool.GetAvailableThreads(out availableThreads, out completionPortThreads); + inUseThreads = maxThreads - availableThreads; + allocatedThreads = -1; + waitingCallbacks = -1; + } + + if (threadPoolUsed != null) + { + sb.AppendFormat("Thread pool used : {0}\n", threadPoolUsed); + sb.AppendFormat("Max threads : {0}\n", maxThreads); + sb.AppendFormat("Min threads : {0}\n", minThreads); + sb.AppendFormat("Allocated threads : {0}\n", allocatedThreads < 0 ? "not applicable" : allocatedThreads.ToString()); + sb.AppendFormat("In use threads : {0}\n", inUseThreads); + sb.AppendFormat("Work items waiting : {0}\n", waitingCallbacks < 0 ? "not available" : waitingCallbacks.ToString()); + } + else + { + sb.AppendFormat("Thread pool not used\n"); + } return sb.ToString(); } @@ -673,5 +1033,16 @@ namespace OpenSim.Framework.Servers if (m_console != null) m_console.OutputFormat(format, components); } + + public virtual void Shutdown() + { + m_serverStatsCollector.Close(); + ShutdownSpecific(); + } + + /// + /// Should be overriden and referenced by descendents if they need to perform extra shutdown processing + /// + protected virtual void ShutdownSpecific() {} } -} \ No newline at end of file +} diff --git a/OpenSim/Framework/Servers/Tests/OSHttpTests.cs b/OpenSim/Framework/Servers/Tests/OSHttpTests.cs index 3412e0f..5c0e0df 100644 --- a/OpenSim/Framework/Servers/Tests/OSHttpTests.cs +++ b/OpenSim/Framework/Servers/Tests/OSHttpTests.cs @@ -41,318 +41,7 @@ namespace OpenSim.Framework.Servers.Tests { [TestFixture] public class OSHttpTests : OpenSimTestCase - { - // we need an IHttpClientContext for our tests - public class TestHttpClientContext: IHttpClientContext - { - private bool _secured; - public bool IsSecured - { - get { return _secured; } - } - public bool Secured - { - get { return _secured; } - } - - public TestHttpClientContext(bool secured) - { - _secured = secured; - } - - public void Disconnect(SocketError error) {} - public void Respond(string httpVersion, HttpStatusCode statusCode, string reason, string body) {} - public void Respond(string httpVersion, HttpStatusCode statusCode, string reason) {} - public void Respond(string body) {} - public void Send(byte[] buffer) {} - public void Send(byte[] buffer, int offset, int size) {} - public void Respond(string httpVersion, HttpStatusCode statusCode, string reason, string body, string contentType) {} - public void Close() { } - public bool EndWhenDone { get { return false;} set { return;}} - - public event EventHandler Disconnected = delegate { }; - /// - /// A request have been received in the context. - /// - public event EventHandler RequestReceived = delegate { }; - - } - - public class TestHttpRequest: IHttpRequest - { - private string _uriPath; - public bool BodyIsComplete - { - get { return true; } - } - public string[] AcceptTypes - { - get {return _acceptTypes; } - } - private string[] _acceptTypes; - public Stream Body - { - get { return _body; } - set { _body = value;} - } - private Stream _body; - public ConnectionType Connection - { - get { return _connection; } - set { _connection = value; } - } - private ConnectionType _connection; - public int ContentLength - { - get { return _contentLength; } - set { _contentLength = value; } - } - private int _contentLength; - public NameValueCollection Headers - { - get { return _headers; } - } - private NameValueCollection _headers = new NameValueCollection(); - public string HttpVersion - { - get { return _httpVersion; } - set { _httpVersion = value; } - } - private string _httpVersion = null; - public string Method - { - get { return _method; } - set { _method = value; } - } - private string _method = null; - public HttpInput QueryString - { - get { return _queryString; } - } - private HttpInput _queryString = null; - public Uri Uri - { - get { return _uri; } - set { _uri = value; } - } - private Uri _uri = null; - public string[] UriParts - { - get { return _uri.Segments; } - } - public HttpParam Param - { - get { return null; } - } - public HttpForm Form - { - get { return null; } - } - public bool IsAjax - { - get { return false; } - } - public RequestCookies Cookies - { - get { return null; } - } - - public TestHttpRequest() {} - - public TestHttpRequest(string contentEncoding, string contentType, string userAgent, - string remoteAddr, string remotePort, string[] acceptTypes, - ConnectionType connectionType, int contentLength, Uri uri) - { - _headers["content-encoding"] = contentEncoding; - _headers["content-type"] = contentType; - _headers["user-agent"] = userAgent; - _headers["remote_addr"] = remoteAddr; - _headers["remote_port"] = remotePort; - - _acceptTypes = acceptTypes; - _connection = connectionType; - _contentLength = contentLength; - _uri = uri; - } - - public void DecodeBody(FormDecoderProvider providers) {} - public void SetCookies(RequestCookies cookies) {} - public void AddHeader(string name, string value) - { - _headers.Add(name, value); - } - public int AddToBody(byte[] bytes, int offset, int length) - { - return 0; - } - public void Clear() {} - - public object Clone() - { - TestHttpRequest clone = new TestHttpRequest(); - clone._acceptTypes = _acceptTypes; - clone._connection = _connection; - clone._contentLength = _contentLength; - clone._uri = _uri; - clone._headers = new NameValueCollection(_headers); - - return clone; - } - public IHttpResponse CreateResponse(IHttpClientContext context) - { - return new HttpResponse(context, this); - } - /// - /// Path and query (will be merged with the host header) and put in Uri - /// - /// - public string UriPath - { - get { return _uriPath; } - set - { - _uriPath = value; - - } - } - - } - - public class TestHttpResponse: IHttpResponse - { - public Stream Body - { - get { return _body; } - - set { _body = value; } - } - private Stream _body; - - public string ProtocolVersion - { - get { return _protocolVersion; } - set { _protocolVersion = value; } - } - private string _protocolVersion; - - public bool Chunked - { - get { return _chunked; } - - set { _chunked = value; } - } - private bool _chunked; - - public ConnectionType Connection - { - get { return _connection; } - - set { _connection = value; } - } - private ConnectionType _connection; - - public Encoding Encoding - { - get { return _encoding; } - - set { _encoding = value; } - } - private Encoding _encoding; - - public int KeepAlive - { - get { return _keepAlive; } - - set { _keepAlive = value; } - } - private int _keepAlive; - - public HttpStatusCode Status - { - get { return _status; } - - set { _status = value; } - } - private HttpStatusCode _status; - - public string Reason - { - get { return _reason; } - - set { _reason = value; } - } - private string _reason; - - public long ContentLength - { - get { return _contentLength; } - - set { _contentLength = value; } - } - private long _contentLength; - - public string ContentType - { - get { return _contentType; } - - set { _contentType = value; } - } - private string _contentType; - - public bool HeadersSent - { - get { return _headersSent; } - } - private bool _headersSent; - - public bool Sent - { - get { return _sent; } - } - private bool _sent; - - public ResponseCookies Cookies - { - get { return _cookies; } - } - private ResponseCookies _cookies = null; - - public TestHttpResponse() - { - _headersSent = false; - _sent = false; - } - - public void AddHeader(string name, string value) {} - public void Send() - { - if (!_headersSent) SendHeaders(); - if (_sent) throw new InvalidOperationException("stuff already sent"); - _sent = true; - } - - public void SendBody(byte[] buffer, int offset, int count) - { - if (!_headersSent) SendHeaders(); - _sent = true; - } - public void SendBody(byte[] buffer) - { - if (!_headersSent) SendHeaders(); - _sent = true; - } - - public void SendHeaders() - { - if (_headersSent) throw new InvalidOperationException("headers already sent"); - _headersSent = true; - } - - public void Redirect(Uri uri) {} - public void Redirect(string url) {} - } - - + { public OSHttpRequest req0; public OSHttpRequest req1; @@ -424,4 +113,4 @@ namespace OpenSim.Framework.Servers.Tests Assert.That(rsp0.ContentType, Is.EqualTo("text/xml")); } } -} +} \ No newline at end of file diff --git a/OpenSim/Framework/Servers/Tests/VersionInfoTests.cs b/OpenSim/Framework/Servers/Tests/VersionInfoTests.cs index 2d72cb8..480f2bb 100644 --- a/OpenSim/Framework/Servers/Tests/VersionInfoTests.cs +++ b/OpenSim/Framework/Servers/Tests/VersionInfoTests.cs @@ -47,7 +47,7 @@ namespace OpenSim.Framework.Servers.Tests { foreach (VersionInfo.Flavour flavour in Enum.GetValues(typeof(VersionInfo.Flavour))) { - Assert.AreEqual(VersionInfo.VERSIONINFO_VERSION_LENGTH, VersionInfo.GetVersionString("0.0.0", "0", flavour).Length, "0.0.0/" + flavour + " failed"); + Assert.AreEqual(VersionInfo.VERSIONINFO_VERSION_LENGTH, VersionInfo.GetVersionString("0.0.0", flavour).Length, "0.0.0/" + flavour + " failed"); } } } diff --git a/OpenSim/Framework/Servers/VersionInfo.cs b/OpenSim/Framework/Servers/VersionInfo.cs deleted file mode 100644 index 54af95e..0000000 --- a/OpenSim/Framework/Servers/VersionInfo.cs +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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 OpenSimulator 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. - */ - -namespace OpenSim -{ - public class VersionInfo - { - private const string VERSION_NUMBER = "0.7.5"; - private const string IG_BUILD_NUMBER = "4"; - private const Flavour VERSION_FLAVOUR = Flavour.IG; - - public enum Flavour - { - Unknown, - Dev, - RC1, - RC2, - Release, - Post_Fixes, - Extended, - IG - } - - public static string Version - { - get { return GetVersionString(VERSION_NUMBER, IG_BUILD_NUMBER, VERSION_FLAVOUR); } - } - - public static string GetVersionString(string versionNumber, string buildNumber, Flavour flavour) - { - string versionString = "OpenSim " + versionNumber + " " + flavour + " build " + buildNumber; - return versionString.PadRight(VERSIONINFO_VERSION_LENGTH); - } - - public const int VERSIONINFO_VERSION_LENGTH = 39; - - /// - /// This is the external interface version. It is separate from the OpenSimulator project version. - /// - /// This version number should be - /// increased by 1 every time a code change makes the previous OpenSimulator revision incompatible - /// with the new revision. This will usually be due to interregion or grid facing interface changes. - /// - /// Changes which are compatible with an older revision (e.g. older revisions experience degraded functionality - /// but not outright failure) do not need a version number increment. - /// - /// Having this version number allows the grid service to reject connections from regions running a version - /// of the code that is too old. - /// - /// - public readonly static int MajorInterfaceVersion = 7; - } -} diff --git a/OpenSim/Framework/ServiceAuth/BasicHttpAuthentication.cs b/OpenSim/Framework/ServiceAuth/BasicHttpAuthentication.cs new file mode 100644 index 0000000..512ac4f --- /dev/null +++ b/OpenSim/Framework/ServiceAuth/BasicHttpAuthentication.cs @@ -0,0 +1,113 @@ +/* + * 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 OpenSimulator 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.Collections.Specialized; +using System.Net; +using System.Reflection; + +using Nini.Config; +using log4net; + +namespace OpenSim.Framework.ServiceAuth +{ + public class BasicHttpAuthentication : IServiceAuth + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public string Name { get { return "BasicHttp"; } } + + private string m_Username, m_Password; + private string m_CredentialsB64; + +// private string remove_me; + + public string Credentials + { + get { return m_CredentialsB64; } + } + + public BasicHttpAuthentication(IConfigSource config, string section) + { +// remove_me = section; + m_Username = Util.GetConfigVarFromSections(config, "HttpAuthUsername", new string[] { "Network", section }, string.Empty); + m_Password = Util.GetConfigVarFromSections(config, "HttpAuthPassword", new string[] { "Network", section }, string.Empty); + string str = m_Username + ":" + m_Password; + byte[] encData_byte = Util.UTF8.GetBytes(str); + + m_CredentialsB64 = Convert.ToBase64String(encData_byte); +// m_log.DebugFormat("[HTTP BASIC AUTH]: {0} {1} [{2}]", m_Username, m_Password, section); + } + + public void AddAuthorization(NameValueCollection headers) + { + //m_log.DebugFormat("[HTTP BASIC AUTH]: Adding authorization for {0}", remove_me); + headers["Authorization"] = "Basic " + m_CredentialsB64; + } + + public bool Authenticate(string data) + { + string recovered = Util.Base64ToString(data); + if (!String.IsNullOrEmpty(recovered)) + { + string[] parts = recovered.Split(new char[] { ':' }); + if (parts.Length >= 2) + { + return m_Username.Equals(parts[0]) && m_Password.Equals(parts[1]); + } + } + + return false; + } + + public bool Authenticate(NameValueCollection requestHeaders, AddHeaderDelegate d, out HttpStatusCode statusCode) + { +// m_log.DebugFormat("[HTTP BASIC AUTH]: Authenticate in {0}", "BasicHttpAuthentication"); + + string value = requestHeaders.Get("Authorization"); + if (value != null) + { + value = value.Trim(); + if (value.StartsWith("Basic ")) + { + value = value.Replace("Basic ", string.Empty); + if (Authenticate(value)) + { + statusCode = HttpStatusCode.OK; + return true; + } + } + } + + d("WWW-Authenticate", "Basic realm = \"Asset Server\""); + + statusCode = HttpStatusCode.Unauthorized; + return false; + } + } +} diff --git a/OpenSim/Framework/ServiceAuth/CompoundAuthentication.cs b/OpenSim/Framework/ServiceAuth/CompoundAuthentication.cs new file mode 100644 index 0000000..a49952c --- /dev/null +++ b/OpenSim/Framework/ServiceAuth/CompoundAuthentication.cs @@ -0,0 +1,82 @@ +/* + * 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 OpenSimulator 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.Collections.Specialized; +using System.Linq; +using System.Net; + +namespace OpenSim.Framework.ServiceAuth +{ + public class CompoundAuthentication : IServiceAuth + { + public string Name { get { return "Compound"; } } + + private List m_authentications = new List(); + + public int Count { get { return m_authentications.Count; } } + + public List GetAuthentors() + { + return new List(m_authentications); + } + + public void AddAuthenticator(IServiceAuth auth) + { + m_authentications.Add(auth); + } + + public void RemoveAuthenticator(IServiceAuth auth) + { + m_authentications.Remove(auth); + } + + public void AddAuthorization(NameValueCollection headers) + { + foreach (IServiceAuth auth in m_authentications) + auth.AddAuthorization(headers); + } + + public bool Authenticate(string data) + { + return m_authentications.TrueForAll(a => a.Authenticate(data)); + } + + public bool Authenticate(NameValueCollection requestHeaders, AddHeaderDelegate d, out HttpStatusCode statusCode) + { + foreach (IServiceAuth auth in m_authentications) + { + if (!auth.Authenticate(requestHeaders, d, out statusCode)) + return false; + } + + statusCode = HttpStatusCode.OK; + return true; + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/ServiceAuth/DisallowLlHttpRequest.cs b/OpenSim/Framework/ServiceAuth/DisallowLlHttpRequest.cs new file mode 100644 index 0000000..e0c413b --- /dev/null +++ b/OpenSim/Framework/ServiceAuth/DisallowLlHttpRequest.cs @@ -0,0 +1,59 @@ +/* + * 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 OpenSimulator 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.Specialized; +using System.Net; + +namespace OpenSim.Framework.ServiceAuth +{ + public class DisallowLlHttpRequest : IServiceAuth + { + public string Name { get { return "DisallowllHTTPRequest"; } } + + public void AddAuthorization(NameValueCollection headers) {} + + public bool Authenticate(string data) + { + return false; + } + + public bool Authenticate(NameValueCollection requestHeaders, AddHeaderDelegate d, out HttpStatusCode statusCode) + { +// Console.WriteLine("DisallowLlHttpRequest"); + + if (requestHeaders["X-SecondLife-Shard"] != null) + { + statusCode = HttpStatusCode.Forbidden; + return false; + } + + statusCode = HttpStatusCode.OK; + return true; + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/ServiceAuth/IServiceAuth.cs b/OpenSim/Framework/ServiceAuth/IServiceAuth.cs new file mode 100644 index 0000000..5f744cb --- /dev/null +++ b/OpenSim/Framework/ServiceAuth/IServiceAuth.cs @@ -0,0 +1,48 @@ +/* + * 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 OpenSimulator 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.Net; +using System.Collections.Generic; +using System.Collections.Specialized; + +namespace OpenSim.Framework.ServiceAuth +{ + public delegate void AddHeaderDelegate(string key, string value); + + public interface IServiceAuth + { + /// + /// Name of this authenticator. + /// + string Name { get; } + + bool Authenticate(string data); + bool Authenticate(NameValueCollection headers, AddHeaderDelegate d, out HttpStatusCode statusCode); + void AddAuthorization(NameValueCollection headers); + } +} diff --git a/OpenSim/Framework/ServiceAuth/ServiceAuth.cs b/OpenSim/Framework/ServiceAuth/ServiceAuth.cs new file mode 100644 index 0000000..51012e3 --- /dev/null +++ b/OpenSim/Framework/ServiceAuth/ServiceAuth.cs @@ -0,0 +1,68 @@ +/* + * 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 OpenSimulator 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.Reflection; +using log4net; +using Nini.Config; + +namespace OpenSim.Framework.ServiceAuth +{ + public class ServiceAuth + { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static IServiceAuth Create(IConfigSource config, string section) + { + CompoundAuthentication compoundAuth = new CompoundAuthentication(); + + bool allowLlHttpRequestIn + = Util.GetConfigVarFromSections(config, "AllowllHTTPRequestIn", new string[] { "Network", section }, false); + + if (!allowLlHttpRequestIn) + compoundAuth.AddAuthenticator(new DisallowLlHttpRequest()); + + string authType = Util.GetConfigVarFromSections(config, "AuthType", new string[] { "Network", section }, "None"); + + switch (authType) + { + case "BasicHttpAuthentication": + compoundAuth.AddAuthenticator(new BasicHttpAuthentication(config, section)); + break; + } + +// foreach (IServiceAuth auth in compoundAuth.GetAuthentors()) +// m_log.DebugFormat("[SERVICE AUTH]: Configured authenticator {0}", auth.Name); + + if (compoundAuth.Count > 0) + return compoundAuth; + else + return null; + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/TaskInventoryItem.cs b/OpenSim/Framework/TaskInventoryItem.cs index a06f8e7..307cb75 100644 --- a/OpenSim/Framework/TaskInventoryItem.cs +++ b/OpenSim/Framework/TaskInventoryItem.cs @@ -124,7 +124,7 @@ namespace OpenSim.Framework { get { - if (_creatorData != null && _creatorData != string.Empty) + if (!string.IsNullOrEmpty(_creatorData)) return _creatorID.ToString() + ';' + _creatorData; else return _creatorID.ToString(); diff --git a/OpenSim/Framework/TerrainData.cs b/OpenSim/Framework/TerrainData.cs new file mode 100644 index 0000000..6b1be4e --- /dev/null +++ b/OpenSim/Framework/TerrainData.cs @@ -0,0 +1,464 @@ +/* + * 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 OpenSimulator 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.Reflection; + +using OpenMetaverse; + +using log4net; + +namespace OpenSim.Framework +{ + public abstract class TerrainData + { + // Terrain always is a square + public int SizeX { get; protected set; } + public int SizeY { get; protected set; } + public int SizeZ { get; protected set; } + + // A height used when the user doesn't specify anything + public const float DefaultTerrainHeight = 21f; + + public abstract float this[int x, int y] { get; set; } + // Someday terrain will have caves + public abstract float this[int x, int y, int z] { get; set; } + + public abstract bool IsTaintedAt(int xx, int yy); + public abstract bool IsTaintedAt(int xx, int yy, bool clearOnTest); + public abstract void TaintAllTerrain(); + public abstract void ClearTaint(); + + public abstract void ClearLand(); + public abstract void ClearLand(float height); + + // Return a representation of this terrain for storing as a blob in the database. + // Returns 'true' to say blob was stored in the 'out' locations. + public abstract bool GetDatabaseBlob(out int DBFormatRevisionCode, out Array blob); + + // Given a revision code and a blob from the database, create and return the right type of TerrainData. + // The sizes passed are the expected size of the region. The database info will be used to + // initialize the heightmap of that sized region with as much data is in the blob. + // Return created TerrainData or 'null' if unsuccessful. + public static TerrainData CreateFromDatabaseBlobFactory(int pSizeX, int pSizeY, int pSizeZ, int pFormatCode, byte[] pBlob) + { + // For the moment, there is only one implementation class + return new HeightmapTerrainData(pSizeX, pSizeY, pSizeZ, pFormatCode, pBlob); + } + + // return a special compressed representation of the heightmap in ints + public abstract int[] GetCompressedMap(); + public abstract float CompressionFactor { get; } + + public abstract float[] GetFloatsSerialized(); + public abstract double[,] GetDoubles(); + public abstract TerrainData Clone(); + } + + // The terrain is stored in the database as a blob with a 'revision' field. + // Some implementations of terrain storage would fill the revision field with + // the time the terrain was stored. When real revisions were added and this + // feature removed, that left some old entries with the time in the revision + // field. + // Thus, if revision is greater than 'RevisionHigh' then terrain db entry is + // left over and it is presumed to be 'Legacy256'. + // Numbers are arbitrary and are chosen to to reduce possible mis-interpretation. + // If a revision does not match any of these, it is assumed to be Legacy256. + public enum DBTerrainRevision + { + // Terrain is 'double[256,256]' + Legacy256 = 11, + // Terrain is 'int32, int32, float[,]' where the ints are X and Y dimensions + // The dimensions are presumed to be multiples of 16 and, more likely, multiples of 256. + Variable2D = 22, + // Terrain is 'int32, int32, int32, int16[]' where the ints are X and Y dimensions + // and third int is the 'compression factor'. The heights are compressed as + // "int compressedHeight = (int)(height * compressionFactor);" + // The dimensions are presumed to be multiples of 16 and, more likely, multiples of 256. + Compressed2D = 27, + // A revision that is not listed above or any revision greater than this value is 'Legacy256'. + RevisionHigh = 1234 + } + + // Version of terrain that is a heightmap. + // This should really be 'LLOptimizedHeightmapTerrainData' as it includes knowledge + // of 'patches' which are 16x16 terrain areas which can be sent separately to the viewer. + // The heighmap is kept as an array of integers. The integer values are converted to + // and from floats by TerrainCompressionFactor. + public class HeightmapTerrainData : TerrainData + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static string LogHeader = "[HEIGHTMAP TERRAIN DATA]"; + + // TerrainData.this[x, y] + public override float this[int x, int y] + { + get { return FromCompressedHeight(m_heightmap[x, y]); } + set { + int newVal = ToCompressedHeight(value); + if (m_heightmap[x, y] != newVal) + { + m_heightmap[x, y] = newVal; + m_taint[x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize] = true; + } + } + } + + // TerrainData.this[x, y, z] + public override float this[int x, int y, int z] + { + get { return this[x, y]; } + set { this[x, y] = value; } + } + + // TerrainData.ClearTaint + public override void ClearTaint() + { + SetAllTaint(false); + } + + // TerrainData.TaintAllTerrain + public override void TaintAllTerrain() + { + SetAllTaint(true); + } + + private void SetAllTaint(bool setting) + { + for (int ii = 0; ii < m_taint.GetLength(0); ii++) + for (int jj = 0; jj < m_taint.GetLength(1); jj++) + m_taint[ii, jj] = setting; + } + + // TerrainData.ClearLand + public override void ClearLand() + { + ClearLand(DefaultTerrainHeight); + } + // TerrainData.ClearLand(float) + public override void ClearLand(float pHeight) + { + int flatHeight = ToCompressedHeight(pHeight); + for (int xx = 0; xx < SizeX; xx++) + for (int yy = 0; yy < SizeY; yy++) + m_heightmap[xx, yy] = flatHeight; + } + + // Return 'true' of the patch that contains these region coordinates has been modified. + // Note that checking the taint clears it. + // There is existing code that relies on this feature. + public override bool IsTaintedAt(int xx, int yy, bool clearOnTest) + { + int tx = xx / Constants.TerrainPatchSize; + int ty = yy / Constants.TerrainPatchSize; + bool ret = m_taint[tx, ty]; + if (ret && clearOnTest) + m_taint[tx, ty] = false; + return ret; + } + + // Old form that clears the taint flag when we check it. + public override bool IsTaintedAt(int xx, int yy) + { + return IsTaintedAt(xx, yy, true /* clearOnTest */); + } + + // TerrainData.GetDatabaseBlob + // The user wants something to store in the database. + public override bool GetDatabaseBlob(out int DBRevisionCode, out Array blob) + { + bool ret = false; + if (SizeX == Constants.RegionSize && SizeY == Constants.RegionSize) + { + DBRevisionCode = (int)DBTerrainRevision.Legacy256; + blob = ToLegacyTerrainSerialization(); + ret = true; + } + else + { + DBRevisionCode = (int)DBTerrainRevision.Compressed2D; + blob = ToCompressedTerrainSerialization(); + ret = true; + } + return ret; + } + + // TerrainData.CompressionFactor + private float m_compressionFactor = 100.0f; + public override float CompressionFactor { get { return m_compressionFactor; } } + + // TerrainData.GetCompressedMap + public override int[] GetCompressedMap() + { + int[] newMap = new int[SizeX * SizeY]; + + int ind = 0; + for (int xx = 0; xx < SizeX; xx++) + for (int yy = 0; yy < SizeY; yy++) + newMap[ind++] = m_heightmap[xx, yy]; + + return newMap; + + } + // TerrainData.Clone + public override TerrainData Clone() + { + HeightmapTerrainData ret = new HeightmapTerrainData(SizeX, SizeY, SizeZ); + ret.m_heightmap = (int[,])this.m_heightmap.Clone(); + return ret; + } + + // TerrainData.GetFloatsSerialized + // This one dimensional version is ordered so height = map[y*sizeX+x]; + // DEPRECATED: don't use this function as it does not retain the dimensions of the terrain + // and the caller will probably do the wrong thing if the terrain is not the legacy 256x256. + public override float[] GetFloatsSerialized() + { + int points = SizeX * SizeY; + float[] heights = new float[points]; + + int idx = 0; + for (int jj = 0; jj < SizeY; jj++) + for (int ii = 0; ii < SizeX; ii++) + { + heights[idx++] = FromCompressedHeight(m_heightmap[ii, jj]); + } + + return heights; + } + + // TerrainData.GetDoubles + public override double[,] GetDoubles() + { + double[,] ret = new double[SizeX, SizeY]; + for (int xx = 0; xx < SizeX; xx++) + for (int yy = 0; yy < SizeY; yy++) + ret[xx, yy] = FromCompressedHeight(m_heightmap[xx, yy]); + + return ret; + } + + + // ============================================================= + + private int[,] m_heightmap; + // Remember subregions of the heightmap that has changed. + private bool[,] m_taint; + + // To save space (especially for large regions), keep the height as a short integer + // that is coded as the float height times the compression factor (usually '100' + // to make for two decimal points). + public int ToCompressedHeight(double pHeight) + { + return (int)(pHeight * CompressionFactor); + } + + public float FromCompressedHeight(int pHeight) + { + return ((float)pHeight) / CompressionFactor; + } + + // To keep with the legacy theme, create an instance of this class based on the + // way terrain used to be passed around. + public HeightmapTerrainData(double[,] pTerrain) + { + SizeX = pTerrain.GetLength(0); + SizeY = pTerrain.GetLength(1); + SizeZ = (int)Constants.RegionHeight; + m_compressionFactor = 100.0f; + + m_heightmap = new int[SizeX, SizeY]; + for (int ii = 0; ii < SizeX; ii++) + { + for (int jj = 0; jj < SizeY; jj++) + { + m_heightmap[ii, jj] = ToCompressedHeight(pTerrain[ii, jj]); + + } + } + // m_log.DebugFormat("{0} new by doubles. sizeX={1}, sizeY={2}, sizeZ={3}", LogHeader, SizeX, SizeY, SizeZ); + + m_taint = new bool[SizeX / Constants.TerrainPatchSize, SizeY / Constants.TerrainPatchSize]; + ClearTaint(); + } + + // Create underlying structures but don't initialize the heightmap assuming the caller will immediately do that + public HeightmapTerrainData(int pX, int pY, int pZ) + { + SizeX = pX; + SizeY = pY; + SizeZ = pZ; + m_compressionFactor = 100.0f; + m_heightmap = new int[SizeX, SizeY]; + m_taint = new bool[SizeX / Constants.TerrainPatchSize, SizeY / Constants.TerrainPatchSize]; + // m_log.DebugFormat("{0} new by dimensions. sizeX={1}, sizeY={2}, sizeZ={3}", LogHeader, SizeX, SizeY, SizeZ); + ClearTaint(); + ClearLand(0f); + } + + public HeightmapTerrainData(int[] cmap, float pCompressionFactor, int pX, int pY, int pZ) : this(pX, pY, pZ) + { + m_compressionFactor = pCompressionFactor; + int ind = 0; + for (int xx = 0; xx < SizeX; xx++) + for (int yy = 0; yy < SizeY; yy++) + m_heightmap[xx, yy] = cmap[ind++]; + // m_log.DebugFormat("{0} new by compressed map. sizeX={1}, sizeY={2}, sizeZ={3}", LogHeader, SizeX, SizeY, SizeZ); + } + + // Create a heighmap from a database blob + public HeightmapTerrainData(int pSizeX, int pSizeY, int pSizeZ, int pFormatCode, byte[] pBlob) : this(pSizeX, pSizeY, pSizeZ) + { + switch ((DBTerrainRevision)pFormatCode) + { + case DBTerrainRevision.Compressed2D: + FromCompressedTerrainSerialization(pBlob); + m_log.DebugFormat("{0} HeightmapTerrainData create from Compressed2D serialization. Size=<{1},{2}>", LogHeader, SizeX, SizeY); + break; + default: + FromLegacyTerrainSerialization(pBlob); + m_log.DebugFormat("{0} HeightmapTerrainData create from legacy serialization. Size=<{1},{2}>", LogHeader, SizeX, SizeY); + break; + } + } + + // Just create an array of doubles. Presumes the caller implicitly knows the size. + public Array ToLegacyTerrainSerialization() + { + Array ret = null; + + using (MemoryStream str = new MemoryStream((int)Constants.RegionSize * (int)Constants.RegionSize * sizeof(double))) + { + using (BinaryWriter bw = new BinaryWriter(str)) + { + for (int xx = 0; xx < Constants.RegionSize; xx++) + { + for (int yy = 0; yy < Constants.RegionSize; yy++) + { + double height = this[xx, yy]; + if (height == 0.0) + height = double.Epsilon; + bw.Write(height); + } + } + } + ret = str.ToArray(); + } + return ret; + } + + // Just create an array of doubles. Presumes the caller implicitly knows the size. + public void FromLegacyTerrainSerialization(byte[] pBlob) + { + // In case database info doesn't match real terrain size, initialize the whole terrain. + ClearLand(); + + using (MemoryStream mstr = new MemoryStream(pBlob)) + { + using (BinaryReader br = new BinaryReader(mstr)) + { + for (int xx = 0; xx < (int)Constants.RegionSize; xx++) + { + for (int yy = 0; yy < (int)Constants.RegionSize; yy++) + { + float val = (float)br.ReadDouble(); + if (xx < SizeX && yy < SizeY) + m_heightmap[xx, yy] = ToCompressedHeight(val); + } + } + } + ClearTaint(); + } + } + + // See the reader below. + public Array ToCompressedTerrainSerialization() + { + Array ret = null; + using (MemoryStream str = new MemoryStream((3 * sizeof(Int32)) + (SizeX * SizeY * sizeof(Int16)))) + { + using (BinaryWriter bw = new BinaryWriter(str)) + { + bw.Write((Int32)DBTerrainRevision.Compressed2D); + bw.Write((Int32)SizeX); + bw.Write((Int32)SizeY); + bw.Write((Int32)CompressionFactor); + for (int yy = 0; yy < SizeY; yy++) + for (int xx = 0; xx < SizeX; xx++) + { + bw.Write((Int16)m_heightmap[xx, yy]); + } + } + ret = str.ToArray(); + } + return ret; + } + + // Initialize heightmap from blob consisting of: + // int32, int32, int32, int32, int16[] + // where the first int32 is format code, next two int32s are the X and y of heightmap data and + // the forth int is the compression factor for the following int16s + // This is just sets heightmap info. The actual size of the region was set on this instance's + // creation and any heights not initialized by theis blob are set to the default height. + public void FromCompressedTerrainSerialization(byte[] pBlob) + { + Int32 hmFormatCode, hmSizeX, hmSizeY, hmCompressionFactor; + + using (MemoryStream mstr = new MemoryStream(pBlob)) + { + using (BinaryReader br = new BinaryReader(mstr)) + { + hmFormatCode = br.ReadInt32(); + hmSizeX = br.ReadInt32(); + hmSizeY = br.ReadInt32(); + hmCompressionFactor = br.ReadInt32(); + + m_compressionFactor = hmCompressionFactor; + + // In case database info doesn't match real terrain size, initialize the whole terrain. + ClearLand(); + + for (int yy = 0; yy < hmSizeY; yy++) + { + for (int xx = 0; xx < hmSizeX; xx++) + { + Int16 val = br.ReadInt16(); + if (xx < SizeX && yy < SizeY) + m_heightmap[xx, yy] = val; + } + } + } + ClearTaint(); + + m_log.InfoFormat("{0} Read compressed 2d heightmap. Heightmap size=<{1},{2}>. Region size=<{3},{4}>. CompFact={5}", + LogHeader, hmSizeX, hmSizeY, SizeX, SizeY, hmCompressionFactor); + } + } + } +} diff --git a/OpenSim/Framework/Tests/AnimationTests.cs b/OpenSim/Framework/Tests/AnimationTests.cs index f3be81b..d8f17d0 100644 --- a/OpenSim/Framework/Tests/AnimationTests.cs +++ b/OpenSim/Framework/Tests/AnimationTests.cs @@ -32,7 +32,6 @@ using OpenMetaverse; using OpenMetaverse.StructuredData; using OpenSim.Framework; using OpenSim.Tests.Common; -using OpenSim.Tests.Common.Mock; using Animation = OpenSim.Framework.Animation; namespace OpenSim.Framework.Tests diff --git a/OpenSim/Framework/Tests/AssetBaseTest.cs b/OpenSim/Framework/Tests/AssetBaseTest.cs index 25d2393..1975a4d 100644 --- a/OpenSim/Framework/Tests/AssetBaseTest.cs +++ b/OpenSim/Framework/Tests/AssetBaseTest.cs @@ -50,19 +50,15 @@ namespace OpenSim.Framework.Tests CheckContainsReferences(AssetType.ImageJPEG , false); CheckContainsReferences(AssetType.ImageTGA , false); CheckContainsReferences(AssetType.Landmark , false); - CheckContainsReferences(AssetType.LostAndFoundFolder, false); CheckContainsReferences(AssetType.LSLBytecode, false); CheckContainsReferences(AssetType.LSLText, false); CheckContainsReferences(AssetType.Notecard, false); CheckContainsReferences(AssetType.Object, false); - CheckContainsReferences(AssetType.RootFolder, false); CheckContainsReferences(AssetType.Simstate, false); - CheckContainsReferences(AssetType.SnapshotFolder, false); CheckContainsReferences(AssetType.Sound, false); CheckContainsReferences(AssetType.SoundWAV, false); CheckContainsReferences(AssetType.Texture, false); CheckContainsReferences(AssetType.TextureTGA, false); - CheckContainsReferences(AssetType.TrashFolder, false); CheckContainsReferences(AssetType.Unknown, false); } diff --git a/OpenSim/Framework/Tests/LocationTest.cs b/OpenSim/Framework/Tests/LocationTest.cs index a56ecb4..3d5d1d2 100644 --- a/OpenSim/Framework/Tests/LocationTest.cs +++ b/OpenSim/Framework/Tests/LocationTest.cs @@ -51,21 +51,28 @@ namespace OpenSim.Framework.Tests [Test] public void locationXYRegionHandle() { - Location TestLocation1 = new Location(256000,256000); - Location TestLocation2 = new Location(1099511628032000); + Location TestLocation1 = new Location(255000,256000); + Location TestLocation2 = new Location(1095216660736000); Assert.That(TestLocation1 == TestLocation2); - Assert.That(TestLocation2.X == 256000 && TestLocation2.Y == 256000, "Test xy location doesn't match regionhandle provided"); + Assert.That(TestLocation1.X == 255000 && TestLocation1.Y == 256000, "Test xy location doesn't match position in the constructor"); + Assert.That(TestLocation2.X == 255000 && TestLocation2.Y == 256000, "Test xy location doesn't match regionhandle provided"); - Assert.That(TestLocation2.RegionHandle == 1099511628032000, + Assert.That(TestLocation2.RegionHandle == 1095216660736000, "Location RegionHandle Property didn't match regionhandle provided in constructor"); + ulong RegionHandle = TestLocation1.RegionHandle; + Assert.That(RegionHandle.Equals(1095216660736000), "Equals(regionhandle) failed to match the position in the constructor"); - TestLocation1 = new Location(256001, 256001); - TestLocation2 = new Location(1099511628032000); + TestLocation2 = new Location(RegionHandle); + Assert.That(TestLocation2.Equals(255000, 256000), "Decoded regionhandle failed to match the original position in the constructor"); + + + TestLocation1 = new Location(255001, 256001); + TestLocation2 = new Location(1095216660736000); Assert.That(TestLocation1 != TestLocation2); - Assert.That(TestLocation1.Equals(256001, 256001), "Equals(x,y) failed to match the position in the constructor"); + Assert.That(TestLocation1.Equals(255001, 256001), "Equals(x,y) failed to match the position in the constructor"); Assert.That(TestLocation2.GetHashCode() == (TestLocation2.X.GetHashCode() ^ TestLocation2.Y.GetHashCode()), "GetHashCode failed to produce the expected hashcode"); diff --git a/OpenSim/Framework/Tests/MundaneFrameworkTests.cs b/OpenSim/Framework/Tests/MundaneFrameworkTests.cs index 1dc8053..3f0a031 100644 --- a/OpenSim/Framework/Tests/MundaneFrameworkTests.cs +++ b/OpenSim/Framework/Tests/MundaneFrameworkTests.cs @@ -100,7 +100,7 @@ namespace OpenSim.Framework.Tests cadu.AVHeight = Size1.Z; AgentPosition position2 = new AgentPosition(); - position2.CopyFrom(cadu); + position2.CopyFrom(cadu, position1.SessionID); Assert.IsTrue( position2.AgentID == position1.AgentID diff --git a/OpenSim/Framework/Tests/UtilTest.cs b/OpenSim/Framework/Tests/UtilTest.cs index 11ca068..cfe3139 100644 --- a/OpenSim/Framework/Tests/UtilTest.cs +++ b/OpenSim/Framework/Tests/UtilTest.cs @@ -173,8 +173,8 @@ namespace OpenSim.Framework.Tests [Test] public void SLUtilTypeConvertTests() { - int[] assettypes = new int[]{-1,0,1,2,3,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22 - ,23,24,25,46,47,48}; + int[] assettypes = new int[]{-1,0,1,2,3,5,6,7,8,10,11,12,13,17,18,19,20,21,22 + ,24,25}; string[] contenttypes = new string[] { "application/octet-stream", @@ -186,26 +186,18 @@ namespace OpenSim.Framework.Tests "application/vnd.ll.primitive", "application/vnd.ll.notecard", "application/vnd.ll.folder", - "application/vnd.ll.rootfolder", "application/vnd.ll.lsltext", "application/vnd.ll.lslbyte", "image/tga", "application/vnd.ll.bodypart", - "application/vnd.ll.trashfolder", - "application/vnd.ll.snapshotfolder", - "application/vnd.ll.lostandfoundfolder", "audio/x-wav", "image/tga", "image/jpeg", "application/vnd.ll.animation", "application/vnd.ll.gesture", "application/x-metaverse-simstate", - "application/vnd.ll.favoritefolder", "application/vnd.ll.link", "application/vnd.ll.linkfolder", - "application/vnd.ll.currentoutfitfolder", - "application/vnd.ll.outfitfolder", - "application/vnd.ll.myoutfitsfolder" }; for (int i=0;i + /// A thread-safe Random since the .NET version is not. + /// See http://msdn.microsoft.com/en-us/library/system.random%28v=vs.100%29.aspx + /// + public class ThreadSafeRandom : Random + { + public ThreadSafeRandom() : base() {} + + public ThreadSafeRandom(int seed): base (seed) {} + + public override int Next() + { + lock (this) + return base.Next(); + } + + public override int Next(int maxValue) + { + lock (this) + return base.Next(maxValue); + } + + public override int Next(int minValue, int maxValue) + { + lock (this) + return base.Next(minValue, maxValue); + } + + public override void NextBytes(byte[] buffer) + { + lock (this) + base.NextBytes(buffer); + } + + public override double NextDouble() + { + lock (this) + return base.NextDouble(); + } + } +} \ No newline at end of file diff --git a/OpenSim/Framework/ThrottleOutPacketType.cs b/OpenSim/Framework/ThrottleOutPacketType.cs index d56231a..ca4b126 100644 --- a/OpenSim/Framework/ThrottleOutPacketType.cs +++ b/OpenSim/Framework/ThrottleOutPacketType.cs @@ -47,9 +47,6 @@ namespace OpenSim.Framework Texture = 5, /// Non-texture assets Asset = 6, - /// Avatar and primitive data - /// This is a sub-category of Task - State = 7, } [Flags] @@ -61,6 +58,5 @@ namespace OpenSim.Framework Task = 1 << 3, Texture = 1 << 4, Asset = 1 << 5, - State = 1 << 6, } } diff --git a/OpenSim/Framework/UntrustedWebRequest.cs b/OpenSim/Framework/UntrustedWebRequest.cs deleted file mode 100644 index e6411cc..0000000 --- a/OpenSim/Framework/UntrustedWebRequest.cs +++ /dev/null @@ -1,230 +0,0 @@ -/* - * 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 OpenSimulator 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.Net.Security; -using System.Text; -using log4net; - -namespace OpenSim.Framework -{ - /// - /// Used for requests to untrusted endpoints that may potentially be - /// malicious - /// - public static class UntrustedHttpWebRequest - { - /// Setting this to true will allow HTTP connections to localhost - private const bool DEBUG = true; - - private static readonly ILog m_log = - LogManager.GetLogger( - System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - - private static readonly ICollection allowableSchemes = new List { "http", "https" }; - - /// - /// Creates an HttpWebRequest that is hardened against malicious - /// endpoints after ensuring the given Uri is safe to retrieve - /// - /// Web location to request - /// A hardened HttpWebRequest if the uri was determined to be safe - /// If uri is null - /// If uri is unsafe - public static HttpWebRequest Create(Uri uri) - { - return Create(uri, DEBUG, 1000 * 5, 1000 * 20, 10); - } - - /// - /// Creates an HttpWebRequest that is hardened against malicious - /// endpoints after ensuring the given Uri is safe to retrieve - /// - /// Web location to request - /// True to allow connections to localhost, otherwise false - /// Read write timeout, in milliseconds - /// Connection timeout, in milliseconds - /// Maximum number of allowed redirects - /// A hardened HttpWebRequest if the uri was determined to be safe - /// If uri is null - /// If uri is unsafe - public static HttpWebRequest Create(Uri uri, bool allowLoopback, int readWriteTimeoutMS, int timeoutMS, int maximumRedirects) - { - if (uri == null) - throw new ArgumentNullException("uri"); - - if (!IsUriAllowable(uri, allowLoopback)) - throw new ArgumentException("Uri " + uri + " was rejected"); - - HttpWebRequest httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(uri); - httpWebRequest.MaximumAutomaticRedirections = maximumRedirects; - httpWebRequest.ReadWriteTimeout = readWriteTimeoutMS; - httpWebRequest.Timeout = timeoutMS; - httpWebRequest.KeepAlive = false; - - return httpWebRequest; - } - - public static string PostToUntrustedUrl(Uri url, string data) - { - try - { - byte[] requestData = System.Text.Encoding.UTF8.GetBytes(data); - - HttpWebRequest request = Create(url); - request.Method = "POST"; - request.ContentLength = requestData.Length; - request.ContentType = "application/x-www-form-urlencoded"; - - using (Stream requestStream = request.GetRequestStream()) - requestStream.Write(requestData, 0, requestData.Length); - - using (WebResponse response = request.GetResponse()) - { - using (Stream responseStream = response.GetResponseStream()) - return responseStream.GetStreamString(); - } - } - catch (Exception ex) - { - m_log.Warn("POST to untrusted URL " + url + " failed: " + ex.Message); - return null; - } - } - - public static string GetUntrustedUrl(Uri url) - { - try - { - HttpWebRequest request = Create(url); - - using (WebResponse response = request.GetResponse()) - { - using (Stream responseStream = response.GetResponseStream()) - return responseStream.GetStreamString(); - } - } - catch (Exception ex) - { - m_log.Warn("GET from untrusted URL " + url + " failed: " + ex.Message); - return null; - } - } - - /// - /// Determines whether a URI is allowed based on scheme and host name. - /// No requireSSL check is done here - /// - /// True to allow loopback addresses to be used - /// The URI to test for whether it should be allowed. - /// - /// true if [is URI allowable] [the specified URI]; otherwise, false. - /// - private static bool IsUriAllowable(Uri uri, bool allowLoopback) - { - if (!allowableSchemes.Contains(uri.Scheme)) - { - m_log.WarnFormat("Rejecting URL {0} because it uses a disallowed scheme.", uri); - return false; - } - - // Try to interpret the hostname as an IP address so we can test for internal - // IP address ranges. Note that IP addresses can appear in many forms - // (e.g. http://127.0.0.1, http://2130706433, http://0x0100007f, http://::1 - // So we convert them to a canonical IPAddress instance, and test for all - // non-routable IP ranges: 10.*.*.*, 127.*.*.*, ::1 - // Note that Uri.IsLoopback is very unreliable, not catching many of these variants. - IPAddress hostIPAddress; - if (IPAddress.TryParse(uri.DnsSafeHost, out hostIPAddress)) - { - byte[] addressBytes = hostIPAddress.GetAddressBytes(); - - // The host is actually an IP address. - switch (hostIPAddress.AddressFamily) - { - case System.Net.Sockets.AddressFamily.InterNetwork: - if (!allowLoopback && (addressBytes[0] == 127 || addressBytes[0] == 10)) - { - m_log.WarnFormat("Rejecting URL {0} because it is a loopback address.", uri); - return false; - } - break; - case System.Net.Sockets.AddressFamily.InterNetworkV6: - if (!allowLoopback && IsIPv6Loopback(hostIPAddress)) - { - m_log.WarnFormat("Rejecting URL {0} because it is a loopback address.", uri); - return false; - } - break; - default: - m_log.WarnFormat("Rejecting URL {0} because it does not use an IPv4 or IPv6 address.", uri); - return false; - } - } - else - { - // The host is given by name. We require names to contain periods to - // help make sure it's not an internal address. - if (!allowLoopback && !uri.Host.Contains(".")) - { - m_log.WarnFormat("Rejecting URL {0} because it does not contain a period in the host name.", uri); - return false; - } - } - - return true; - } - - /// - /// Determines whether an IP address is the IPv6 equivalent of "localhost/127.0.0.1". - /// - /// The ip address to check. - /// - /// true if this is a loopback IP address; false otherwise. - /// - private static bool IsIPv6Loopback(IPAddress ip) - { - if (ip == null) - throw new ArgumentNullException("ip"); - - byte[] addressBytes = ip.GetAddressBytes(); - for (int i = 0; i < addressBytes.Length - 1; i++) - { - if (addressBytes[i] != 0) - return false; - } - - if (addressBytes[addressBytes.Length - 1] != 1) - return false; - - return true; - } - } -} diff --git a/OpenSim/Framework/UserProfileData.cs b/OpenSim/Framework/UserProfileData.cs index 9bac739..266ccf0 100644 --- a/OpenSim/Framework/UserProfileData.cs +++ b/OpenSim/Framework/UserProfileData.cs @@ -160,15 +160,19 @@ namespace OpenSim.Framework public virtual ulong HomeRegion { get - { - return Utils.UIntsToLong( - m_homeRegionX * (uint)Constants.RegionSize, m_homeRegionY * (uint)Constants.RegionSize); + { + return Util.RegionWorldLocToHandle(Util.RegionToWorldLoc(m_homeRegionX), Util.RegionToWorldLoc(m_homeRegionY)); + // return Utils.UIntsToLong( m_homeRegionX * (uint)Constants.RegionSize, m_homeRegionY * (uint)Constants.RegionSize); } set { - m_homeRegionX = (uint) (value >> 40); - m_homeRegionY = (((uint) (value)) >> 8); + uint regionWorldLocX, regionWorldLocY; + Util.RegionHandleToWorldLoc(value, out regionWorldLocX, out regionWorldLocY); + m_homeRegionX = Util.WorldToRegionLoc(regionWorldLocX); + m_homeRegionY = Util.WorldToRegionLoc(regionWorldLocY); + // m_homeRegionX = (uint) (value >> 40); + // m_homeRegionY = (((uint) (value)) >> 8); } } diff --git a/OpenSim/Framework/UserProfiles.cs b/OpenSim/Framework/UserProfiles.cs new file mode 100644 index 0000000..98ab651 --- /dev/null +++ b/OpenSim/Framework/UserProfiles.cs @@ -0,0 +1,126 @@ +/* + * 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 OpenSimulator 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 OpenMetaverse; + +namespace OpenSim.Framework +{ + public class UserClassifiedAdd + { + public UUID ClassifiedId = UUID.Zero; + public UUID CreatorId = UUID.Zero; + public int CreationDate = 0; + public int ExpirationDate = 0; + public int Category = 0; + public string Name = string.Empty; + public string Description = string.Empty; + public UUID ParcelId = UUID.Zero; + public int ParentEstate = 0; + public UUID SnapshotId = UUID.Zero; + public string SimName = string.Empty; + public string GlobalPos = "<0,0,0>"; + public string ParcelName = string.Empty; + public byte Flags = 0; + public int Price = 0; + } + + public class UserProfileProperties + { + public UUID UserId = UUID.Zero; + public UUID PartnerId = UUID.Zero; + public bool PublishProfile = false; + public bool PublishMature = false; + public string WebUrl = string.Empty; + public int WantToMask = 0; + public string WantToText = string.Empty; + public int SkillsMask = 0; + public string SkillsText = string.Empty; + public string Language = string.Empty; + public UUID ImageId = UUID.Zero; + public string AboutText = string.Empty; + public UUID FirstLifeImageId = UUID.Zero; + public string FirstLifeText = string.Empty; + } + + public class UserProfilePick + { + public UUID PickId = UUID.Zero; + public UUID CreatorId = UUID.Zero; + public bool TopPick = false; + public string Name = string.Empty; + public string OriginalName = string.Empty; + public string Desc = string.Empty; + public UUID ParcelId = UUID.Zero; + public UUID SnapshotId = UUID.Zero; + public string ParcelName = string.Empty; + public string SimName = string.Empty; + public string GlobalPos = "<0,0,0>"; + public string Gatekeeper = string.Empty; + public int SortOrder = 0; + public bool Enabled = false; + } + + public class UserProfileNotes + { + public UUID UserId; + public UUID TargetId; + public string Notes; + } + + public class UserPreferences + { + public UUID UserId; + public bool IMViaEmail = false; + public bool Visible = false; + public string EMail = string.Empty; + } + + public class UserAccountProperties + { + public string EmailAddress = string.Empty; + public string Firstname = string.Empty; + public string LastName = string.Empty; + public string Password = string.Empty; + public string UserId = string.Empty; + } + + public class UserAccountAuth + { + public string UserId = UUID.Zero.ToString(); + public string Password = string.Empty; + } + + public class UserAppData + { + public string TagId = string.Empty; + public string DataKey = string.Empty; + public string UserId = UUID.Zero.ToString(); + public string DataVal = string.Empty; + } +} + diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index 0bd2977..1f74168 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -45,14 +45,33 @@ using System.Text.RegularExpressions; using System.Xml; using System.Threading; using log4net; +using log4net.Appender; using Nini.Config; using Nwc.XmlRpc; using OpenMetaverse; using OpenMetaverse.StructuredData; using Amib.Threading; +using System.Collections.Concurrent; +using System.Collections.Specialized; +using System.Web; namespace OpenSim.Framework { + [Flags] + public enum PermissionMask : uint + { + None = 0, + Transfer = 1 << 13, + Modify = 1 << 14, + Copy = 1 << 15, + Export = 1 << 16, + Move = 1 << 19, + Damage = 1 << 20, + // All does not contain Export, which is special and must be + // explicitly given + All = (1 << 13) | (1 << 14) | (1 << 15) | (1 << 19) + } + /// /// The method used by Util.FireAndForget for asynchronously firing events /// @@ -73,14 +92,53 @@ namespace OpenSim.Framework } /// + /// Class for delivering SmartThreadPool statistical information + /// + /// + /// We do it this way so that we do not directly expose STP. + /// + public class STPInfo + { + public string Name { get; set; } + public STPStartInfo STPStartInfo { get; set; } + public WIGStartInfo WIGStartInfo { get; set; } + public bool IsIdle { get; set; } + public bool IsShuttingDown { get; set; } + public int MaxThreads { get; set; } + public int MinThreads { get; set; } + public int InUseThreads { get; set; } + public int ActiveThreads { get; set; } + public int WaitingCallbacks { get; set; } + public int MaxConcurrentWorkItems { get; set; } + } + + /// /// Miscellaneous utility functions /// - public class Util + public static class Util { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + /// + /// Log-level for the thread pool: + /// 0 = no logging + /// 1 = only first line of stack trace; don't log common threads + /// 2 = full stack trace; don't log common threads + /// 3 = full stack trace, including common threads + /// + public static int LogThreadPool { get; set; } + public static bool LogOverloads { get; set; } + + public static readonly int MAX_THREADPOOL_LEVEL = 3; + + static Util() + { + LogThreadPool = 0; + LogOverloads = true; + } + private static uint nextXferID = 5000; - private static Random randomClass = new Random(); + private static Random randomClass = new ThreadSafeRandom(); // Get a list of invalid file characters (OS dependent) private static string regexInvalidFileChars = "[" + new String(Path.GetInvalidFileNameChars()) + "]"; @@ -92,8 +150,11 @@ namespace OpenSim.Framework /// private static SmartThreadPool m_ThreadPool; + // Watchdog timer that aborts threads that have timed-out + private static Timer m_threadPoolWatchdog; + // Unix-epoch starts at January 1st 1970, 00:00:00 UTC. And all our times in the server are (or at least should be) in UTC. - private static readonly DateTime unixEpoch = + public static readonly DateTime UnixEpoch = DateTime.ParseExact("1970-01-01 00:00:00 +0", "yyyy-MM-dd hh:mm:ss z", DateTimeFormatInfo.InvariantInfo).ToUniversalTime(); private static readonly string rawUUIDPattern @@ -104,6 +165,11 @@ namespace OpenSim.Framework public static FireAndForgetMethod DefaultFireAndForgetMethod = FireAndForgetMethod.SmartThreadPool; public static FireAndForgetMethod FireAndForgetMethod = DefaultFireAndForgetMethod; + public static bool IsPlatformMono + { + get { return Type.GetType("Mono.Runtime") != null; } + } + /// /// Gets the name of the directory where the current running executable /// is located @@ -291,6 +357,49 @@ namespace OpenSim.Framework return Utils.UIntsToLong(X, Y); } + // Regions are identified with a 'handle' made up of its region coordinates packed into a ulong. + // Several places rely on the ability to extract a region's location from its handle. + // Note the location is in 'world coordinates' (see below). + // Region handles are based on the lowest coordinate of the region so trim the passed x,y to be the regions 0,0. + public static ulong RegionWorldLocToHandle(uint X, uint Y) + { + return Utils.UIntsToLong(X, Y); + } + + public static ulong RegionLocToHandle(uint X, uint Y) + { + return Utils.UIntsToLong(Util.RegionToWorldLoc(X), Util.RegionToWorldLoc(Y)); + } + + public static void RegionHandleToWorldLoc(ulong handle, out uint X, out uint Y) + { + X = (uint)(handle >> 32); + Y = (uint)(handle & (ulong)uint.MaxValue); + } + + public static void RegionHandleToRegionLoc(ulong handle, out uint X, out uint Y) + { + uint worldX, worldY; + RegionHandleToWorldLoc(handle, out worldX, out worldY); + X = WorldToRegionLoc(worldX); + Y = WorldToRegionLoc(worldY); + } + + // A region location can be 'world coordinates' (meters from zero) or 'region coordinates' + // (number of regions from zero). This measurement of regions relies on the legacy 256 region size. + // These routines exist to make what is being converted explicit so the next person knows what was meant. + // Convert a region's 'world coordinate' to its 'region coordinate'. + public static uint WorldToRegionLoc(uint worldCoord) + { + return worldCoord / Constants.RegionSize; + } + + // Convert a region's 'region coordinate' to its 'world coordinate'. + public static uint RegionToWorldLoc(uint regionCoord) + { + return regionCoord * Constants.RegionSize; + } + public static T Clamp(T x, T min, T max) where T : IComparable { @@ -302,15 +411,31 @@ namespace OpenSim.Framework // Clamp the maximum magnitude of a vector public static Vector3 ClampV(Vector3 x, float max) { - Vector3 ret = x; float lenSq = x.LengthSquared(); if (lenSq > (max * max)) { x = x / x.Length() * max; } + return x; } + /// + /// Check if any of the values in a Vector3 are NaN or Infinity + /// + /// Vector3 to check + /// + public static bool IsNanOrInfinity(Vector3 v) + { + if (float.IsNaN(v.X) || float.IsNaN(v.Y) || float.IsNaN(v.Z)) + return true; + + if (float.IsInfinity(v.X) || float.IsInfinity(v.Y) || float.IsNaN(v.Z)) + return true; + + return false; + } + // Inclusive, within range test (true if equal to the endpoints) public static bool InRange(T x, T min, T max) where T : IComparable @@ -400,6 +525,19 @@ namespace OpenSim.Framework return sb.ToString(); } + public static byte[] DocToBytes(XmlDocument doc) + { + using (MemoryStream ms = new MemoryStream()) + using (XmlTextWriter xw = new XmlTextWriter(ms, null)) + { + xw.Formatting = Formatting.Indented; + doc.WriteTo(xw); + xw.Flush(); + + return ms.ToArray(); + } + } + /// /// Is the platform Windows? /// @@ -416,6 +554,11 @@ namespace OpenSim.Framework public static bool LoadArchSpecificWindowsDll(string libraryName) { + return LoadArchSpecificWindowsDll(libraryName, string.Empty); + } + + public static bool LoadArchSpecificWindowsDll(string libraryName, string path) + { // We do this so that OpenSimulator on Windows loads the correct native library depending on whether // it's running as a 32-bit process or a 64-bit one. By invoking LoadLibary here, later DLLImports // will find it already loaded later on. @@ -425,9 +568,9 @@ namespace OpenSim.Framework string nativeLibraryPath; if (Util.Is64BitProcess()) - nativeLibraryPath = "lib64/" + libraryName; + nativeLibraryPath = Path.Combine(Path.Combine(path, "lib64"), libraryName); else - nativeLibraryPath = "lib32/" + libraryName; + nativeLibraryPath = Path.Combine(Path.Combine(path, "lib32"), libraryName); m_log.DebugFormat("[UTIL]: Loading native Windows library at {0}", nativeLibraryPath); @@ -479,20 +622,18 @@ namespace OpenSim.Framework public static int ToUnixTime(DateTime stamp) { - TimeSpan t = stamp.ToUniversalTime() - unixEpoch; - return (int) t.TotalSeconds; + TimeSpan t = stamp.ToUniversalTime() - UnixEpoch; + return (int)t.TotalSeconds; } public static DateTime ToDateTime(ulong seconds) { - DateTime epoch = unixEpoch; - return epoch.AddSeconds(seconds); + return UnixEpoch.AddSeconds(seconds); } public static DateTime ToDateTime(int seconds) { - DateTime epoch = unixEpoch; - return epoch.AddSeconds(seconds); + return UnixEpoch.AddSeconds(seconds); } /// @@ -664,16 +805,6 @@ namespace OpenSim.Framework } /// - /// Converts a URL to a IPAddress - /// - /// URL Standard Format - /// A resolved IP Address - public static IPAddress GetHostFromURL(string url) - { - return GetHostFromDNS(url.Split(new char[] {'/', ':'})[3]); - } - - /// /// Returns a IP address from a specified DNS, favouring IPv4 addresses. /// /// DNS Hostname @@ -763,6 +894,54 @@ namespace OpenSim.Framework } /// + /// Parses a foreign asset ID. + /// + /// A possibly-foreign asset ID: http://grid.example.com:8002/00000000-0000-0000-0000-000000000000 + /// The URL: http://grid.example.com:8002 + /// The asset ID: 00000000-0000-0000-0000-000000000000. Returned even if 'id' isn't foreign. + /// True: this is a foreign asset ID; False: it isn't + public static bool ParseForeignAssetID(string id, out string url, out string assetID) + { + url = String.Empty; + assetID = String.Empty; + + UUID uuid; + if (UUID.TryParse(id, out uuid)) + { + assetID = uuid.ToString(); + return false; + } + + if ((id.Length == 0) || (id[0] != 'h' && id[0] != 'H')) + return false; + + Uri assetUri; + if (!Uri.TryCreate(id, UriKind.Absolute, out assetUri) || assetUri.Scheme != Uri.UriSchemeHttp) + return false; + + // Simian + if (assetUri.Query != string.Empty) + { + NameValueCollection qscoll = HttpUtility.ParseQueryString(assetUri.Query); + assetID = qscoll["id"]; + if (assetID != null) + url = id.Replace(assetID, ""); // Malformed again, as simian expects + else + url = id; // !!! best effort + } + else // robust + { + url = "http://" + assetUri.Authority; + assetID = assetUri.LocalPath.Trim(new char[] { '/' }); + } + + if (!UUID.TryParse(assetID, out uuid)) + return false; + + return true; + } + + /// /// Removes all invalid path chars (OS dependent) /// /// path @@ -816,9 +995,22 @@ namespace OpenSim.Framework return "."; } + public static string logFile() + { + foreach (IAppender appender in LogManager.GetRepository().GetAppenders()) + { + if (appender is FileAppender) + { + return ((FileAppender)appender).File; + } + } + + return "./OpenSim.log"; + } + public static string logDir() { - return "."; + return Path.GetDirectoryName(logFile()); } // From: http://coercedcode.blogspot.com/2008/03/c-generate-unique-filenames-within.html @@ -849,12 +1041,13 @@ namespace OpenSim.Framework return FileName; } - // Nini (config) related Methods + #region Nini (config) related Methods + public static IConfigSource ConvertDataRowToXMLConfig(DataRow row, string fileName) { if (!File.Exists(fileName)) { - //create new file + // create new file } XmlConfigSource config = new XmlConfigSource(fileName); AddDataRowToConfig(config, row); @@ -872,6 +1065,202 @@ namespace OpenSim.Framework } } + /// + /// Gets the value of a configuration variable by looking into + /// multiple sections in order. The latter sections overwrite + /// any values previously found. + /// + /// Type of the variable + /// The configuration object + /// The configuration variable + /// Ordered sequence of sections to look at + /// + public static T GetConfigVarFromSections(IConfigSource config, string varname, string[] sections) + { + return GetConfigVarFromSections(config, varname, sections, default(T)); + } + + /// + /// Gets the value of a configuration variable by looking into + /// multiple sections in order. The latter sections overwrite + /// any values previously found. + /// + /// + /// If no value is found then the given default value is returned + /// + /// Type of the variable + /// The configuration object + /// The configuration variable + /// Ordered sequence of sections to look at + /// Default value + /// + public static T GetConfigVarFromSections(IConfigSource config, string varname, string[] sections, object val) + { + foreach (string section in sections) + { + IConfig cnf = config.Configs[section]; + if (cnf == null) + continue; + + if (typeof(T) == typeof(String)) + val = cnf.GetString(varname, (string)val); + else if (typeof(T) == typeof(Boolean)) + val = cnf.GetBoolean(varname, (bool)val); + else if (typeof(T) == typeof(Int32)) + val = cnf.GetInt(varname, (int)val); + else if (typeof(T) == typeof(float)) + val = cnf.GetFloat(varname, (float)val); + else + m_log.ErrorFormat("[UTIL]: Unhandled type {0}", typeof(T)); + } + + return (T)val; + } + + public static void MergeEnvironmentToConfig(IConfigSource ConfigSource) + { + IConfig enVars = ConfigSource.Configs["Environment"]; + // if section does not exist then user isn't expecting them, so don't bother. + if( enVars != null ) + { + // load the values from the environment + EnvConfigSource envConfigSource = new EnvConfigSource(); + // add the requested keys + string[] env_keys = enVars.GetKeys(); + foreach ( string key in env_keys ) + { + envConfigSource.AddEnv(key, string.Empty); + } + // load the values from environment + envConfigSource.LoadEnv(); + // add them in to the master + ConfigSource.Merge(envConfigSource); + ConfigSource.ExpandKeyValues(); + } + } + + public static T ReadSettingsFromIniFile(IConfig config, T settingsClass) + { + Type settingsType = settingsClass.GetType(); + + FieldInfo[] fieldInfos = settingsType.GetFields(); + foreach (FieldInfo fieldInfo in fieldInfos) + { + if (!fieldInfo.IsStatic) + { + if (fieldInfo.FieldType == typeof(System.String)) + { + fieldInfo.SetValue(settingsClass, config.Get(fieldInfo.Name, (string)fieldInfo.GetValue(settingsClass))); + } + else if (fieldInfo.FieldType == typeof(System.Boolean)) + { + fieldInfo.SetValue(settingsClass, config.GetBoolean(fieldInfo.Name, (bool)fieldInfo.GetValue(settingsClass))); + } + else if (fieldInfo.FieldType == typeof(System.Int32)) + { + fieldInfo.SetValue(settingsClass, config.GetInt(fieldInfo.Name, (int)fieldInfo.GetValue(settingsClass))); + } + else if (fieldInfo.FieldType == typeof(System.Single)) + { + fieldInfo.SetValue(settingsClass, config.GetFloat(fieldInfo.Name, (float)fieldInfo.GetValue(settingsClass))); + } + else if (fieldInfo.FieldType == typeof(System.UInt32)) + { + fieldInfo.SetValue(settingsClass, Convert.ToUInt32(config.Get(fieldInfo.Name, ((uint)fieldInfo.GetValue(settingsClass)).ToString()))); + } + } + } + + PropertyInfo[] propertyInfos = settingsType.GetProperties(); + foreach (PropertyInfo propInfo in propertyInfos) + { + if ((propInfo.CanRead) && (propInfo.CanWrite)) + { + if (propInfo.PropertyType == typeof(System.String)) + { + propInfo.SetValue(settingsClass, config.Get(propInfo.Name, (string)propInfo.GetValue(settingsClass, null)), null); + } + else if (propInfo.PropertyType == typeof(System.Boolean)) + { + propInfo.SetValue(settingsClass, config.GetBoolean(propInfo.Name, (bool)propInfo.GetValue(settingsClass, null)), null); + } + else if (propInfo.PropertyType == typeof(System.Int32)) + { + propInfo.SetValue(settingsClass, config.GetInt(propInfo.Name, (int)propInfo.GetValue(settingsClass, null)), null); + } + else if (propInfo.PropertyType == typeof(System.Single)) + { + propInfo.SetValue(settingsClass, config.GetFloat(propInfo.Name, (float)propInfo.GetValue(settingsClass, null)), null); + } + if (propInfo.PropertyType == typeof(System.UInt32)) + { + propInfo.SetValue(settingsClass, Convert.ToUInt32(config.Get(propInfo.Name, ((uint)propInfo.GetValue(settingsClass, null)).ToString())), null); + } + } + } + + return settingsClass; + } + + /// + /// Reads a configuration file, configFile, merging it with the main configuration, config. + /// If the file doesn't exist, it copies a given exampleConfigFile onto configFile, and then + /// merges it. + /// + /// The main configuration data + /// The name of a configuration file in ConfigDirectory variable, no path + /// Full path to an example configuration file + /// Full path ConfigDirectory/configFileName + /// True if the file was created in ConfigDirectory, false if it existed + /// True if success + public static bool MergeConfigurationFile(IConfigSource config, string configFileName, string exampleConfigFile, out string configFilePath, out bool created) + { + created = false; + configFilePath = string.Empty; + + IConfig cnf = config.Configs["Startup"]; + if (cnf == null) + { + m_log.WarnFormat("[UTILS]: Startup section doesn't exist"); + return false; + } + + string configDirectory = cnf.GetString("ConfigDirectory", "."); + string configFile = Path.Combine(configDirectory, configFileName); + + if (!File.Exists(configFile) && !string.IsNullOrEmpty(exampleConfigFile)) + { + // We need to copy the example onto it + + if (!Directory.Exists(configDirectory)) + Directory.CreateDirectory(configDirectory); + + try + { + File.Copy(exampleConfigFile, configFile); + created = true; + } + catch (Exception e) + { + m_log.WarnFormat("[UTILS]: Exception copying configuration file {0} to {1}: {2}", configFile, exampleConfigFile, e.Message); + return false; + } + } + + if (File.Exists(configFile)) + { + // Merge + config.Merge(new IniConfigSource(configFile)); + config.ExpandKeyValues(); + configFilePath = configFile; + return true; + } + else + return false; + } + + #endregion + public static float Clip(float x, float min, float max) { return Math.Min(Math.Max(x, min), max); @@ -999,46 +1388,6 @@ namespace OpenSim.Framework return ret; } - public static string Compress(string text) - { - byte[] buffer = Util.UTF8.GetBytes(text); - MemoryStream memory = new MemoryStream(); - using (GZipStream compressor = new GZipStream(memory, CompressionMode.Compress, true)) - { - compressor.Write(buffer, 0, buffer.Length); - } - - memory.Position = 0; - - byte[] compressed = new byte[memory.Length]; - memory.Read(compressed, 0, compressed.Length); - - byte[] compressedBuffer = new byte[compressed.Length + 4]; - Buffer.BlockCopy(compressed, 0, compressedBuffer, 4, compressed.Length); - Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, compressedBuffer, 0, 4); - return Convert.ToBase64String(compressedBuffer); - } - - public static string Decompress(string compressedText) - { - byte[] compressedBuffer = Convert.FromBase64String(compressedText); - using (MemoryStream memory = new MemoryStream()) - { - int msgLength = BitConverter.ToInt32(compressedBuffer, 0); - memory.Write(compressedBuffer, 4, compressedBuffer.Length - 4); - - byte[] buffer = new byte[msgLength]; - - memory.Position = 0; - using (GZipStream decompressor = new GZipStream(memory, CompressionMode.Decompress)) - { - decompressor.Read(buffer, 0, buffer.Length); - } - - return Util.UTF8.GetString(buffer); - } - } - /// /// Copy data from one stream to another, leaving the read position of both streams at the beginning. /// @@ -1119,7 +1468,7 @@ namespace OpenSim.Framework byte[] bytes = { (byte)regionHandle, (byte)(regionHandle >> 8), (byte)(regionHandle >> 16), (byte)(regionHandle >> 24), - (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle << 56), + (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle >> 56), (byte)x, (byte)(x >> 8), 0, 0, (byte)y, (byte)(y >> 8), 0, 0 }; return new UUID(bytes, 0); @@ -1130,7 +1479,7 @@ namespace OpenSim.Framework byte[] bytes = { (byte)regionHandle, (byte)(regionHandle >> 8), (byte)(regionHandle >> 16), (byte)(regionHandle >> 24), - (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle << 56), + (byte)(regionHandle >> 32), (byte)(regionHandle >> 40), (byte)(regionHandle >> 48), (byte)(regionHandle >> 56), (byte)x, (byte)(x >> 8), (byte)z, (byte)(z >> 8), (byte)y, (byte)(y >> 8), 0, 0 }; return new UUID(bytes, 0); @@ -1203,7 +1552,7 @@ namespace OpenSim.Framework ru = "OSX/Mono"; else { - if (Type.GetType("Mono.Runtime") != null) + if (IsPlatformMono) ru = "Win/Mono"; else ru = "Win/.NET"; @@ -1242,79 +1591,56 @@ namespace OpenSim.Framework return displayConnectionString; } - public static T ReadSettingsFromIniFile(IConfig config, T settingsClass) + public static string Base64ToString(string str) { - Type settingsType = settingsClass.GetType(); + Decoder utf8Decode = Encoding.UTF8.GetDecoder(); - FieldInfo[] fieldInfos = settingsType.GetFields(); - foreach (FieldInfo fieldInfo in fieldInfos) + byte[] todecode_byte = Convert.FromBase64String(str); + int charCount = utf8Decode.GetCharCount(todecode_byte, 0, todecode_byte.Length); + char[] decoded_char = new char[charCount]; + utf8Decode.GetChars(todecode_byte, 0, todecode_byte.Length, decoded_char, 0); + string result = new String(decoded_char); + return result; + } + + public static void BinaryToASCII(char[] chars) + { + for (int i = 0; i < chars.Length; i++) { - if (!fieldInfo.IsStatic) - { - if (fieldInfo.FieldType == typeof(System.String)) - { - fieldInfo.SetValue(settingsClass, config.Get(fieldInfo.Name, (string)fieldInfo.GetValue(settingsClass))); - } - else if (fieldInfo.FieldType == typeof(System.Boolean)) - { - fieldInfo.SetValue(settingsClass, config.GetBoolean(fieldInfo.Name, (bool)fieldInfo.GetValue(settingsClass))); - } - else if (fieldInfo.FieldType == typeof(System.Int32)) - { - fieldInfo.SetValue(settingsClass, config.GetInt(fieldInfo.Name, (int)fieldInfo.GetValue(settingsClass))); - } - else if (fieldInfo.FieldType == typeof(System.Single)) - { - fieldInfo.SetValue(settingsClass, config.GetFloat(fieldInfo.Name, (float)fieldInfo.GetValue(settingsClass))); - } - else if (fieldInfo.FieldType == typeof(System.UInt32)) - { - fieldInfo.SetValue(settingsClass, Convert.ToUInt32(config.Get(fieldInfo.Name, ((uint)fieldInfo.GetValue(settingsClass)).ToString()))); - } - } - } - - PropertyInfo[] propertyInfos = settingsType.GetProperties(); - foreach (PropertyInfo propInfo in propertyInfos) - { - if ((propInfo.CanRead) && (propInfo.CanWrite)) - { - if (propInfo.PropertyType == typeof(System.String)) - { - propInfo.SetValue(settingsClass, config.Get(propInfo.Name, (string)propInfo.GetValue(settingsClass, null)), null); - } - else if (propInfo.PropertyType == typeof(System.Boolean)) - { - propInfo.SetValue(settingsClass, config.GetBoolean(propInfo.Name, (bool)propInfo.GetValue(settingsClass, null)), null); - } - else if (propInfo.PropertyType == typeof(System.Int32)) - { - propInfo.SetValue(settingsClass, config.GetInt(propInfo.Name, (int)propInfo.GetValue(settingsClass, null)), null); - } - else if (propInfo.PropertyType == typeof(System.Single)) - { - propInfo.SetValue(settingsClass, config.GetFloat(propInfo.Name, (float)propInfo.GetValue(settingsClass, null)), null); - } - if (propInfo.PropertyType == typeof(System.UInt32)) - { - propInfo.SetValue(settingsClass, Convert.ToUInt32(config.Get(propInfo.Name, ((uint)propInfo.GetValue(settingsClass, null)).ToString())), null); - } - } + char ch = chars[i]; + if (ch < 32 || ch > 127) + chars[i] = '.'; } + } - return settingsClass; + public static string BinaryToASCII(string src) + { + char[] chars = src.ToCharArray(); + BinaryToASCII(chars); + return new String(chars); } - public static string Base64ToString(string str) + /// + /// Reads a known number of bytes from a stream. + /// Throws EndOfStreamException if the stream doesn't contain enough data. + /// + /// The stream to read data from + /// The array to write bytes into. The array + /// will be completely filled from the stream, so an appropriate + /// size must be given. + public static void ReadStream(Stream stream, byte[] data) { - Decoder utf8Decode = Encoding.UTF8.GetDecoder(); + int offset = 0; + int remaining = data.Length; - byte[] todecode_byte = Convert.FromBase64String(str); - int charCount = utf8Decode.GetCharCount(todecode_byte, 0, todecode_byte.Length); - char[] decoded_char = new char[charCount]; - utf8Decode.GetChars(todecode_byte, 0, todecode_byte.Length, decoded_char, 0); - string result = new String(decoded_char); - return result; + while (remaining > 0) + { + int read = stream.Read(data, offset, remaining); + if (read <= 0) + throw new EndOfStreamException(String.Format("End of stream reached with {0} bytes left to read", remaining)); + remaining -= read; + offset += read; + } } public static Guid GetHashGuid(string data, string salt) @@ -1466,32 +1792,6 @@ namespace OpenSim.Framework return found.ToArray(); } - public static string ServerURI(string uri) - { - if (uri == string.Empty) - return string.Empty; - - // Get rid of eventual slashes at the end - uri = uri.TrimEnd('/'); - - IPAddress ipaddr1 = null; - string port1 = ""; - try - { - ipaddr1 = Util.GetHostFromURL(uri); - } - catch { } - - try - { - port1 = uri.Split(new char[] { ':' })[2]; - } - catch { } - - // We tried our best to convert the domain names to IP addresses - return (ipaddr1 != null) ? "http://" + ipaddr1.ToString() + ":" + port1 : uri; - } - /// /// Convert a string to a byte format suitable for transport in an LLUDP packet. The output is truncated to 256 bytes if necessary. /// @@ -1518,17 +1818,26 @@ namespace OpenSim.Framework /// public static byte[] StringToBytes256(string str) { - if (String.IsNullOrEmpty(str)) { return Utils.EmptyBytes; } - if (str.Length > 254) str = str.Remove(254); - if (!str.EndsWith("\0")) { str += "\0"; } + if (String.IsNullOrEmpty(str)) + return Utils.EmptyBytes; + + if (!str.EndsWith("\0")) + str += "\0"; // Because this is UTF-8 encoding and not ASCII, it's possible we // might have gotten an oversized array even after the string trim byte[] data = UTF8.GetBytes(str); + if (data.Length > 256) { - Array.Resize(ref data, 256); - data[255] = 0; + int cut = 255; + if((data[cut] & 0x80 ) != 0 ) + { + while(cut > 0 && (data[cut] & 0xc0) != 0xc0) + cut--; + } + Array.Resize(ref data, cut + 1); + data[cut] = 0; } return data; @@ -1560,23 +1869,56 @@ namespace OpenSim.Framework /// public static byte[] StringToBytes1024(string str) { - if (String.IsNullOrEmpty(str)) { return Utils.EmptyBytes; } - if (str.Length > 1023) str = str.Remove(1023); - if (!str.EndsWith("\0")) { str += "\0"; } + if (String.IsNullOrEmpty(str)) + return Utils.EmptyBytes; + + if (!str.EndsWith("\0")) + str += "\0"; // Because this is UTF-8 encoding and not ASCII, it's possible we // might have gotten an oversized array even after the string trim byte[] data = UTF8.GetBytes(str); + if (data.Length > 1024) { - Array.Resize(ref data, 1024); - data[1023] = 0; + int cut = 1023; + if((data[cut] & 0x80 ) != 0 ) + { + while(cut > 0 && (data[cut] & 0xc0) != 0xc0) + cut--; + } + Array.Resize(ref data, cut + 1); + data[cut] = 0; } return data; } /// + /// Pretty format the hashtable contents to a single line. + /// + /// + /// Used for debugging output. + /// + /// + public static string PrettyFormatToSingleLine(Hashtable ht) + { + StringBuilder sb = new StringBuilder(); + + int i = 0; + + foreach (string key in ht.Keys) + { + sb.AppendFormat("{0}:{1}", key, ht[key]); + + if (++i < ht.Count) + sb.AppendFormat(", "); + } + + return sb.ToString(); + } + + /// /// Used to trigger an early library load on Windows systems. /// /// @@ -1646,25 +1988,28 @@ namespace OpenSim.Framework } } - public static void FireAndForget(System.Threading.WaitCallback callback) - { - FireAndForget(callback, null); - } - - public static void InitThreadPool(int maxThreads) + public static void InitThreadPool(int minThreads, int maxThreads) { if (maxThreads < 2) throw new ArgumentOutOfRangeException("maxThreads", "maxThreads must be greater than 2"); + + if (minThreads > maxThreads || minThreads < 2) + throw new ArgumentOutOfRangeException("minThreads", "minThreads must be greater than 2 and less than or equal to maxThreads"); + if (m_ThreadPool != null) - throw new InvalidOperationException("SmartThreadPool is already initialized"); + { + m_log.Warn("SmartThreadPool is already initialized. Ignoring request."); + return; + } STPStartInfo startInfo = new STPStartInfo(); startInfo.ThreadPoolName = "Util"; startInfo.IdleTimeout = 2000; startInfo.MaxWorkerThreads = maxThreads; - startInfo.MinWorkerThreads = 2; + startInfo.MinWorkerThreads = minThreads; m_ThreadPool = new SmartThreadPool(startInfo); + m_threadPoolWatchdog = new Timer(ThreadPoolWatchdog, null, 0, 1000); } public static int FireAndForgetCount() @@ -1687,15 +2032,179 @@ namespace OpenSim.Framework throw new NotImplementedException(); } } + + /// + /// Additional information about threads in the main thread pool. Used to time how long the + /// thread has been running, and abort it if it has timed-out. + /// + private class ThreadInfo + { + public long ThreadFuncNum { get; set; } + public string StackTrace { get; set; } + private string context; + public bool LogThread { get; set; } + + public IWorkItemResult WorkItem { get; set; } + public Thread Thread { get; set; } + public bool Running { get; set; } + public bool Aborted { get; set; } + private int started; + + public ThreadInfo(long threadFuncNum, string context) + { + ThreadFuncNum = threadFuncNum; + this.context = context; + LogThread = false; + Thread = null; + Running = false; + Aborted = false; + } + + public void Started() + { + Thread = Thread.CurrentThread; + started = EnvironmentTickCount(); + Running = true; + } + + public void Ended() + { + Running = false; + } + + public int Elapsed() + { + return EnvironmentTickCountSubtract(started); + } + + public void Abort() + { + Aborted = true; + WorkItem.Cancel(true); + } + + /// + /// Returns the thread's stack trace. + /// + /// + /// May return one of two stack traces. First, tries to get the thread's active stack + /// trace. But this can fail, so as a fallback this method will return the stack + /// trace that was active when the task was queued. + /// + public string GetStackTrace() + { + string ret = (context == null) ? "" : ("(" + context + ") "); + + StackTrace activeStackTrace = Util.GetStackTrace(Thread); + if (activeStackTrace != null) + ret += activeStackTrace.ToString(); + else if (StackTrace != null) + ret += "(Stack trace when queued) " + StackTrace; + // else, no stack trace available + + return ret; + } + } + + private static long nextThreadFuncNum = 0; + private static long numQueuedThreadFuncs = 0; + private static long numRunningThreadFuncs = 0; + private static long numTotalThreadFuncsCalled = 0; + private static Int32 threadFuncOverloadMode = 0; + + public static long TotalQueuedFireAndForgetCalls { get { return numQueuedThreadFuncs; } } + public static long TotalRunningFireAndForgetCalls { get { return numRunningThreadFuncs; } } + + // Maps (ThreadFunc number -> Thread) + private static ConcurrentDictionary activeThreads = new ConcurrentDictionary(); + + private static readonly int THREAD_TIMEOUT = 10 * 60 * 1000; // 10 minutes + + /// + /// Finds threads in the main thread pool that have timed-out, and aborts them. + /// + private static void ThreadPoolWatchdog(object state) + { + foreach (KeyValuePair entry in activeThreads) + { + ThreadInfo t = entry.Value; + if (t.Running && !t.Aborted && (t.Elapsed() >= THREAD_TIMEOUT)) + { + m_log.WarnFormat("Timeout in threadfunc {0} ({1}) {2}", t.ThreadFuncNum, t.Thread.Name, t.GetStackTrace()); + t.Abort(); + + ThreadInfo dummy; + activeThreads.TryRemove(entry.Key, out dummy); + + // It's possible that the thread won't abort. To make sure the thread pool isn't + // depleted, increase the pool size. + m_ThreadPool.MaxThreads++; + } + } + } + + public static long TotalFireAndForgetCallsMade { get { return numTotalThreadFuncsCalled; } } + + public static Dictionary GetFireAndForgetCallsMade() + { + return new Dictionary(m_fireAndForgetCallsMade); + } + + private static Dictionary m_fireAndForgetCallsMade = new Dictionary(); + + public static Dictionary GetFireAndForgetCallsInProgress() + { + return new Dictionary(m_fireAndForgetCallsInProgress); + } + + private static Dictionary m_fireAndForgetCallsInProgress = new Dictionary(); + + public static void FireAndForget(System.Threading.WaitCallback callback) + { + FireAndForget(callback, null, null); + } public static void FireAndForget(System.Threading.WaitCallback callback, object obj) { + FireAndForget(callback, obj, null); + } + + public static void FireAndForget(System.Threading.WaitCallback callback, object obj, string context) + { + Interlocked.Increment(ref numTotalThreadFuncsCalled); + + if (context != null) + { + if (!m_fireAndForgetCallsMade.ContainsKey(context)) + m_fireAndForgetCallsMade[context] = 1; + else + m_fireAndForgetCallsMade[context]++; + + if (!m_fireAndForgetCallsInProgress.ContainsKey(context)) + m_fireAndForgetCallsInProgress[context] = 1; + else + m_fireAndForgetCallsInProgress[context]++; + } + WaitCallback realCallback; + bool loggingEnabled = LogThreadPool > 0; + + long threadFuncNum = Interlocked.Increment(ref nextThreadFuncNum); + ThreadInfo threadInfo = new ThreadInfo(threadFuncNum, context); + if (FireAndForgetMethod == FireAndForgetMethod.RegressionTest) { // If we're running regression tests, then we want any exceptions to rise up to the test code. - realCallback = o => { Culture.SetCurrentCulture(); callback(o); }; + realCallback = + o => + { + Culture.SetCurrentCulture(); + callback(o); + + if (context != null) + m_fireAndForgetCallsInProgress[context]--; + }; } else { @@ -1704,118 +2213,291 @@ namespace OpenSim.Framework // for decimals places but is read by a culture that treats commas as number seperators. realCallback = o => { - Culture.SetCurrentCulture(); + long numQueued1 = Interlocked.Decrement(ref numQueuedThreadFuncs); + long numRunning1 = Interlocked.Increment(ref numRunningThreadFuncs); + threadInfo.Started(); + activeThreads[threadFuncNum] = threadInfo; try { + if ((loggingEnabled || (threadFuncOverloadMode == 1)) && threadInfo.LogThread) + m_log.DebugFormat("Run threadfunc {0} (Queued {1}, Running {2})", threadFuncNum, numQueued1, numRunning1); + + Culture.SetCurrentCulture(); + callback(o); } + catch (ThreadAbortException e) + { + m_log.Error(string.Format("Aborted threadfunc {0} ", threadFuncNum), e); + } catch (Exception e) { - m_log.ErrorFormat( - "[UTIL]: Continuing after async_call_method thread terminated with exception {0}{1}", - e.Message, e.StackTrace); + m_log.Error(string.Format("[UTIL]: Util STP threadfunc {0} terminated with error ", threadFuncNum), e); + } + finally + { + Interlocked.Decrement(ref numRunningThreadFuncs); + threadInfo.Ended(); + ThreadInfo dummy; + activeThreads.TryRemove(threadFuncNum, out dummy); + if ((loggingEnabled || (threadFuncOverloadMode == 1)) && threadInfo.LogThread) + m_log.DebugFormat("Exit threadfunc {0} ({1})", threadFuncNum, FormatDuration(threadInfo.Elapsed())); + + if (context != null) + m_fireAndForgetCallsInProgress[context]--; } }; } - switch (FireAndForgetMethod) + long numQueued = Interlocked.Increment(ref numQueuedThreadFuncs); + try { - case FireAndForgetMethod.RegressionTest: - case FireAndForgetMethod.None: - realCallback.Invoke(obj); - break; - case FireAndForgetMethod.UnsafeQueueUserWorkItem: - ThreadPool.UnsafeQueueUserWorkItem(realCallback, obj); - break; - case FireAndForgetMethod.QueueUserWorkItem: - ThreadPool.QueueUserWorkItem(realCallback, obj); - break; - case FireAndForgetMethod.BeginInvoke: - FireAndForgetWrapper wrapper = FireAndForgetWrapper.Instance; - wrapper.FireAndForget(realCallback, obj); - break; - case FireAndForgetMethod.SmartThreadPool: - if (m_ThreadPool == null) - InitThreadPool(15); - m_ThreadPool.QueueWorkItem(SmartThreadPoolCallback, new object[] { realCallback, obj }); - break; - case FireAndForgetMethod.Thread: - Thread thread = new Thread(delegate(object o) { realCallback(o); }); - thread.Start(obj); - break; - default: - throw new NotImplementedException(); + long numRunning = numRunningThreadFuncs; + + if (m_ThreadPool != null && LogOverloads) + { + if ((threadFuncOverloadMode == 0) && (numRunning >= m_ThreadPool.MaxThreads)) + { + if (Interlocked.CompareExchange(ref threadFuncOverloadMode, 1, 0) == 0) + m_log.DebugFormat("Threadfunc: enable overload mode (Queued {0}, Running {1})", numQueued, numRunning); + } + else if ((threadFuncOverloadMode == 1) && (numRunning <= (m_ThreadPool.MaxThreads * 2) / 3)) + { + if (Interlocked.CompareExchange(ref threadFuncOverloadMode, 0, 1) == 1) + m_log.DebugFormat("Threadfunc: disable overload mode (Queued {0}, Running {1})", numQueued, numRunning); + } + } + + if (loggingEnabled || (threadFuncOverloadMode == 1)) + { + string full, partial; + GetFireAndForgetStackTrace(out full, out partial); + threadInfo.StackTrace = full; + threadInfo.LogThread = ShouldLogThread(partial); + + if (threadInfo.LogThread) + { + m_log.DebugFormat("Queue threadfunc {0} (Queued {1}, Running {2}) {3}{4}", + threadFuncNum, numQueued, numRunningThreadFuncs, + (context == null) ? "" : ("(" + context + ") "), + (LogThreadPool >= 2) ? full : partial); + } + } + else + { + // Since we didn't log "Queue threadfunc", don't log "Run threadfunc" or "End threadfunc" either. + // Those log lines aren't useful when we don't know which function is running in the thread. + threadInfo.LogThread = false; + } + + switch (FireAndForgetMethod) + { + case FireAndForgetMethod.RegressionTest: + case FireAndForgetMethod.None: + realCallback.Invoke(obj); + break; + case FireAndForgetMethod.UnsafeQueueUserWorkItem: + ThreadPool.UnsafeQueueUserWorkItem(realCallback, obj); + break; + case FireAndForgetMethod.QueueUserWorkItem: + ThreadPool.QueueUserWorkItem(realCallback, obj); + break; + case FireAndForgetMethod.BeginInvoke: + FireAndForgetWrapper wrapper = FireAndForgetWrapper.Instance; + wrapper.FireAndForget(realCallback, obj); + break; + case FireAndForgetMethod.SmartThreadPool: + if (m_ThreadPool == null) + InitThreadPool(2, 15); + threadInfo.WorkItem = m_ThreadPool.QueueWorkItem((cb, o) => cb(o), realCallback, obj); + break; + case FireAndForgetMethod.Thread: + Thread thread = new Thread(delegate(object o) { realCallback(o); }); + thread.Start(obj); + break; + default: + throw new NotImplementedException(); + } + } + catch (Exception) + { + Interlocked.Decrement(ref numQueuedThreadFuncs); + ThreadInfo dummy; + activeThreads.TryRemove(threadFuncNum, out dummy); + throw; } } /// - /// Get a thread pool report. + /// Returns whether the thread should be logged. Some very common threads aren't logged, + /// to avoid filling up the log. /// - /// - public static string GetThreadPoolReport() + /// A partial stack trace of where the thread was queued + /// Whether to log this thread + private static bool ShouldLogThread(string stackTrace) { - string threadPoolUsed = null; - int maxThreads = 0; - int minThreads = 0; - int allocatedThreads = 0; - int inUseThreads = 0; - int waitingCallbacks = 0; - int completionPortThreads = 0; + if (LogThreadPool < 3) + { + if (stackTrace.Contains("BeginFireQueueEmpty")) + return false; + } + + return true; + } - StringBuilder sb = new StringBuilder(); - if (FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool) + /// + /// Returns a stack trace for a thread added using FireAndForget(). + /// + /// Will contain the full stack trace + /// Will contain only the first frame of the stack trace + private static void GetFireAndForgetStackTrace(out string full, out string partial) + { + string src = Environment.StackTrace; + string[] lines = src.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); + + StringBuilder dest = new StringBuilder(src.Length); + + bool started = false; + bool first = true; + partial = ""; + + for (int i = 0; i < lines.Length; i++) { - // ROBUST currently leaves this the FireAndForgetMethod but never actually initializes the threadpool. - if (m_ThreadPool != null) + string line = lines[i]; + + if (!started) { - threadPoolUsed = "SmartThreadPool"; - maxThreads = m_ThreadPool.MaxThreads; - minThreads = m_ThreadPool.MinThreads; - inUseThreads = m_ThreadPool.InUseThreads; - allocatedThreads = m_ThreadPool.ActiveThreads; - waitingCallbacks = m_ThreadPool.WaitingCallbacks; + // Skip the initial stack frames, because they're of no interest for debugging + if (line.Contains("StackTrace") || line.Contains("FireAndForget")) + continue; + started = true; } + + if (first) + { + line = line.TrimStart(); + first = false; + partial = line; + } + + bool last = (i == lines.Length - 1); + if (last) + dest.Append(line); + else + dest.AppendLine(line); } - else if ( - FireAndForgetMethod == FireAndForgetMethod.UnsafeQueueUserWorkItem - || FireAndForgetMethod == FireAndForgetMethod.UnsafeQueueUserWorkItem) + + full = dest.ToString(); + } + +#pragma warning disable 0618 + /// + /// Return the stack trace of a different thread. + /// + /// + /// This is complicated because the thread needs to be paused in order to get its stack + /// trace. And pausing another thread can cause a deadlock. This method attempts to + /// avoid deadlock by using a short timeout (200ms), after which it gives up and + /// returns 'null' instead of the stack trace. + /// + /// Take from: http://stackoverflow.com/a/14935378 + /// + /// WARNING: this doesn't work in Mono. See https://bugzilla.novell.com/show_bug.cgi?id=571691 + /// + /// + /// The stack trace, or null if failed to get it + private static StackTrace GetStackTrace(Thread targetThread) + { + if (IsPlatformMono) { - threadPoolUsed = "BuiltInThreadPool"; - ThreadPool.GetMaxThreads(out maxThreads, out completionPortThreads); - ThreadPool.GetMinThreads(out minThreads, out completionPortThreads); - int availableThreads; - ThreadPool.GetAvailableThreads(out availableThreads, out completionPortThreads); - inUseThreads = maxThreads - availableThreads; - allocatedThreads = -1; - waitingCallbacks = -1; + // This doesn't work in Mono + return null; } - if (threadPoolUsed != null) + ManualResetEventSlim fallbackThreadReady = new ManualResetEventSlim(); + ManualResetEventSlim exitedSafely = new ManualResetEventSlim(); + + try { - sb.AppendFormat("Thread pool used : {0}\n", threadPoolUsed); - sb.AppendFormat("Max threads : {0}\n", maxThreads); - sb.AppendFormat("Min threads : {0}\n", minThreads); - sb.AppendFormat("Allocated threads : {0}\n", allocatedThreads < 0 ? "not applicable" : allocatedThreads.ToString()); - sb.AppendFormat("In use threads : {0}\n", inUseThreads); - sb.AppendFormat("Work items waiting : {0}\n", waitingCallbacks < 0 ? "not available" : waitingCallbacks.ToString()); + new Thread(delegate() + { + fallbackThreadReady.Set(); + while (!exitedSafely.Wait(200)) + { + try + { + targetThread.Resume(); + } + catch (Exception) + { + // Whatever happens, do never stop to resume the main-thread regularly until the main-thread has exited safely. + } + } + }).Start(); + + fallbackThreadReady.Wait(); + // From here, you have about 200ms to get the stack-trace + + targetThread.Suspend(); + + StackTrace trace = null; + try + { + trace = new StackTrace(targetThread, true); + } + catch (ThreadStateException) + { + //failed to get stack trace, since the fallback-thread resumed the thread + //possible reasons: + //1.) This thread was just too slow + //2.) A deadlock ocurred + //Automatic retry seems too risky here, so just return null. + } + + try + { + targetThread.Resume(); + } + catch (ThreadStateException) + { + // Thread is running again already + } + + return trace; } - else + finally { - sb.AppendFormat("Thread pool not used\n"); + // Signal the fallack-thread to stop + exitedSafely.Set(); } - - return sb.ToString(); } +#pragma warning restore 0618 - private static object SmartThreadPoolCallback(object o) + /// + /// Get information about the current state of the smart thread pool. + /// + /// + /// null if this isn't the pool being used for non-scriptengine threads. + /// + public static STPInfo GetSmartThreadPoolInfo() { - object[] array = (object[])o; - WaitCallback callback = (WaitCallback)array[0]; - object obj = array[1]; + if (m_ThreadPool == null) + return null; - callback(obj); - return null; + STPInfo stpi = new STPInfo(); + stpi.Name = m_ThreadPool.Name; + stpi.STPStartInfo = m_ThreadPool.STPStartInfo; + stpi.IsIdle = m_ThreadPool.IsIdle; + stpi.IsShuttingDown = m_ThreadPool.IsShuttingdown; + stpi.MaxThreads = m_ThreadPool.MaxThreads; + stpi.MinThreads = m_ThreadPool.MinThreads; + stpi.InUseThreads = m_ThreadPool.InUseThreads; + stpi.ActiveThreads = m_ThreadPool.ActiveThreads; + stpi.WaitingCallbacks = m_ThreadPool.WaitingCallbacks; + stpi.MaxConcurrentWorkItems = m_ThreadPool.Concurrency; + + return stpi; } #endregion FireAndForget Threading Pattern @@ -1876,6 +2558,60 @@ namespace OpenSim.Framework } /// + /// Formats a duration (given in milliseconds). + /// + public static string FormatDuration(int ms) + { + TimeSpan span = new TimeSpan(ms * TimeSpan.TicksPerMillisecond); + + string str = ""; + string suffix = null; + + int hours = (int)span.TotalHours; + if (hours > 0) + { + str += hours.ToString(str.Length == 0 ? "0" : "00"); + suffix = "hours"; + } + + if ((hours > 0) || (span.Minutes > 0)) + { + if (str.Length > 0) + str += ":"; + str += span.Minutes.ToString(str.Length == 0 ? "0" : "00"); + if (suffix == null) + suffix = "min"; + } + + if ((hours > 0) || (span.Minutes > 0) || (span.Seconds > 0)) + { + if (str.Length > 0) + str += ":"; + str += span.Seconds.ToString(str.Length == 0 ? "0" : "00"); + if (suffix == null) + suffix = "sec"; + } + + if (suffix == null) + suffix = "ms"; + + if (span.TotalMinutes < 1) + { + int ms1 = span.Milliseconds; + if (str.Length > 0) + { + ms1 /= 100; + str += "."; + } + str += ms1.ToString("0"); + } + + str += " " + suffix; + + return str; + } + + /// /// Prints the call stack at any given point. Useful for debugging. /// public static void PrintCallStack() @@ -1942,16 +2678,18 @@ namespace OpenSim.Framework } #region Xml Serialization Utilities - public static bool ReadBoolean(XmlTextReader reader) + public static bool ReadBoolean(XmlReader reader) { + // AuroraSim uses "int" for some fields that are boolean in OpenSim, e.g. "PassCollisions". Don't fail because of this. reader.ReadStartElement(); - bool result = Boolean.Parse(reader.ReadContentAsString().ToLower()); + string val = reader.ReadContentAsString().ToLower(); + bool result = val.Equals("true") || val.Equals("1"); reader.ReadEndElement(); return result; } - public static UUID ReadUUID(XmlTextReader reader, string name) + public static UUID ReadUUID(XmlReader reader, string name) { UUID id; string idStr; @@ -1970,7 +2708,7 @@ namespace OpenSim.Framework return id; } - public static Vector3 ReadVector(XmlTextReader reader, string name) + public static Vector3 ReadVector(XmlReader reader, string name) { Vector3 vec; @@ -1983,7 +2721,7 @@ namespace OpenSim.Framework return vec; } - public static Quaternion ReadQuaternion(XmlTextReader reader, string name) + public static Quaternion ReadQuaternion(XmlReader reader, string name) { Quaternion quat = new Quaternion(); @@ -2012,7 +2750,7 @@ namespace OpenSim.Framework return quat; } - public static T ReadEnum(XmlTextReader reader, string name) + public static T ReadEnum(XmlReader reader, string name) { string value = reader.ReadElementContentAsString(name, String.Empty); // !!!!! to deal with flags without commas @@ -2024,7 +2762,9 @@ namespace OpenSim.Framework #endregion #region Universal User Identifiers - /// + + /// + /// Attempts to parse a UUI into its components: UUID, name and URL. /// /// uuid[;endpoint[;first last[;secret]]] /// the uuid part @@ -2034,7 +2774,7 @@ namespace OpenSim.Framework /// the secret part public static bool ParseUniversalUserIdentifier(string value, out UUID uuid, out string url, out string firstname, out string lastname, out string secret) { - uuid = UUID.Zero; url = string.Empty; firstname = "Unknown"; lastname = "User"; secret = string.Empty; + uuid = UUID.Zero; url = string.Empty; firstname = "Unknown"; lastname = "UserUPUUI"; secret = string.Empty; string[] parts = value.Split(';'); if (parts.Length >= 1) @@ -2060,6 +2800,27 @@ namespace OpenSim.Framework } /// + /// For foreign avatars, extracts their original name and Server URL from their First Name and Last Name. + /// + public static bool ParseForeignAvatarName(string firstname, string lastname, + out string realFirstName, out string realLastName, out string serverURI) + { + realFirstName = realLastName = serverURI = string.Empty; + + if (!lastname.Contains("@")) + return false; + + if (!firstname.Contains(".")) + return false; + + realFirstName = firstname.Split('.')[0]; + realLastName = firstname.Split('.')[1]; + serverURI = new Uri("http://" + lastname.Replace("@", "")).ToString(); + + return true; + } + + /// /// Produces a universal (HG) system-facing identifier given the information /// /// @@ -2092,10 +2853,15 @@ namespace OpenSim.Framework { string[] parts = firstName.Split(new char[] { '.' }); if (parts.Length == 2) - return id.ToString() + ";" + agentsURI + ";" + parts[0] + " " + parts[1]; + return CalcUniversalIdentifier(id, agentsURI, parts[0] + " " + parts[1]); } - return id.ToString() + ";" + agentsURI + ";" + firstName + " " + lastName; + + return CalcUniversalIdentifier(id, agentsURI, firstName + " " + lastName); + } + private static string CalcUniversalIdentifier(UUID id, string agentsURI, string name) + { + return id.ToString() + ";" + agentsURI + ";" + name; } /// @@ -2119,5 +2885,221 @@ namespace OpenSim.Framework return firstName + "." + lastName + " " + "@" + uri.Authority; } #endregion + + /// + /// Escapes the special characters used in "LIKE". + /// + /// + /// For example: EscapeForLike("foo_bar%baz") = "foo\_bar\%baz" + /// + public static string EscapeForLike(string str) + { + return str.Replace("_", "\\_").Replace("%", "\\%"); + } + + /// + /// Returns the name of the user's viewer. + /// + /// + /// This method handles two ways that viewers specify their name: + /// 1. Viewer = "Firestorm-Release 4.4.2.34167", Channel = "(don't care)" -> "Firestorm-Release 4.4.2.34167" + /// 2. Viewer = "4.5.1.38838", Channel = "Firestorm-Beta" -> "Firestorm-Beta 4.5.1.38838" + /// + public static string GetViewerName(AgentCircuitData agent) + { + string name = agent.Viewer; + if (name == null) + name = ""; + else + name = name.Trim(); + + // Check if 'Viewer' is just a version number. If it's *not*, then we + // assume that it contains the real viewer name, and we return it. + foreach (char c in name) + { + if (Char.IsLetter(c)) + return name; + } + + // The 'Viewer' string contains just a version number. If there's anything in + // 'Channel' then assume that it's the viewer name. + if ((agent.Channel != null) && (agent.Channel.Length > 0)) + name = agent.Channel.Trim() + " " + name; + + return name; + } + + public static void LogFailedXML(string message, string xml) + { + int length = xml.Length; + if (length > 2000) + xml = xml.Substring(0, 2000) + "..."; + + m_log.ErrorFormat("{0} Failed XML ({1} bytes) = {2}", message, length, xml); + } + + } + + public class DoubleQueue where T:class + { + private Queue m_lowQueue = new Queue(); + private Queue m_highQueue = new Queue(); + + private object m_syncRoot = new object(); + private Semaphore m_s = new Semaphore(0, 1); + + public DoubleQueue() + { + } + + public virtual int Count + { + get + { + lock (m_syncRoot) + return m_highQueue.Count + m_lowQueue.Count; + } + } + + public virtual void Enqueue(T data) + { + Enqueue(m_lowQueue, data); + } + + public virtual void EnqueueLow(T data) + { + Enqueue(m_lowQueue, data); + } + + public virtual void EnqueueHigh(T data) + { + Enqueue(m_highQueue, data); + } + + private void Enqueue(Queue q, T data) + { + lock (m_syncRoot) + { + q.Enqueue(data); + m_s.WaitOne(0); + m_s.Release(); + } + } + + public virtual T Dequeue() + { + return Dequeue(Timeout.Infinite); + } + + public virtual T Dequeue(int tmo) + { + return Dequeue(TimeSpan.FromMilliseconds(tmo)); + } + + public virtual T Dequeue(TimeSpan wait) + { + T res = null; + + if (!Dequeue(wait, ref res)) + return null; + + return res; + } + + public bool Dequeue(int timeout, ref T res) + { + return Dequeue(TimeSpan.FromMilliseconds(timeout), ref res); + } + + public bool Dequeue(TimeSpan wait, ref T res) + { + if (!m_s.WaitOne(wait)) + return false; + + lock (m_syncRoot) + { + if (m_highQueue.Count > 0) + res = m_highQueue.Dequeue(); + else if (m_lowQueue.Count > 0) + res = m_lowQueue.Dequeue(); + + if (m_highQueue.Count == 0 && m_lowQueue.Count == 0) + return true; + + try + { + m_s.Release(); + } + catch + { + } + + return true; + } + } + + public virtual void Clear() + { + + lock (m_syncRoot) + { + // Make sure sem count is 0 + m_s.WaitOne(0); + + m_lowQueue.Clear(); + m_highQueue.Clear(); + } + } + } + + public class BetterRandom + { + private const int BufferSize = 1024; // must be a multiple of 4 + private byte[] RandomBuffer; + private int BufferOffset; + private RNGCryptoServiceProvider rng; + public BetterRandom() + { + RandomBuffer = new byte[BufferSize]; + rng = new RNGCryptoServiceProvider(); + BufferOffset = RandomBuffer.Length; + } + private void FillBuffer() + { + rng.GetBytes(RandomBuffer); + BufferOffset = 0; + } + public int Next() + { + if (BufferOffset >= RandomBuffer.Length) + { + FillBuffer(); + } + int val = BitConverter.ToInt32(RandomBuffer, BufferOffset) & 0x7fffffff; + BufferOffset += sizeof(int); + return val; + } + public int Next(int maxValue) + { + return Next() % maxValue; + } + public int Next(int minValue, int maxValue) + { + if (maxValue < minValue) + { + throw new ArgumentOutOfRangeException("maxValue must be greater than or equal to minValue"); + } + int range = maxValue - minValue; + return minValue + Next(range); + } + public double NextDouble() + { + int val = Next(); + return (double)val / int.MaxValue; + } + public void GetBytes(byte[] buff) + { + rng.GetBytes(buff); + } } } diff --git a/OpenSim/Framework/VersionInfo.cs b/OpenSim/Framework/VersionInfo.cs new file mode 100644 index 0000000..356e720 --- /dev/null +++ b/OpenSim/Framework/VersionInfo.cs @@ -0,0 +1,92 @@ +/* + * 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 OpenSimulator 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. + */ + +namespace OpenSim +{ + public class VersionInfo + { + public const string VersionNumber = "0.8.2.1"; + private const string IG_BUILD_NUMBER = "4"; + private const Flavour VERSION_FLAVOUR = Flavour.IG; + + public enum Flavour + { + Unknown, + Dev, + RC1, + RC2, + RC3, + Release, + Post_Fixes, + Extended, + IG + } + + public static string Version + { + get { return GetVersionString(VersionNumber, IG_BUILD_NUMBER, IG_BUILD_NUMBER, VERSION_FLAVOUR); } + } + + public static string GetVersionString(string versionNumber, string buildNumber, Flavour flavour) + { + string versionString = "OpenSim " + versionNumber + " " + flavour + " build " + buildNumber; + return versionString.PadRight(VERSIONINFO_VERSION_LENGTH); + } + + public const int VERSIONINFO_VERSION_LENGTH = 39; + + /// + /// This is the external interface version. It is separate from the OpenSimulator project version. + /// + /// + /// Commented because it's not used anymore, see below for new + /// versioning method. + //public readonly static int MajorInterfaceVersion = 8; + + /// + /// This rules versioning regarding teleports, and compatibility between simulators in that regard. + /// + /// + /// + /// The protocol version that we will use for outgoing transfers + /// Valid values are + /// "SIMULATION/0.3" + /// - This is the latest, and it supports teleports to variable-sized regions + /// - Older versions can teleport to this one, but only if the destination region + /// is 256x256 + /// "SIMULATION/0.2" + /// - A source simulator which only implements "SIMULATION/0.1" can still teleport here + /// - this protocol is more efficient than "SIMULATION/0.1" + /// "SIMULATION/0.1" + /// - this is an older teleport protocol used in OpenSimulator 0.7.5 and before. + /// + public readonly static float SimulationServiceVersionAcceptedMin = 0.3f; + public readonly static float SimulationServiceVersionAcceptedMax = 0.5f; + public readonly static float SimulationServiceVersionSupportedMin = 0.3f; + public readonly static float SimulationServiceVersionSupportedMax = 0.5f; + } +} diff --git a/OpenSim/Framework/WearableCacheItem.cs b/OpenSim/Framework/WearableCacheItem.cs new file mode 100644 index 0000000..1aecf79 --- /dev/null +++ b/OpenSim/Framework/WearableCacheItem.cs @@ -0,0 +1,157 @@ +/* + * 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 OpenSimulator 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 OpenMetaverse; +using OpenMetaverse.StructuredData; + +namespace OpenSim.Framework +{ + [Serializable] + public class WearableCacheItem + { + public uint TextureIndex { get; set; } + public UUID CacheId { get; set; } + public UUID TextureID { get; set; } + public AssetBase TextureAsset { get; set; } + + + public static WearableCacheItem[] GetDefaultCacheItem() + { + int itemmax = 21; + WearableCacheItem[] retitems = new WearableCacheItem[itemmax]; + for (uint i=0;i ret = new List(); + if (pInput.Type == OSDType.Array) + { + OSDArray itemarray = (OSDArray) pInput; + foreach (OSDMap item in itemarray) + { + ret.Add(new WearableCacheItem() + { + TextureIndex = item["textureindex"].AsUInteger(), + CacheId = item["cacheid"].AsUUID(), + TextureID = item["textureid"].AsUUID() + }); + + if (dataCache != null && item.ContainsKey("assetdata")) + { + AssetBase asset = new AssetBase(item["textureid"].AsUUID(),"BakedTexture",(sbyte)AssetType.Texture,UUID.Zero.ToString()); + asset.Temporary = true; + asset.Data = item["assetdata"].AsBinary(); + dataCache.Cache(asset); + } + } + } + else if (pInput.Type == OSDType.Map) + { + OSDMap item = (OSDMap) pInput; + ret.Add(new WearableCacheItem(){ + TextureIndex = item["textureindex"].AsUInteger(), + CacheId = item["cacheid"].AsUUID(), + TextureID = item["textureid"].AsUUID() + }); + if (dataCache != null && item.ContainsKey("assetdata")) + { + string assetCreator = item["assetcreator"].AsString(); + string assetName = item["assetname"].AsString(); + AssetBase asset = new AssetBase(item["textureid"].AsUUID(), assetName, (sbyte)AssetType.Texture, assetCreator); + asset.Temporary = true; + asset.Data = item["assetdata"].AsBinary(); + dataCache.Cache(asset); + } + } + else + { + return new WearableCacheItem[0]; + } + return ret.ToArray(); + + } + public static OSD ToOSD(WearableCacheItem[] pcacheItems, IImprovedAssetCache dataCache) + { + OSDArray arr = new OSDArray(); + foreach (WearableCacheItem item in pcacheItems) + { + OSDMap itemmap = new OSDMap(); + itemmap.Add("textureindex", OSD.FromUInteger(item.TextureIndex)); + itemmap.Add("cacheid", OSD.FromUUID(item.CacheId)); + itemmap.Add("textureid", OSD.FromUUID(item.TextureID)); + if (dataCache != null) + { + if (dataCache.Check(item.TextureID.ToString())) + { + AssetBase assetItem = dataCache.Get(item.TextureID.ToString()); + if (assetItem != null) + { + itemmap.Add("assetdata", OSD.FromBinary(assetItem.Data)); + itemmap.Add("assetcreator", OSD.FromString(assetItem.CreatorID)); + itemmap.Add("assetname", OSD.FromString(assetItem.Name)); + } + } + } + arr.Add(itemmap); + } + return arr; + } + public static WearableCacheItem SearchTextureIndex(uint pTextureIndex,WearableCacheItem[] pcacheItems) + { + for (int i = 0; i < pcacheItems.Length; i++) + { + if (pcacheItems[i].TextureIndex == pTextureIndex) + return pcacheItems[i]; + } + return null; + } + public static WearableCacheItem SearchTextureCacheId(UUID pCacheId, WearableCacheItem[] pcacheItems) + { + for (int i = 0; i < pcacheItems.Length; i++) + { + if (pcacheItems[i].CacheId == pCacheId) + return pcacheItems[i]; + } + return null; + } + public static WearableCacheItem SearchTextureTextureId(UUID pTextureId, WearableCacheItem[] pcacheItems) + { + for (int i = 0; i < pcacheItems.Length; i++) + { + if (pcacheItems[i].TextureID == pTextureId) + return pcacheItems[i]; + } + return null; + } + } + + +} diff --git a/OpenSim/Framework/WebUtil.cs b/OpenSim/Framework/WebUtil.cs index 5c34cf4..b180c8a 100644 --- a/OpenSim/Framework/WebUtil.cs +++ b/OpenSim/Framework/WebUtil.cs @@ -39,8 +39,13 @@ using System.Text; using System.Web; using System.Xml; using System.Xml.Serialization; +using System.Xml.Linq; using log4net; +using Nwc.XmlRpc; using OpenMetaverse.StructuredData; +using XMLResponseHelper = OpenSim.Framework.SynchronousRestObjectRequester.XMLResponseHelper; + +using OpenSim.Framework.ServiceAuth; namespace OpenSim.Framework { @@ -64,7 +69,12 @@ namespace OpenSim.Framework /// /// Request number for diagnostic purposes. /// - public static int RequestNumber { get; internal set; } + public static int RequestNumber { get; set; } + + /// + /// Control where OSD requests should be serialized per endpoint. + /// + public static bool SerializeOSDRequestsPerEndpoint { get; set; } /// /// this is the header field used to communicate the local request id @@ -84,8 +94,9 @@ namespace OpenSim.Framework /// /// This is to truncate any really large post data, such as an asset. In theory, the first section should /// give us useful information about the call (which agent it relates to if applicable, etc.). + /// This is also used to truncate messages when using DebugLevel 5. /// - public const int MaxRequestDiagLength = 100; + public const int MaxRequestDiagLength = 200; /// /// Dictionary of end points @@ -120,45 +131,104 @@ namespace OpenSim.Framework /// public static OSDMap PutToServiceCompressed(string url, OSDMap data, int timeout) { - return ServiceOSDRequest(url,data, "PUT", timeout, true); + return ServiceOSDRequest(url,data, "PUT", timeout, true, false); } public static OSDMap PutToService(string url, OSDMap data, int timeout) { - return ServiceOSDRequest(url,data, "PUT", timeout, false); + return ServiceOSDRequest(url,data, "PUT", timeout, false, false); } - public static OSDMap PostToService(string url, OSDMap data, int timeout) + public static OSDMap PostToService(string url, OSDMap data, int timeout, bool rpc) { - return ServiceOSDRequest(url, data, "POST", timeout, false); + return ServiceOSDRequest(url, data, "POST", timeout, false, rpc); } public static OSDMap PostToServiceCompressed(string url, OSDMap data, int timeout) { - return ServiceOSDRequest(url, data, "POST", timeout, true); + return ServiceOSDRequest(url, data, "POST", timeout, true, false); } public static OSDMap GetFromService(string url, int timeout) { - return ServiceOSDRequest(url, null, "GET", timeout, false); + return ServiceOSDRequest(url, null, "GET", timeout, false, false); } - - public static OSDMap ServiceOSDRequest(string url, OSDMap data, string method, int timeout, bool compressed) + + public static OSDMap ServiceOSDRequest(string url, OSDMap data, string method, int timeout, bool compressed, bool rpc) { - lock (EndPointLock(url)) + if (SerializeOSDRequestsPerEndpoint) + { + lock (EndPointLock(url)) + { + return ServiceOSDRequestWorker(url, data, method, timeout, compressed, rpc); + } + } + else { - return ServiceOSDRequestWorker(url,data,method,timeout,compressed); + return ServiceOSDRequestWorker(url, data, method, timeout, compressed, rpc); } } - private static OSDMap ServiceOSDRequestWorker(string url, OSDMap data, string method, int timeout, bool compressed) + public static void LogOutgoingDetail(Stream outputStream) + { + LogOutgoingDetail("", outputStream); + } + + public static void LogOutgoingDetail(string context, Stream outputStream) + { + using (Stream stream = Util.Copy(outputStream)) + using (StreamReader reader = new StreamReader(stream, Encoding.UTF8)) + { + string output; + + if (DebugLevel == 5) + { + char[] chars = new char[WebUtil.MaxRequestDiagLength + 1]; // +1 so we know to add "..." only if needed + int len = reader.Read(chars, 0, WebUtil.MaxRequestDiagLength + 1); + output = new string(chars, 0, len); + } + else + { + output = reader.ReadToEnd(); + } + + LogOutgoingDetail(context, output); + } + } + + public static void LogOutgoingDetail(string type, int reqnum, string output) + { + LogOutgoingDetail(string.Format("{0} {1}: ", type, reqnum), output); + } + + public static void LogOutgoingDetail(string context, string output) + { + if (DebugLevel == 5) + { + if (output.Length > MaxRequestDiagLength) + output = output.Substring(0, MaxRequestDiagLength) + "..."; + } + + m_log.DebugFormat("[LOGHTTP]: {0}{1}", context, Util.BinaryToASCII(output)); + } + + public static void LogResponseDetail(int reqnum, Stream inputStream) + { + LogOutgoingDetail(string.Format("RESPONSE {0}: ", reqnum), inputStream); + } + + public static void LogResponseDetail(int reqnum, string input) + { + LogOutgoingDetail(string.Format("RESPONSE {0}: ", reqnum), input); + } + + private static OSDMap ServiceOSDRequestWorker(string url, OSDMap data, string method, int timeout, bool compressed, bool rpc) { int reqnum = RequestNumber++; if (DebugLevel >= 3) - m_log.DebugFormat( - "[WEB UTIL]: HTTP OUT {0} ServiceOSD {1} {2} (timeout {3}, compressed {4})", - reqnum, method, url, timeout, compressed); + m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} JSON-RPC {1} to {2}", + reqnum, method, url); string errorMessage = "unknown error"; int tickstart = Util.EnvironmentTickCount(); @@ -178,20 +248,27 @@ namespace OpenSim.Framework // If there is some input, write it into the request if (data != null) { - strBuffer = OSDParser.SerializeJsonString(data); + strBuffer = OSDParser.SerializeJsonString(data); + + if (DebugLevel >= 5) + LogOutgoingDetail("SEND", reqnum, strBuffer); + byte[] buffer = System.Text.Encoding.UTF8.GetBytes(strBuffer); + request.ContentType = rpc ? "application/json-rpc" : "application/json"; + if (compressed) { - request.ContentType = "application/x-gzip"; + request.Headers["X-Content-Encoding"] = "gzip"; // can't set "Content-Encoding" because old OpenSims fail if they get an unrecognized Content-Encoding + using (MemoryStream ms = new MemoryStream()) { - using (GZipStream comp = new GZipStream(ms, CompressionMode.Compress)) + using (GZipStream comp = new GZipStream(ms, CompressionMode.Compress, true)) { comp.Write(buffer, 0, buffer.Length); // We need to close the gzip stream before we write it anywhere // because apparently something important related to gzip compression - // gets written on the strteam upon Dispose() + // gets written on the stream upon Dispose() } byte[] buf = ms.ToArray(); request.ContentLength = buf.Length; //Count bytes to send @@ -201,10 +278,9 @@ namespace OpenSim.Framework } else { - request.ContentType = "application/json"; request.ContentLength = buffer.Length; //Count bytes to send using (Stream requestStream = request.GetRequestStream()) - requestStream.Write(buffer, 0, buffer.Length); //Send it + requestStream.Write(buffer, 0, buffer.Length); //Send it } } @@ -216,10 +292,13 @@ namespace OpenSim.Framework { using (Stream responseStream = response.GetResponseStream()) { - string responseStr = null; - responseStr = responseStream.GetStreamString(); - // m_log.DebugFormat("[WEB UTIL]: <{0}> response is <{1}>",reqnum,responseStr); - return CanonicalizeResults(responseStr); + using (StreamReader reader = new StreamReader(responseStream)) + { + string responseStr = reader.ReadToEnd(); + if (WebUtil.DebugLevel >= 5) + WebUtil.LogResponseDetail(reqnum, responseStr); + return CanonicalizeResults(responseStr); + } } } } @@ -228,8 +307,8 @@ namespace OpenSim.Framework errorMessage = we.Message; if (we.Status == WebExceptionStatus.ProtocolError) { - HttpWebResponse webResponse = (HttpWebResponse)we.Response; - errorMessage = String.Format("[{0}] {1}",webResponse.StatusCode,webResponse.StatusDescription); + using (HttpWebResponse webResponse = (HttpWebResponse)we.Response) + errorMessage = String.Format("[{0}] {1}", webResponse.StatusCode, webResponse.StatusDescription); } } catch (Exception ex) @@ -240,24 +319,23 @@ namespace OpenSim.Framework { int tickdiff = Util.EnvironmentTickCountSubtract(tickstart); if (tickdiff > LongCallTime) + { m_log.InfoFormat( - "[WEB UTIL]: Slow ServiceOSD request {0} {1} {2} took {3}ms, {4}ms writing, {5}", - reqnum, - method, - url, - tickdiff, - tickdata, + "[LOGHTTP]: Slow JSON-RPC request {0} {1} to {2} took {3}ms, {4}ms writing, {5}", + reqnum, method, url, tickdiff, tickdata, strBuffer != null ? (strBuffer.Length > MaxRequestDiagLength ? strBuffer.Remove(MaxRequestDiagLength) : strBuffer) : ""); + } else if (DebugLevel >= 4) - m_log.DebugFormat( - "[WEB UTIL]: HTTP OUT {0} took {1}ms, {2}ms writing", + { + m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} took {1}ms, {2}ms writing", reqnum, tickdiff, tickdata); + } } m_log.DebugFormat( - "[WEB UTIL]: ServiceOSD request {0} {1} {2} FAILED: {3}", reqnum, url, method, errorMessage); + "[LOGHTTP]: JSON-RPC request {0} {1} to {2} FAILED: {3}", reqnum, method, url, errorMessage); return ErrorResponseMap(errorMessage); } @@ -335,9 +413,8 @@ namespace OpenSim.Framework string method = (data != null && data["RequestMethod"] != null) ? data["RequestMethod"] : "unknown"; if (DebugLevel >= 3) - m_log.DebugFormat( - "[WEB UTIL]: HTTP OUT {0} ServiceForm {1} {2} (timeout {3})", - reqnum, method, url, timeout); + m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} ServiceForm '{1}' to {2}", + reqnum, method, url); string errorMessage = "unknown error"; int tickstart = Util.EnvironmentTickCount(); @@ -357,6 +434,10 @@ namespace OpenSim.Framework if (data != null) { queryString = BuildQueryString(data); + + if (DebugLevel >= 5) + LogOutgoingDetail("SEND", reqnum, queryString); + byte[] buffer = System.Text.Encoding.UTF8.GetBytes(queryString); request.ContentLength = buffer.Length; @@ -373,12 +454,16 @@ namespace OpenSim.Framework { using (Stream responseStream = response.GetResponseStream()) { - string responseStr = null; + using (StreamReader reader = new StreamReader(responseStream)) + { + string responseStr = reader.ReadToEnd(); + if (WebUtil.DebugLevel >= 5) + WebUtil.LogResponseDetail(reqnum, responseStr); + OSD responseOSD = OSDParser.Deserialize(responseStr); - responseStr = responseStream.GetStreamString(); - OSD responseOSD = OSDParser.Deserialize(responseStr); - if (responseOSD.Type == OSDType.Map) - return (OSDMap)responseOSD; + if (responseOSD.Type == OSDType.Map) + return (OSDMap)responseOSD; + } } } } @@ -387,8 +472,8 @@ namespace OpenSim.Framework errorMessage = we.Message; if (we.Status == WebExceptionStatus.ProtocolError) { - HttpWebResponse webResponse = (HttpWebResponse)we.Response; - errorMessage = String.Format("[{0}] {1}",webResponse.StatusCode,webResponse.StatusDescription); + using (HttpWebResponse webResponse = (HttpWebResponse)we.Response) + errorMessage = String.Format("[{0}] {1}",webResponse.StatusCode,webResponse.StatusDescription); } } catch (Exception ex) @@ -399,23 +484,22 @@ namespace OpenSim.Framework { int tickdiff = Util.EnvironmentTickCountSubtract(tickstart); if (tickdiff > LongCallTime) + { m_log.InfoFormat( - "[WEB UTIL]: Slow ServiceForm request {0} {1} {2} took {3}ms, {4}ms writing, {5}", - reqnum, - method, - url, - tickdiff, - tickdata, + "[LOGHTTP]: Slow ServiceForm request {0} '{1}' to {2} took {3}ms, {4}ms writing, {5}", + reqnum, method, url, tickdiff, tickdata, queryString != null ? (queryString.Length > MaxRequestDiagLength) ? queryString.Remove(MaxRequestDiagLength) : queryString : ""); + } else if (DebugLevel >= 4) - m_log.DebugFormat( - "[WEB UTIL]: HTTP OUT {0} took {1}ms, {2}ms writing", + { + m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} took {1}ms, {2}ms writing", reqnum, tickdiff, tickdata); + } } - m_log.WarnFormat("[WEB UTIL]: ServiceForm request {0} {1} {2} failed: {2}", reqnum, method, url, errorMessage); + m_log.WarnFormat("[LOGHTTP]: ServiceForm request {0} '{1}' to {2} failed: {3}", reqnum, method, url, errorMessage); return ErrorResponseMap(errorMessage); } @@ -592,38 +676,6 @@ namespace OpenSim.Framework return totalCopiedBytes; } - /// - /// Converts an entire stream to a string, regardless of current stream - /// position - /// - /// The stream to convert to a string - /// - /// When this method is done, the stream position will be - /// reset to its previous position before this method was called - public static string GetStreamString(this Stream stream) - { - string value = null; - - if (stream != null && stream.CanRead) - { - long rewindPos = -1; - - if (stream.CanSeek) - { - rewindPos = stream.Position; - stream.Seek(0, SeekOrigin.Begin); - } - - StreamReader reader = new StreamReader(stream); - value = reader.ReadToEnd(); - - if (rewindPos >= 0) - stream.Seek(rewindPos, SeekOrigin.Begin); - } - - return value; - } - #endregion Stream public class QBasedComparer : IComparer @@ -667,7 +719,7 @@ namespace OpenSim.Framework /// public static string[] GetPreferredImageTypes(string accept) { - if (accept == null || accept == string.Empty) + if (string.IsNullOrEmpty(accept)) return new string[0]; string[] types = accept.Split(new char[] { ',' }); @@ -724,11 +776,17 @@ namespace OpenSim.Framework string requestUrl, TRequest obj, Action action, int maxConnections) { + MakeRequest(verb, requestUrl, obj, action, maxConnections, null); + } + + public static void MakeRequest(string verb, + string requestUrl, TRequest obj, Action action, + int maxConnections, IServiceAuth auth) + { int reqnum = WebUtil.RequestNumber++; if (WebUtil.DebugLevel >= 3) - m_log.DebugFormat( - "[WEB UTIL]: HTTP OUT {0} AsynchronousRequestObject {1} {2}", + m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} AsynchronousRequestObject {1} to {2}", reqnum, verb, requestUrl); int tickstart = Util.EnvironmentTickCount(); @@ -738,171 +796,174 @@ namespace OpenSim.Framework WebRequest request = WebRequest.Create(requestUrl); HttpWebRequest ht = (HttpWebRequest)request; + + if (auth != null) + auth.AddAuthorization(ht.Headers); + if (maxConnections > 0 && ht.ServicePoint.ConnectionLimit < maxConnections) ht.ServicePoint.ConnectionLimit = maxConnections; - WebResponse response = null; TResponse deserial = default(TResponse); - XmlSerializer deserializer = new XmlSerializer(typeof(TResponse)); request.Method = verb; + MemoryStream buffer = null; - if (verb == "POST") + try { - request.ContentType = "text/xml"; + if (verb == "POST") + { + request.ContentType = "text/xml"; - buffer = new MemoryStream(); + buffer = new MemoryStream(); - XmlWriterSettings settings = new XmlWriterSettings(); - settings.Encoding = Encoding.UTF8; + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Encoding = Encoding.UTF8; - using (XmlWriter writer = XmlWriter.Create(buffer, settings)) - { - XmlSerializer serializer = new XmlSerializer(type); - serializer.Serialize(writer, obj); - writer.Flush(); - } + using (XmlWriter writer = XmlWriter.Create(buffer, settings)) + { + XmlSerializer serializer = new XmlSerializer(type); + serializer.Serialize(writer, obj); + writer.Flush(); + } - int length = (int)buffer.Length; - request.ContentLength = length; + int length = (int)buffer.Length; + request.ContentLength = length; + byte[] data = buffer.ToArray(); - request.BeginGetRequestStream(delegate(IAsyncResult res) - { - Stream requestStream = request.EndGetRequestStream(res); + if (WebUtil.DebugLevel >= 5) + WebUtil.LogOutgoingDetail("SEND", reqnum, System.Text.Encoding.UTF8.GetString(data)); - requestStream.Write(buffer.ToArray(), 0, length); - requestStream.Close(); + request.BeginGetRequestStream(delegate(IAsyncResult res) + { + using (Stream requestStream = request.EndGetRequestStream(res)) + requestStream.Write(data, 0, length); - // capture how much time was spent writing - tickdata = Util.EnvironmentTickCountSubtract(tickstart); + // capture how much time was spent writing + tickdata = Util.EnvironmentTickCountSubtract(tickstart); - request.BeginGetResponse(delegate(IAsyncResult ar) - { - response = request.EndGetResponse(ar); - Stream respStream = null; - try + request.BeginGetResponse(delegate(IAsyncResult ar) { - respStream = response.GetResponseStream(); - deserial = (TResponse)deserializer.Deserialize( - respStream); - } - catch (System.InvalidOperationException) - { - } - finally - { - // Let's not close this - //buffer.Close(); - respStream.Close(); - response.Close(); - } + using (WebResponse response = request.EndGetResponse(ar)) + { + try + { + using (Stream respStream = response.GetResponseStream()) + { + deserial = XMLResponseHelper.LogAndDeserialize( + reqnum, respStream, response.ContentLength); + } + } + catch (System.InvalidOperationException) + { + } + } - action(deserial); + action(deserial); + }, null); }, null); - }, null); - } - else - { - request.BeginGetResponse(delegate(IAsyncResult res2) + } + else { - try + request.BeginGetResponse(delegate(IAsyncResult res2) { - // If the server returns a 404, this appears to trigger a System.Net.WebException even though that isn't - // documented in MSDN - response = request.EndGetResponse(res2); - - Stream respStream = null; try { - respStream = response.GetResponseStream(); - deserial = (TResponse)deserializer.Deserialize(respStream); - } - catch (System.InvalidOperationException) - { - } - finally - { - respStream.Close(); - response.Close(); + // If the server returns a 404, this appears to trigger a System.Net.WebException even though that isn't + // documented in MSDN + using (WebResponse response = request.EndGetResponse(res2)) + { + try + { + using (Stream respStream = response.GetResponseStream()) + { + deserial = XMLResponseHelper.LogAndDeserialize( + reqnum, respStream, response.ContentLength); + } + } + catch (System.InvalidOperationException) + { + } + } } - } - catch (WebException e) - { - if (e.Status == WebExceptionStatus.ProtocolError) + catch (WebException e) { - if (e.Response is HttpWebResponse) + if (e.Status == WebExceptionStatus.ProtocolError) { - HttpWebResponse httpResponse = (HttpWebResponse)e.Response; - - if (httpResponse.StatusCode != HttpStatusCode.NotFound) + if (e.Response is HttpWebResponse) { - // We don't appear to be handling any other status codes, so log these feailures to that - // people don't spend unnecessary hours hunting phantom bugs. - m_log.DebugFormat( - "[ASYNC REQUEST]: Request {0} {1} failed with unexpected status code {2}", - verb, requestUrl, httpResponse.StatusCode); + using (HttpWebResponse httpResponse = (HttpWebResponse)e.Response) + { + if (httpResponse.StatusCode != HttpStatusCode.NotFound) + { + // We don't appear to be handling any other status codes, so log these feailures to that + // people don't spend unnecessary hours hunting phantom bugs. + m_log.DebugFormat( + "[ASYNC REQUEST]: Request {0} {1} failed with unexpected status code {2}", + verb, requestUrl, httpResponse.StatusCode); + } + } } } + else + { + m_log.ErrorFormat( + "[ASYNC REQUEST]: Request {0} {1} failed with status {2} and message {3}", + verb, requestUrl, e.Status, e.Message); + } } - else + catch (Exception e) { m_log.ErrorFormat( - "[ASYNC REQUEST]: Request {0} {1} failed with status {2} and message {3}", - verb, requestUrl, e.Status, e.Message); + "[ASYNC REQUEST]: Request {0} {1} failed with exception {2}{3}", + verb, requestUrl, e.Message, e.StackTrace); } - } - catch (Exception e) - { - m_log.ErrorFormat( - "[ASYNC REQUEST]: Request {0} {1} failed with exception {2}{3}", - verb, requestUrl, e.Message, e.StackTrace); - } - - // m_log.DebugFormat("[ASYNC REQUEST]: Received {0}", deserial.ToString()); + + // m_log.DebugFormat("[ASYNC REQUEST]: Received {0}", deserial.ToString()); - try - { - action(deserial); - } - catch (Exception e) + try + { + action(deserial); + } + catch (Exception e) + { + m_log.ErrorFormat( + "[ASYNC REQUEST]: Request {0} {1} callback failed with exception {2}{3}", + verb, requestUrl, e.Message, e.StackTrace); + } + + }, null); + } + + int tickdiff = Util.EnvironmentTickCountSubtract(tickstart); + if (tickdiff > WebUtil.LongCallTime) + { + string originalRequest = null; + + if (buffer != null) { - m_log.ErrorFormat( - "[ASYNC REQUEST]: Request {0} {1} callback failed with exception {2}{3}", - verb, requestUrl, e.Message, e.StackTrace); - } - - }, null); - } + originalRequest = Encoding.UTF8.GetString(buffer.ToArray()); - int tickdiff = Util.EnvironmentTickCountSubtract(tickstart); - if (tickdiff > WebUtil.LongCallTime) - { - string originalRequest = null; + if (originalRequest.Length > WebUtil.MaxRequestDiagLength) + originalRequest = originalRequest.Remove(WebUtil.MaxRequestDiagLength); + } - if (buffer != null) + m_log.InfoFormat( + "[LOGHTTP]: Slow AsynchronousRequestObject request {0} {1} to {2} took {3}ms, {4}ms writing, {5}", + reqnum, verb, requestUrl, tickdiff, tickdata, + originalRequest); + } + else if (WebUtil.DebugLevel >= 4) { - originalRequest = Encoding.UTF8.GetString(buffer.ToArray()); - - if (originalRequest.Length > WebUtil.MaxRequestDiagLength) - originalRequest = originalRequest.Remove(WebUtil.MaxRequestDiagLength); + m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} took {1}ms, {2}ms writing", + reqnum, tickdiff, tickdata); } - - m_log.InfoFormat( - "[ASYNC REQUEST]: Slow request {0} {1} {2} took {3}ms, {4}ms writing, {5}", - reqnum, - verb, - requestUrl, - tickdiff, - tickdata, - originalRequest); } - else if (WebUtil.DebugLevel >= 4) - { - m_log.DebugFormat( - "[WEB UTIL]: HTTP OUT {0} took {1}ms, {2}ms writing", - reqnum, tickdiff, tickdata); + finally + { + if (buffer != null) + buffer.Dispose(); } } } @@ -917,17 +978,17 @@ namespace OpenSim.Framework /// /// /// + /// /// /// /// Thrown if we encounter a network issue while posting /// the request. You'll want to make sure you deal with this as they're not uncommon - public static string MakeRequest(string verb, string requestUrl, string obj) + public static string MakeRequest(string verb, string requestUrl, string obj, int timeoutsecs, IServiceAuth auth) { int reqnum = WebUtil.RequestNumber++; if (WebUtil.DebugLevel >= 3) - m_log.DebugFormat( - "[WEB UTIL]: HTTP OUT {0} SynchronousRestForms {1} {2}", + m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} SynchronousRestForms {1} to {2}", reqnum, verb, requestUrl); int tickstart = Util.EnvironmentTickCount(); @@ -935,6 +996,12 @@ namespace OpenSim.Framework WebRequest request = WebRequest.Create(requestUrl); request.Method = verb; + if (timeoutsecs > 0) + request.Timeout = timeoutsecs * 1000; + + if (auth != null) + auth.AddAuthorization(request.Headers); + string respstring = String.Empty; using (MemoryStream buffer = new MemoryStream()) @@ -952,22 +1019,27 @@ namespace OpenSim.Framework length = (int)obj.Length; request.ContentLength = length; + byte[] data = buffer.ToArray(); + + if (WebUtil.DebugLevel >= 5) + WebUtil.LogOutgoingDetail("SEND", reqnum, System.Text.Encoding.UTF8.GetString(data)); Stream requestStream = null; try { requestStream = request.GetRequestStream(); - requestStream.Write(buffer.ToArray(), 0, length); + requestStream.Write(data, 0, length); } catch (Exception e) { - m_log.DebugFormat( - "[FORMS]: exception occured {0} {1}: {2}{3}", verb, requestUrl, e.Message, e.StackTrace); + m_log.InfoFormat("[FORMS]: Error sending request to {0}: {1}. Request: {2}", requestUrl, e.Message, + obj.Length > WebUtil.MaxRequestDiagLength ? obj.Remove(WebUtil.MaxRequestDiagLength) : obj); + throw e; } finally { if (requestStream != null) - requestStream.Close(); + requestStream.Dispose(); // capture how much time was spent writing tickdata = Util.EnvironmentTickCountSubtract(tickstart); @@ -980,53 +1052,54 @@ namespace OpenSim.Framework { if (resp.ContentLength != 0) { - Stream respStream = null; - try - { - respStream = resp.GetResponseStream(); + using (Stream respStream = resp.GetResponseStream()) using (StreamReader reader = new StreamReader(respStream)) - { respstring = reader.ReadToEnd(); - } - } - catch (Exception e) - { - m_log.DebugFormat( - "[FORMS]: Exception occured on receiving {0} {1}: {2}{3}", - verb, requestUrl, e.Message, e.StackTrace); - } - finally - { - if (respStream != null) - respStream.Close(); - } } } } - catch (System.InvalidOperationException) + catch (Exception e) { - // This is what happens when there is invalid XML - m_log.DebugFormat("[FORMS]: InvalidOperationException on receiving {0} {1}", verb, requestUrl); + m_log.InfoFormat("[FORMS]: Error receiving response from {0}: {1}. Request: {2}", requestUrl, e.Message, + obj.Length > WebUtil.MaxRequestDiagLength ? obj.Remove(WebUtil.MaxRequestDiagLength) : obj); + throw e; } } int tickdiff = Util.EnvironmentTickCountSubtract(tickstart); if (tickdiff > WebUtil.LongCallTime) + { m_log.InfoFormat( - "[FORMS]: Slow request {0} {1} {2} took {3}ms, {4}ms writing, {5}", - reqnum, - verb, - requestUrl, - tickdiff, - tickdata, + "[LOGHTTP]: Slow SynchronousRestForms request {0} {1} to {2} took {3}ms, {4}ms writing, {5}", + reqnum, verb, requestUrl, tickdiff, tickdata, obj.Length > WebUtil.MaxRequestDiagLength ? obj.Remove(WebUtil.MaxRequestDiagLength) : obj); + } else if (WebUtil.DebugLevel >= 4) - m_log.DebugFormat( - "[WEB UTIL]: HTTP OUT {0} took {1}ms, {2}ms writing", + { + m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} took {1}ms, {2}ms writing", reqnum, tickdiff, tickdata); + } + + if (WebUtil.DebugLevel >= 5) + WebUtil.LogResponseDetail(reqnum, respstring); return respstring; } + + public static string MakeRequest(string verb, string requestUrl, string obj, int timeoutsecs) + { + return MakeRequest(verb, requestUrl, obj, timeoutsecs, null); + } + + public static string MakeRequest(string verb, string requestUrl, string obj) + { + return MakeRequest(verb, requestUrl, obj, -1); + } + + public static string MakeRequest(string verb, string requestUrl, string obj, IServiceAuth auth) + { + return MakeRequest(verb, requestUrl, obj, -1, auth); + } } public class SynchronousRestObjectRequester @@ -1040,28 +1113,80 @@ namespace OpenSim.Framework /// /// /// - /// - /// - /// - /// Thrown if we encounter a network issue while posting - /// the request. You'll want to make sure you deal with this as they're not uncommon + /// + /// + /// The response. If there was an internal exception, then the default(TResponse) is returned. + /// public static TResponse MakeRequest(string verb, string requestUrl, TRequest obj) { return MakeRequest(verb, requestUrl, obj, 0); } + public static TResponse MakeRequest(string verb, string requestUrl, TRequest obj, IServiceAuth auth) + { + return MakeRequest(verb, requestUrl, obj, 0, auth); + } + /// + /// Perform a synchronous REST request. + /// + /// + /// + /// + /// + /// Request timeout in milliseconds. Timeout.Infinite indicates no timeout. If 0 is passed then the default HttpWebRequest timeout is used (100 seconds) + /// + /// + /// The response. If there was an internal exception or the request timed out, + /// then the default(TResponse) is returned. + /// public static TResponse MakeRequest(string verb, string requestUrl, TRequest obj, int pTimeout) { return MakeRequest(verb, requestUrl, obj, pTimeout, 0); } + public static TResponse MakeRequest(string verb, string requestUrl, TRequest obj, int pTimeout, IServiceAuth auth) + { + return MakeRequest(verb, requestUrl, obj, pTimeout, 0, auth); + } + + /// Perform a synchronous REST request. + /// + /// + /// + /// + /// + /// Request timeout in milliseconds. Timeout.Infinite indicates no timeout. If 0 is passed then the default HttpWebRequest timeout is used (100 seconds) + /// + /// + /// + /// The response. If there was an internal exception or the request timed out, + /// then the default(TResponse) is returned. + /// public static TResponse MakeRequest(string verb, string requestUrl, TRequest obj, int pTimeout, int maxConnections) { + return MakeRequest(verb, requestUrl, obj, pTimeout, maxConnections, null); + } + + /// + /// Perform a synchronous REST request. + /// + /// + /// + /// + /// + /// Request timeout in milliseconds. Timeout.Infinite indicates no timeout. If 0 is passed then the default HttpWebRequest timeout is used (100 seconds) + /// + /// + /// + /// The response. If there was an internal exception or the request timed out, + /// then the default(TResponse) is returned. + /// + public static TResponse MakeRequest(string verb, string requestUrl, TRequest obj, int pTimeout, int maxConnections, IServiceAuth auth) + { int reqnum = WebUtil.RequestNumber++; if (WebUtil.DebugLevel >= 3) - m_log.DebugFormat( - "[WEB UTIL]: HTTP OUT {0} SynchronousRestObject {1} {2}", + m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} SynchronousRestObject {1} to {2}", reqnum, verb, requestUrl); int tickstart = Util.EnvironmentTickCount(); @@ -1072,129 +1197,252 @@ namespace OpenSim.Framework WebRequest request = WebRequest.Create(requestUrl); HttpWebRequest ht = (HttpWebRequest)request; + + if (auth != null) + auth.AddAuthorization(ht.Headers); + + if (pTimeout != 0) + ht.Timeout = pTimeout; + if (maxConnections > 0 && ht.ServicePoint.ConnectionLimit < maxConnections) ht.ServicePoint.ConnectionLimit = maxConnections; request.Method = verb; MemoryStream buffer = null; - if ((verb == "POST") || (verb == "PUT")) + try { - request.ContentType = "text/xml"; + if ((verb == "POST") || (verb == "PUT")) + { + request.ContentType = "text/xml"; - buffer = new MemoryStream(); + buffer = new MemoryStream(); - XmlWriterSettings settings = new XmlWriterSettings(); - settings.Encoding = Encoding.UTF8; + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Encoding = Encoding.UTF8; - using (XmlWriter writer = XmlWriter.Create(buffer, settings)) - { - XmlSerializer serializer = new XmlSerializer(type); - serializer.Serialize(writer, obj); - writer.Flush(); - } + using (XmlWriter writer = XmlWriter.Create(buffer, settings)) + { + XmlSerializer serializer = new XmlSerializer(type); + serializer.Serialize(writer, obj); + writer.Flush(); + } + + int length = (int)buffer.Length; + request.ContentLength = length; + byte[] data = buffer.ToArray(); + + if (WebUtil.DebugLevel >= 5) + WebUtil.LogOutgoingDetail("SEND", reqnum, System.Text.Encoding.UTF8.GetString(data)); + + try + { + using (Stream requestStream = request.GetRequestStream()) + requestStream.Write(data, 0, length); + } + catch (Exception e) + { + m_log.DebugFormat( + "[SynchronousRestObjectRequester]: Exception in making request {0} {1}: {2}{3}", + verb, requestUrl, e.Message, e.StackTrace); - int length = (int)buffer.Length; - request.ContentLength = length; + return deserial; + } + finally + { + // capture how much time was spent writing + tickdata = Util.EnvironmentTickCountSubtract(tickstart); + } + } - Stream requestStream = null; try { - requestStream = request.GetRequestStream(); - requestStream.Write(buffer.ToArray(), 0, length); + using (HttpWebResponse resp = (HttpWebResponse)request.GetResponse()) + { + if (resp.ContentLength != 0) + { + using (Stream respStream = resp.GetResponseStream()) + { + deserial = XMLResponseHelper.LogAndDeserialize( + reqnum, respStream, resp.ContentLength); + } + } + else + { + m_log.DebugFormat( + "[SynchronousRestObjectRequester]: Oops! no content found in response stream from {0} {1}", + verb, requestUrl); + } + } } - catch (Exception e) + catch (WebException e) { + using (HttpWebResponse hwr = (HttpWebResponse)e.Response) + { + if (hwr != null) + { + if (hwr.StatusCode == HttpStatusCode.NotFound) + return deserial; + if (hwr.StatusCode == HttpStatusCode.Unauthorized) + { + m_log.Error(string.Format( + "[SynchronousRestObjectRequester]: Web request {0} requires authentication ", + requestUrl)); + return deserial; + } + } + else + m_log.Error(string.Format( + "[SynchronousRestObjectRequester]: WebException for {0} {1} {2} ", + verb, requestUrl, typeof(TResponse).ToString()), e); + } + } + catch (System.InvalidOperationException) + { + // This is what happens when there is invalid XML m_log.DebugFormat( - "[SynchronousRestObjectRequester]: Exception in making request {0} {1}: {2}{3}", - verb, requestUrl, e.Message, e.StackTrace); - - return deserial; + "[SynchronousRestObjectRequester]: Invalid XML from {0} {1} {2}", + verb, requestUrl, typeof(TResponse).ToString()); } - finally + catch (Exception e) { - if (requestStream != null) - requestStream.Close(); - - // capture how much time was spent writing - tickdata = Util.EnvironmentTickCountSubtract(tickstart); + m_log.Debug(string.Format( + "[SynchronousRestObjectRequester]: Exception on response from {0} {1} ", + verb, requestUrl), e); } - } - try - { - using (HttpWebResponse resp = (HttpWebResponse)request.GetResponse()) + int tickdiff = Util.EnvironmentTickCountSubtract(tickstart); + if (tickdiff > WebUtil.LongCallTime) { - if (resp.ContentLength != 0) - { - Stream respStream = resp.GetResponseStream(); - XmlSerializer deserializer = new XmlSerializer(typeof(TResponse)); - deserial = (TResponse)deserializer.Deserialize(respStream); - respStream.Close(); - } - else + string originalRequest = null; + + if (buffer != null) { - m_log.DebugFormat( - "[SynchronousRestObjectRequester]: Oops! no content found in response stream from {0} {1}", - verb, requestUrl); + originalRequest = Encoding.UTF8.GetString(buffer.ToArray()); + + if (originalRequest.Length > WebUtil.MaxRequestDiagLength) + originalRequest = originalRequest.Remove(WebUtil.MaxRequestDiagLength); } - } - } - catch (WebException e) - { - HttpWebResponse hwr = (HttpWebResponse)e.Response; - if (hwr != null && hwr.StatusCode == HttpStatusCode.NotFound) - return deserial; - else - m_log.ErrorFormat( - "[SynchronousRestObjectRequester]: WebException for {0} {1} {2}: {3} {4}", - verb, requestUrl, typeof(TResponse).ToString(), e.Message, e.StackTrace); + m_log.InfoFormat( + "[LOGHTTP]: Slow SynchronousRestObject request {0} {1} to {2} took {3}ms, {4}ms writing, {5}", + reqnum, verb, requestUrl, tickdiff, tickdata, + originalRequest); + } + else if (WebUtil.DebugLevel >= 4) + { + m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} took {1}ms, {2}ms writing", + reqnum, tickdiff, tickdata); + } } - catch (System.InvalidOperationException) + finally { - // This is what happens when there is invalid XML - m_log.DebugFormat( - "[SynchronousRestObjectRequester]: Invalid XML from {0} {1} {2}", - verb, requestUrl, typeof(TResponse).ToString()); + if (buffer != null) + buffer.Dispose(); } - catch (Exception e) + + return deserial; + } + + + public static class XMLResponseHelper + { + public static TResponse LogAndDeserialize(int reqnum, Stream respStream, long contentLength) { - m_log.DebugFormat( - "[SynchronousRestObjectRequester]: Exception on response from {0} {1}: {2}{3}", - verb, requestUrl, e.Message, e.StackTrace); + XmlSerializer deserializer = new XmlSerializer(typeof(TResponse)); + + if (WebUtil.DebugLevel >= 5) + { + byte[] data = new byte[contentLength]; + Util.ReadStream(respStream, data); + + WebUtil.LogResponseDetail(reqnum, System.Text.Encoding.UTF8.GetString(data)); + + using (MemoryStream temp = new MemoryStream(data)) + return (TResponse)deserializer.Deserialize(temp); + } + else + { + return (TResponse)deserializer.Deserialize(respStream); + } } + } + } - int tickdiff = Util.EnvironmentTickCountSubtract(tickstart); - if (tickdiff > WebUtil.LongCallTime) + + public static class XMLRPCRequester + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public static Hashtable SendRequest(Hashtable ReqParams, string method, string url) + { + int reqnum = WebUtil.RequestNumber++; + + if (WebUtil.DebugLevel >= 3) + m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} XML-RPC '{1}' to {2}", + reqnum, method, url); + + int tickstart = Util.EnvironmentTickCount(); + string responseStr = null; + + try { - string originalRequest = null; + ArrayList SendParams = new ArrayList(); + SendParams.Add(ReqParams); - if (buffer != null) + XmlRpcRequest Req = new XmlRpcRequest(method, SendParams); + + if (WebUtil.DebugLevel >= 5) + { + string str = Req.ToString(); + str = XElement.Parse(str).ToString(SaveOptions.DisableFormatting); + WebUtil.LogOutgoingDetail("SEND", reqnum, str); + } + + XmlRpcResponse Resp = Req.Send(url, 30000); + + try { - originalRequest = Encoding.UTF8.GetString(buffer.ToArray()); + responseStr = Resp.ToString(); + responseStr = XElement.Parse(responseStr).ToString(SaveOptions.DisableFormatting); - if (originalRequest.Length > WebUtil.MaxRequestDiagLength) - originalRequest = originalRequest.Remove(WebUtil.MaxRequestDiagLength); + if (WebUtil.DebugLevel >= 5) + WebUtil.LogResponseDetail(reqnum, responseStr); + } + catch (Exception e) + { + m_log.Error("Error parsing XML-RPC response", e); + } + + if (Resp.IsFault) + { + m_log.DebugFormat( + "[LOGHTTP]: XML-RPC request {0} '{1}' to {2} FAILED: FaultCode={3}, FaultMessage={4}", + reqnum, method, url, Resp.FaultCode, Resp.FaultString); + return null; } - m_log.InfoFormat( - "[SynchronousRestObjectRequester]: Slow request {0} {1} {2} took {3}ms, {4}ms writing, {5}", - reqnum, - verb, - requestUrl, - tickdiff, - tickdata, - originalRequest); + Hashtable RespData = (Hashtable)Resp.Value; + return RespData; } - else if (WebUtil.DebugLevel >= 4) + finally { - m_log.DebugFormat( - "[WEB UTIL]: HTTP OUT {0} took {1}ms, {2}ms writing", - reqnum, tickdiff, tickdata); + int tickdiff = Util.EnvironmentTickCountSubtract(tickstart); + if (tickdiff > WebUtil.LongCallTime) + { + m_log.InfoFormat( + "[LOGHTTP]: Slow XML-RPC request {0} '{1}' to {2} took {3}ms, {4}", + reqnum, method, url, tickdiff, + responseStr != null + ? (responseStr.Length > WebUtil.MaxRequestDiagLength ? responseStr.Remove(WebUtil.MaxRequestDiagLength) : responseStr) + : ""); + } + else if (WebUtil.DebugLevel >= 4) + { + m_log.DebugFormat("[LOGHTTP]: HTTP OUT {0} took {1}ms", reqnum, tickdiff); + } } - - return deserial; } + } } -- cgit v1.1