aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Framework')
-rw-r--r--OpenSim/Framework/AssemblyInfo.cs3
-rw-r--r--OpenSim/Framework/AssetLoader/Filesystem/Properties/AssemblyInfo.cs4
-rw-r--r--OpenSim/Framework/Communications/Properties/AssemblyInfo.cs4
-rw-r--r--OpenSim/Framework/Configuration/HTTP/HTTPConfiguration.cs32
-rw-r--r--OpenSim/Framework/Configuration/HTTP/Properties/AssemblyInfo.cs4
-rw-r--r--OpenSim/Framework/Configuration/XML/Properties/AssemblyInfo.cs4
-rw-r--r--OpenSim/Framework/Console/AssemblyInfo.cs2
-rw-r--r--OpenSim/Framework/Console/CommandConsole.cs10
-rw-r--r--OpenSim/Framework/Console/ConsoleDisplayTable.cs15
-rw-r--r--OpenSim/Framework/Console/ConsoleUtil.cs36
-rw-r--r--OpenSim/Framework/DAMap.cs273
-rw-r--r--OpenSim/Framework/DoubleDictionaryThreadAbortSafe.cs508
-rw-r--r--OpenSim/Framework/Monitoring/AssetStatsCollector.cs26
-rw-r--r--OpenSim/Framework/Monitoring/BaseStatsCollector.cs7
-rw-r--r--OpenSim/Framework/Monitoring/Interfaces/IStatsCollector.cs9
-rw-r--r--OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs4
-rw-r--r--OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs25
-rwxr-xr-xOpenSim/Framework/Monitoring/Stats/CounterStat.cs228
-rw-r--r--OpenSim/Framework/Monitoring/Stats/Stat.cs26
-rw-r--r--OpenSim/Framework/Monitoring/StatsManager.cs69
-rw-r--r--OpenSim/Framework/Monitoring/UserStatsCollector.cs18
-rw-r--r--OpenSim/Framework/PluginManager.cs563
-rw-r--r--OpenSim/Framework/PrimitiveBaseShape.cs32
-rw-r--r--OpenSim/Framework/RegionInfo.cs27
-rw-r--r--OpenSim/Framework/RegionLoader/Filesystem/Properties/AssemblyInfo.cs4
-rw-r--r--OpenSim/Framework/RegionLoader/Web/Properties/AssemblyInfo.cs4
-rw-r--r--OpenSim/Framework/Serialization/ArchiveConstants.cs5
-rw-r--r--OpenSim/Framework/Serialization/Properties/AssemblyInfo.cs4
-rw-r--r--OpenSim/Framework/Serialization/Tests/LandDataSerializerTests.cs2
-rw-r--r--OpenSim/Framework/Serialization/Tests/RegionSettingsSerializerTests.cs2
-rw-r--r--OpenSim/Framework/Servers/BaseOpenSimServer.cs122
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs236
-rw-r--r--OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs14
-rw-r--r--OpenSim/Framework/Servers/HttpServer/JsonRPCMethod.cs34
-rw-r--r--OpenSim/Framework/Servers/HttpServer/JsonRpcResponse.cs150
-rw-r--r--OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs16
-rw-r--r--OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs4
-rw-r--r--OpenSim/Framework/Servers/HttpServer/RestSessionService.cs15
-rw-r--r--OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs1102
-rw-r--r--OpenSim/Framework/Servers/MainServer.cs11
-rw-r--r--OpenSim/Framework/Servers/Properties/AssemblyInfo.cs2
-rw-r--r--OpenSim/Framework/Servers/ServerBase.cs111
-rw-r--r--OpenSim/Framework/Servers/Tests/OSHttpTests.cs8
-rw-r--r--OpenSim/Framework/Servers/Tests/VersionInfoTests.cs3
-rw-r--r--OpenSim/Framework/Servers/VersionInfo.cs2
-rw-r--r--OpenSim/Framework/Tests/AgentCircuitDataTest.cs5
-rw-r--r--OpenSim/Framework/Tests/AnimationTests.cs2
-rw-r--r--OpenSim/Framework/Tests/AssetBaseTest.cs3
-rw-r--r--OpenSim/Framework/Tests/CacheTests.cs3
-rw-r--r--OpenSim/Framework/Tests/LocationTest.cs3
-rw-r--r--OpenSim/Framework/Tests/MundaneFrameworkTests.cs11
-rw-r--r--OpenSim/Framework/Tests/PrimeNumberHelperTests.cs3
-rw-r--r--OpenSim/Framework/Tests/UtilTest.cs2
-rw-r--r--OpenSim/Framework/Util.cs155
-rw-r--r--OpenSim/Framework/WebUtil.cs61
55 files changed, 3663 insertions, 365 deletions
diff --git a/OpenSim/Framework/AssemblyInfo.cs b/OpenSim/Framework/AssemblyInfo.cs
index 02986d5..d6b4e6a 100644
--- a/OpenSim/Framework/AssemblyInfo.cs
+++ b/OpenSim/Framework/AssemblyInfo.cs
@@ -59,5 +59,4 @@ using System.Runtime.InteropServices;
59// Revision 59// Revision
60// 60//
61 61
62[assembly : AssemblyVersion("0.7.5.*")] 62[assembly : AssemblyVersion("0.7.6.*")]
63[assembly : AssemblyFileVersion("0.6.5.0")] \ No newline at end of file
diff --git a/OpenSim/Framework/AssetLoader/Filesystem/Properties/AssemblyInfo.cs b/OpenSim/Framework/AssetLoader/Filesystem/Properties/AssemblyInfo.cs
index 0498ed4..feffa26 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;
29// Build Number 29// Build Number
30// Revision 30// Revision
31// 31//
32[assembly: AssemblyVersion("0.7.5.*")] 32[assembly: AssemblyVersion("0.7.6.*")]
33[assembly: AssemblyFileVersion("1.0.0.0")] 33
diff --git a/OpenSim/Framework/Communications/Properties/AssemblyInfo.cs b/OpenSim/Framework/Communications/Properties/AssemblyInfo.cs
index 6d1c03a..df8eb52 100644
--- a/OpenSim/Framework/Communications/Properties/AssemblyInfo.cs
+++ b/OpenSim/Framework/Communications/Properties/AssemblyInfo.cs
@@ -61,5 +61,5 @@ using System.Runtime.InteropServices;
61// You can specify all the values or you can default the Revision and Build Numbers 61// You can specify all the values or you can default the Revision and Build Numbers
62// by using the '*' as shown below: 62// by using the '*' as shown below:
63 63
64[assembly : AssemblyVersion("0.7.5.*")] 64[assembly : AssemblyVersion("0.7.6.*")]
65[assembly : AssemblyFileVersion("0.6.5.0")] 65
diff --git a/OpenSim/Framework/Configuration/HTTP/HTTPConfiguration.cs b/OpenSim/Framework/Configuration/HTTP/HTTPConfiguration.cs
index 3dce578..6681c37 100644
--- a/OpenSim/Framework/Configuration/HTTP/HTTPConfiguration.cs
+++ b/OpenSim/Framework/Configuration/HTTP/HTTPConfiguration.cs
@@ -65,23 +65,27 @@ namespace OpenSim.Framework.Configuration.HTTP
65 byte[] buf = new byte[8192]; 65 byte[] buf = new byte[8192];
66 HttpWebRequest request = 66 HttpWebRequest request =
67 (HttpWebRequest) WebRequest.Create(remoteConfigSettings.baseConfigURL + configFileName); 67 (HttpWebRequest) WebRequest.Create(remoteConfigSettings.baseConfigURL + configFileName);
68 HttpWebResponse response = (HttpWebResponse) request.GetResponse(); 68 using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
69
70 Stream resStream = response.GetResponseStream();
71
72 string tempString = null;
73 int count = 0;
74
75 do
76 { 69 {
77 count = resStream.Read(buf, 0, buf.Length); 70 using (Stream resStream = response.GetResponseStream())
78 if (count != 0)
79 { 71 {
80 tempString = Util.UTF8.GetString(buf, 0, count); 72 string tempString = null;
81 sb.Append(tempString); 73 int count = 0;
74
75 do
76 {
77 count = resStream.Read(buf, 0, buf.Length);
78 if (count != 0)
79 {
80 tempString = Util.UTF8.GetString(buf, 0, count);
81 sb.Append(tempString);
82 }
83 }
84 while (count > 0);
85
86 LoadDataFromString(sb.ToString());
82 } 87 }
83 } while (count > 0); 88 }
84 LoadDataFromString(sb.ToString());
85 } 89 }
86 catch (WebException) 90 catch (WebException)
87 { 91 {
diff --git a/OpenSim/Framework/Configuration/HTTP/Properties/AssemblyInfo.cs b/OpenSim/Framework/Configuration/HTTP/Properties/AssemblyInfo.cs
index 0674656..3ef9682 100644
--- a/OpenSim/Framework/Configuration/HTTP/Properties/AssemblyInfo.cs
+++ b/OpenSim/Framework/Configuration/HTTP/Properties/AssemblyInfo.cs
@@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
29// Build Number 29// Build Number
30// Revision 30// Revision
31// 31//
32[assembly: AssemblyVersion("0.7.5.*")] 32[assembly: AssemblyVersion("0.7.6.*")]
33[assembly: AssemblyFileVersion("1.0.0.0")] 33
diff --git a/OpenSim/Framework/Configuration/XML/Properties/AssemblyInfo.cs b/OpenSim/Framework/Configuration/XML/Properties/AssemblyInfo.cs
index 1095b23..cbdffeb 100644
--- a/OpenSim/Framework/Configuration/XML/Properties/AssemblyInfo.cs
+++ b/OpenSim/Framework/Configuration/XML/Properties/AssemblyInfo.cs
@@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
29// Build Number 29// Build Number
30// Revision 30// Revision
31// 31//
32[assembly: AssemblyVersion("0.7.5.*")] 32[assembly: AssemblyVersion("0.7.6.*")]
33[assembly: AssemblyFileVersion("1.0.0.0")] 33
diff --git a/OpenSim/Framework/Console/AssemblyInfo.cs b/OpenSim/Framework/Console/AssemblyInfo.cs
index 37c7304..c618454 100644
--- a/OpenSim/Framework/Console/AssemblyInfo.cs
+++ b/OpenSim/Framework/Console/AssemblyInfo.cs
@@ -55,4 +55,4 @@ using System.Runtime.InteropServices;
55// You can specify all values by your own or you can build default build and revision 55// You can specify all values by your own or you can build default build and revision
56// numbers with the '*' character (the default): 56// numbers with the '*' character (the default):
57 57
58[assembly : AssemblyVersion("0.7.5.*")] 58[assembly : AssemblyVersion("0.7.6.*")]
diff --git a/OpenSim/Framework/Console/CommandConsole.cs b/OpenSim/Framework/Console/CommandConsole.cs
index d703d78..9490013 100644
--- a/OpenSim/Framework/Console/CommandConsole.cs
+++ b/OpenSim/Framework/Console/CommandConsole.cs
@@ -110,10 +110,11 @@ namespace OpenSim.Framework.Console
110 // Remove initial help keyword 110 // Remove initial help keyword
111 helpParts.RemoveAt(0); 111 helpParts.RemoveAt(0);
112 112
113 help.Add(""); // Will become a newline.
114
113 // General help 115 // General help
114 if (helpParts.Count == 0) 116 if (helpParts.Count == 0)
115 { 117 {
116 help.Add(""); // Will become a newline.
117 help.Add(GeneralHelpText); 118 help.Add(GeneralHelpText);
118 help.AddRange(CollectAllCommandsHelp()); 119 help.AddRange(CollectAllCommandsHelp());
119 } 120 }
@@ -129,6 +130,8 @@ namespace OpenSim.Framework.Console
129 help.AddRange(CollectHelp(helpParts)); 130 help.AddRange(CollectHelp(helpParts));
130 } 131 }
131 132
133 help.Add(""); // Will become a newline.
134
132 return help; 135 return help;
133 } 136 }
134 137
@@ -199,14 +202,11 @@ namespace OpenSim.Framework.Console
199 202
200 string descriptiveHelp = commandInfo.descriptive_help; 203 string descriptiveHelp = commandInfo.descriptive_help;
201 204
202 // If we do have some descriptive help then insert a spacing line before and after for readability. 205 // If we do have some descriptive help then insert a spacing line before for readability.
203 if (descriptiveHelp != string.Empty) 206 if (descriptiveHelp != string.Empty)
204 help.Add(string.Empty); 207 help.Add(string.Empty);
205 208
206 help.Add(commandInfo.descriptive_help); 209 help.Add(commandInfo.descriptive_help);
207
208 if (descriptiveHelp != string.Empty)
209 help.Add(string.Empty);
210 } 210 }
211 else 211 else
212 { 212 {
diff --git a/OpenSim/Framework/Console/ConsoleDisplayTable.cs b/OpenSim/Framework/Console/ConsoleDisplayTable.cs
index c620dfe..711a337 100644
--- a/OpenSim/Framework/Console/ConsoleDisplayTable.cs
+++ b/OpenSim/Framework/Console/ConsoleDisplayTable.cs
@@ -56,7 +56,7 @@ namespace OpenSim.Framework.Console
56 public List<ConsoleDisplayTableRow> Rows { get; private set; } 56 public List<ConsoleDisplayTableRow> Rows { get; private set; }
57 57
58 /// <summary> 58 /// <summary>
59 /// Number of spaces to indent the table. 59 /// Number of spaces to indent the whole table.
60 /// </summary> 60 /// </summary>
61 public int Indent { get; set; } 61 public int Indent { get; set; }
62 62
@@ -84,7 +84,7 @@ namespace OpenSim.Framework.Console
84 Columns.Add(new ConsoleDisplayTableColumn(name, width)); 84 Columns.Add(new ConsoleDisplayTableColumn(name, width));
85 } 85 }
86 86
87 public void AddRow(params string[] cells) 87 public void AddRow(params object[] cells)
88 { 88 {
89 Rows.Add(new ConsoleDisplayTableRow(cells)); 89 Rows.Add(new ConsoleDisplayTableRow(cells));
90 } 90 }
@@ -113,7 +113,8 @@ namespace OpenSim.Framework.Console
113 113
114 for (int i = 0; i < Columns.Count; i++) 114 for (int i = 0; i < Columns.Count; i++)
115 { 115 {
116 formatSb.Append(' ', TableSpacing); 116 if (i != 0)
117 formatSb.Append(' ', TableSpacing);
117 118
118 // Can only do left formatting for now 119 // Can only do left formatting for now
119 formatSb.AppendFormat("{{{0},-{1}}}", i, Columns[i].Width); 120 formatSb.AppendFormat("{{{0},-{1}}}", i, Columns[i].Width);
@@ -139,16 +140,16 @@ namespace OpenSim.Framework.Console
139 140
140 public struct ConsoleDisplayTableRow 141 public struct ConsoleDisplayTableRow
141 { 142 {
142 public List<string> Cells { get; private set; } 143 public List<object> Cells { get; private set; }
143 144
144 public ConsoleDisplayTableRow(List<string> cells) : this() 145 public ConsoleDisplayTableRow(List<object> cells) : this()
145 { 146 {
146 Cells = cells; 147 Cells = cells;
147 } 148 }
148 149
149 public ConsoleDisplayTableRow(params string[] cells) : this() 150 public ConsoleDisplayTableRow(params object[] cells) : this()
150 { 151 {
151 Cells = new List<string>(cells); 152 Cells = new List<object>(cells);
152 } 153 }
153 } 154 }
154} \ No newline at end of file 155} \ No newline at end of file
diff --git a/OpenSim/Framework/Console/ConsoleUtil.cs b/OpenSim/Framework/Console/ConsoleUtil.cs
index 16a63e0..97a86a8 100644
--- a/OpenSim/Framework/Console/ConsoleUtil.cs
+++ b/OpenSim/Framework/Console/ConsoleUtil.cs
@@ -49,14 +49,14 @@ namespace OpenSim.Framework.Console
49 = @"Each component of the coord is comma separated. There must be no spaces between the commas. 49 = @"Each component of the coord is comma separated. There must be no spaces between the commas.
50 If you don't care about the z component you can simply omit it. 50 If you don't care about the z component you can simply omit it.
51 If you don't care about the x or y components then you can leave them blank (though a comma is still required) 51 If you don't care about the x or y components then you can leave them blank (though a comma is still required)
52 If you want to specify the maxmimum value of a component then you can use ~ instead of a number 52 If you want to specify the maximum value of a component then you can use ~ instead of a number
53 If you want to specify the minimum value of a component then you can use -~ instead of a number 53 If you want to specify the minimum value of a component then you can use -~ instead of a number
54 e.g. 54 e.g.
55 delete object pos 20,20,20 to 40,40,40 55 show object pos 20,20,20 to 40,40,40
56 delete object pos 20,20 to 40,40 56 delete object pos 20,20 to 40,40
57 delete object pos ,20,20 to ,40,40 57 show object pos ,20,20 to ,40,40
58 delete object pos ,,30 to ,,~ 58 delete object pos ,,30 to ,,~
59 delete object pos ,,-~ to ,,30"; 59 show object pos ,,-~ to ,,30";
60 60
61 public const string MinRawConsoleVectorValue = "-~"; 61 public const string MinRawConsoleVectorValue = "-~";
62 public const string MaxRawConsoleVectorValue = "~"; 62 public const string MaxRawConsoleVectorValue = "~";
@@ -97,7 +97,7 @@ namespace OpenSim.Framework.Console
97 if (!UUID.TryParse(rawUuid, out uuid)) 97 if (!UUID.TryParse(rawUuid, out uuid))
98 { 98 {
99 if (console != null) 99 if (console != null)
100 console.OutputFormat("{0} is not a valid uuid", rawUuid); 100 console.OutputFormat("ERROR: {0} is not a valid uuid", rawUuid);
101 101
102 return false; 102 return false;
103 } 103 }
@@ -110,7 +110,7 @@ namespace OpenSim.Framework.Console
110 if (!uint.TryParse(rawLocalId, out localId)) 110 if (!uint.TryParse(rawLocalId, out localId))
111 { 111 {
112 if (console != null) 112 if (console != null)
113 console.OutputFormat("{0} is not a valid local id", localId); 113 console.OutputFormat("ERROR: {0} is not a valid local id", localId);
114 114
115 return false; 115 return false;
116 } 116 }
@@ -118,7 +118,7 @@ namespace OpenSim.Framework.Console
118 if (localId == 0) 118 if (localId == 0)
119 { 119 {
120 if (console != null) 120 if (console != null)
121 console.OutputFormat("{0} is not a valid local id - it must be greater than 0", localId); 121 console.OutputFormat("ERROR: {0} is not a valid local id - it must be greater than 0", localId);
122 122
123 return false; 123 return false;
124 } 124 }
@@ -150,10 +150,30 @@ namespace OpenSim.Framework.Console
150 } 150 }
151 151
152 if (console != null) 152 if (console != null)
153 console.OutputFormat("{0} is not a valid UUID or local id", rawId); 153 console.OutputFormat("ERROR: {0} is not a valid UUID or local id", rawId);
154 154
155 return false; 155 return false;
156 } 156 }
157
158 /// <summary>
159 /// Convert a minimum vector input from the console to an OpenMetaverse.Vector3
160 /// </summary>
161 /// <param name='console'>Can be null if no console is available.</param>
162 /// <param name='rawConsoleVector'>/param>
163 /// <param name='vector'></param>
164 /// <returns></returns>
165 public static bool TryParseConsoleInt(ICommandConsole console, string rawConsoleInt, out int i)
166 {
167 if (!int.TryParse(rawConsoleInt, out i))
168 {
169 if (console != null)
170 console.OutputFormat("ERROR: {0} is not a valid integer", rawConsoleInt);
171
172 return false;
173 }
174
175 return true;
176 }
157 177
158 /// <summary> 178 /// <summary>
159 /// Convert a minimum vector input from the console to an OpenMetaverse.Vector3 179 /// Convert a minimum vector input from the console to an OpenMetaverse.Vector3
diff --git a/OpenSim/Framework/DAMap.cs b/OpenSim/Framework/DAMap.cs
new file mode 100644
index 0000000..64cea77
--- /dev/null
+++ b/OpenSim/Framework/DAMap.cs
@@ -0,0 +1,273 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.IO;
32using System.Text;
33using System.Xml;
34using System.Xml.Schema;
35using System.Xml.Serialization;
36using OpenMetaverse;
37using OpenMetaverse.StructuredData;
38
39namespace OpenSim.Framework
40{
41 /// <summary>
42 /// This class stores and retrieves dynamic attributes.
43 /// </summary>
44 /// <remarks>
45 /// Modules that want to use dynamic attributes need to do so in a private data store
46 /// which is accessed using a unique name. DAMap provides access to the data stores,
47 /// each of which is an OSDMap. Modules are free to store any type of data they want
48 /// within their data store. However, avoid storing large amounts of data because that
49 /// would slow down database access.
50 /// </remarks>
51 public class DAMap : IDictionary<string, OSDMap>, IXmlSerializable
52 {
53 private static readonly int MIN_STORE_NAME_LENGTH = 4;
54
55 protected OSDMap m_map;
56
57 public DAMap() { m_map = new OSDMap(); }
58
59 public XmlSchema GetSchema() { return null; }
60
61 public static DAMap FromXml(string rawXml)
62 {
63 DAMap map = new DAMap();
64 map.ReadXml(rawXml);
65 return map;
66 }
67
68 public void ReadXml(string rawXml)
69 {
70 // System.Console.WriteLine("Trying to deserialize [{0}]", rawXml);
71
72 lock (this)
73 m_map = (OSDMap)OSDParser.DeserializeLLSDXml(rawXml);
74 }
75
76 // WARNING: this is temporary for experimentation only, it will be removed!!!!
77 public OSDMap TopLevelMap
78 {
79 get { return m_map; }
80 set { m_map = value; }
81 }
82
83
84 public void ReadXml(XmlReader reader)
85 {
86 ReadXml(reader.ReadInnerXml());
87 }
88
89 public string ToXml()
90 {
91 lock (this)
92 return OSDParser.SerializeLLSDXmlString(m_map);
93 }
94
95 public void WriteXml(XmlWriter writer)
96 {
97 writer.WriteRaw(ToXml());
98 }
99
100 public void CopyFrom(DAMap other)
101 {
102 // Deep copy
103
104 string data = null;
105 lock (other)
106 {
107 if (other.Count > 0)
108 {
109 data = OSDParser.SerializeLLSDXmlString(other.m_map);
110 }
111 }
112
113 lock (this)
114 {
115 if (data == null)
116 Clear();
117 else
118 m_map = (OSDMap)OSDParser.DeserializeLLSDXml(data);
119 }
120 }
121
122 /// <summary>
123 /// Returns the number of data stores.
124 /// </summary>
125 public int Count { get { lock (this) { return m_map.Count; } } }
126
127 public bool IsReadOnly { get { return false; } }
128
129 /// <summary>
130 /// Returns the names of the data stores.
131 /// </summary>
132 public ICollection<string> Keys { get { lock (this) { return m_map.Keys; } } }
133
134 /// <summary>
135 /// Returns all the data stores.
136 /// </summary>
137 public ICollection<OSDMap> Values
138 {
139 get
140 {
141 lock (this)
142 {
143 List<OSDMap> stores = new List<OSDMap>(m_map.Count);
144 foreach (OSD llsd in m_map.Values)
145 stores.Add((OSDMap)llsd);
146 return stores;
147 }
148 }
149 }
150
151 /// <summary>
152 /// Gets or sets one data store.
153 /// </summary>
154 /// <param name="key">Store name</param>
155 /// <returns></returns>
156 public OSDMap this[string key]
157 {
158 get
159 {
160 OSD llsd;
161
162 lock (this)
163 {
164 if (m_map.TryGetValue(key, out llsd))
165 return (OSDMap)llsd;
166 else
167 return null;
168 }
169 }
170
171 set
172 {
173 ValidateKey(key);
174 lock (this)
175 m_map[key] = value;
176 }
177 }
178
179 /// <summary>
180 /// Validate the key used for storing separate data stores.
181 /// </summary>
182 /// <param name='key'></param>
183 private static void ValidateKey(string key)
184 {
185 if (key.Length < MIN_STORE_NAME_LENGTH)
186 throw new Exception("Minimum store name length is " + MIN_STORE_NAME_LENGTH);
187 }
188
189 public bool ContainsKey(string key)
190 {
191 lock (this)
192 return m_map.ContainsKey(key);
193 }
194
195 public void Add(string key, OSDMap store)
196 {
197 ValidateKey(key);
198 lock (this)
199 m_map.Add(key, store);
200 }
201
202 public void Add(KeyValuePair<string, OSDMap> kvp)
203 {
204 ValidateKey(kvp.Key);
205 lock (this)
206 m_map.Add(kvp.Key, kvp.Value);
207 }
208
209 public bool Remove(string key)
210 {
211 lock (this)
212 return m_map.Remove(key);
213 }
214
215 public bool TryGetValue(string key, out OSDMap store)
216 {
217 lock (this)
218 {
219 OSD llsd;
220 if (m_map.TryGetValue(key, out llsd))
221 {
222 store = (OSDMap)llsd;
223 return true;
224 }
225 else
226 {
227 store = null;
228 return false;
229 }
230 }
231 }
232
233 public void Clear()
234 {
235 lock (this)
236 m_map.Clear();
237 }
238
239 public bool Contains(KeyValuePair<string, OSDMap> kvp)
240 {
241 lock (this)
242 return m_map.ContainsKey(kvp.Key);
243 }
244
245 public void CopyTo(KeyValuePair<string, OSDMap>[] array, int index)
246 {
247 throw new NotImplementedException();
248 }
249
250 public bool Remove(KeyValuePair<string, OSDMap> kvp)
251 {
252 lock (this)
253 return m_map.Remove(kvp.Key);
254 }
255
256 public System.Collections.IDictionaryEnumerator GetEnumerator()
257 {
258 lock (this)
259 return m_map.GetEnumerator();
260 }
261
262 IEnumerator<KeyValuePair<string, OSDMap>> IEnumerable<KeyValuePair<string, OSDMap>>.GetEnumerator()
263 {
264 return null;
265 }
266
267 IEnumerator IEnumerable.GetEnumerator()
268 {
269 lock (this)
270 return m_map.GetEnumerator();
271 }
272 }
273} \ No newline at end of file
diff --git a/OpenSim/Framework/DoubleDictionaryThreadAbortSafe.cs b/OpenSim/Framework/DoubleDictionaryThreadAbortSafe.cs
new file mode 100644
index 0000000..9056548
--- /dev/null
+++ b/OpenSim/Framework/DoubleDictionaryThreadAbortSafe.cs
@@ -0,0 +1,508 @@
1/*
2 * Copyright (c) 2008, openmetaverse.org, http://opensimulator.org/
3 * All rights reserved.
4 *
5 * - Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * - Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 * - Neither the name of the openmetaverse.org nor the names
11 * of its contributors may be used to endorse or promote products derived from
12 * this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24 * POSSIBILITY OF SUCH DAMAGE.
25 */
26
27using System;
28using System.Threading;
29using System.Collections.Generic;
30
31namespace OpenSim.Framework
32{
33 /// <summary>
34 /// A double dictionary that is thread abort safe.
35 /// </summary>
36 /// <remarks>
37 /// This adapts OpenMetaverse.DoubleDictionary to be thread-abort safe by acquiring ReaderWriterLockSlim within
38 /// a finally section (which can't be interrupted by Thread.Abort()).
39 /// </remarks>
40 public class DoubleDictionaryThreadAbortSafe<TKey1, TKey2, TValue>
41 {
42 Dictionary<TKey1, TValue> Dictionary1;
43 Dictionary<TKey2, TValue> Dictionary2;
44 ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
45
46 public DoubleDictionaryThreadAbortSafe()
47 {
48 Dictionary1 = new Dictionary<TKey1,TValue>();
49 Dictionary2 = new Dictionary<TKey2,TValue>();
50 }
51
52 public DoubleDictionaryThreadAbortSafe(int capacity)
53 {
54 Dictionary1 = new Dictionary<TKey1, TValue>(capacity);
55 Dictionary2 = new Dictionary<TKey2, TValue>(capacity);
56 }
57
58 public void Add(TKey1 key1, TKey2 key2, TValue value)
59 {
60 bool gotLock = false;
61
62 try
63 {
64 // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
65 // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
66 // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
67 try {}
68 finally
69 {
70 rwLock.EnterWriteLock();
71 gotLock = true;
72 }
73
74 if (Dictionary1.ContainsKey(key1))
75 {
76 if (!Dictionary2.ContainsKey(key2))
77 throw new ArgumentException("key1 exists in the dictionary but not key2");
78 }
79 else if (Dictionary2.ContainsKey(key2))
80 {
81 if (!Dictionary1.ContainsKey(key1))
82 throw new ArgumentException("key2 exists in the dictionary but not key1");
83 }
84
85 Dictionary1[key1] = value;
86 Dictionary2[key2] = value;
87 }
88 finally
89 {
90 if (gotLock)
91 rwLock.ExitWriteLock();
92 }
93 }
94
95 public bool Remove(TKey1 key1, TKey2 key2)
96 {
97 bool success;
98 bool gotLock = false;
99
100 try
101 {
102 // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
103 // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
104 // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
105 try {}
106 finally
107 {
108 rwLock.EnterWriteLock();
109 gotLock = true;
110 }
111
112 Dictionary1.Remove(key1);
113 success = Dictionary2.Remove(key2);
114 }
115 finally
116 {
117 if (gotLock)
118 rwLock.ExitWriteLock();
119 }
120
121 return success;
122 }
123
124 public bool Remove(TKey1 key1)
125 {
126 bool found = false;
127 bool gotLock = false;
128
129 try
130 {
131 // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
132 // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
133 // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
134 try {}
135 finally
136 {
137 rwLock.EnterWriteLock();
138 gotLock = true;
139 }
140
141 // This is an O(n) operation!
142 TValue value;
143 if (Dictionary1.TryGetValue(key1, out value))
144 {
145 foreach (KeyValuePair<TKey2, TValue> kvp in Dictionary2)
146 {
147 if (kvp.Value.Equals(value))
148 {
149 Dictionary1.Remove(key1);
150 Dictionary2.Remove(kvp.Key);
151 found = true;
152 break;
153 }
154 }
155 }
156 }
157 finally
158 {
159 if (gotLock)
160 rwLock.ExitWriteLock();
161 }
162
163 return found;
164 }
165
166 public bool Remove(TKey2 key2)
167 {
168 bool found = false;
169 bool gotLock = false;
170
171 try
172 {
173 // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
174 // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
175 // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
176 try {}
177 finally
178 {
179 rwLock.EnterWriteLock();
180 gotLock = true;
181 }
182
183 // This is an O(n) operation!
184 TValue value;
185 if (Dictionary2.TryGetValue(key2, out value))
186 {
187 foreach (KeyValuePair<TKey1, TValue> kvp in Dictionary1)
188 {
189 if (kvp.Value.Equals(value))
190 {
191 Dictionary2.Remove(key2);
192 Dictionary1.Remove(kvp.Key);
193 found = true;
194 break;
195 }
196 }
197 }
198 }
199 finally
200 {
201 if (gotLock)
202 rwLock.ExitWriteLock();
203 }
204
205 return found;
206 }
207
208 public void Clear()
209 {
210 bool gotLock = false;
211
212 try
213 {
214 // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
215 // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
216 // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
217 try {}
218 finally
219 {
220 rwLock.EnterWriteLock();
221 gotLock = true;
222 }
223
224 Dictionary1.Clear();
225 Dictionary2.Clear();
226 }
227 finally
228 {
229 if (gotLock)
230 rwLock.ExitWriteLock();
231 }
232 }
233
234 public int Count
235 {
236 get { return Dictionary1.Count; }
237 }
238
239 public bool ContainsKey(TKey1 key)
240 {
241 return Dictionary1.ContainsKey(key);
242 }
243
244 public bool ContainsKey(TKey2 key)
245 {
246 return Dictionary2.ContainsKey(key);
247 }
248
249 public bool TryGetValue(TKey1 key, out TValue value)
250 {
251 bool success;
252 bool gotLock = false;
253
254 try
255 {
256 // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
257 // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
258 // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
259 try {}
260 finally
261 {
262 rwLock.EnterReadLock();
263 gotLock = true;
264 }
265
266 success = Dictionary1.TryGetValue(key, out value);
267 }
268 finally
269 {
270 if (gotLock)
271 rwLock.ExitReadLock();
272 }
273
274 return success;
275 }
276
277 public bool TryGetValue(TKey2 key, out TValue value)
278 {
279 bool success;
280 bool gotLock = false;
281
282 try
283 {
284 // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
285 // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
286 // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
287 try {}
288 finally
289 {
290 rwLock.EnterReadLock();
291 gotLock = true;
292 }
293
294 success = Dictionary2.TryGetValue(key, out value);
295 }
296 finally
297 {
298 if (gotLock)
299 rwLock.ExitReadLock();
300 }
301
302 return success;
303 }
304
305 public void ForEach(Action<TValue> action)
306 {
307 bool gotLock = false;
308
309 try
310 {
311 // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
312 // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
313 // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
314 try {}
315 finally
316 {
317 rwLock.EnterReadLock();
318 gotLock = true;
319 }
320
321 foreach (TValue value in Dictionary1.Values)
322 action(value);
323 }
324 finally
325 {
326 if (gotLock)
327 rwLock.ExitReadLock();
328 }
329 }
330
331 public void ForEach(Action<KeyValuePair<TKey1, TValue>> action)
332 {
333 bool gotLock = false;
334
335 try
336 {
337 // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
338 // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
339 // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
340 try {}
341 finally
342 {
343 rwLock.EnterReadLock();
344 gotLock = true;
345 }
346
347 foreach (KeyValuePair<TKey1, TValue> entry in Dictionary1)
348 action(entry);
349 }
350 finally
351 {
352 if (gotLock)
353 rwLock.ExitReadLock();
354 }
355 }
356
357 public void ForEach(Action<KeyValuePair<TKey2, TValue>> action)
358 {
359 bool gotLock = false;
360
361 try
362 {
363 // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
364 // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
365 // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
366 try {}
367 finally
368 {
369 rwLock.EnterReadLock();
370 gotLock = true;
371 }
372
373 foreach (KeyValuePair<TKey2, TValue> entry in Dictionary2)
374 action(entry);
375 }
376 finally
377 {
378 if (gotLock)
379 rwLock.ExitReadLock();
380 }
381 }
382
383 public TValue FindValue(Predicate<TValue> predicate)
384 {
385 bool gotLock = false;
386
387 try
388 {
389 // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
390 // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
391 // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
392 try {}
393 finally
394 {
395 rwLock.EnterReadLock();
396 gotLock = true;
397 }
398
399 foreach (TValue value in Dictionary1.Values)
400 {
401 if (predicate(value))
402 return value;
403 }
404 }
405 finally
406 {
407 if (gotLock)
408 rwLock.ExitReadLock();
409 }
410
411 return default(TValue);
412 }
413
414 public IList<TValue> FindAll(Predicate<TValue> predicate)
415 {
416 IList<TValue> list = new List<TValue>();
417 bool gotLock = false;
418
419 try
420 {
421 // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
422 // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
423 // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
424 try {}
425 finally
426 {
427 rwLock.EnterReadLock();
428 gotLock = true;
429 }
430
431 foreach (TValue value in Dictionary1.Values)
432 {
433 if (predicate(value))
434 list.Add(value);
435 }
436 }
437 finally
438 {
439 if (gotLock)
440 rwLock.ExitReadLock();
441 }
442
443 return list;
444 }
445
446 public int RemoveAll(Predicate<TValue> predicate)
447 {
448 IList<TKey1> list = new List<TKey1>();
449 bool gotUpgradeableLock = false;
450
451 try
452 {
453 // Avoid an asynchronous Thread.Abort() from possibly never existing an acquired lock by placing
454 // the acquision inside the main try. The inner finally block is needed because thread aborts cannot
455 // interrupt code in these blocks (hence gotLock is guaranteed to be set correctly).
456 try {}
457 finally
458 {
459 rwLock.EnterUpgradeableReadLock();
460 gotUpgradeableLock = true;
461 }
462
463 foreach (KeyValuePair<TKey1, TValue> kvp in Dictionary1)
464 {
465 if (predicate(kvp.Value))
466 list.Add(kvp.Key);
467 }
468
469 IList<TKey2> list2 = new List<TKey2>(list.Count);
470 foreach (KeyValuePair<TKey2, TValue> kvp in Dictionary2)
471 {
472 if (predicate(kvp.Value))
473 list2.Add(kvp.Key);
474 }
475
476 bool gotWriteLock = false;
477
478 try
479 {
480 try {}
481 finally
482 {
483 rwLock.EnterUpgradeableReadLock();
484 gotWriteLock = true;
485 }
486
487 for (int i = 0; i < list.Count; i++)
488 Dictionary1.Remove(list[i]);
489
490 for (int i = 0; i < list2.Count; i++)
491 Dictionary2.Remove(list2[i]);
492 }
493 finally
494 {
495 if (gotWriteLock)
496 rwLock.ExitWriteLock();
497 }
498 }
499 finally
500 {
501 if (gotUpgradeableLock)
502 rwLock.ExitUpgradeableReadLock();
503 }
504
505 return list.Count;
506 }
507 }
508} \ No newline at end of file
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 @@
28using System; 28using System;
29using System.Timers; 29using System.Timers;
30 30
31using OpenMetaverse.StructuredData;
32
31namespace OpenSim.Framework.Monitoring 33namespace OpenSim.Framework.Monitoring
32{ 34{
33 /// <summary> 35 /// <summary>
@@ -100,5 +102,29 @@ Asset requests yesterday : {3} ({4} per hour) of which {5} were not found",
100 AssetRequestsToday, assetRequestsTodayPerHour, AssetRequestsNotFoundToday, 102 AssetRequestsToday, assetRequestsTodayPerHour, AssetRequestsNotFoundToday,
101 AssetRequestsYesterday, assetRequestsYesterdayPerHour, AssetRequestsNotFoundYesterday); 103 AssetRequestsYesterday, assetRequestsYesterdayPerHour, AssetRequestsNotFoundYesterday);
102 } 104 }
105
106 public override string XReport(string uptime, string version)
107 {
108 return OSDParser.SerializeJsonString(OReport(uptime, version));
109 }
110
111 public override OSDMap OReport(string uptime, string version)
112 {
113 double elapsedHours = (DateTime.Now - startTime).TotalHours;
114 if (elapsedHours <= 0) { elapsedHours = 1; } // prevent divide by zero
115
116 long assetRequestsTodayPerHour = (long)Math.Round(AssetRequestsToday / elapsedHours);
117 long assetRequestsYesterdayPerHour = (long)Math.Round(AssetRequestsYesterday / 24.0);
118
119 OSDMap ret = new OSDMap();
120 ret.Add("AssetRequestsToday", OSD.FromLong(AssetRequestsToday));
121 ret.Add("AssetRequestsTodayPerHour", OSD.FromLong(assetRequestsTodayPerHour));
122 ret.Add("AssetRequestsNotFoundToday", OSD.FromLong(AssetRequestsNotFoundToday));
123 ret.Add("AssetRequestsYesterday", OSD.FromLong(AssetRequestsYesterday));
124 ret.Add("AssetRequestsYesterdayPerHour", OSD.FromLong(assetRequestsYesterdayPerHour));
125 ret.Add("AssetRequestsNotFoundYesterday", OSD.FromLong(assetRequestsNotFoundYesterday));
126
127 return ret;
128 }
103 } 129 }
104} 130}
diff --git a/OpenSim/Framework/Monitoring/BaseStatsCollector.cs b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs
index 446e3c0..23dba09 100644
--- a/OpenSim/Framework/Monitoring/BaseStatsCollector.cs
+++ b/OpenSim/Framework/Monitoring/BaseStatsCollector.cs
@@ -80,5 +80,12 @@ namespace OpenSim.Framework.Monitoring
80 { 80 {
81 return (string) Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0).ToString() ; 81 return (string) Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0).ToString() ;
82 } 82 }
83
84 public virtual OSDMap OReport(string uptime, string version)
85 {
86 OSDMap ret = new OSDMap();
87 ret.Add("TotalMemory", new OSDReal(Math.Round(GC.GetTotalMemory(false) / 1024.0 / 1024.0)));
88 return ret;
89 }
83 } 90 }
84} 91}
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 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27 27
28using OpenMetaverse.StructuredData;
29
28namespace OpenSim.Framework.Monitoring 30namespace OpenSim.Framework.Monitoring
29{ 31{
30 /// <summary> 32 /// <summary>
@@ -45,5 +47,12 @@ namespace OpenSim.Framework.Monitoring
45 /// A <see cref="System.String"/> 47 /// A <see cref="System.String"/>
46 /// </returns> 48 /// </returns>
47 string XReport(string uptime, string version); 49 string XReport(string uptime, string version);
50
51 /// <summary>
52 /// Report back collected statistical information as an OSDMap of key/values
53 /// </summary>
54 /// <returns>
55 /// </returns>
56 OSDMap OReport(string uptime, string version);
48 } 57 }
49} 58}
diff --git a/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs b/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs
index 1f2bb40..36678bb 100644
--- a/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs
+++ b/OpenSim/Framework/Monitoring/Properties/AssemblyInfo.cs
@@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
29// Build Number 29// Build Number
30// Revision 30// Revision
31// 31//
32[assembly: AssemblyVersion("0.7.5.*")] 32[assembly: AssemblyVersion("0.7.6.*")]
33[assembly: AssemblyFileVersion("1.0.0.0")] 33
diff --git a/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs
index aa86202..109a58f 100644
--- a/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs
+++ b/OpenSim/Framework/Monitoring/SimExtraStatsCollector.cs
@@ -359,11 +359,11 @@ Asset service request failures: {3}" + Environment.NewLine,
359 inPacketsPerSecond, outPacketsPerSecond, pendingDownloads, pendingUploads, unackedBytes, totalFrameTime, 359 inPacketsPerSecond, outPacketsPerSecond, pendingDownloads, pendingUploads, unackedBytes, totalFrameTime,
360 netFrameTime, physicsFrameTime, otherFrameTime, agentFrameTime, imageFrameTime)); 360 netFrameTime, physicsFrameTime, otherFrameTime, agentFrameTime, imageFrameTime));
361 361
362 Dictionary<string, Dictionary<string, Stat>> sceneStats; 362 SortedDictionary<string, SortedDictionary<string, Stat>> sceneStats;
363 363
364 if (StatsManager.TryGetStats("scene", out sceneStats)) 364 if (StatsManager.TryGetStats("scene", out sceneStats))
365 { 365 {
366 foreach (KeyValuePair<string, Dictionary<string, Stat>> kvp in sceneStats) 366 foreach (KeyValuePair<string, SortedDictionary<string, Stat>> kvp in sceneStats)
367 { 367 {
368 foreach (Stat stat in kvp.Value.Values) 368 foreach (Stat stat in kvp.Value.Values)
369 { 369 {
@@ -405,6 +405,15 @@ Asset service request failures: {3}" + Environment.NewLine,
405 /// <returns></returns> 405 /// <returns></returns>
406 public override string XReport(string uptime, string version) 406 public override string XReport(string uptime, string version)
407 { 407 {
408 return OSDParser.SerializeJsonString(OReport(uptime, version));
409 }
410
411 /// <summary>
412 /// Report back collected statistical information as an OSDMap
413 /// </summary>
414 /// <returns></returns>
415 public override OSDMap OReport(string uptime, string version)
416 {
408 OSDMap args = new OSDMap(30); 417 OSDMap args = new OSDMap(30);
409// args["AssetsInCache"] = OSD.FromString (String.Format ("{0:0.##}", AssetsInCache)); 418// args["AssetsInCache"] = OSD.FromString (String.Format ("{0:0.##}", AssetsInCache));
410// args["TimeAfterCacheMiss"] = OSD.FromString (String.Format ("{0:0.##}", 419// args["TimeAfterCacheMiss"] = OSD.FromString (String.Format ("{0:0.##}",
@@ -442,13 +451,11 @@ Asset service request failures: {3}" + Environment.NewLine,
442 args["Uptime"] = OSD.FromString (uptime); 451 args["Uptime"] = OSD.FromString (uptime);
443 args["Version"] = OSD.FromString (version); 452 args["Version"] = OSD.FromString (version);
444 453
445 string strBuffer = ""; 454 return args;
446 strBuffer = OSDParser.SerializeJsonString(args);
447
448 return strBuffer;
449 } 455 }
450 } 456 }
451 457
458
452 /// <summary> 459 /// <summary>
453 /// Pull packet queue stats from packet queues and report 460 /// Pull packet queue stats from packet queues and report
454 /// </summary> 461 /// </summary>
@@ -474,5 +481,11 @@ Asset service request failures: {3}" + Environment.NewLine,
474 { 481 {
475 return ""; 482 return "";
476 } 483 }
484
485 public OSDMap OReport(string uptime, string version)
486 {
487 OSDMap ret = new OSDMap();
488 return ret;
489 }
477 } 490 }
478} 491}
diff --git a/OpenSim/Framework/Monitoring/Stats/CounterStat.cs b/OpenSim/Framework/Monitoring/Stats/CounterStat.cs
new file mode 100755
index 0000000..caea30d
--- /dev/null
+++ b/OpenSim/Framework/Monitoring/Stats/CounterStat.cs
@@ -0,0 +1,228 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Linq;
31using System.Text;
32
33using OpenMetaverse.StructuredData;
34
35namespace OpenSim.Framework.Monitoring
36{
37// Create a time histogram of events. The histogram is built in a wrap-around
38// array of equally distributed buckets.
39// For instance, a minute long histogram of second sized buckets would be:
40// new EventHistogram(60, 1000)
41public class EventHistogram
42{
43 private int m_timeBase;
44 private int m_numBuckets;
45 private int m_bucketMilliseconds;
46 private int m_lastBucket;
47 private int m_totalHistogramMilliseconds;
48 private long[] m_histogram;
49 private object histoLock = new object();
50
51 public EventHistogram(int numberOfBuckets, int millisecondsPerBucket)
52 {
53 m_numBuckets = numberOfBuckets;
54 m_bucketMilliseconds = millisecondsPerBucket;
55 m_totalHistogramMilliseconds = m_numBuckets * m_bucketMilliseconds;
56
57 m_histogram = new long[m_numBuckets];
58 Zero();
59 m_lastBucket = 0;
60 m_timeBase = Util.EnvironmentTickCount();
61 }
62
63 public void Event()
64 {
65 this.Event(1);
66 }
67
68 // Record an event at time 'now' in the histogram.
69 public void Event(int cnt)
70 {
71 lock (histoLock)
72 {
73 // The time as displaced from the base of the histogram
74 int bucketTime = Util.EnvironmentTickCountSubtract(m_timeBase);
75
76 // If more than the total time of the histogram, we just start over
77 if (bucketTime > m_totalHistogramMilliseconds)
78 {
79 Zero();
80 m_lastBucket = 0;
81 m_timeBase = Util.EnvironmentTickCount();
82 }
83 else
84 {
85 // To which bucket should we add this event?
86 int bucket = bucketTime / m_bucketMilliseconds;
87
88 // Advance m_lastBucket to the new bucket. Zero any buckets skipped over.
89 while (bucket != m_lastBucket)
90 {
91 // Zero from just after the last bucket to the new bucket or the end
92 for (int jj = m_lastBucket + 1; jj <= Math.Min(bucket, m_numBuckets - 1); jj++)
93 {
94 m_histogram[jj] = 0;
95 }
96 m_lastBucket = bucket;
97 // If the new bucket is off the end, wrap around to the beginning
98 if (bucket > m_numBuckets)
99 {
100 bucket -= m_numBuckets;
101 m_lastBucket = 0;
102 m_histogram[m_lastBucket] = 0;
103 m_timeBase += m_totalHistogramMilliseconds;
104 }
105 }
106 }
107 m_histogram[m_lastBucket] += cnt;
108 }
109 }
110
111 // Get a copy of the current histogram
112 public long[] GetHistogram()
113 {
114 long[] ret = new long[m_numBuckets];
115 lock (histoLock)
116 {
117 int indx = m_lastBucket + 1;
118 for (int ii = 0; ii < m_numBuckets; ii++, indx++)
119 {
120 if (indx >= m_numBuckets)
121 indx = 0;
122 ret[ii] = m_histogram[indx];
123 }
124 }
125 return ret;
126 }
127
128 public OSDMap GetHistogramAsOSDMap()
129 {
130 OSDMap ret = new OSDMap();
131
132 ret.Add("Buckets", OSD.FromInteger(m_numBuckets));
133 ret.Add("BucketMilliseconds", OSD.FromInteger(m_bucketMilliseconds));
134 ret.Add("TotalMilliseconds", OSD.FromInteger(m_totalHistogramMilliseconds));
135
136 // Compute a number for the first bucket in the histogram.
137 // This will allow readers to know how this histogram relates to any previously read histogram.
138 int baseBucketNum = (m_timeBase / m_bucketMilliseconds) + m_lastBucket + 1;
139 ret.Add("BaseNumber", OSD.FromInteger(baseBucketNum));
140
141 ret.Add("Values", GetHistogramAsOSDArray());
142
143 return ret;
144 }
145 // Get a copy of the current histogram
146 public OSDArray GetHistogramAsOSDArray()
147 {
148 OSDArray ret = new OSDArray(m_numBuckets);
149 lock (histoLock)
150 {
151 int indx = m_lastBucket + 1;
152 for (int ii = 0; ii < m_numBuckets; ii++, indx++)
153 {
154 if (indx >= m_numBuckets)
155 indx = 0;
156 ret[ii] = OSD.FromLong(m_histogram[indx]);
157 }
158 }
159 return ret;
160 }
161
162 // Zero out the histogram
163 public void Zero()
164 {
165 lock (histoLock)
166 {
167 for (int ii = 0; ii < m_numBuckets; ii++)
168 m_histogram[ii] = 0;
169 }
170 }
171}
172
173// A statistic that wraps a counter.
174// Built this way mostly so histograms and history can be created.
175public class CounterStat : Stat
176{
177 private SortedDictionary<string, EventHistogram> m_histograms;
178 private object counterLock = new object();
179
180 public CounterStat(
181 string shortName,
182 string name,
183 string description,
184 string unitName,
185 string category,
186 string container,
187 StatVerbosity verbosity)
188 : base(shortName, name, description, unitName, category, container, StatType.Push, null, verbosity)
189 {
190 m_histograms = new SortedDictionary<string, EventHistogram>();
191 }
192
193 // Histograms are presumably added at intialization time and the list does not change thereafter.
194 // Thus no locking of the histogram list.
195 public void AddHistogram(string histoName, EventHistogram histo)
196 {
197 m_histograms.Add(histoName, histo);
198 }
199
200 public delegate void ProcessHistogram(string name, EventHistogram histo);
201 public void ForEachHistogram(ProcessHistogram process)
202 {
203 foreach (KeyValuePair<string, EventHistogram> kvp in m_histograms)
204 {
205 process(kvp.Key, kvp.Value);
206 }
207 }
208
209 public void Event()
210 {
211 this.Event(1);
212 }
213
214 // Count the underlying counter.
215 public void Event(int cnt)
216 {
217 lock (counterLock)
218 {
219 base.Value += cnt;
220
221 foreach (EventHistogram histo in m_histograms.Values)
222 {
223 histo.Event(cnt);
224 }
225 }
226 }
227}
228}
diff --git a/OpenSim/Framework/Monitoring/Stats/Stat.cs b/OpenSim/Framework/Monitoring/Stats/Stat.cs
index f91251b..2e7665f 100644
--- a/OpenSim/Framework/Monitoring/Stats/Stat.cs
+++ b/OpenSim/Framework/Monitoring/Stats/Stat.cs
@@ -29,12 +29,14 @@ using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Text; 30using System.Text;
31 31
32using OpenMetaverse.StructuredData;
33
32namespace OpenSim.Framework.Monitoring 34namespace OpenSim.Framework.Monitoring
33{ 35{
34 /// <summary> 36 /// <summary>
35 /// Holds individual statistic details 37 /// Holds individual statistic details
36 /// </summary> 38 /// </summary>
37 public class Stat 39 public class Stat : IDisposable
38 { 40 {
39 /// <summary> 41 /// <summary>
40 /// Category of this stat (e.g. cache, scene, etc). 42 /// Category of this stat (e.g. cache, scene, etc).
@@ -181,6 +183,12 @@ namespace OpenSim.Framework.Monitoring
181 Verbosity = verbosity; 183 Verbosity = verbosity;
182 } 184 }
183 185
186 // IDisposable.Dispose()
187 public virtual void Dispose()
188 {
189 return;
190 }
191
184 /// <summary> 192 /// <summary>
185 /// Record a value in the sample set. 193 /// Record a value in the sample set.
186 /// </summary> 194 /// </summary>
@@ -203,13 +211,27 @@ namespace OpenSim.Framework.Monitoring
203 public virtual string ToConsoleString() 211 public virtual string ToConsoleString()
204 { 212 {
205 StringBuilder sb = new StringBuilder(); 213 StringBuilder sb = new StringBuilder();
206 sb.AppendFormat("{0}.{1}.{2} : {3}{4}", Category, Container, ShortName, Value, UnitName); 214 sb.AppendFormat("{0}.{1}.{2} : {3} {4}", Category, Container, ShortName, Value, UnitName);
207 215
208 AppendMeasuresOfInterest(sb); 216 AppendMeasuresOfInterest(sb);
209 217
210 return sb.ToString(); 218 return sb.ToString();
211 } 219 }
212 220
221 public virtual OSDMap ToOSDMap()
222 {
223 OSDMap ret = new OSDMap();
224 ret.Add("Category", OSD.FromString(Category));
225 ret.Add("Container", OSD.FromString(Container));
226 ret.Add("ShortName", OSD.FromString(ShortName));
227 ret.Add("Name", OSD.FromString(Name));
228 ret.Add("Description", OSD.FromString(Description));
229 ret.Add("UnitName", OSD.FromString(UnitName));
230 ret.Add("Value", OSD.FromReal(Value));
231
232 return ret;
233 }
234
213 protected void AppendMeasuresOfInterest(StringBuilder sb) 235 protected void AppendMeasuresOfInterest(StringBuilder sb)
214 { 236 {
215 if ((MeasuresOfInterest & MeasuresOfInterest.AverageChangeOverTime) 237 if ((MeasuresOfInterest & MeasuresOfInterest.AverageChangeOverTime)
diff --git a/OpenSim/Framework/Monitoring/StatsManager.cs b/OpenSim/Framework/Monitoring/StatsManager.cs
index 0762b01..24db6d4 100644
--- a/OpenSim/Framework/Monitoring/StatsManager.cs
+++ b/OpenSim/Framework/Monitoring/StatsManager.cs
@@ -51,8 +51,8 @@ namespace OpenSim.Framework.Monitoring
51 /// <remarks> 51 /// <remarks>
52 /// Do not add or remove directly from this dictionary. 52 /// Do not add or remove directly from this dictionary.
53 /// </remarks> 53 /// </remarks>
54 public static Dictionary<string, Dictionary<string, Dictionary<string, Stat>>> RegisteredStats 54 public static SortedDictionary<string, SortedDictionary<string, SortedDictionary<string, Stat>>> RegisteredStats
55 = new Dictionary<string, Dictionary<string, Dictionary<string, Stat>>>(); 55 = new SortedDictionary<string, SortedDictionary<string, SortedDictionary<string, Stat>>>();
56 56
57 private static AssetStatsCollector assetStats; 57 private static AssetStatsCollector assetStats;
58 private static UserStatsCollector userStats; 58 private static UserStatsCollector userStats;
@@ -85,6 +85,7 @@ namespace OpenSim.Framework.Monitoring
85 if (cmd.Length > 2) 85 if (cmd.Length > 2)
86 { 86 {
87 var categoryName = cmd[2]; 87 var categoryName = cmd[2];
88 var containerName = cmd.Length > 3 ? cmd[3] : String.Empty;
88 89
89 if (categoryName == AllSubCommand) 90 if (categoryName == AllSubCommand)
90 { 91 {
@@ -101,14 +102,27 @@ namespace OpenSim.Framework.Monitoring
101 } 102 }
102 else 103 else
103 { 104 {
104 Dictionary<string, Dictionary<string, Stat>> category; 105 SortedDictionary<string, SortedDictionary<string, Stat>> category;
105 if (!RegisteredStats.TryGetValue(categoryName, out category)) 106 if (!RegisteredStats.TryGetValue(categoryName, out category))
106 { 107 {
107 con.OutputFormat("No such category as {0}", categoryName); 108 con.OutputFormat("No such category as {0}", categoryName);
108 } 109 }
109 else 110 else
110 { 111 {
111 OutputCategoryStatsToConsole(con, category); 112 if (String.IsNullOrEmpty(containerName))
113 OutputCategoryStatsToConsole(con, category);
114 else
115 {
116 SortedDictionary<string, Stat> container;
117 if (category.TryGetValue(containerName, out container))
118 {
119 OutputContainerStatsToConsole(con, container);
120 }
121 else
122 {
123 con.OutputFormat("No such container {0} in category {1}", containerName, categoryName);
124 }
125 }
112 } 126 }
113 } 127 }
114 } 128 }
@@ -120,14 +134,19 @@ namespace OpenSim.Framework.Monitoring
120 } 134 }
121 135
122 private static void OutputCategoryStatsToConsole( 136 private static void OutputCategoryStatsToConsole(
123 ICommandConsole con, Dictionary<string, Dictionary<string, Stat>> category) 137 ICommandConsole con, SortedDictionary<string, SortedDictionary<string, Stat>> category)
124 { 138 {
125 foreach (var container in category.Values) 139 foreach (var container in category.Values)
126 { 140 {
127 foreach (Stat stat in container.Values) 141 OutputContainerStatsToConsole(con, container);
128 { 142 }
129 con.Output(stat.ToConsoleString()); 143 }
130 } 144
145 private static void OutputContainerStatsToConsole( ICommandConsole con, SortedDictionary<string, Stat> container)
146 {
147 foreach (Stat stat in container.Values)
148 {
149 con.Output(stat.ToConsoleString());
131 } 150 }
132 } 151 }
133 152
@@ -160,8 +179,8 @@ namespace OpenSim.Framework.Monitoring
160 /// <returns></returns> 179 /// <returns></returns>
161 public static bool RegisterStat(Stat stat) 180 public static bool RegisterStat(Stat stat)
162 { 181 {
163 Dictionary<string, Dictionary<string, Stat>> category = null, newCategory; 182 SortedDictionary<string, SortedDictionary<string, Stat>> category = null, newCategory;
164 Dictionary<string, Stat> container = null, newContainer; 183 SortedDictionary<string, Stat> container = null, newContainer;
165 184
166 lock (RegisteredStats) 185 lock (RegisteredStats)
167 { 186 {
@@ -175,14 +194,14 @@ namespace OpenSim.Framework.Monitoring
175 // This means that we don't need to lock or copy them on iteration, which will be a much more 194 // This means that we don't need to lock or copy them on iteration, which will be a much more
176 // common operation after startup. 195 // common operation after startup.
177 if (container != null) 196 if (container != null)
178 newContainer = new Dictionary<string, Stat>(container); 197 newContainer = new SortedDictionary<string, Stat>(container);
179 else 198 else
180 newContainer = new Dictionary<string, Stat>(); 199 newContainer = new SortedDictionary<string, Stat>();
181 200
182 if (category != null) 201 if (category != null)
183 newCategory = new Dictionary<string, Dictionary<string, Stat>>(category); 202 newCategory = new SortedDictionary<string, SortedDictionary<string, Stat>>(category);
184 else 203 else
185 newCategory = new Dictionary<string, Dictionary<string, Stat>>(); 204 newCategory = new SortedDictionary<string, SortedDictionary<string, Stat>>();
186 205
187 newContainer[stat.ShortName] = stat; 206 newContainer[stat.ShortName] = stat;
188 newCategory[stat.Container] = newContainer; 207 newCategory[stat.Container] = newContainer;
@@ -196,21 +215,21 @@ namespace OpenSim.Framework.Monitoring
196 /// Deregister a statistic 215 /// Deregister a statistic
197 /// </summary>> 216 /// </summary>>
198 /// <param name='stat'></param> 217 /// <param name='stat'></param>
199 /// <returns></returns 218 /// <returns></returns>
200 public static bool DeregisterStat(Stat stat) 219 public static bool DeregisterStat(Stat stat)
201 { 220 {
202 Dictionary<string, Dictionary<string, Stat>> category = null, newCategory; 221 SortedDictionary<string, SortedDictionary<string, Stat>> category = null, newCategory;
203 Dictionary<string, Stat> container = null, newContainer; 222 SortedDictionary<string, Stat> container = null, newContainer;
204 223
205 lock (RegisteredStats) 224 lock (RegisteredStats)
206 { 225 {
207 if (!TryGetStat(stat, out category, out container)) 226 if (!TryGetStat(stat, out category, out container))
208 return false; 227 return false;
209 228
210 newContainer = new Dictionary<string, Stat>(container); 229 newContainer = new SortedDictionary<string, Stat>(container);
211 newContainer.Remove(stat.ShortName); 230 newContainer.Remove(stat.ShortName);
212 231
213 newCategory = new Dictionary<string, Dictionary<string, Stat>>(category); 232 newCategory = new SortedDictionary<string, SortedDictionary<string, Stat>>(category);
214 newCategory.Remove(stat.Container); 233 newCategory.Remove(stat.Container);
215 234
216 newCategory[stat.Container] = newContainer; 235 newCategory[stat.Container] = newContainer;
@@ -220,15 +239,15 @@ namespace OpenSim.Framework.Monitoring
220 } 239 }
221 } 240 }
222 241
223 public static bool TryGetStats(string category, out Dictionary<string, Dictionary<string, Stat>> stats) 242 public static bool TryGetStats(string category, out SortedDictionary<string, SortedDictionary<string, Stat>> stats)
224 { 243 {
225 return RegisteredStats.TryGetValue(category, out stats); 244 return RegisteredStats.TryGetValue(category, out stats);
226 } 245 }
227 246
228 public static bool TryGetStat( 247 public static bool TryGetStat(
229 Stat stat, 248 Stat stat,
230 out Dictionary<string, Dictionary<string, Stat>> category, 249 out SortedDictionary<string, SortedDictionary<string, Stat>> category,
231 out Dictionary<string, Stat> container) 250 out SortedDictionary<string, Stat> container)
232 { 251 {
233 category = null; 252 category = null;
234 container = null; 253 container = null;
@@ -252,9 +271,9 @@ namespace OpenSim.Framework.Monitoring
252 { 271 {
253 lock (RegisteredStats) 272 lock (RegisteredStats)
254 { 273 {
255 foreach (Dictionary<string, Dictionary<string, Stat>> category in RegisteredStats.Values) 274 foreach (SortedDictionary<string, SortedDictionary<string, Stat>> category in RegisteredStats.Values)
256 { 275 {
257 foreach (Dictionary<string, Stat> container in category.Values) 276 foreach (SortedDictionary<string, Stat> container in category.Values)
258 { 277 {
259 foreach (Stat stat in container.Values) 278 foreach (Stat stat in container.Values)
260 { 279 {
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 @@
27 27
28using System.Timers; 28using System.Timers;
29 29
30using OpenMetaverse.StructuredData;
31
30namespace OpenSim.Framework.Monitoring 32namespace OpenSim.Framework.Monitoring
31{ 33{
32 /// <summary> 34 /// <summary>
@@ -88,5 +90,21 @@ namespace OpenSim.Framework.Monitoring
88 Logouts total : {3}", 90 Logouts total : {3}",
89 SuccessfulLogins, SuccessfulLoginsToday, SuccessfulLoginsYesterday, Logouts); 91 SuccessfulLogins, SuccessfulLoginsToday, SuccessfulLoginsYesterday, Logouts);
90 } 92 }
93
94 public override string XReport(string uptime, string version)
95 {
96 return OSDParser.SerializeJsonString(OReport(uptime, version));
97 }
98
99 public override OSDMap OReport(string uptime, string version)
100 {
101 OSDMap ret = new OSDMap();
102 ret.Add("SuccessfulLogins", OSD.FromInteger(SuccessfulLogins));
103 ret.Add("SuccessfulLoginsToday", OSD.FromInteger(SuccessfulLoginsToday));
104 ret.Add("SuccessfulLoginsYesterday", OSD.FromInteger(SuccessfulLoginsYesterday));
105 ret.Add("Logouts", OSD.FromInteger(Logouts));
106
107 return ret;
108 }
91 } 109 }
92} 110}
diff --git a/OpenSim/Framework/PluginManager.cs b/OpenSim/Framework/PluginManager.cs
new file mode 100644
index 0000000..00263f5
--- /dev/null
+++ b/OpenSim/Framework/PluginManager.cs
@@ -0,0 +1,563 @@
1
2/*
3 * Copyright (c) Contributors, http://opensimulator.org/
4 * See CONTRIBUTORS.TXT for a full list of copyright holders.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the OpenSimulator Project nor the
14 * names of its contributors may be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29
30using System;
31using System.Text;
32using System.Linq;
33using System.Collections;
34using System.Collections.Generic;
35using System.Collections.ObjectModel;
36using Mono.Addins;
37using Mono.Addins.Setup;
38using Mono.Addins.Description;
39using OpenSim.Framework;
40
41
42namespace OpenSim.Framework
43{
44 /// <summary>
45 /// Manager for registries and plugins
46 /// </summary>
47 public class PluginManager : SetupService
48 {
49 public AddinRegistry PluginRegistry;
50
51 public PluginManager(AddinRegistry registry): base (registry)
52 {
53 PluginRegistry = registry;
54
55 }
56
57 /// <summary>
58 /// Installs the plugin.
59 /// </summary>
60 /// <returns>
61 /// The plugin.
62 /// </returns>
63 /// <param name='args'>
64 /// Arguments.
65 /// </param>
66 public bool InstallPlugin(int ndx, out Dictionary<string, object> result)
67 {
68 Dictionary<string, object> res = new Dictionary<string, object>();
69
70 PackageCollection pack = new PackageCollection();
71 PackageCollection toUninstall;
72 DependencyCollection unresolved;
73
74 IProgressStatus ps = new ConsoleProgressStatus(false);
75
76 AddinRepositoryEntry[] available = GetSortedAvailbleAddins();
77
78 if (ndx > (available.Length - 1))
79 {
80 MainConsole.Instance.Output("Selection out of range");
81 result = res;
82 return false;
83 }
84
85 AddinRepositoryEntry aentry = available[ndx];
86
87 Package p = Package.FromRepository(aentry);
88 pack.Add(p);
89
90 ResolveDependencies(ps, pack, out toUninstall, out unresolved);
91
92 // Attempt to install the plugin disabled
93 if (Install(ps, pack) == true)
94 {
95 MainConsole.Instance.Output("Ignore the following error...");
96 PluginRegistry.Update(ps);
97 Addin addin = PluginRegistry.GetAddin(aentry.Addin.Id);
98 PluginRegistry.DisableAddin(addin.Id);
99 addin.Enabled = false;
100
101 MainConsole.Instance.Output("Installation Success");
102 ListInstalledAddins(out res);
103 result = res;
104 return true;
105 }
106 else
107 {
108 MainConsole.Instance.Output("Installation Failed");
109 result = res;
110 return false;
111 }
112 }
113
114 // Remove plugin
115 /// <summary>
116 /// Uns the install.
117 /// </summary>
118 /// <param name='args'>
119 /// Arguments.
120 /// </param>
121 public void UnInstall(int ndx)
122 {
123 Addin[] addins = GetSortedAddinList("RobustPlugin");
124
125 if (ndx > (addins.Length -1))
126 {
127 MainConsole.Instance.Output("Selection out of range");
128 return;
129 }
130
131 Addin addin = addins[ndx];
132 MainConsole.Instance.OutputFormat("Uninstalling plugin {0}", addin.Id);
133 AddinManager.Registry.DisableAddin(addin.Id);
134 addin.Enabled = false;
135 IProgressStatus ps = new ConsoleProgressStatus(false);
136 Uninstall(ps, addin.Id);
137 MainConsole.Instance.Output("Uninstall Success - restart to complete operation");
138 return;
139 }
140
141 /// <summary>
142 /// Checks the installed.
143 /// </summary>
144 /// <returns>
145 /// The installed.
146 /// </returns>
147 public string CheckInstalled()
148 {
149 return "CheckInstall";
150 }
151
152 /// <summary>
153 /// Lists the installed addins.
154 /// </summary>
155 /// <param name='result'>
156 /// Result.
157 /// </param>
158 public void ListInstalledAddins(out Dictionary<string, object> result)
159 {
160 Dictionary<string, object> res = new Dictionary<string, object>();
161
162 Addin[] addins = GetSortedAddinList("RobustPlugin");
163 if(addins.Count() < 1)
164 {
165 MainConsole.Instance.Output("Error!");
166 }
167 int count = 0;
168 foreach (Addin addin in addins)
169 {
170 Dictionary<string, object> r = new Dictionary<string, object>();
171 r["enabled"] = addin.Enabled == true ? true : false;
172 r["name"] = addin.LocalId;
173 r["version"] = addin.Version;
174
175 res.Add(count.ToString(), r);
176
177 count++;
178 }
179 result = res;
180 return;
181 }
182
183 // List compatible plugins in registered repositories
184 /// <summary>
185 /// Lists the available.
186 /// </summary>
187 /// <param name='result'>
188 /// Result.
189 /// </param>
190 public void ListAvailable(out Dictionary<string, object> result)
191 {
192 Dictionary<string, object> res = new Dictionary<string, object>();
193
194 AddinRepositoryEntry[] addins = GetSortedAvailbleAddins();
195
196 int count = 0;
197 foreach (AddinRepositoryEntry addin in addins)
198 {
199 Dictionary<string, object> r = new Dictionary<string, object>();
200 r["name"] = addin.Addin.Name;
201 r["version"] = addin.Addin.Version;
202 r["repository"] = addin.RepositoryName;
203
204 res.Add(count.ToString(), r);
205 count++;
206 }
207 result = res;
208 return;
209 }
210
211 // List available updates ** 1
212 /// <summary>
213 /// Lists the updates.
214 /// </summary>
215 public void ListUpdates()
216 {
217 IProgressStatus ps = new ConsoleProgressStatus(true);
218 Console.WriteLine ("Looking for updates...");
219 Repositories.UpdateAllRepositories (ps);
220 Console.WriteLine ("Available add-in updates:");
221 bool found = false;
222 AddinRepositoryEntry[] entries = Repositories.GetAvailableUpdates();
223
224 foreach (AddinRepositoryEntry entry in entries)
225 {
226 Console.WriteLine(String.Format("{0}",entry.Addin.Id));
227 }
228 }
229
230 // Sync to repositories
231 /// <summary>
232 /// Update this instance.
233 /// </summary>
234 public string Update()
235 {
236 IProgressStatus ps = new ConsoleProgressStatus(true);
237 Repositories.UpdateAllRepositories(ps);
238 return "Update";
239 }
240
241 // Register a repository
242 /// <summary>
243 /// Register a repository with our server.
244 /// </summary>
245 /// <returns>
246 /// result of the action
247 /// </returns>
248 /// <param name='repo'>
249 /// The URL of the repository we want to add
250 /// </param>
251 public bool AddRepository(string repo)
252 {
253 Repositories.RegisterRepository(null, repo, true);
254 PluginRegistry.Rebuild(null);
255
256 return true;
257 }
258
259 /// <summary>
260 /// Gets the repository.
261 /// </summary>
262 public void GetRepository()
263 {
264 Repositories.UpdateAllRepositories(new ConsoleProgressStatus(false));
265 }
266
267 // Remove a repository from the list
268 /// <summary>
269 /// Removes the repository.
270 /// </summary>
271 /// <param name='args'>
272 /// Arguments.
273 /// </param>
274 public void RemoveRepository(string[] args)
275 {
276 AddinRepository[] reps = Repositories.GetRepositories();
277 Array.Sort(reps, (r1,r2) => r1.Title.CompareTo(r2.Title));
278 if (reps.Length == 0)
279 {
280 MainConsole.Instance.Output("No repositories have been registered.");
281 return;
282 }
283
284 int n = Convert.ToInt16(args[2]);
285 if (n > (reps.Length -1))
286 {
287 MainConsole.Instance.Output("Selection out of range");
288 return;
289 }
290
291 AddinRepository rep = reps[n];
292 Repositories.RemoveRepository(rep.Url);
293 return;
294 }
295
296 // Enable repository
297 /// <summary>
298 /// Enables the repository.
299 /// </summary>
300 /// <param name='args'>
301 /// Arguments.
302 /// </param>
303 public void EnableRepository(string[] args)
304 {
305 AddinRepository[] reps = Repositories.GetRepositories();
306 Array.Sort(reps, (r1,r2) => r1.Title.CompareTo(r2.Title));
307 if (reps.Length == 0)
308 {
309 MainConsole.Instance.Output("No repositories have been registered.");
310 return;
311 }
312
313 int n = Convert.ToInt16(args[2]);
314 if (n > (reps.Length -1))
315 {
316 MainConsole.Instance.Output("Selection out of range");
317 return;
318 }
319
320 AddinRepository rep = reps[n];
321 Repositories.SetRepositoryEnabled(rep.Url, true);
322 return;
323 }
324
325 // Disable a repository
326 /// <summary>
327 /// Disables the repository.
328 /// </summary>
329 /// <param name='args'>
330 /// Arguments.
331 /// </param>
332 public void DisableRepository(string[] args)
333 {
334 AddinRepository[] reps = Repositories.GetRepositories();
335 Array.Sort(reps, (r1,r2) => r1.Title.CompareTo(r2.Title));
336 if (reps.Length == 0)
337 {
338 MainConsole.Instance.Output("No repositories have been registered.");
339 return;
340 }
341
342 int n = Convert.ToInt16(args[2]);
343 if (n > (reps.Length -1))
344 {
345 MainConsole.Instance.Output("Selection out of range");
346 return;
347 }
348
349 AddinRepository rep = reps[n];
350 Repositories.SetRepositoryEnabled(rep.Url, false);
351 return;
352 }
353
354 // List registered repositories
355 /// <summary>
356 /// Lists the repositories.
357 /// </summary>
358 /// <param name='result'>
359 /// Result.
360 /// </param>
361 public void ListRepositories(out Dictionary<string, object> result)
362 {
363 Dictionary<string, object> res = new Dictionary<string, object>();
364 result = res;
365
366 AddinRepository[] reps = GetSortedAddinRepo();
367 if (reps.Length == 0)
368 {
369 MainConsole.Instance.Output("No repositories have been registered.");
370 return;
371 }
372
373 int count = 0;
374 foreach (AddinRepository rep in reps)
375 {
376 Dictionary<string, object> r = new Dictionary<string, object>();
377 r["enabled"] = rep.Enabled == true ? true : false;
378 r["name"] = rep.Name;
379 r["url"] = rep.Url;
380
381 res.Add(count.ToString(), r);
382 count++;
383 }
384 return;
385 }
386
387 /// <summary>
388 /// Updates the registry.
389 /// </summary>
390 public void UpdateRegistry()
391 {
392 PluginRegistry.Update();
393 }
394
395 // Show plugin info
396 /// <summary>
397 /// Addins the info.
398 /// </summary>
399 /// <returns>
400 /// The info.
401 /// </returns>
402 /// <param name='args'>
403 /// Arguments.
404 /// </param>
405 public bool AddinInfo(int ndx, out Dictionary<string, object> result)
406 {
407 Dictionary<string, object> res = new Dictionary<string, object>();
408 result = res;
409
410 Addin[] addins = GetSortedAddinList("RobustPlugin");
411
412 if (ndx > (addins.Length - 1))
413 {
414 MainConsole.Instance.Output("Selection out of range");
415 return false;
416 }
417 // author category description
418 Addin addin = addins[ndx];
419
420 res["author"] = addin.Description.Author;
421 res["category"] = addin.Description.Category;
422 res["description"] = addin.Description.Description;
423 res["name"] = addin.Name;
424 res["url"] = addin.Description.Url;
425 res["file_name"] = addin.Description.FileName;
426
427 result = res;
428 return true;
429 }
430
431 // Disable a plugin
432 /// <summary>
433 /// Disables the plugin.
434 /// </summary>
435 /// <param name='args'>
436 /// Arguments.
437 /// </param>
438 public void DisablePlugin(string[] args)
439 {
440 Addin[] addins = GetSortedAddinList("RobustPlugin");
441
442 int n = Convert.ToInt16(args[2]);
443 if (n > (addins.Length -1))
444 {
445 MainConsole.Instance.Output("Selection out of range");
446 return;
447 }
448
449 Addin addin = addins[n];
450 AddinManager.Registry.DisableAddin(addin.Id);
451 addin.Enabled = false;
452 return;
453 }
454
455 // Enable plugin
456 /// <summary>
457 /// Enables the plugin.
458 /// </summary>
459 /// <param name='args'>
460 /// Arguments.
461 /// </param>
462 public void EnablePlugin(string[] args)
463 {
464 Addin[] addins = GetSortedAddinList("RobustPlugin");
465
466 int n = Convert.ToInt16(args[2]);
467 if (n > (addins.Length -1))
468 {
469 MainConsole.Instance.Output("Selection out of range");
470 return;
471 }
472
473 Addin addin = addins[n];
474
475 addin.Enabled = true;
476 AddinManager.Registry.EnableAddin(addin.Id);
477 // AddinManager.Registry.Update();
478 if(PluginRegistry.IsAddinEnabled(addin.Id))
479 {
480 ConsoleProgressStatus ps = new ConsoleProgressStatus(false);
481 if (!AddinManager.AddinEngine.IsAddinLoaded(addin.Id))
482 {
483 MainConsole.Instance.Output("Ignore the following error...");
484 AddinManager.Registry.Rebuild(ps);
485 AddinManager.AddinEngine.LoadAddin(ps, addin.Id);
486 }
487 }
488 else
489 {
490 MainConsole.Instance.OutputFormat("Not Enabled in this domain {0}", addin.Name);
491 }
492 return;
493 }
494
495
496
497 #region Util
498 private void Testing()
499 {
500 Addin[] list = Registry.GetAddins();
501
502 var addins = list.Where( a => a.Description.Category == "RobustPlugin");
503
504 foreach (Addin addin in addins)
505 {
506 MainConsole.Instance.OutputFormat("Addin {0}", addin.Name);
507 }
508 }
509
510 // These will let us deal with numbered lists instead
511 // of needing to type in the full ids
512 private AddinRepositoryEntry[] GetSortedAvailbleAddins()
513 {
514 ArrayList list = new ArrayList();
515 list.AddRange(Repositories.GetAvailableAddins());
516
517 AddinRepositoryEntry[] addins = list.ToArray(typeof(AddinRepositoryEntry)) as AddinRepositoryEntry[];
518
519 Array.Sort(addins,(r1,r2) => r1.Addin.Id.CompareTo(r2.Addin.Id));
520
521 return addins;
522 }
523
524 private AddinRepository[] GetSortedAddinRepo()
525 {
526 ArrayList list = new ArrayList();
527 list.AddRange(Repositories.GetRepositories());
528
529 AddinRepository[] repos = list.ToArray(typeof(AddinRepository)) as AddinRepository[];
530 Array.Sort (repos,(r1,r2) => r1.Name.CompareTo(r2.Name));
531
532 return repos;
533 }
534
535 private Addin[] GetSortedAddinList(string category)
536 {
537
538 ArrayList xlist = new ArrayList();
539 ArrayList list = new ArrayList();
540 try
541 {
542 list.AddRange(PluginRegistry.GetAddins());
543 }
544 catch(Exception e)
545 {
546 Addin[] x = xlist.ToArray(typeof(Addin)) as Addin[];
547 return x;
548 }
549
550 foreach (Addin addin in list)
551 {
552 if (addin.Description.Category == category)
553 xlist.Add(addin);
554 }
555
556 Addin[] addins = xlist.ToArray(typeof(Addin)) as Addin[];
557 Array.Sort(addins,(r1,r2) => r1.Id.CompareTo(r2.Id));
558
559 return addins;
560 }
561 #endregion Util
562 }
563}
diff --git a/OpenSim/Framework/PrimitiveBaseShape.cs b/OpenSim/Framework/PrimitiveBaseShape.cs
index fcc9873..df928dc 100644
--- a/OpenSim/Framework/PrimitiveBaseShape.cs
+++ b/OpenSim/Framework/PrimitiveBaseShape.cs
@@ -192,18 +192,7 @@ namespace OpenSim.Framework
192 192
193 public PrimitiveBaseShape() 193 public PrimitiveBaseShape()
194 { 194 {
195 PCode = (byte) PCodeEnum.Primitive;
196 ExtraParams = new byte[1];
197 m_textureEntry = DEFAULT_TEXTURE;
198 }
199
200 public PrimitiveBaseShape(bool noShape)
201 {
202 if (noShape)
203 return;
204
205 PCode = (byte)PCodeEnum.Primitive; 195 PCode = (byte)PCodeEnum.Primitive;
206 ExtraParams = new byte[1];
207 m_textureEntry = DEFAULT_TEXTURE; 196 m_textureEntry = DEFAULT_TEXTURE;
208 } 197 }
209 198
@@ -216,7 +205,6 @@ namespace OpenSim.Framework
216// m_log.DebugFormat("[PRIMITIVE BASE SHAPE]: Creating from {0}", prim.ID); 205// m_log.DebugFormat("[PRIMITIVE BASE SHAPE]: Creating from {0}", prim.ID);
217 206
218 PCode = (byte)prim.PrimData.PCode; 207 PCode = (byte)prim.PrimData.PCode;
219 ExtraParams = new byte[1];
220 208
221 State = prim.PrimData.State; 209 State = prim.PrimData.State;
222 PathBegin = Primitive.PackBeginCut(prim.PrimData.PathBegin); 210 PathBegin = Primitive.PackBeginCut(prim.PrimData.PathBegin);
@@ -248,7 +236,10 @@ namespace OpenSim.Framework
248 SculptTexture = prim.Sculpt.SculptTexture; 236 SculptTexture = prim.Sculpt.SculptTexture;
249 SculptType = (byte)prim.Sculpt.Type; 237 SculptType = (byte)prim.Sculpt.Type;
250 } 238 }
251 else SculptType = (byte)OpenMetaverse.SculptType.None; 239 else
240 {
241 SculptType = (byte)OpenMetaverse.SculptType.None;
242 }
252 } 243 }
253 244
254 [XmlIgnore] 245 [XmlIgnore]
@@ -340,9 +331,9 @@ namespace OpenSim.Framework
340 _scale = new Vector3(side, side, side); 331 _scale = new Vector3(side, side, side);
341 } 332 }
342 333
343 public void SetHeigth(float heigth) 334 public void SetHeigth(float height)
344 { 335 {
345 _scale.Z = heigth; 336 _scale.Z = height;
346 } 337 }
347 338
348 public void SetRadius(float radius) 339 public void SetRadius(float radius)
@@ -631,6 +622,8 @@ namespace OpenSim.Framework
631 } 622 }
632 } 623 }
633 624
625 // This is only used at runtime. For sculpties this holds the texture data, and for meshes
626 // the mesh data.
634 public byte[] SculptData 627 public byte[] SculptData
635 { 628 {
636 get 629 get
@@ -1184,14 +1177,13 @@ namespace OpenSim.Framework
1184 1177
1185 public void ReadSculptData(byte[] data, int pos) 1178 public void ReadSculptData(byte[] data, int pos)
1186 { 1179 {
1187 byte[] SculptTextureUUID = new byte[16]; 1180 UUID SculptUUID;
1188 UUID SculptUUID = UUID.Zero; 1181 byte SculptTypel;
1189 byte SculptTypel = data[16+pos];
1190 1182
1191 if (data.Length+pos >= 17) 1183 if (data.Length-pos >= 17)
1192 { 1184 {
1193 _sculptEntry = true; 1185 _sculptEntry = true;
1194 SculptTextureUUID = new byte[16]; 1186 byte[] SculptTextureUUID = new byte[16];
1195 SculptTypel = data[16 + pos]; 1187 SculptTypel = data[16 + pos];
1196 Array.Copy(data, pos, SculptTextureUUID,0, 16); 1188 Array.Copy(data, pos, SculptTextureUUID,0, 16);
1197 SculptUUID = new UUID(SculptTextureUUID, 0); 1189 SculptUUID = new UUID(SculptTextureUUID, 0);
diff --git a/OpenSim/Framework/RegionInfo.cs b/OpenSim/Framework/RegionInfo.cs
index e7bed6a..6dde62f 100644
--- a/OpenSim/Framework/RegionInfo.cs
+++ b/OpenSim/Framework/RegionInfo.cs
@@ -145,6 +145,7 @@ namespace OpenSim.Framework
145 public UUID RegionID = UUID.Zero; 145 public UUID RegionID = UUID.Zero;
146 public string RemotingAddress; 146 public string RemotingAddress;
147 public UUID ScopeID = UUID.Zero; 147 public UUID ScopeID = UUID.Zero;
148 private UUID m_maptileStaticUUID = UUID.Zero;
148 149
149 private Dictionary<String, String> m_otherSettings = new Dictionary<string, string>(); 150 private Dictionary<String, String> m_otherSettings = new Dictionary<string, string>();
150 151
@@ -340,6 +341,11 @@ namespace OpenSim.Framework
340 get { return m_regionType; } 341 get { return m_regionType; }
341 } 342 }
342 343
344 public UUID MaptileStaticUUID
345 {
346 get { return m_maptileStaticUUID; }
347 }
348
343 /// <summary> 349 /// <summary>
344 /// The port by which http communication occurs with the region (most noticeably, CAPS communication) 350 /// The port by which http communication occurs with the region (most noticeably, CAPS communication)
345 /// </summary> 351 /// </summary>
@@ -643,7 +649,7 @@ namespace OpenSim.Framework
643 m_regionType = config.GetString("RegionType", String.Empty); 649 m_regionType = config.GetString("RegionType", String.Empty);
644 allKeys.Remove("RegionType"); 650 allKeys.Remove("RegionType");
645 651
646 #region Prim stuff 652 #region Prim and map stuff
647 653
648 m_nonphysPrimMin = config.GetFloat("NonPhysicalPrimMin", 0); 654 m_nonphysPrimMin = config.GetFloat("NonPhysicalPrimMin", 0);
649 allKeys.Remove("NonPhysicalPrimMin"); 655 allKeys.Remove("NonPhysicalPrimMin");
@@ -665,6 +671,13 @@ namespace OpenSim.Framework
665 671
666 m_linksetCapacity = config.GetInt("LinksetPrims", 0); 672 m_linksetCapacity = config.GetInt("LinksetPrims", 0);
667 allKeys.Remove("LinksetPrims"); 673 allKeys.Remove("LinksetPrims");
674
675 allKeys.Remove("MaptileStaticUUID");
676 string mapTileStaticUUID = config.GetString("MaptileStaticUUID", UUID.Zero.ToString());
677 if (UUID.TryParse(mapTileStaticUUID.Trim(), out m_maptileStaticUUID))
678 {
679 config.Set("MaptileStaticUUID", m_maptileStaticUUID.ToString());
680 }
668 681
669 #endregion 682 #endregion
670 683
@@ -734,6 +747,9 @@ namespace OpenSim.Framework
734 747
735 if (RegionType != String.Empty) 748 if (RegionType != String.Empty)
736 config.Set("RegionType", RegionType); 749 config.Set("RegionType", RegionType);
750
751 if (m_maptileStaticUUID != UUID.Zero)
752 config.Set("MaptileStaticUUID", m_maptileStaticUUID.ToString());
737 } 753 }
738 754
739 public bool ignoreIncomingConfiguration(string configuration_key, object configuration_result) 755 public bool ignoreIncomingConfiguration(string configuration_key, object configuration_result)
@@ -832,6 +848,9 @@ namespace OpenSim.Framework
832 848
833 configMember.addConfigurationOption("region_type", ConfigurationOption.ConfigurationTypes.TYPE_STRING, 849 configMember.addConfigurationOption("region_type", ConfigurationOption.ConfigurationTypes.TYPE_STRING,
834 "Free form string describing the type of region", String.Empty, true); 850 "Free form string describing the type of region", String.Empty, true);
851
852 configMember.addConfigurationOption("region_static_maptile", ConfigurationOption.ConfigurationTypes.TYPE_UUID,
853 "UUID of a texture to use as the map for this region", m_maptileStaticUUID.ToString(), true);
835 } 854 }
836 855
837 public void loadConfigurationOptions() 856 public void loadConfigurationOptions()
@@ -885,6 +904,9 @@ namespace OpenSim.Framework
885 904
886 configMember.addConfigurationOption("region_type", ConfigurationOption.ConfigurationTypes.TYPE_STRING, 905 configMember.addConfigurationOption("region_type", ConfigurationOption.ConfigurationTypes.TYPE_STRING,
887 "Region Type", String.Empty, true); 906 "Region Type", String.Empty, true);
907
908 configMember.addConfigurationOption("region_static_maptile", ConfigurationOption.ConfigurationTypes.TYPE_UUID,
909 "UUID of a texture to use as the map for this region", String.Empty, true);
888 } 910 }
889 911
890 public bool handleIncomingConfiguration(string configuration_key, object configuration_result) 912 public bool handleIncomingConfiguration(string configuration_key, object configuration_result)
@@ -957,6 +979,9 @@ namespace OpenSim.Framework
957 case "region_type": 979 case "region_type":
958 m_regionType = (string)configuration_result; 980 m_regionType = (string)configuration_result;
959 break; 981 break;
982 case "region_static_maptile":
983 m_maptileStaticUUID = (UUID)configuration_result;
984 break;
960 } 985 }
961 986
962 return true; 987 return true;
diff --git a/OpenSim/Framework/RegionLoader/Filesystem/Properties/AssemblyInfo.cs b/OpenSim/Framework/RegionLoader/Filesystem/Properties/AssemblyInfo.cs
index d670f2f..d4806f1 100644
--- a/OpenSim/Framework/RegionLoader/Filesystem/Properties/AssemblyInfo.cs
+++ b/OpenSim/Framework/RegionLoader/Filesystem/Properties/AssemblyInfo.cs
@@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
29// Build Number 29// Build Number
30// Revision 30// Revision
31// 31//
32[assembly: AssemblyVersion("0.7.5.*")] 32[assembly: AssemblyVersion("0.7.6.*")]
33[assembly: AssemblyFileVersion("1.0.0.0")] 33
diff --git a/OpenSim/Framework/RegionLoader/Web/Properties/AssemblyInfo.cs b/OpenSim/Framework/RegionLoader/Web/Properties/AssemblyInfo.cs
index 7309a12..1541a5b 100644
--- a/OpenSim/Framework/RegionLoader/Web/Properties/AssemblyInfo.cs
+++ b/OpenSim/Framework/RegionLoader/Web/Properties/AssemblyInfo.cs
@@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
29// Build Number 29// Build Number
30// Revision 30// Revision
31// 31//
32[assembly: AssemblyVersion("0.7.5.*")] 32[assembly: AssemblyVersion("0.7.6.*")]
33[assembly: AssemblyFileVersion("1.0.0.0")] 33
diff --git a/OpenSim/Framework/Serialization/ArchiveConstants.cs b/OpenSim/Framework/Serialization/ArchiveConstants.cs
index 48f1c4f..0c12787 100644
--- a/OpenSim/Framework/Serialization/ArchiveConstants.cs
+++ b/OpenSim/Framework/Serialization/ArchiveConstants.cs
@@ -154,6 +154,11 @@ namespace OpenSim.Framework.Serialization
154 EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "trashfolder.txt"] = (sbyte)AssetType.TrashFolder; 154 EXTENSION_TO_ASSET_TYPE[ASSET_EXTENSION_SEPARATOR + "trashfolder.txt"] = (sbyte)AssetType.TrashFolder;
155 } 155 }
156 156
157 public static string CreateOarLandDataPath(LandData ld)
158 {
159 return string.Format("{0}{1}.xml", ArchiveConstants.LANDDATA_PATH, ld.GlobalID);
160 }
161
157 /// <summary> 162 /// <summary>
158 /// Create the filename used to store an object in an OpenSim Archive. 163 /// Create the filename used to store an object in an OpenSim Archive.
159 /// </summary> 164 /// </summary>
diff --git a/OpenSim/Framework/Serialization/Properties/AssemblyInfo.cs b/OpenSim/Framework/Serialization/Properties/AssemblyInfo.cs
index 11efa4b..a8dff93 100644
--- a/OpenSim/Framework/Serialization/Properties/AssemblyInfo.cs
+++ b/OpenSim/Framework/Serialization/Properties/AssemblyInfo.cs
@@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
29// Build Number 29// Build Number
30// Revision 30// Revision
31// 31//
32[assembly: AssemblyVersion("0.7.5.*")] 32[assembly: AssemblyVersion("0.7.6.*")]
33[assembly: AssemblyFileVersion("1.0.0.0")] 33
diff --git a/OpenSim/Framework/Serialization/Tests/LandDataSerializerTests.cs b/OpenSim/Framework/Serialization/Tests/LandDataSerializerTests.cs
index 8b9756b..ea100ee 100644
--- a/OpenSim/Framework/Serialization/Tests/LandDataSerializerTests.cs
+++ b/OpenSim/Framework/Serialization/Tests/LandDataSerializerTests.cs
@@ -37,7 +37,7 @@ using OpenSim.Tests.Common;
37namespace OpenSim.Framework.Serialization.Tests 37namespace OpenSim.Framework.Serialization.Tests
38{ 38{
39 [TestFixture] 39 [TestFixture]
40 public class LandDataSerializerTest 40 public class LandDataSerializerTest : OpenSimTestCase
41 { 41 {
42 private LandData land; 42 private LandData land;
43 private LandData landWithParcelAccessList; 43 private LandData landWithParcelAccessList;
diff --git a/OpenSim/Framework/Serialization/Tests/RegionSettingsSerializerTests.cs b/OpenSim/Framework/Serialization/Tests/RegionSettingsSerializerTests.cs
index 09b6f6d..142726b 100644
--- a/OpenSim/Framework/Serialization/Tests/RegionSettingsSerializerTests.cs
+++ b/OpenSim/Framework/Serialization/Tests/RegionSettingsSerializerTests.cs
@@ -37,7 +37,7 @@ using OpenSim.Tests.Common;
37namespace OpenSim.Framework.Serialization.Tests 37namespace OpenSim.Framework.Serialization.Tests
38{ 38{
39 [TestFixture] 39 [TestFixture]
40 public class RegionSettingsSerializerTests 40 public class RegionSettingsSerializerTests : OpenSimTestCase
41 { 41 {
42 private string m_serializedRs = @"<?xml version=""1.0"" encoding=""utf-16""?> 42 private string m_serializedRs = @"<?xml version=""1.0"" encoding=""utf-16""?>
43<RegionSettings> 43<RegionSettings>
diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs
index 2c21800..cb47cbf 100644
--- a/OpenSim/Framework/Servers/BaseOpenSimServer.cs
+++ b/OpenSim/Framework/Servers/BaseOpenSimServer.cs
@@ -27,7 +27,6 @@
27 27
28using System; 28using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Diagnostics;
31using System.IO; 30using System.IO;
32using System.Reflection; 31using System.Reflection;
33using System.Text; 32using System.Text;
@@ -99,34 +98,6 @@ namespace OpenSim.Framework.Servers
99 m_console.Commands.AddCommand("General", false, "shutdown", 98 m_console.Commands.AddCommand("General", false, "shutdown",
100 "shutdown", 99 "shutdown",
101 "Quit the application", HandleQuit); 100 "Quit the application", HandleQuit);
102
103 m_console.Commands.AddCommand("General", false, "show threads",
104 "show threads",
105 "Show thread status", HandleShow);
106
107 m_console.Commands.AddCommand("General", false, "show version",
108 "show version",
109 "Show server version", HandleShow);
110
111 m_console.Commands.AddCommand("General", false, "threads abort",
112 "threads abort <thread-id>",
113 "Abort a managed thread. Use \"show threads\" to find possible threads.", HandleThreadsAbort);
114
115 m_console.Commands.AddCommand("General", false, "threads show",
116 "threads show",
117 "Show thread status. Synonym for \"show threads\"",
118 (string module, string[] args) => Notice(GetThreadsReport()));
119
120 m_console.Commands.AddCommand("General", false, "force gc",
121 "force gc",
122 "Manually invoke runtime garbage collection. For debugging purposes",
123 HandleForceGc);
124 }
125
126 private void HandleForceGc(string module, string[] args)
127 {
128 MainConsole.Instance.Output("Manually invoking runtime garbage collection");
129 GC.Collect();
130 } 101 }
131 102
132 /// <summary> 103 /// <summary>
@@ -159,54 +130,6 @@ namespace OpenSim.Framework.Servers
159 } 130 }
160 131
161 /// <summary> 132 /// <summary>
162 /// Get a report about the registered threads in this server.
163 /// </summary>
164 protected string GetThreadsReport()
165 {
166 // This should be a constant field.
167 string reportFormat = "{0,6} {1,35} {2,16} {3,13} {4,10} {5,30}";
168
169 StringBuilder sb = new StringBuilder();
170 Watchdog.ThreadWatchdogInfo[] threads = Watchdog.GetThreadsInfo();
171
172 sb.Append(threads.Length + " threads are being tracked:" + Environment.NewLine);
173
174 int timeNow = Environment.TickCount & Int32.MaxValue;
175
176 sb.AppendFormat(reportFormat, "ID", "NAME", "LAST UPDATE (MS)", "LIFETIME (MS)", "PRIORITY", "STATE");
177 sb.Append(Environment.NewLine);
178
179 foreach (Watchdog.ThreadWatchdogInfo twi in threads)
180 {
181 Thread t = twi.Thread;
182
183 sb.AppendFormat(
184 reportFormat,
185 t.ManagedThreadId,
186 t.Name,
187 timeNow - twi.LastTick,
188 timeNow - twi.FirstTick,
189 t.Priority,
190 t.ThreadState);
191
192 sb.Append("\n");
193 }
194
195 sb.Append("\n");
196
197 // For some reason mono 2.6.7 returns an empty threads set! Not going to confuse people by reporting
198 // zero active threads.
199 int totalThreads = Process.GetCurrentProcess().Threads.Count;
200 if (totalThreads > 0)
201 sb.AppendFormat("Total threads active: {0}\n\n", totalThreads);
202
203 sb.Append("Main threadpool (excluding script engine pools)\n");
204 sb.Append(Util.GetThreadPoolReport());
205
206 return sb.ToString();
207 }
208
209 /// <summary>
210 /// Performs initialisation of the scene, such as loading configuration from disk. 133 /// Performs initialisation of the scene, such as loading configuration from disk.
211 /// </summary> 134 /// </summary>
212 public virtual void Startup() 135 public virtual void Startup()
@@ -246,50 +169,7 @@ namespace OpenSim.Framework.Servers
246 private void HandleQuit(string module, string[] args) 169 private void HandleQuit(string module, string[] args)
247 { 170 {
248 Shutdown(); 171 Shutdown();
249 } 172 }
250
251 public override void HandleShow(string module, string[] cmd)
252 {
253 base.HandleShow(module, cmd);
254
255 List<string> args = new List<string>(cmd);
256
257 args.RemoveAt(0);
258
259 string[] showParams = args.ToArray();
260
261 switch (showParams[0])
262 {
263 case "threads":
264 Notice(GetThreadsReport());
265 break;
266
267 case "version":
268 Notice(GetVersionText());
269 break;
270 }
271 }
272
273 public virtual void HandleThreadsAbort(string module, string[] cmd)
274 {
275 if (cmd.Length != 3)
276 {
277 MainConsole.Instance.Output("Usage: threads abort <thread-id>");
278 return;
279 }
280
281 int threadId;
282 if (!int.TryParse(cmd[2], out threadId))
283 {
284 MainConsole.Instance.Output("ERROR: Thread id must be an integer");
285 return;
286 }
287
288 if (Watchdog.AbortThread(threadId))
289 MainConsole.Instance.OutputFormat("Aborted thread with id {0}", threadId);
290 else
291 MainConsole.Instance.OutputFormat("ERROR - Thread with id {0} not found in managed threads", threadId);
292 }
293 173
294 public string osSecret { 174 public string osSecret {
295 // Secret uuid for the simulator 175 // Secret uuid for the simulator
diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
index 77fce9e..df1950d 100644
--- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
+++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
@@ -54,6 +54,16 @@ namespace OpenSim.Framework.Servers.HttpServer
54 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 54 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
55 private HttpServerLogWriter httpserverlog = new HttpServerLogWriter(); 55 private HttpServerLogWriter httpserverlog = new HttpServerLogWriter();
56 56
57
58 /// <summary>
59 /// This is a pending websocket request before it got an sucessful upgrade response.
60 /// The consumer must call handler.HandshakeAndUpgrade() to signal to the handler to
61 /// start the connection and optionally provide an origin authentication method.
62 /// </summary>
63 /// <param name="servicepath"></param>
64 /// <param name="handler"></param>
65 public delegate void WebSocketRequestDelegate(string servicepath, WebSocketHttpServerHandler handler);
66
57 /// <summary> 67 /// <summary>
58 /// Gets or sets the debug level. 68 /// Gets or sets the debug level.
59 /// </summary> 69 /// </summary>
@@ -77,6 +87,7 @@ namespace OpenSim.Framework.Servers.HttpServer
77 // protected HttpListener m_httpListener; 87 // protected HttpListener m_httpListener;
78 protected CoolHTTPListener m_httpListener2; 88 protected CoolHTTPListener m_httpListener2;
79 protected Dictionary<string, XmlRpcMethod> m_rpcHandlers = new Dictionary<string, XmlRpcMethod>(); 89 protected Dictionary<string, XmlRpcMethod> m_rpcHandlers = new Dictionary<string, XmlRpcMethod>();
90 protected Dictionary<string, JsonRPCMethod> jsonRpcHandlers = new Dictionary<string, JsonRPCMethod>();
80 protected Dictionary<string, bool> m_rpcHandlersKeepAlive = new Dictionary<string, bool>(); 91 protected Dictionary<string, bool> m_rpcHandlersKeepAlive = new Dictionary<string, bool>();
81 protected DefaultLLSDMethod m_defaultLlsdHandler = null; // <-- Moving away from the monolithic.. and going to /registered/ 92 protected DefaultLLSDMethod m_defaultLlsdHandler = null; // <-- Moving away from the monolithic.. and going to /registered/
82 protected Dictionary<string, LLSDMethod> m_llsdHandlers = new Dictionary<string, LLSDMethod>(); 93 protected Dictionary<string, LLSDMethod> m_llsdHandlers = new Dictionary<string, LLSDMethod>();
@@ -86,6 +97,9 @@ namespace OpenSim.Framework.Servers.HttpServer
86 protected Dictionary<string, PollServiceEventArgs> m_pollHandlers = 97 protected Dictionary<string, PollServiceEventArgs> m_pollHandlers =
87 new Dictionary<string, PollServiceEventArgs>(); 98 new Dictionary<string, PollServiceEventArgs>();
88 99
100 protected Dictionary<string, WebSocketRequestDelegate> m_WebSocketHandlers =
101 new Dictionary<string, WebSocketRequestDelegate>();
102
89 protected uint m_port; 103 protected uint m_port;
90 protected uint m_sslport; 104 protected uint m_sslport;
91 protected bool m_ssl; 105 protected bool m_ssl;
@@ -169,6 +183,22 @@ namespace OpenSim.Framework.Servers.HttpServer
169 } 183 }
170 } 184 }
171 185
186 public void AddWebSocketHandler(string servicepath, WebSocketRequestDelegate handler)
187 {
188 lock (m_WebSocketHandlers)
189 {
190 if (!m_WebSocketHandlers.ContainsKey(servicepath))
191 m_WebSocketHandlers.Add(servicepath, handler);
192 }
193 }
194
195 public void RemoveWebSocketHandler(string servicepath)
196 {
197 lock (m_WebSocketHandlers)
198 if (m_WebSocketHandlers.ContainsKey(servicepath))
199 m_WebSocketHandlers.Remove(servicepath);
200 }
201
172 public List<string> GetStreamHandlerKeys() 202 public List<string> GetStreamHandlerKeys()
173 { 203 {
174 lock (m_streamHandlers) 204 lock (m_streamHandlers)
@@ -217,6 +247,37 @@ namespace OpenSim.Framework.Servers.HttpServer
217 return new List<string>(m_rpcHandlers.Keys); 247 return new List<string>(m_rpcHandlers.Keys);
218 } 248 }
219 249
250 // JsonRPC
251 public bool AddJsonRPCHandler(string method, JsonRPCMethod handler)
252 {
253 lock(jsonRpcHandlers)
254 {
255 jsonRpcHandlers.Add(method, handler);
256 }
257 return true;
258 }
259
260 public JsonRPCMethod GetJsonRPCHandler(string method)
261 {
262 lock (jsonRpcHandlers)
263 {
264 if (jsonRpcHandlers.ContainsKey(method))
265 {
266 return jsonRpcHandlers[method];
267 }
268 else
269 {
270 return null;
271 }
272 }
273 }
274
275 public List<string> GetJsonRpcHandlerKeys()
276 {
277 lock (jsonRpcHandlers)
278 return new List<string>(jsonRpcHandlers.Keys);
279 }
280
220 public bool AddHTTPHandler(string methodName, GenericHTTPMethod handler) 281 public bool AddHTTPHandler(string methodName, GenericHTTPMethod handler)
221 { 282 {
222 //m_log.DebugFormat("[BASE HTTP SERVER]: Registering {0}", methodName); 283 //m_log.DebugFormat("[BASE HTTP SERVER]: Registering {0}", methodName);
@@ -378,9 +439,24 @@ namespace OpenSim.Framework.Servers.HttpServer
378 439
379 public void OnHandleRequestIOThread(IHttpClientContext context, IHttpRequest request) 440 public void OnHandleRequestIOThread(IHttpClientContext context, IHttpRequest request)
380 { 441 {
442
381 OSHttpRequest req = new OSHttpRequest(context, request); 443 OSHttpRequest req = new OSHttpRequest(context, request);
444 WebSocketRequestDelegate dWebSocketRequestDelegate = null;
445 lock (m_WebSocketHandlers)
446 {
447 if (m_WebSocketHandlers.ContainsKey(req.RawUrl))
448 dWebSocketRequestDelegate = m_WebSocketHandlers[req.RawUrl];
449 }
450 if (dWebSocketRequestDelegate != null)
451 {
452 dWebSocketRequestDelegate(req.Url.AbsolutePath, new WebSocketHttpServerHandler(req, context, 8192));
453 return;
454 }
455
382 OSHttpResponse resp = new OSHttpResponse(new HttpResponse(context, request),context); 456 OSHttpResponse resp = new OSHttpResponse(new HttpResponse(context, request),context);
457
383 HandleRequest(req, resp); 458 HandleRequest(req, resp);
459
384 460
385 // !!!HACK ALERT!!! 461 // !!!HACK ALERT!!!
386 // There seems to be a bug in the underlying http code that makes subsequent requests 462 // There seems to be a bug in the underlying http code that makes subsequent requests
@@ -437,7 +513,7 @@ namespace OpenSim.Framework.Servers.HttpServer
437// reqnum = String.Format("{0}:{1}",request.RemoteIPEndPoint,request.Headers["opensim-request-id"]); 513// reqnum = String.Format("{0}:{1}",request.RemoteIPEndPoint,request.Headers["opensim-request-id"]);
438 //m_log.DebugFormat("[BASE HTTP SERVER]: <{0}> handle request for {1}",reqnum,request.RawUrl); 514 //m_log.DebugFormat("[BASE HTTP SERVER]: <{0}> handle request for {1}",reqnum,request.RawUrl);
439 515
440 Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US", true); 516 Culture.SetCurrentCulture();
441 517
442// // This is the REST agent interface. We require an agent to properly identify 518// // This is the REST agent interface. We require an agent to properly identify
443// // itself. If the REST handler recognizes the prefix it will attempt to 519// // itself. If the REST handler recognizes the prefix it will attempt to
@@ -469,7 +545,7 @@ namespace OpenSim.Framework.Servers.HttpServer
469 LogIncomingToStreamHandler(request, requestHandler); 545 LogIncomingToStreamHandler(request, requestHandler);
470 546
471 response.ContentType = requestHandler.ContentType; // Lets do this defaulting before in case handler has varying content type. 547 response.ContentType = requestHandler.ContentType; // Lets do this defaulting before in case handler has varying content type.
472 548
473 if (requestHandler is IStreamedRequestHandler) 549 if (requestHandler is IStreamedRequestHandler)
474 { 550 {
475 IStreamedRequestHandler streamedRequestHandler = requestHandler as IStreamedRequestHandler; 551 IStreamedRequestHandler streamedRequestHandler = requestHandler as IStreamedRequestHandler;
@@ -557,10 +633,18 @@ namespace OpenSim.Framework.Servers.HttpServer
557 633
558 buffer = HandleLLSDRequests(request, response); 634 buffer = HandleLLSDRequests(request, response);
559 break; 635 break;
636
637 case "application/json-rpc":
638 if (DebugLevel >= 3)
639 LogIncomingToContentTypeHandler(request);
640
641 buffer = HandleJsonRpcRequests(request, response);
642 break;
560 643
561 case "text/xml": 644 case "text/xml":
562 case "application/xml": 645 case "application/xml":
563 case "application/json": 646 case "application/json":
647
564 default: 648 default:
565 //m_log.Info("[Debug BASE HTTP SERVER]: in default handler"); 649 //m_log.Info("[Debug BASE HTTP SERVER]: in default handler");
566 // Point of note.. the DoWeHaveA methods check for an EXACT path 650 // Point of note.. the DoWeHaveA methods check for an EXACT path
@@ -986,6 +1070,93 @@ namespace OpenSim.Framework.Servers.HttpServer
986 return buffer; 1070 return buffer;
987 } 1071 }
988 1072
1073 // JsonRpc (v2.0 only)
1074 // Batch requests not yet supported
1075 private byte[] HandleJsonRpcRequests(OSHttpRequest request, OSHttpResponse response)
1076 {
1077 Stream requestStream = request.InputStream;
1078 JsonRpcResponse jsonRpcResponse = new JsonRpcResponse();
1079 OSDMap jsonRpcRequest = null;
1080
1081 try
1082 {
1083 jsonRpcRequest = (OSDMap)OSDParser.DeserializeJson(requestStream);
1084 }
1085 catch (LitJson.JsonException e)
1086 {
1087 jsonRpcResponse.Error.Code = ErrorCode.InternalError;
1088 jsonRpcResponse.Error.Message = e.Message;
1089 }
1090
1091 requestStream.Close();
1092
1093 if (jsonRpcRequest != null)
1094 {
1095 if (jsonRpcRequest.ContainsKey("jsonrpc") || jsonRpcRequest["jsonrpc"].AsString() == "2.0")
1096 {
1097 jsonRpcResponse.JsonRpc = "2.0";
1098
1099 // If we have no id, then it's a "notification"
1100 if (jsonRpcRequest.ContainsKey("id"))
1101 {
1102 jsonRpcResponse.Id = jsonRpcRequest["id"].AsString();
1103 }
1104
1105 string methodname = jsonRpcRequest["method"];
1106 JsonRPCMethod method;
1107
1108 if (jsonRpcHandlers.ContainsKey(methodname))
1109 {
1110 lock(jsonRpcHandlers)
1111 {
1112 jsonRpcHandlers.TryGetValue(methodname, out method);
1113 }
1114 bool res = false;
1115 try
1116 {
1117 res = method(jsonRpcRequest, ref jsonRpcResponse);
1118 if(!res)
1119 {
1120 // The handler sent back an unspecified error
1121 if(jsonRpcResponse.Error.Code == 0)
1122 {
1123 jsonRpcResponse.Error.Code = ErrorCode.InternalError;
1124 }
1125 }
1126 }
1127 catch (Exception e)
1128 {
1129 string ErrorMessage = string.Format("[BASE HTTP SERVER]: Json-Rpc Handler Error method {0} - {1}", methodname, e.Message);
1130 m_log.Error(ErrorMessage);
1131 jsonRpcResponse.Error.Code = ErrorCode.InternalError;
1132 jsonRpcResponse.Error.Message = ErrorMessage;
1133 }
1134 }
1135 else // Error no hanlder defined for requested method
1136 {
1137 jsonRpcResponse.Error.Code = ErrorCode.InvalidRequest;
1138 jsonRpcResponse.Error.Message = string.Format ("No handler defined for {0}", methodname);
1139 }
1140 }
1141 else // not json-rpc 2.0 could be v1
1142 {
1143 jsonRpcResponse.Error.Code = ErrorCode.InvalidRequest;
1144 jsonRpcResponse.Error.Message = "Must be valid json-rpc 2.0 see: http://www.jsonrpc.org/specification";
1145
1146 if (jsonRpcRequest.ContainsKey("id"))
1147 jsonRpcResponse.Id = jsonRpcRequest["id"].AsString();
1148 }
1149 }
1150
1151 response.KeepAlive = true;
1152 string responseData = string.Empty;
1153
1154 responseData = jsonRpcResponse.Serialize();
1155
1156 byte[] buffer = Encoding.UTF8.GetBytes(responseData);
1157 return buffer;
1158 }
1159
989 private byte[] HandleLLSDRequests(OSHttpRequest request, OSHttpResponse response) 1160 private byte[] HandleLLSDRequests(OSHttpRequest request, OSHttpResponse response)
990 { 1161 {
991 //m_log.Warn("[BASE HTTP SERVER]: We've figured out it's a LLSD Request"); 1162 //m_log.Warn("[BASE HTTP SERVER]: We've figured out it's a LLSD Request");
@@ -1283,59 +1454,6 @@ namespace OpenSim.Framework.Servers.HttpServer
1283 map["login"] = OSD.FromString("false"); 1454 map["login"] = OSD.FromString("false");
1284 return map; 1455 return map;
1285 } 1456 }
1286 /// <summary>
1287 /// A specific agent handler was provided. Such a handler is expecetd to have an
1288 /// intimate, and highly specific relationship with the client. Consequently,
1289 /// nothing is done here.
1290 /// </summary>
1291 /// <param name="handler"></param>
1292 /// <param name="request"></param>
1293 /// <param name="response"></param>
1294
1295 private bool HandleAgentRequest(IHttpAgentHandler handler, OSHttpRequest request, OSHttpResponse response)
1296 {
1297 // In the case of REST, then handler is responsible for ALL aspects of
1298 // the request/response handling. Nothing is done here, not even encoding.
1299
1300 try
1301 {
1302 return handler.Handle(request, response);
1303 }
1304 catch (Exception e)
1305 {
1306 // If the handler did in fact close the stream, then this will blow
1307 // chunks. So that that doesn't disturb anybody we throw away any
1308 // and all exceptions raised. We've done our best to release the
1309 // client.
1310 try
1311 {
1312 m_log.Warn("[HTTP-AGENT]: Error - " + e.Message);
1313 response.SendChunked = false;
1314 response.KeepAlive = true;
1315 response.StatusCode = (int)OSHttpStatusCode.ServerErrorInternalError;
1316 //response.OutputStream.Close();
1317 try
1318 {
1319 response.Send();
1320 //response.FreeContext();
1321 }
1322 catch (SocketException f)
1323 {
1324 // This has to be here to prevent a Linux/Mono crash
1325 m_log.Warn(
1326 String.Format("[BASE HTTP SERVER]: XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux. ", f.Message), f);
1327 }
1328 }
1329 catch(Exception)
1330 {
1331 }
1332 }
1333
1334 // Indicate that the request has been "handled"
1335
1336 return true;
1337
1338 }
1339 1457
1340 public byte[] HandleHTTPRequest(OSHttpRequest request, OSHttpResponse response) 1458 public byte[] HandleHTTPRequest(OSHttpRequest request, OSHttpResponse response)
1341 { 1459 {
@@ -1775,6 +1893,8 @@ namespace OpenSim.Framework.Servers.HttpServer
1775 HTTPDRunning = false; 1893 HTTPDRunning = false;
1776 try 1894 try
1777 { 1895 {
1896// m_PollServiceManager.Stop();
1897
1778 m_httpListener2.ExceptionThrown -= httpServerException; 1898 m_httpListener2.ExceptionThrown -= httpServerException;
1779 //m_httpListener2.DisconnectHandler = null; 1899 //m_httpListener2.DisconnectHandler = null;
1780 1900
@@ -1840,6 +1960,12 @@ namespace OpenSim.Framework.Servers.HttpServer
1840 m_rpcHandlers.Remove(method); 1960 m_rpcHandlers.Remove(method);
1841 } 1961 }
1842 1962
1963 public void RemoveJsonRPCHandler(string method)
1964 {
1965 lock(jsonRpcHandlers)
1966 jsonRpcHandlers.Remove(method);
1967 }
1968
1843 public bool RemoveLLSDHandler(string path, LLSDMethod handler) 1969 public bool RemoveLLSDHandler(string path, LLSDMethod handler)
1844 { 1970 {
1845 lock (m_llsdHandlers) 1971 lock (m_llsdHandlers)
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
97 bool AddXmlRPCHandler(string method, XmlRpcMethod handler); 97 bool AddXmlRPCHandler(string method, XmlRpcMethod handler);
98 bool AddXmlRPCHandler(string method, XmlRpcMethod handler, bool keepAlive); 98 bool AddXmlRPCHandler(string method, XmlRpcMethod handler, bool keepAlive);
99 99
100 bool AddJsonRPCHandler(string method, JsonRPCMethod handler);
101
102 /// <summary>
103 /// Websocket HTTP server handlers.
104 /// </summary>
105 /// <param name="servicepath"></param>
106 /// <param name="handler"></param>
107 void AddWebSocketHandler(string servicepath, BaseHttpServer.WebSocketRequestDelegate handler);
108
109
110 void RemoveWebSocketHandler(string servicepath);
111
100 /// <summary> 112 /// <summary>
101 /// Gets the XML RPC handler for given method name 113 /// Gets the XML RPC handler for given method name
102 /// </summary> 114 /// </summary>
@@ -128,6 +140,8 @@ namespace OpenSim.Framework.Servers.HttpServer
128 void RemoveStreamHandler(string httpMethod, string path); 140 void RemoveStreamHandler(string httpMethod, string path);
129 141
130 void RemoveXmlRPCHandler(string method); 142 void RemoveXmlRPCHandler(string method);
143
144 void RemoveJsonRPCHandler(string method);
131 145
132 string GetHTTP404(string host); 146 string GetHTTP404(string host);
133 147
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 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System.Net;
29using OpenMetaverse.StructuredData;
30
31namespace OpenSim.Framework.Servers.HttpServer
32{
33 public delegate bool JsonRPCMethod(OSDMap jsonRpcRequest, ref JsonRpcResponse response);
34}
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 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Net;
29using OpenMetaverse.StructuredData;
30
31namespace OpenSim.Framework.Servers.HttpServer
32{
33 public sealed class ErrorCode
34 {
35 private ErrorCode() {}
36
37 public const int ParseError = -32700;
38 public const int InvalidRequest = -32600;
39 public const int MethodNotFound = -32601;
40 public const int InvalidParams = -32602;
41 public const int InternalError = -32604;
42
43 }
44
45 public class JsonRpcError
46 {
47 internal OSDMap Error = new OSDMap();
48
49 public int Code
50 {
51 get
52 {
53 if (Error.ContainsKey("code"))
54 return Error["code"].AsInteger();
55 else
56 return 0;
57 }
58 set
59 {
60 Error["code"] = OSD.FromInteger(value);
61 }
62 }
63
64 public string Message
65 {
66 get
67 {
68 if (Error.ContainsKey("message"))
69 return Error["message"].AsString();
70 else
71 return null;
72 }
73 set
74 {
75 Error["message"] = OSD.FromString(value);
76 }
77 }
78
79 public OSD Data
80 {
81 get; set;
82 }
83 }
84
85 public class JsonRpcResponse
86 {
87 public string JsonRpc
88 {
89 get
90 {
91 return Reply["jsonrpc"].AsString();
92 }
93 set
94 {
95 Reply["jsonrpc"] = OSD.FromString(value);
96 }
97 }
98
99 public string Id
100 {
101 get
102 {
103 return Reply["id"].AsString();
104 }
105 set
106 {
107 Reply["id"] = OSD.FromString(value);
108 }
109 }
110
111 public OSD Result
112 {
113 get; set;
114 }
115
116 public JsonRpcError Error
117 {
118 get; set;
119 }
120
121 public OSDMap Reply = new OSDMap();
122
123 public JsonRpcResponse()
124 {
125 Error = new JsonRpcError();
126 }
127
128 public string Serialize()
129 {
130 if (Result != null)
131 Reply["result"] = Result;
132
133 if (Error.Code != 0)
134 {
135 Reply["error"] = (OSD)Error.Error;
136 }
137
138 string result = string.Empty;
139 try
140 {
141 result = OSDParser.SerializeJsonString(Reply);
142 }
143 catch
144 {
145
146 }
147 return result;
148 }
149 }
150}
diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
index 4be8bf4..07bd48a 100644
--- a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
+++ b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
@@ -50,19 +50,26 @@ namespace OpenSim.Framework.Servers.HttpServer
50 private uint m_WorkerThreadCount = 0; 50 private uint m_WorkerThreadCount = 0;
51 private Thread[] m_workerThreads; 51 private Thread[] m_workerThreads;
52 private PollServiceWorkerThread[] m_PollServiceWorkerThreads; 52 private PollServiceWorkerThread[] m_PollServiceWorkerThreads;
53 private bool m_running = true; 53 private volatile bool m_running = true;
54 private int m_pollTimeout;
54 55
55 public PollServiceRequestManager(BaseHttpServer pSrv, uint pWorkerThreadCount, int pTimeout) 56 public PollServiceRequestManager(BaseHttpServer pSrv, uint pWorkerThreadCount, int pTimeout)
56 { 57 {
57 m_server = pSrv; 58 m_server = pSrv;
58 m_WorkerThreadCount = pWorkerThreadCount; 59 m_WorkerThreadCount = pWorkerThreadCount;
60 m_pollTimeout = pTimeout;
61 }
62
63 public void Start()
64 {
65 m_running = true;
59 m_workerThreads = new Thread[m_WorkerThreadCount]; 66 m_workerThreads = new Thread[m_WorkerThreadCount];
60 m_PollServiceWorkerThreads = new PollServiceWorkerThread[m_WorkerThreadCount]; 67 m_PollServiceWorkerThreads = new PollServiceWorkerThread[m_WorkerThreadCount];
61 68
62 //startup worker threads 69 //startup worker threads
63 for (uint i = 0; i < m_WorkerThreadCount; i++) 70 for (uint i = 0; i < m_WorkerThreadCount; i++)
64 { 71 {
65 m_PollServiceWorkerThreads[i] = new PollServiceWorkerThread(m_server, pTimeout); 72 m_PollServiceWorkerThreads[i] = new PollServiceWorkerThread(m_server, m_pollTimeout);
66 m_PollServiceWorkerThreads[i].ReQueue += ReQueueEvent; 73 m_PollServiceWorkerThreads[i].ReQueue += ReQueueEvent;
67 74
68 m_workerThreads[i] 75 m_workerThreads[i]
@@ -141,8 +148,10 @@ namespace OpenSim.Framework.Servers.HttpServer
141 148
142 } 149 }
143 150
144 ~PollServiceRequestManager() 151 public void Stop()
145 { 152 {
153 m_running = false;
154
146 foreach (object o in m_requests) 155 foreach (object o in m_requests)
147 { 156 {
148 PollServiceHttpRequest req = (PollServiceHttpRequest) o; 157 PollServiceHttpRequest req = (PollServiceHttpRequest) o;
@@ -157,7 +166,6 @@ namespace OpenSim.Framework.Servers.HttpServer
157 { 166 {
158 t.Abort(); 167 t.Abort();
159 } 168 }
160 m_running = false;
161 } 169 }
162 } 170 }
163} 171}
diff --git a/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs b/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs
index 02ecc25..8e592c1 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;
29// Build Number 29// Build Number
30// Revision 30// Revision
31// 31//
32[assembly: AssemblyVersion("0.7.5.*")] 32[assembly: AssemblyVersion("0.7.6.*")]
33[assembly: AssemblyFileVersion("1.0.0.0")] 33
diff --git a/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs b/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs
index 19c03a8..edcd134 100644
--- a/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs
+++ b/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs
@@ -101,20 +101,11 @@ namespace OpenSim.Framework.Servers.HttpServer
101 using (WebResponse resp = request.GetResponse()) 101 using (WebResponse resp = request.GetResponse())
102 { 102 {
103 XmlSerializer deserializer = new XmlSerializer(typeof(TResponse)); 103 XmlSerializer deserializer = new XmlSerializer(typeof(TResponse));
104 Stream respStream = null; 104
105 try 105 using (Stream respStream = resp.GetResponseStream())
106 {
107 respStream = resp.GetResponseStream();
108 deserial = (TResponse)deserializer.Deserialize(respStream); 106 deserial = (TResponse)deserializer.Deserialize(respStream);
109 }
110 catch { }
111 finally
112 {
113 if (respStream != null)
114 respStream.Close();
115 resp.Close();
116 }
117 } 107 }
108
118 return deserial; 109 return deserial;
119 } 110 }
120 } 111 }
diff --git a/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs b/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs
new file mode 100644
index 0000000..ee96b47
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs
@@ -0,0 +1,1102 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Security.Cryptography;
32using System.Text;
33using HttpServer;
34
35namespace OpenSim.Framework.Servers.HttpServer
36{
37 // Sealed class. If you're going to unseal it, implement IDisposable.
38 /// <summary>
39 /// This class implements websockets. It grabs the network context from C#Webserver and utilizes it directly as a tcp streaming service
40 /// </summary>
41 public sealed class WebSocketHttpServerHandler : BaseRequestHandler
42 {
43
44 private class WebSocketState
45 {
46 public List<byte> ReceivedBytes;
47 public int ExpectedBytes;
48 public WebsocketFrameHeader Header;
49 public bool FrameComplete;
50 public WebSocketFrame ContinuationFrame;
51 }
52
53 /// <summary>
54 /// Binary Data will trigger this event
55 /// </summary>
56 public event DataDelegate OnData;
57
58 /// <summary>
59 /// Textual Data will trigger this event
60 /// </summary>
61 public event TextDelegate OnText;
62
63 /// <summary>
64 /// A ping request form the other side will trigger this event.
65 /// This class responds to the ping automatically. You shouldn't send a pong.
66 /// it's informational.
67 /// </summary>
68 public event PingDelegate OnPing;
69
70 /// <summary>
71 /// This is a response to a ping you sent.
72 /// </summary>
73 public event PongDelegate OnPong;
74
75 /// <summary>
76 /// This is a regular HTTP Request... This may be removed in the future.
77 /// </summary>
78 public event RegularHttpRequestDelegate OnRegularHttpRequest;
79
80 /// <summary>
81 /// When the upgrade from a HTTP request to a Websocket is completed, this will be fired
82 /// </summary>
83 public event UpgradeCompletedDelegate OnUpgradeCompleted;
84
85 /// <summary>
86 /// If the upgrade failed, this will be fired
87 /// </summary>
88 public event UpgradeFailedDelegate OnUpgradeFailed;
89
90 /// <summary>
91 /// When the websocket is closed, this will be fired.
92 /// </summary>
93 public event CloseDelegate OnClose;
94
95 /// <summary>
96 /// Set this delegate to allow your module to validate the origin of the
97 /// Websocket request. Primary line of defense against cross site scripting
98 /// </summary>
99 public ValidateHandshake HandshakeValidateMethodOverride = null;
100
101 private OSHttpRequest _request;
102 private HTTPNetworkContext _networkContext;
103 private IHttpClientContext _clientContext;
104
105 private int _pingtime = 0;
106 private byte[] _buffer;
107 private int _bufferPosition;
108 private int _bufferLength;
109 private bool _closing;
110 private bool _upgraded;
111 private int _maxPayloadBytes = 41943040;
112
113 private const string HandshakeAcceptText =
114 "HTTP/1.1 101 Switching Protocols\r\n" +
115 "upgrade: websocket\r\n" +
116 "Connection: Upgrade\r\n" +
117 "sec-websocket-accept: {0}\r\n\r\n";// +
118 //"{1}";
119
120 private const string HandshakeDeclineText =
121 "HTTP/1.1 {0} {1}\r\n" +
122 "Connection: close\r\n\r\n";
123
124 /// <summary>
125 /// Mysterious constant defined in RFC6455 to append to the client provided security key
126 /// </summary>
127 private const string WebsocketHandshakeAcceptHashConstant = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
128
129 public WebSocketHttpServerHandler(OSHttpRequest preq, IHttpClientContext pContext, int bufferlen)
130 : base(preq.HttpMethod, preq.Url.OriginalString)
131 {
132 _request = preq;
133 _networkContext = pContext.GiveMeTheNetworkStreamIKnowWhatImDoing();
134 _clientContext = pContext;
135 _bufferLength = bufferlen;
136 _buffer = new byte[_bufferLength];
137 }
138
139 // Sealed class implments destructor and an internal dispose method. complies with C# unmanaged resource best practices.
140 ~WebSocketHttpServerHandler()
141 {
142 Dispose();
143
144 }
145
146 /// <summary>
147 /// Sets the length of the stream buffer
148 /// </summary>
149 /// <param name="pChunk">Byte length.</param>
150 public void SetChunksize(int pChunk)
151 {
152 if (!_upgraded)
153 {
154 _buffer = new byte[pChunk];
155 }
156 else
157 {
158 throw new InvalidOperationException("You must set the chunksize before the connection is upgraded");
159 }
160 }
161
162 /// <summary>
163 /// This is the famous nagle.
164 /// </summary>
165 public bool NoDelay_TCP_Nagle
166 {
167 get
168 {
169 if (_networkContext != null && _networkContext.Socket != null)
170 {
171 return _networkContext.Socket.NoDelay;
172 }
173 else
174 {
175 throw new InvalidOperationException("The socket has been shutdown");
176 }
177 }
178 set
179 {
180 if (_networkContext != null && _networkContext.Socket != null)
181 _networkContext.Socket.NoDelay = value;
182 else
183 {
184 throw new InvalidOperationException("The socket has been shutdown");
185 }
186 }
187 }
188
189 /// <summary>
190 /// This triggers the websocket to start the upgrade process...
191 /// This is a Generalized Networking 'common sense' helper method. Some people expect to call Start() instead
192 /// of the more context appropriate HandshakeAndUpgrade()
193 /// </summary>
194 public void Start()
195 {
196 HandshakeAndUpgrade();
197 }
198
199 /// <summary>
200 /// Max Payload Size in bytes. Defaults to 40MB, but could be set upon connection before calling handshake and upgrade.
201 /// </summary>
202 public int MaxPayloadSize
203 {
204 get { return _maxPayloadBytes; }
205 set { _maxPayloadBytes = value; }
206 }
207
208 /// <summary>
209 /// This triggers the websocket start the upgrade process
210 /// </summary>
211 public void HandshakeAndUpgrade()
212 {
213 string webOrigin = string.Empty;
214 string websocketKey = string.Empty;
215 string acceptKey = string.Empty;
216 string accepthost = string.Empty;
217 if (!string.IsNullOrEmpty(_request.Headers["origin"]))
218 webOrigin = _request.Headers["origin"];
219
220 if (!string.IsNullOrEmpty(_request.Headers["sec-websocket-key"]))
221 websocketKey = _request.Headers["sec-websocket-key"];
222
223 if (!string.IsNullOrEmpty(_request.Headers["host"]))
224 accepthost = _request.Headers["host"];
225
226 if (string.IsNullOrEmpty(_request.Headers["upgrade"]))
227 {
228 FailUpgrade(OSHttpStatusCode.ClientErrorBadRequest, "no upgrade request submitted");
229 }
230
231 string connectionheader = _request.Headers["upgrade"];
232 if (connectionheader.ToLower() != "websocket")
233 {
234 FailUpgrade(OSHttpStatusCode.ClientErrorBadRequest, "no connection upgrade request submitted");
235 }
236
237 // If the object consumer provided a method to validate the origin, we should call it and give the client a success or fail.
238 // If not.. we should accept any. The assumption here is that there would be no Websocket handlers registered in baseHTTPServer unless
239 // Something asked for it...
240 if (HandshakeValidateMethodOverride != null)
241 {
242 if (HandshakeValidateMethodOverride(webOrigin, websocketKey, accepthost))
243 {
244 acceptKey = GenerateAcceptKey(websocketKey);
245 string rawaccept = string.Format(HandshakeAcceptText, acceptKey);
246 SendUpgradeSuccess(rawaccept);
247
248 }
249 else
250 {
251 FailUpgrade(OSHttpStatusCode.ClientErrorForbidden, "Origin Validation Failed");
252 }
253 }
254 else
255 {
256 acceptKey = GenerateAcceptKey(websocketKey);
257 string rawaccept = string.Format(HandshakeAcceptText, acceptKey);
258 SendUpgradeSuccess(rawaccept);
259 }
260 }
261
262 /// <summary>
263 /// Generates a handshake response key string based on the client's
264 /// provided key to prove to the client that we're allowing the Websocket
265 /// upgrade of our own free will and we were not coerced into doing it.
266 /// </summary>
267 /// <param name="key">Client provided security key</param>
268 /// <returns></returns>
269 private static string GenerateAcceptKey(string key)
270 {
271 if (string.IsNullOrEmpty(key))
272 return string.Empty;
273
274 string acceptkey = key + WebsocketHandshakeAcceptHashConstant;
275
276 SHA1 hashobj = SHA1.Create();
277 string ret = Convert.ToBase64String(hashobj.ComputeHash(Encoding.UTF8.GetBytes(acceptkey)));
278 hashobj.Clear();
279
280 return ret;
281 }
282
283 /// <summary>
284 /// Informs the otherside that we accepted their upgrade request
285 /// </summary>
286 /// <param name="pHandshakeResponse">The HTTP 1.1 101 response that says Yay \o/ </param>
287 private void SendUpgradeSuccess(string pHandshakeResponse)
288 {
289 // Create a new websocket state so we can keep track of data in between network reads.
290 WebSocketState socketState = new WebSocketState() { ReceivedBytes = new List<byte>(), Header = WebsocketFrameHeader.HeaderDefault(), FrameComplete = true};
291
292 byte[] bhandshakeResponse = Encoding.UTF8.GetBytes(pHandshakeResponse);
293 try
294 {
295
296 // Begin reading the TCP stream before writing the Upgrade success message to the other side of the stream.
297 _networkContext.Stream.BeginRead(_buffer, 0, _bufferLength, OnReceive, socketState);
298
299 // Write the upgrade handshake success message
300 _networkContext.Stream.Write(bhandshakeResponse, 0, bhandshakeResponse.Length);
301 _networkContext.Stream.Flush();
302 _upgraded = true;
303 UpgradeCompletedDelegate d = OnUpgradeCompleted;
304 if (d != null)
305 d(this, new UpgradeCompletedEventArgs());
306 }
307 catch (IOException fail)
308 {
309 Close(string.Empty);
310 }
311 catch (ObjectDisposedException fail)
312 {
313 Close(string.Empty);
314 }
315
316 }
317
318 /// <summary>
319 /// The server has decided not to allow the upgrade to a websocket for some reason. The Http 1.1 response that says Nay >:(
320 /// </summary>
321 /// <param name="pCode">HTTP Status reflecting the reason why</param>
322 /// <param name="pMessage">Textual reason for the upgrade fail</param>
323 private void FailUpgrade(OSHttpStatusCode pCode, string pMessage )
324 {
325 string handshakeResponse = string.Format(HandshakeDeclineText, (int)pCode, pMessage.Replace("\n", string.Empty).Replace("\r", string.Empty));
326 byte[] bhandshakeResponse = Encoding.UTF8.GetBytes(handshakeResponse);
327 _networkContext.Stream.Write(bhandshakeResponse, 0, bhandshakeResponse.Length);
328 _networkContext.Stream.Flush();
329 _networkContext.Stream.Dispose();
330
331 UpgradeFailedDelegate d = OnUpgradeFailed;
332 if (d != null)
333 d(this,new UpgradeFailedEventArgs());
334 }
335
336
337 /// <summary>
338 /// This is our ugly Async OnReceive event handler.
339 /// This chunks the input stream based on the length of the provided buffer and processes out
340 /// as many frames as it can. It then moves the unprocessed data to the beginning of the buffer.
341 /// </summary>
342 /// <param name="ar">Our Async State from beginread</param>
343 private void OnReceive(IAsyncResult ar)
344 {
345 WebSocketState _socketState = ar.AsyncState as WebSocketState;
346 try
347 {
348 int bytesRead = _networkContext.Stream.EndRead(ar);
349 if (bytesRead == 0)
350 {
351 // Do Disconnect
352 _networkContext.Stream.Dispose();
353 _networkContext = null;
354 return;
355 }
356 _bufferPosition += bytesRead;
357
358 if (_bufferPosition > _bufferLength)
359 {
360 // Message too big for chunksize.. not sure how this happened...
361 //Close(string.Empty);
362 }
363
364 int offset = 0;
365 bool headerread = true;
366 int headerforwardposition = 0;
367 while (headerread && offset < bytesRead)
368 {
369 if (_socketState.FrameComplete)
370 {
371 WebsocketFrameHeader pheader = WebsocketFrameHeader.ZeroHeader;
372
373 headerread = WebSocketReader.TryReadHeader(_buffer, offset, _bufferPosition - offset, out pheader,
374 out headerforwardposition);
375 offset += headerforwardposition;
376
377 if (headerread)
378 {
379 _socketState.FrameComplete = false;
380 if (pheader.PayloadLen > (ulong) _maxPayloadBytes)
381 {
382 Close("Invalid Payload size");
383
384 return;
385 }
386 if (pheader.PayloadLen > 0)
387 {
388 if ((int) pheader.PayloadLen > _bufferPosition - offset)
389 {
390 byte[] writebytes = new byte[_bufferPosition - offset];
391
392 Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) _bufferPosition - offset);
393 _socketState.ExpectedBytes = (int) pheader.PayloadLen;
394 _socketState.ReceivedBytes.AddRange(writebytes);
395 _socketState.Header = pheader; // We need to add the header so that we can unmask it
396 offset += (int) _bufferPosition - offset;
397 }
398 else
399 {
400 byte[] writebytes = new byte[pheader.PayloadLen];
401 Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) pheader.PayloadLen);
402 WebSocketReader.Mask(pheader.Mask, writebytes);
403 pheader.IsMasked = false;
404 _socketState.FrameComplete = true;
405 _socketState.ReceivedBytes.AddRange(writebytes);
406 _socketState.Header = pheader;
407 offset += (int) pheader.PayloadLen;
408 }
409 }
410 else
411 {
412 pheader.Mask = 0;
413 _socketState.FrameComplete = true;
414 _socketState.Header = pheader;
415 }
416
417
418
419 if (_socketState.FrameComplete)
420 {
421 ProcessFrame(_socketState);
422 _socketState.Header.SetDefault();
423 _socketState.ReceivedBytes.Clear();
424 _socketState.ExpectedBytes = 0;
425
426 }
427
428 }
429 }
430 else
431 {
432 WebsocketFrameHeader frameHeader = _socketState.Header;
433 int bytesleft = _socketState.ExpectedBytes - _socketState.ReceivedBytes.Count;
434
435 if (bytesleft > _bufferPosition)
436 {
437 byte[] writebytes = new byte[_bufferPosition];
438
439 Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) _bufferPosition);
440 _socketState.ReceivedBytes.AddRange(writebytes);
441 _socketState.Header = frameHeader; // We need to add the header so that we can unmask it
442 offset += (int) _bufferPosition;
443 }
444 else
445 {
446 byte[] writebytes = new byte[_bufferPosition];
447 Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) _bufferPosition);
448 _socketState.FrameComplete = true;
449 _socketState.ReceivedBytes.AddRange(writebytes);
450 _socketState.Header = frameHeader;
451 offset += (int) _bufferPosition;
452 }
453 if (_socketState.FrameComplete)
454 {
455 ProcessFrame(_socketState);
456 _socketState.Header.SetDefault();
457 _socketState.ReceivedBytes.Clear();
458 _socketState.ExpectedBytes = 0;
459 // do some processing
460 }
461
462 }
463 }
464 if (offset > 0)
465 {
466 // If the buffer is maxed out.. we can just move the cursor. Nothing to move to the beginning.
467 if (offset <_buffer.Length)
468 Buffer.BlockCopy(_buffer, offset, _buffer, 0, _bufferPosition - offset);
469 _bufferPosition -= offset;
470 }
471 if (_networkContext.Stream != null && _networkContext.Stream.CanRead && !_closing)
472 {
473 _networkContext.Stream.BeginRead(_buffer, _bufferPosition, _bufferLength - _bufferPosition, OnReceive,
474 _socketState);
475 }
476 else
477 {
478 // We can't read the stream anymore...
479 }
480
481 }
482 catch (IOException fail)
483 {
484 Close(string.Empty);
485 }
486 catch (ObjectDisposedException fail)
487 {
488 Close(string.Empty);
489 }
490 }
491
492 /// <summary>
493 /// Sends a string to the other side
494 /// </summary>
495 /// <param name="message">the string message that is to be sent</param>
496 public void SendMessage(string message)
497 {
498 byte[] messagedata = Encoding.UTF8.GetBytes(message);
499 WebSocketFrame textMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = messagedata };
500 textMessageFrame.Header.Opcode = WebSocketReader.OpCode.Text;
501 textMessageFrame.Header.IsEnd = true;
502 SendSocket(textMessageFrame.ToBytes());
503
504 }
505
506 public void SendData(byte[] data)
507 {
508 WebSocketFrame dataMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = data};
509 dataMessageFrame.Header.IsEnd = true;
510 dataMessageFrame.Header.Opcode = WebSocketReader.OpCode.Binary;
511 SendSocket(dataMessageFrame.ToBytes());
512
513 }
514
515 /// <summary>
516 /// Writes raw bytes to the websocket. Unframed data will cause disconnection
517 /// </summary>
518 /// <param name="data"></param>
519 private void SendSocket(byte[] data)
520 {
521 if (!_closing)
522 {
523 try
524 {
525
526 _networkContext.Stream.Write(data, 0, data.Length);
527 }
528 catch (IOException)
529 {
530
531 }
532 }
533 }
534
535 /// <summary>
536 /// 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.
537 /// </summary>
538 public void SendPingCheck()
539 {
540 WebSocketFrame pingFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = new byte[0] };
541 pingFrame.Header.Opcode = WebSocketReader.OpCode.Ping;
542 pingFrame.Header.IsEnd = true;
543 _pingtime = Util.EnvironmentTickCount();
544 SendSocket(pingFrame.ToBytes());
545 }
546
547 /// <summary>
548 /// Closes the websocket connection. Sends a close message to the other side if it hasn't already done so.
549 /// </summary>
550 /// <param name="message"></param>
551 public void Close(string message)
552 {
553 if (_networkContext == null)
554 return;
555 if (_networkContext.Stream != null)
556 {
557 if (_networkContext.Stream.CanWrite)
558 {
559 byte[] messagedata = Encoding.UTF8.GetBytes(message);
560 WebSocketFrame closeResponseFrame = new WebSocketFrame()
561 {
562 Header = WebsocketFrameHeader.HeaderDefault(),
563 WebSocketPayload = messagedata
564 };
565 closeResponseFrame.Header.Opcode = WebSocketReader.OpCode.Close;
566 closeResponseFrame.Header.PayloadLen = (ulong) messagedata.Length;
567 closeResponseFrame.Header.IsEnd = true;
568 SendSocket(closeResponseFrame.ToBytes());
569 }
570 }
571 CloseDelegate closeD = OnClose;
572 if (closeD != null)
573 {
574 closeD(this, new CloseEventArgs());
575 }
576
577 _closing = true;
578 }
579
580 /// <summary>
581 /// Processes a websocket frame and triggers consumer events
582 /// </summary>
583 /// <param name="psocketState">We need to modify the websocket state here depending on the frame</param>
584 private void ProcessFrame(WebSocketState psocketState)
585 {
586 if (psocketState.Header.IsMasked)
587 {
588 byte[] unmask = psocketState.ReceivedBytes.ToArray();
589 WebSocketReader.Mask(psocketState.Header.Mask, unmask);
590 psocketState.ReceivedBytes = new List<byte>(unmask);
591 }
592
593 switch (psocketState.Header.Opcode)
594 {
595 case WebSocketReader.OpCode.Ping:
596 PingDelegate pingD = OnPing;
597 if (pingD != null)
598 {
599 pingD(this, new PingEventArgs());
600 }
601
602 WebSocketFrame pongFrame = new WebSocketFrame(){Header = WebsocketFrameHeader.HeaderDefault(),WebSocketPayload = new byte[0]};
603 pongFrame.Header.Opcode = WebSocketReader.OpCode.Pong;
604 pongFrame.Header.IsEnd = true;
605 SendSocket(pongFrame.ToBytes());
606 break;
607 case WebSocketReader.OpCode.Pong:
608
609 PongDelegate pongD = OnPong;
610 if (pongD != null)
611 {
612 pongD(this, new PongEventArgs(){PingResponseMS = Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(),_pingtime)});
613 }
614 break;
615 case WebSocketReader.OpCode.Binary:
616 if (!psocketState.Header.IsEnd) // Not done, so we need to store this and wait for the end frame.
617 {
618 psocketState.ContinuationFrame = new WebSocketFrame
619 {
620 Header = psocketState.Header,
621 WebSocketPayload =
622 psocketState.ReceivedBytes.ToArray()
623 };
624 }
625 else
626 {
627 // Send Done Event!
628 DataDelegate dataD = OnData;
629 if (dataD != null)
630 {
631 dataD(this,new WebsocketDataEventArgs(){Data = psocketState.ReceivedBytes.ToArray()});
632 }
633 }
634 break;
635 case WebSocketReader.OpCode.Text:
636 if (!psocketState.Header.IsEnd) // Not done, so we need to store this and wait for the end frame.
637 {
638 psocketState.ContinuationFrame = new WebSocketFrame
639 {
640 Header = psocketState.Header,
641 WebSocketPayload =
642 psocketState.ReceivedBytes.ToArray()
643 };
644 }
645 else
646 {
647 TextDelegate textD = OnText;
648 if (textD != null)
649 {
650 textD(this, new WebsocketTextEventArgs() { Data = Encoding.UTF8.GetString(psocketState.ReceivedBytes.ToArray()) });
651 }
652
653 // Send Done Event!
654 }
655 break;
656 case WebSocketReader.OpCode.Continue: // Continuation. Multiple frames worth of data for one message. Only valid when not using Control Opcodes
657 //Console.WriteLine("currhead " + psocketState.Header.IsEnd);
658 //Console.WriteLine("Continuation! " + psocketState.ContinuationFrame.Header.IsEnd);
659 byte[] combineddata = new byte[psocketState.ReceivedBytes.Count+psocketState.ContinuationFrame.WebSocketPayload.Length];
660 byte[] newdata = psocketState.ReceivedBytes.ToArray();
661 Buffer.BlockCopy(psocketState.ContinuationFrame.WebSocketPayload, 0, combineddata, 0, psocketState.ContinuationFrame.WebSocketPayload.Length);
662 Buffer.BlockCopy(newdata, 0, combineddata,
663 psocketState.ContinuationFrame.WebSocketPayload.Length, newdata.Length);
664 psocketState.ContinuationFrame.WebSocketPayload = combineddata;
665 psocketState.Header.PayloadLen = (ulong)combineddata.Length;
666 if (psocketState.Header.IsEnd)
667 {
668 if (psocketState.ContinuationFrame.Header.Opcode == WebSocketReader.OpCode.Text)
669 {
670 // Send Done event
671 TextDelegate textD = OnText;
672 if (textD != null)
673 {
674 textD(this, new WebsocketTextEventArgs() { Data = Encoding.UTF8.GetString(combineddata) });
675 }
676 }
677 else if (psocketState.ContinuationFrame.Header.Opcode == WebSocketReader.OpCode.Binary)
678 {
679 // Send Done event
680 DataDelegate dataD = OnData;
681 if (dataD != null)
682 {
683 dataD(this, new WebsocketDataEventArgs() { Data = combineddata });
684 }
685 }
686 else
687 {
688 // protocol violation
689 }
690 psocketState.ContinuationFrame = null;
691 }
692 break;
693 case WebSocketReader.OpCode.Close:
694 Close(string.Empty);
695
696 break;
697
698 }
699 psocketState.Header.SetDefault();
700 psocketState.ReceivedBytes.Clear();
701 psocketState.ExpectedBytes = 0;
702 }
703 public void Dispose()
704 {
705 if (_networkContext != null && _networkContext.Stream != null)
706 {
707 if (_networkContext.Stream.CanWrite)
708 _networkContext.Stream.Flush();
709 _networkContext.Stream.Close();
710 _networkContext.Stream.Dispose();
711 _networkContext.Stream = null;
712 }
713
714 if (_request != null && _request.InputStream != null)
715 {
716 _request.InputStream.Close();
717 _request.InputStream.Dispose();
718 _request = null;
719 }
720
721 if (_clientContext != null)
722 {
723 _clientContext.Close();
724 _clientContext = null;
725 }
726 }
727 }
728
729 /// <summary>
730 /// Reads a byte stream and returns Websocket frames.
731 /// </summary>
732 public class WebSocketReader
733 {
734 /// <summary>
735 /// Bit to determine if the frame read on the stream is the last frame in a sequence of fragmented frames
736 /// </summary>
737 private const byte EndBit = 0x80;
738
739 /// <summary>
740 /// These are the Frame Opcodes
741 /// </summary>
742 public enum OpCode
743 {
744 // Data Opcodes
745 Continue = 0x0,
746 Text = 0x1,
747 Binary = 0x2,
748
749 // Control flow Opcodes
750 Close = 0x8,
751 Ping = 0x9,
752 Pong = 0xA
753 }
754
755 /// <summary>
756 /// Masks and Unmasks data using the frame mask. Mask is applied per octal
757 /// Note: Frames from clients MUST be masked
758 /// Note: Frames from servers MUST NOT be masked
759 /// </summary>
760 /// <param name="pMask">Int representing 32 bytes of mask data. Mask is applied per octal</param>
761 /// <param name="pBuffer"></param>
762 public static void Mask(int pMask, byte[] pBuffer)
763 {
764 byte[] maskKey = BitConverter.GetBytes(pMask);
765 int currentMaskIndex = 0;
766 for (int i = 0; i < pBuffer.Length; i++)
767 {
768 pBuffer[i] = (byte)(pBuffer[i] ^ maskKey[currentMaskIndex]);
769 if (currentMaskIndex == 3)
770 {
771 currentMaskIndex = 0;
772 }
773 else
774 {
775 currentMaskIndex++;
776
777 }
778
779 }
780 }
781
782 /// <summary>
783 /// Attempts to read a header off the provided buffer. Returns true, exports a WebSocketFrameheader,
784 /// and an int to move the buffer forward when it reads a header. False when it can't read a header
785 /// </summary>
786 /// <param name="pBuffer">Bytes read from the stream</param>
787 /// <param name="pOffset">Starting place in the stream to begin trying to read from</param>
788 /// <param name="length">Lenth in the stream to try and read from. Provided for cases where the
789 /// buffer's length is larger then the data in it</param>
790 /// <param name="oHeader">Outputs the read WebSocket frame header</param>
791 /// <param name="moveBuffer">Informs the calling stream to move the buffer forward</param>
792 /// <returns>True if it got a header, False if it didn't get a header</returns>
793 public static bool TryReadHeader(byte[] pBuffer, int pOffset, int length, out WebsocketFrameHeader oHeader,
794 out int moveBuffer)
795 {
796 oHeader = WebsocketFrameHeader.ZeroHeader;
797 int minumheadersize = 2;
798 if (length > pBuffer.Length - pOffset)
799 throw new ArgumentOutOfRangeException("The Length specified was larger the byte array supplied");
800 if (length < minumheadersize)
801 {
802 moveBuffer = 0;
803 return false;
804 }
805
806 byte nibble1 = (byte)(pBuffer[pOffset] & 0xF0); //FIN/RSV1/RSV2/RSV3
807 byte nibble2 = (byte)(pBuffer[pOffset] & 0x0F); // Opcode block
808
809 oHeader = new WebsocketFrameHeader();
810 oHeader.SetDefault();
811
812 if ((nibble1 & WebSocketReader.EndBit) == WebSocketReader.EndBit)
813 {
814 oHeader.IsEnd = true;
815 }
816 else
817 {
818 oHeader.IsEnd = false;
819 }
820 //Opcode
821 oHeader.Opcode = (WebSocketReader.OpCode)nibble2;
822 //Mask
823 oHeader.IsMasked = Convert.ToBoolean((pBuffer[pOffset + 1] & 0x80) >> 7);
824
825 // Payload length
826 oHeader.PayloadLen = (byte)(pBuffer[pOffset + 1] & 0x7F);
827
828 int index = 2; // LargerPayload length starts at byte 3
829
830 switch (oHeader.PayloadLen)
831 {
832 case 126:
833 minumheadersize += 2;
834 if (length < minumheadersize)
835 {
836 moveBuffer = 0;
837 return false;
838 }
839 Array.Reverse(pBuffer, pOffset + index, 2); // two bytes
840 oHeader.PayloadLen = BitConverter.ToUInt16(pBuffer, pOffset + index);
841 index += 2;
842 break;
843 case 127: // we got more this is a bigger frame
844 // 8 bytes - uint64 - most significant bit 0 network byte order
845 minumheadersize += 8;
846 if (length < minumheadersize)
847 {
848 moveBuffer = 0;
849 return false;
850 }
851 Array.Reverse(pBuffer, pOffset + index, 8);
852 oHeader.PayloadLen = BitConverter.ToUInt64(pBuffer, pOffset + index);
853 index += 8;
854 break;
855
856 }
857 //oHeader.PayloadLeft = oHeader.PayloadLen; // Start the count in case it's chunked over the network. This is different then frame fragmentation
858 if (oHeader.IsMasked)
859 {
860 minumheadersize += 4;
861 if (length < minumheadersize)
862 {
863 moveBuffer = 0;
864 return false;
865 }
866 oHeader.Mask = BitConverter.ToInt32(pBuffer, pOffset + index);
867 index += 4;
868 }
869 moveBuffer = index;
870 return true;
871
872 }
873 }
874
875 /// <summary>
876 /// RFC6455 Websocket Frame
877 /// </summary>
878 public class WebSocketFrame
879 {
880 /*
881 * RFC6455
882nib 0 1 2 3 4 5 6 7
883byt 0 1 2 3
884dec 0 1 2 3
885 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
886 +-+-+-+-+-------+-+-------------+-------------------------------+
887 |F|R|R|R| opcode|M| Payload len | Extended payload length |
888 |I|S|S|S| (4) |A| (7) | (16/64) +
889 |N|V|V|V| |S| | (if payload len==126/127) |
890 | |1|2|3| |K| | +
891 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
892 | Extended payload length continued, if payload len == 127 |
893 + - - - - - - - - - - - - - - - +-------------------------------+
894 | |Masking-key, if MASK set to 1 |
895 +-------------------------------+-------------------------------+
896 | Masking-key (continued) | Payload Data |
897 +-------------------------------- - - - - - - - - - - - - - - - +
898 : Payload Data continued ... :
899 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
900 | Payload Data continued ... |
901 +---------------------------------------------------------------+
902
903 * When reading these, the frames are possibly fragmented and interleaved with control frames
904 * the fragmented frames are not interleaved with data frames. Just control frames
905 */
906 public static readonly WebSocketFrame DefaultFrame = new WebSocketFrame(){Header = new WebsocketFrameHeader(),WebSocketPayload = new byte[0]};
907 public WebsocketFrameHeader Header;
908 public byte[] WebSocketPayload;
909
910 public byte[] ToBytes()
911 {
912 Header.PayloadLen = (ulong)WebSocketPayload.Length;
913 return Header.ToBytes(WebSocketPayload);
914 }
915
916 }
917
918 public struct WebsocketFrameHeader
919 {
920 //public byte CurrentMaskIndex;
921 /// <summary>
922 /// The last frame in a sequence of fragmented frames or the one and only frame for this message.
923 /// </summary>
924 public bool IsEnd;
925
926 /// <summary>
927 /// Returns whether the payload data is masked or not. Data from Clients MUST be masked, Data from Servers MUST NOT be masked
928 /// </summary>
929 public bool IsMasked;
930
931 /// <summary>
932 /// A set of cryptologically sound random bytes XoR-ed against the payload octally. Looped
933 /// </summary>
934 public int Mask;
935 /*
936byt 0 1 2 3
937 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
938 +---------------+---------------+---------------+---------------+
939 | Octal 1 | Octal 2 | Octal 3 | Octal 4 |
940 +---------------+---------------+---------------+---------------+
941*/
942
943
944 public WebSocketReader.OpCode Opcode;
945
946 public UInt64 PayloadLen;
947 //public UInt64 PayloadLeft;
948 // Payload is X + Y
949 //public UInt64 ExtensionDataLength;
950 //public UInt64 ApplicationDataLength;
951 public static readonly WebsocketFrameHeader ZeroHeader = WebsocketFrameHeader.HeaderDefault();
952
953 public void SetDefault()
954 {
955
956 //CurrentMaskIndex = 0;
957 IsEnd = true;
958 IsMasked = true;
959 Mask = 0;
960 Opcode = WebSocketReader.OpCode.Close;
961 // PayloadLeft = 0;
962 PayloadLen = 0;
963 // ExtensionDataLength = 0;
964 // ApplicationDataLength = 0;
965
966 }
967
968 /// <summary>
969 /// Returns a byte array representing the Frame header
970 /// </summary>
971 /// <param name="payload">This is the frame data payload. The header describes the size of the payload.
972 /// If payload is null, a Zero sized payload is assumed</param>
973 /// <returns>Returns a byte array representing the frame header</returns>
974 public byte[] ToBytes(byte[] payload)
975 {
976 List<byte> result = new List<byte>();
977
978 // Squeeze in our opcode and our ending bit.
979 result.Add((byte)((byte)Opcode | (IsEnd?0x80:0x00) ));
980
981 // Again with the three different byte interpretations of size..
982
983 //bytesize
984 if (PayloadLen <= 125)
985 {
986 result.Add((byte) PayloadLen);
987 } //Uint16
988 else if (PayloadLen <= ushort.MaxValue)
989 {
990 result.Add(126);
991 byte[] payloadLengthByte = BitConverter.GetBytes(Convert.ToUInt16(PayloadLen));
992 Array.Reverse(payloadLengthByte);
993 result.AddRange(payloadLengthByte);
994 } //UInt64
995 else
996 {
997 result.Add(127);
998 byte[] payloadLengthByte = BitConverter.GetBytes(PayloadLen);
999 Array.Reverse(payloadLengthByte);
1000 result.AddRange(payloadLengthByte);
1001 }
1002
1003 // Only add a payload if it's not null
1004 if (payload != null)
1005 {
1006 result.AddRange(payload);
1007 }
1008 return result.ToArray();
1009 }
1010
1011 /// <summary>
1012 /// A Helper method to define the defaults
1013 /// </summary>
1014 /// <returns></returns>
1015
1016 public static WebsocketFrameHeader HeaderDefault()
1017 {
1018 return new WebsocketFrameHeader
1019 {
1020 //CurrentMaskIndex = 0,
1021 IsEnd = false,
1022 IsMasked = true,
1023 Mask = 0,
1024 Opcode = WebSocketReader.OpCode.Close,
1025 //PayloadLeft = 0,
1026 PayloadLen = 0,
1027 // ExtensionDataLength = 0,
1028 // ApplicationDataLength = 0
1029 };
1030 }
1031 }
1032
1033 public delegate void DataDelegate(object sender, WebsocketDataEventArgs data);
1034
1035 public delegate void TextDelegate(object sender, WebsocketTextEventArgs text);
1036
1037 public delegate void PingDelegate(object sender, PingEventArgs pingdata);
1038
1039 public delegate void PongDelegate(object sender, PongEventArgs pongdata);
1040
1041 public delegate void RegularHttpRequestDelegate(object sender, RegularHttpRequestEvnetArgs request);
1042
1043 public delegate void UpgradeCompletedDelegate(object sender, UpgradeCompletedEventArgs completeddata);
1044
1045 public delegate void UpgradeFailedDelegate(object sender, UpgradeFailedEventArgs faileddata);
1046
1047 public delegate void CloseDelegate(object sender, CloseEventArgs closedata);
1048
1049 public delegate bool ValidateHandshake(string pWebOrigin, string pWebSocketKey, string pHost);
1050
1051
1052 public class WebsocketDataEventArgs : EventArgs
1053 {
1054 public byte[] Data;
1055 }
1056
1057 public class WebsocketTextEventArgs : EventArgs
1058 {
1059 public string Data;
1060 }
1061
1062 public class PingEventArgs : EventArgs
1063 {
1064 /// <summary>
1065 /// The ping event can arbitrarily contain data
1066 /// </summary>
1067 public byte[] Data;
1068 }
1069
1070 public class PongEventArgs : EventArgs
1071 {
1072 /// <summary>
1073 /// The pong event can arbitrarily contain data
1074 /// </summary>
1075 public byte[] Data;
1076
1077 public int PingResponseMS;
1078
1079 }
1080
1081 public class RegularHttpRequestEvnetArgs : EventArgs
1082 {
1083
1084 }
1085
1086 public class UpgradeCompletedEventArgs : EventArgs
1087 {
1088
1089 }
1090
1091 public class UpgradeFailedEventArgs : EventArgs
1092 {
1093
1094 }
1095
1096 public class CloseEventArgs : EventArgs
1097 {
1098
1099 }
1100
1101
1102}
diff --git a/OpenSim/Framework/Servers/MainServer.cs b/OpenSim/Framework/Servers/MainServer.cs
index ae7d515..cfd34bb 100644
--- a/OpenSim/Framework/Servers/MainServer.cs
+++ b/OpenSim/Framework/Servers/MainServer.cs
@@ -227,9 +227,16 @@ namespace OpenSim.Framework.Servers
227 handlers.AppendFormat("\t{0}\n", s); 227 handlers.AppendFormat("\t{0}\n", s);
228 228
229 handlers.AppendFormat("* HTTP:\n"); 229 handlers.AppendFormat("* HTTP:\n");
230 List<String> poll = httpServer.GetPollServiceHandlerKeys();
231 foreach (String s in httpServer.GetHTTPHandlerKeys()) 230 foreach (String s in httpServer.GetHTTPHandlerKeys())
232 handlers.AppendFormat("\t{0} {1}\n", s, (poll.Contains(s) ? "(poll service)" : string.Empty)); 231 handlers.AppendFormat("\t{0}\n", s);
232
233 handlers.AppendFormat("* HTTP (poll):\n");
234 foreach (String s in httpServer.GetPollServiceHandlerKeys())
235 handlers.AppendFormat("\t{0}\n", s);
236
237 handlers.AppendFormat("* JSONRPC:\n");
238 foreach (String s in httpServer.GetJsonRpcHandlerKeys())
239 handlers.AppendFormat("\t{0}\n", s);
233 240
234// handlers.AppendFormat("* Agent:\n"); 241// handlers.AppendFormat("* Agent:\n");
235// foreach (String s in httpServer.GetAgentHandlerKeys()) 242// foreach (String s in httpServer.GetAgentHandlerKeys())
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;
29// Build Number 29// Build Number
30// Revision 30// Revision
31// 31//
32[assembly: AssemblyVersion("0.7.5.*")] 32[assembly: AssemblyVersion("0.7.6.*")]
33[assembly: AssemblyFileVersion("1.0.0.0")] 33[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/OpenSim/Framework/Servers/ServerBase.cs b/OpenSim/Framework/Servers/ServerBase.cs
index 9eb2281..65ccd10 100644
--- a/OpenSim/Framework/Servers/ServerBase.cs
+++ b/OpenSim/Framework/Servers/ServerBase.cs
@@ -27,16 +27,19 @@
27 27
28using System; 28using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Diagnostics;
30using System.IO; 31using System.IO;
31using System.Reflection; 32using System.Reflection;
32using System.Text; 33using System.Text;
33using System.Text.RegularExpressions; 34using System.Text.RegularExpressions;
35using System.Threading;
34using log4net; 36using log4net;
35using log4net.Appender; 37using log4net.Appender;
36using log4net.Core; 38using log4net.Core;
37using log4net.Repository; 39using log4net.Repository;
38using Nini.Config; 40using Nini.Config;
39using OpenSim.Framework.Console; 41using OpenSim.Framework.Console;
42using OpenSim.Framework.Monitoring;
40 43
41namespace OpenSim.Framework.Servers 44namespace OpenSim.Framework.Servers
42{ 45{
@@ -168,6 +171,9 @@ namespace OpenSim.Framework.Servers
168 "General", false, "show info", "show info", "Show general information about the server", HandleShow); 171 "General", false, "show info", "show info", "Show general information about the server", HandleShow);
169 172
170 m_console.Commands.AddCommand( 173 m_console.Commands.AddCommand(
174 "General", false, "show version", "show version", "Show server version", HandleShow);
175
176 m_console.Commands.AddCommand(
171 "General", false, "show uptime", "show uptime", "Show server uptime", HandleShow); 177 "General", false, "show uptime", "show uptime", "Show server uptime", HandleShow);
172 178
173 m_console.Commands.AddCommand( 179 m_console.Commands.AddCommand(
@@ -206,6 +212,34 @@ namespace OpenSim.Framework.Servers
206 "General", false, "command-script", 212 "General", false, "command-script",
207 "command-script <script>", 213 "command-script <script>",
208 "Run a command script from file", HandleScript); 214 "Run a command script from file", HandleScript);
215
216 m_console.Commands.AddCommand(
217 "General", false, "show threads",
218 "show threads",
219 "Show thread status", HandleShow);
220
221 m_console.Commands.AddCommand(
222 "General", false, "threads abort",
223 "threads abort <thread-id>",
224 "Abort a managed thread. Use \"show threads\" to find possible threads.", HandleThreadsAbort);
225
226 m_console.Commands.AddCommand(
227 "General", false, "threads show",
228 "threads show",
229 "Show thread status. Synonym for \"show threads\"",
230 (string module, string[] args) => Notice(GetThreadsReport()));
231
232 m_console.Commands.AddCommand(
233 "General", false, "force gc",
234 "force gc",
235 "Manually invoke runtime garbage collection. For debugging purposes",
236 HandleForceGc);
237 }
238
239 private void HandleForceGc(string module, string[] args)
240 {
241 Notice("Manually invoking runtime garbage collection");
242 GC.Collect();
209 } 243 }
210 244
211 public virtual void HandleShow(string module, string[] cmd) 245 public virtual void HandleShow(string module, string[] cmd)
@@ -222,9 +256,17 @@ namespace OpenSim.Framework.Servers
222 ShowInfo(); 256 ShowInfo();
223 break; 257 break;
224 258
259 case "version":
260 Notice(GetVersionText());
261 break;
262
225 case "uptime": 263 case "uptime":
226 Notice(GetUptimeReport()); 264 Notice(GetUptimeReport());
227 break; 265 break;
266
267 case "threads":
268 Notice(GetThreadsReport());
269 break;
228 } 270 }
229 } 271 }
230 272
@@ -537,6 +579,75 @@ namespace OpenSim.Framework.Servers
537 } 579 }
538 580
539 /// <summary> 581 /// <summary>
582 /// Get a report about the registered threads in this server.
583 /// </summary>
584 protected string GetThreadsReport()
585 {
586 // This should be a constant field.
587 string reportFormat = "{0,6} {1,35} {2,16} {3,13} {4,10} {5,30}";
588
589 StringBuilder sb = new StringBuilder();
590 Watchdog.ThreadWatchdogInfo[] threads = Watchdog.GetThreadsInfo();
591
592 sb.Append(threads.Length + " threads are being tracked:" + Environment.NewLine);
593
594 int timeNow = Environment.TickCount & Int32.MaxValue;
595
596 sb.AppendFormat(reportFormat, "ID", "NAME", "LAST UPDATE (MS)", "LIFETIME (MS)", "PRIORITY", "STATE");
597 sb.Append(Environment.NewLine);
598
599 foreach (Watchdog.ThreadWatchdogInfo twi in threads)
600 {
601 Thread t = twi.Thread;
602
603 sb.AppendFormat(
604 reportFormat,
605 t.ManagedThreadId,
606 t.Name,
607 timeNow - twi.LastTick,
608 timeNow - twi.FirstTick,
609 t.Priority,
610 t.ThreadState);
611
612 sb.Append("\n");
613 }
614
615 sb.Append("\n");
616
617 // For some reason mono 2.6.7 returns an empty threads set! Not going to confuse people by reporting
618 // zero active threads.
619 int totalThreads = Process.GetCurrentProcess().Threads.Count;
620 if (totalThreads > 0)
621 sb.AppendFormat("Total threads active: {0}\n\n", totalThreads);
622
623 sb.Append("Main threadpool (excluding script engine pools)\n");
624 sb.Append(Util.GetThreadPoolReport());
625
626 return sb.ToString();
627 }
628
629 public virtual void HandleThreadsAbort(string module, string[] cmd)
630 {
631 if (cmd.Length != 3)
632 {
633 MainConsole.Instance.Output("Usage: threads abort <thread-id>");
634 return;
635 }
636
637 int threadId;
638 if (!int.TryParse(cmd[2], out threadId))
639 {
640 MainConsole.Instance.Output("ERROR: Thread id must be an integer");
641 return;
642 }
643
644 if (Watchdog.AbortThread(threadId))
645 MainConsole.Instance.OutputFormat("Aborted thread with id {0}", threadId);
646 else
647 MainConsole.Instance.OutputFormat("ERROR - Thread with id {0} not found in managed threads", threadId);
648 }
649
650 /// <summary>
540 /// Console output is only possible if a console has been established. 651 /// Console output is only possible if a console has been established.
541 /// That is something that cannot be determined within this class. So 652 /// That is something that cannot be determined within this class. So
542 /// all attempts to use the console MUST be verified. 653 /// all attempts to use the console MUST be verified.
diff --git a/OpenSim/Framework/Servers/Tests/OSHttpTests.cs b/OpenSim/Framework/Servers/Tests/OSHttpTests.cs
index 4c2f586..deae45c 100644
--- a/OpenSim/Framework/Servers/Tests/OSHttpTests.cs
+++ b/OpenSim/Framework/Servers/Tests/OSHttpTests.cs
@@ -35,11 +35,12 @@ using HttpServer;
35using HttpServer.FormDecoders; 35using HttpServer.FormDecoders;
36using NUnit.Framework; 36using NUnit.Framework;
37using OpenSim.Framework.Servers.HttpServer; 37using OpenSim.Framework.Servers.HttpServer;
38using OpenSim.Tests.Common;
38 39
39namespace OpenSim.Framework.Servers.Tests 40namespace OpenSim.Framework.Servers.Tests
40{ 41{
41 [TestFixture] 42 [TestFixture]
42 public class OSHttpTests 43 public class OSHttpTests : OpenSimTestCase
43 { 44 {
44 // we need an IHttpClientContext for our tests 45 // we need an IHttpClientContext for our tests
45 public class TestHttpClientContext: IHttpClientContext 46 public class TestHttpClientContext: IHttpClientContext
@@ -69,6 +70,11 @@ namespace OpenSim.Framework.Servers.Tests
69 public void Close() { } 70 public void Close() { }
70 public bool EndWhenDone { get { return false;} set { return;}} 71 public bool EndWhenDone { get { return false;} set { return;}}
71 72
73 public HTTPNetworkContext GiveMeTheNetworkStreamIKnowWhatImDoing()
74 {
75 return new HTTPNetworkContext();
76 }
77
72 public event EventHandler<DisconnectedEventArgs> Disconnected = delegate { }; 78 public event EventHandler<DisconnectedEventArgs> Disconnected = delegate { };
73 /// <summary> 79 /// <summary>
74 /// A request have been received in the context. 80 /// A request have been received in the context.
diff --git a/OpenSim/Framework/Servers/Tests/VersionInfoTests.cs b/OpenSim/Framework/Servers/Tests/VersionInfoTests.cs
index 49e5061..480f2bb 100644
--- a/OpenSim/Framework/Servers/Tests/VersionInfoTests.cs
+++ b/OpenSim/Framework/Servers/Tests/VersionInfoTests.cs
@@ -29,11 +29,12 @@ using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Text; 30using System.Text;
31using NUnit.Framework; 31using NUnit.Framework;
32using OpenSim.Tests.Common;
32 33
33namespace OpenSim.Framework.Servers.Tests 34namespace OpenSim.Framework.Servers.Tests
34{ 35{
35 [TestFixture] 36 [TestFixture]
36 public class VersionInfoTests 37 public class VersionInfoTests : OpenSimTestCase
37 { 38 {
38 [Test] 39 [Test]
39 public void TestVersionLength() 40 public void TestVersionLength()
diff --git a/OpenSim/Framework/Servers/VersionInfo.cs b/OpenSim/Framework/Servers/VersionInfo.cs
index bb094ed..737c14d 100644
--- a/OpenSim/Framework/Servers/VersionInfo.cs
+++ b/OpenSim/Framework/Servers/VersionInfo.cs
@@ -29,7 +29,7 @@ namespace OpenSim
29{ 29{
30 public class VersionInfo 30 public class VersionInfo
31 { 31 {
32 private const string VERSION_NUMBER = "0.7.5CM"; 32 private const string VERSION_NUMBER = "0.7.6CM";
33 private const Flavour VERSION_FLAVOUR = Flavour.Dev; 33 private const Flavour VERSION_FLAVOUR = Flavour.Dev;
34 34
35 public enum Flavour 35 public enum Flavour
diff --git a/OpenSim/Framework/Tests/AgentCircuitDataTest.cs b/OpenSim/Framework/Tests/AgentCircuitDataTest.cs
index 0dce414..95e9439 100644
--- a/OpenSim/Framework/Tests/AgentCircuitDataTest.cs
+++ b/OpenSim/Framework/Tests/AgentCircuitDataTest.cs
@@ -24,16 +24,17 @@
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27
27using System.Collections.Generic; 28using System.Collections.Generic;
28using OpenMetaverse; 29using OpenMetaverse;
29using OpenMetaverse.StructuredData; 30using OpenMetaverse.StructuredData;
30using NUnit.Framework; 31using NUnit.Framework;
31 32using OpenSim.Tests.Common;
32 33
33namespace OpenSim.Framework.Tests 34namespace OpenSim.Framework.Tests
34{ 35{
35 [TestFixture] 36 [TestFixture]
36 public class AgentCircuitDataTest 37 public class AgentCircuitDataTest : OpenSimTestCase
37 { 38 {
38 private UUID AgentId; 39 private UUID AgentId;
39 private AvatarAppearance AvAppearance; 40 private AvatarAppearance AvAppearance;
diff --git a/OpenSim/Framework/Tests/AnimationTests.cs b/OpenSim/Framework/Tests/AnimationTests.cs
index 967a355..f3be81b 100644
--- a/OpenSim/Framework/Tests/AnimationTests.cs
+++ b/OpenSim/Framework/Tests/AnimationTests.cs
@@ -38,7 +38,7 @@ using Animation = OpenSim.Framework.Animation;
38namespace OpenSim.Framework.Tests 38namespace OpenSim.Framework.Tests
39{ 39{
40 [TestFixture] 40 [TestFixture]
41 public class AnimationTests 41 public class AnimationTests : OpenSimTestCase
42 { 42 {
43 private Animation anim1 = null; 43 private Animation anim1 = null;
44 private Animation anim2 = null; 44 private Animation anim2 = null;
diff --git a/OpenSim/Framework/Tests/AssetBaseTest.cs b/OpenSim/Framework/Tests/AssetBaseTest.cs
index 6db1aa0..25d2393 100644
--- a/OpenSim/Framework/Tests/AssetBaseTest.cs
+++ b/OpenSim/Framework/Tests/AssetBaseTest.cs
@@ -30,11 +30,12 @@ using System.Collections.Generic;
30using System.Text; 30using System.Text;
31using NUnit.Framework; 31using NUnit.Framework;
32using OpenMetaverse; 32using OpenMetaverse;
33using OpenSim.Tests.Common;
33 34
34namespace OpenSim.Framework.Tests 35namespace OpenSim.Framework.Tests
35{ 36{
36 [TestFixture] 37 [TestFixture]
37 public class AssetBaseTest 38 public class AssetBaseTest : OpenSimTestCase
38 { 39 {
39 [Test] 40 [Test]
40 public void TestContainsReferences() 41 public void TestContainsReferences()
diff --git a/OpenSim/Framework/Tests/CacheTests.cs b/OpenSim/Framework/Tests/CacheTests.cs
index c3613e6..c709860 100644
--- a/OpenSim/Framework/Tests/CacheTests.cs
+++ b/OpenSim/Framework/Tests/CacheTests.cs
@@ -28,11 +28,12 @@
28using System; 28using System;
29using NUnit.Framework; 29using NUnit.Framework;
30using OpenMetaverse; 30using OpenMetaverse;
31using OpenSim.Tests.Common;
31 32
32namespace OpenSim.Framework.Tests 33namespace OpenSim.Framework.Tests
33{ 34{
34 [TestFixture] 35 [TestFixture]
35 public class CacheTests 36 public class CacheTests : OpenSimTestCase
36 { 37 {
37 private Cache cache; 38 private Cache cache;
38 private UUID cacheItemUUID; 39 private UUID cacheItemUUID;
diff --git a/OpenSim/Framework/Tests/LocationTest.cs b/OpenSim/Framework/Tests/LocationTest.cs
index 2707afa..a56ecb4 100644
--- a/OpenSim/Framework/Tests/LocationTest.cs
+++ b/OpenSim/Framework/Tests/LocationTest.cs
@@ -26,11 +26,12 @@
26 */ 26 */
27 27
28using NUnit.Framework; 28using NUnit.Framework;
29using OpenSim.Tests.Common;
29 30
30namespace OpenSim.Framework.Tests 31namespace OpenSim.Framework.Tests
31{ 32{
32 [TestFixture] 33 [TestFixture]
33 public class LocationTest 34 public class LocationTest : OpenSimTestCase
34 { 35 {
35 [Test] 36 [Test]
36 public void locationRegionHandleRegionHandle() 37 public void locationRegionHandleRegionHandle()
diff --git a/OpenSim/Framework/Tests/MundaneFrameworkTests.cs b/OpenSim/Framework/Tests/MundaneFrameworkTests.cs
index 6fde488..0fbdaf3 100644
--- a/OpenSim/Framework/Tests/MundaneFrameworkTests.cs
+++ b/OpenSim/Framework/Tests/MundaneFrameworkTests.cs
@@ -32,11 +32,12 @@ using OpenMetaverse.StructuredData;
32using System; 32using System;
33using System.Globalization; 33using System.Globalization;
34using System.Threading; 34using System.Threading;
35using OpenSim.Tests.Common;
35 36
36namespace OpenSim.Framework.Tests 37namespace OpenSim.Framework.Tests
37{ 38{
38 [TestFixture] 39 [TestFixture]
39 public class MundaneFrameworkTests 40 public class MundaneFrameworkTests : OpenSimTestCase
40 { 41 {
41 private bool m_RegionSettingsOnSaveEventFired; 42 private bool m_RegionSettingsOnSaveEventFired;
42 private bool m_RegionLightShareDataOnSaveEventFired; 43 private bool m_RegionLightShareDataOnSaveEventFired;
@@ -302,10 +303,6 @@ namespace OpenSim.Framework.Tests
302 Culture.SetCurrentCulture(); 303 Culture.SetCurrentCulture();
303 Assert.That(Thread.CurrentThread.CurrentCulture.Name == ci.Name, "SetCurrentCulture failed to set thread culture to en-US"); 304 Assert.That(Thread.CurrentThread.CurrentCulture.Name == ci.Name, "SetCurrentCulture failed to set thread culture to en-US");
304 305
305 } 306 }
306
307
308
309 } 307 }
310} 308} \ No newline at end of file
311
diff --git a/OpenSim/Framework/Tests/PrimeNumberHelperTests.cs b/OpenSim/Framework/Tests/PrimeNumberHelperTests.cs
index 36bc6e7..82e13e5 100644
--- a/OpenSim/Framework/Tests/PrimeNumberHelperTests.cs
+++ b/OpenSim/Framework/Tests/PrimeNumberHelperTests.cs
@@ -31,11 +31,12 @@ using NUnit.Framework;
31using OpenMetaverse; 31using OpenMetaverse;
32using OpenMetaverse.StructuredData; 32using OpenMetaverse.StructuredData;
33using OpenSim.Framework; 33using OpenSim.Framework;
34using OpenSim.Tests.Common;
34 35
35namespace OpenSim.Framework.Tests 36namespace OpenSim.Framework.Tests
36{ 37{
37 [TestFixture] 38 [TestFixture]
38 public class PrimeNumberHelperTests 39 public class PrimeNumberHelperTests : OpenSimTestCase
39 { 40 {
40 [Test] 41 [Test]
41 public void TestGetPrime() 42 public void TestGetPrime()
diff --git a/OpenSim/Framework/Tests/UtilTest.cs b/OpenSim/Framework/Tests/UtilTest.cs
index f0d2a3f..11ca068 100644
--- a/OpenSim/Framework/Tests/UtilTest.cs
+++ b/OpenSim/Framework/Tests/UtilTest.cs
@@ -33,7 +33,7 @@ using OpenSim.Tests.Common;
33namespace OpenSim.Framework.Tests 33namespace OpenSim.Framework.Tests
34{ 34{
35 [TestFixture] 35 [TestFixture]
36 public class UtilTests 36 public class UtilTests : OpenSimTestCase
37 { 37 {
38 [Test] 38 [Test]
39 public void VectorOperationTests() 39 public void VectorOperationTests()
diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs
index 48f3f8b..96644ec 100644
--- a/OpenSim/Framework/Util.cs
+++ b/OpenSim/Framework/Util.cs
@@ -45,6 +45,7 @@ using System.Text.RegularExpressions;
45using System.Xml; 45using System.Xml;
46using System.Threading; 46using System.Threading;
47using log4net; 47using log4net;
48using log4net.Appender;
48using Nini.Config; 49using Nini.Config;
49using Nwc.XmlRpc; 50using Nwc.XmlRpc;
50using OpenMetaverse; 51using OpenMetaverse;
@@ -299,6 +300,25 @@ namespace OpenSim.Framework
299 x; 300 x;
300 } 301 }
301 302
303 // Clamp the maximum magnitude of a vector
304 public static Vector3 ClampV(Vector3 x, float max)
305 {
306 Vector3 ret = x;
307 float lenSq = x.LengthSquared();
308 if (lenSq > (max * max))
309 {
310 x = x / x.Length() * max;
311 }
312 return x;
313 }
314
315 // Inclusive, within range test (true if equal to the endpoints)
316 public static bool InRange<T>(T x, T min, T max)
317 where T : IComparable<T>
318 {
319 return x.CompareTo(max) <= 0 && x.CompareTo(min) >= 0;
320 }
321
302 public static uint GetNextXferID() 322 public static uint GetNextXferID()
303 { 323 {
304 uint id = 0; 324 uint id = 0;
@@ -809,9 +829,22 @@ namespace OpenSim.Framework
809 return "."; 829 return ".";
810 } 830 }
811 831
832 public static string logFile()
833 {
834 foreach (IAppender appender in LogManager.GetRepository().GetAppenders())
835 {
836 if (appender is FileAppender)
837 {
838 return ((FileAppender)appender).File;
839 }
840 }
841
842 return "./OpenSim.log";
843 }
844
812 public static string logDir() 845 public static string logDir()
813 { 846 {
814 return "."; 847 return Path.GetDirectoryName(logFile());
815 } 848 }
816 849
817 // From: http://coercedcode.blogspot.com/2008/03/c-generate-unique-filenames-within.html 850 // From: http://coercedcode.blogspot.com/2008/03/c-generate-unique-filenames-within.html
@@ -842,7 +875,7 @@ namespace OpenSim.Framework
842 return FileName; 875 return FileName;
843 } 876 }
844 877
845 // Nini (config) related Methods 878 #region Nini (config) related Methods
846 public static IConfigSource ConvertDataRowToXMLConfig(DataRow row, string fileName) 879 public static IConfigSource ConvertDataRowToXMLConfig(DataRow row, string fileName)
847 { 880 {
848 if (!File.Exists(fileName)) 881 if (!File.Exists(fileName))
@@ -865,6 +898,79 @@ namespace OpenSim.Framework
865 } 898 }
866 } 899 }
867 900
901 public static string GetConfigVarWithDefaultSection(IConfigSource config, string varname, string section)
902 {
903 // First, check the Startup section, the default section
904 IConfig cnf = config.Configs["Startup"];
905 if (cnf == null)
906 return string.Empty;
907 string val = cnf.GetString(varname, string.Empty);
908
909 // Then check for an overwrite of the default in the given section
910 if (!string.IsNullOrEmpty(section))
911 {
912 cnf = config.Configs[section];
913 if (cnf != null)
914 val = cnf.GetString(varname, val);
915 }
916
917 return val;
918 }
919
920 /// <summary>
921 /// Gets the value of a configuration variable by looking into
922 /// multiple sections in order. The latter sections overwrite
923 /// any values previously found.
924 /// </summary>
925 /// <typeparam name="T">Type of the variable</typeparam>
926 /// <param name="config">The configuration object</param>
927 /// <param name="varname">The configuration variable</param>
928 /// <param name="sections">Ordered sequence of sections to look at</param>
929 /// <returns></returns>
930 public static T GetConfigVarFromSections<T>(IConfigSource config, string varname, string[] sections)
931 {
932 return GetConfigVarFromSections<T>(config, varname, sections, default(T));
933 }
934
935 /// <summary>
936 /// Gets the value of a configuration variable by looking into
937 /// multiple sections in order. The latter sections overwrite
938 /// any values previously found.
939 /// </summary>
940 /// <remarks>
941 /// If no value is found then the given default value is returned
942 /// </remarks>
943 /// <typeparam name="T">Type of the variable</typeparam>
944 /// <param name="config">The configuration object</param>
945 /// <param name="varname">The configuration variable</param>
946 /// <param name="sections">Ordered sequence of sections to look at</param>
947 /// <param name="val">Default value</param>
948 /// <returns></returns>
949 public static T GetConfigVarFromSections<T>(IConfigSource config, string varname, string[] sections, object val)
950 {
951 foreach (string section in sections)
952 {
953 IConfig cnf = config.Configs[section];
954 if (cnf == null)
955 continue;
956
957 if (typeof(T) == typeof(String))
958 val = cnf.GetString(varname, (string)val);
959 else if (typeof(T) == typeof(Boolean))
960 val = cnf.GetBoolean(varname, (bool)val);
961 else if (typeof(T) == typeof(Int32))
962 val = cnf.GetInt(varname, (int)val);
963 else if (typeof(T) == typeof(float))
964 val = cnf.GetFloat(varname, (int)val);
965 else
966 m_log.ErrorFormat("[UTIL]: Unhandled type {0}", typeof(T));
967 }
968
969 return (T)val;
970 }
971
972 #endregion
973
868 public static float Clip(float x, float min, float max) 974 public static float Clip(float x, float min, float max)
869 { 975 {
870 return Math.Min(Math.Max(x, min), max); 976 return Math.Min(Math.Max(x, min), max);
@@ -1651,7 +1757,13 @@ namespace OpenSim.Framework
1651 if (m_ThreadPool != null) 1757 if (m_ThreadPool != null)
1652 throw new InvalidOperationException("SmartThreadPool is already initialized"); 1758 throw new InvalidOperationException("SmartThreadPool is already initialized");
1653 1759
1654 m_ThreadPool = new SmartThreadPool(2000, maxThreads, 2); 1760 STPStartInfo startInfo = new STPStartInfo();
1761 startInfo.ThreadPoolName = "Util";
1762 startInfo.IdleTimeout = 2000;
1763 startInfo.MaxWorkerThreads = maxThreads;
1764 startInfo.MinWorkerThreads = 2;
1765
1766 m_ThreadPool = new SmartThreadPool(startInfo);
1655 } 1767 }
1656 1768
1657 public static int FireAndForgetCount() 1769 public static int FireAndForgetCount()
@@ -1724,7 +1836,7 @@ namespace OpenSim.Framework
1724 break; 1836 break;
1725 case FireAndForgetMethod.SmartThreadPool: 1837 case FireAndForgetMethod.SmartThreadPool:
1726 if (m_ThreadPool == null) 1838 if (m_ThreadPool == null)
1727 m_ThreadPool = new SmartThreadPool(2000, 15, 2); 1839 InitThreadPool(15);
1728 m_ThreadPool.QueueWorkItem(SmartThreadPoolCallback, new object[] { realCallback, obj }); 1840 m_ThreadPool.QueueWorkItem(SmartThreadPoolCallback, new object[] { realCallback, obj });
1729 break; 1841 break;
1730 case FireAndForgetMethod.Thread: 1842 case FireAndForgetMethod.Thread:
@@ -1753,12 +1865,16 @@ namespace OpenSim.Framework
1753 StringBuilder sb = new StringBuilder(); 1865 StringBuilder sb = new StringBuilder();
1754 if (FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool) 1866 if (FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool)
1755 { 1867 {
1756 threadPoolUsed = "SmartThreadPool"; 1868 // ROBUST currently leaves this the FireAndForgetMethod but never actually initializes the threadpool.
1757 maxThreads = m_ThreadPool.MaxThreads; 1869 if (m_ThreadPool != null)
1758 minThreads = m_ThreadPool.MinThreads; 1870 {
1759 inUseThreads = m_ThreadPool.InUseThreads; 1871 threadPoolUsed = "SmartThreadPool";
1760 allocatedThreads = m_ThreadPool.ActiveThreads; 1872 maxThreads = m_ThreadPool.MaxThreads;
1761 waitingCallbacks = m_ThreadPool.WaitingCallbacks; 1873 minThreads = m_ThreadPool.MinThreads;
1874 inUseThreads = m_ThreadPool.InUseThreads;
1875 allocatedThreads = m_ThreadPool.ActiveThreads;
1876 waitingCallbacks = m_ThreadPool.WaitingCallbacks;
1877 }
1762 } 1878 }
1763 else if ( 1879 else if (
1764 FireAndForgetMethod == FireAndForgetMethod.UnsafeQueueUserWorkItem 1880 FireAndForgetMethod == FireAndForgetMethod.UnsafeQueueUserWorkItem
@@ -1863,6 +1979,12 @@ namespace OpenSim.Framework
1863 /// </summary> 1979 /// </summary>
1864 public static void PrintCallStack() 1980 public static void PrintCallStack()
1865 { 1981 {
1982 PrintCallStack(m_log.DebugFormat);
1983 }
1984
1985 public delegate void DebugPrinter(string msg, params Object[] parm);
1986 public static void PrintCallStack(DebugPrinter printer)
1987 {
1866 StackTrace stackTrace = new StackTrace(true); // get call stack 1988 StackTrace stackTrace = new StackTrace(true); // get call stack
1867 StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames) 1989 StackFrame[] stackFrames = stackTrace.GetFrames(); // get method calls (frames)
1868 1990
@@ -1870,7 +1992,7 @@ namespace OpenSim.Framework
1870 foreach (StackFrame stackFrame in stackFrames) 1992 foreach (StackFrame stackFrame in stackFrames)
1871 { 1993 {
1872 MethodBase mb = stackFrame.GetMethod(); 1994 MethodBase mb = stackFrame.GetMethod();
1873 m_log.DebugFormat("{0}.{1}:{2}", mb.DeclaringType, mb.Name, stackFrame.GetFileLineNumber()); // write method name 1995 printer("{0}.{1}:{2}", mb.DeclaringType, mb.Name, stackFrame.GetFileLineNumber()); // write method name
1874 } 1996 }
1875 } 1997 }
1876 1998
@@ -2096,6 +2218,17 @@ namespace OpenSim.Framework
2096 return firstName + "." + lastName + " " + "@" + uri.Authority; 2218 return firstName + "." + lastName + " " + "@" + uri.Authority;
2097 } 2219 }
2098 #endregion 2220 #endregion
2221
2222 /// <summary>
2223 /// Escapes the special characters used in "LIKE".
2224 /// </summary>
2225 /// <remarks>
2226 /// For example: EscapeForLike("foo_bar%baz") = "foo\_bar\%baz"
2227 /// </remarks>
2228 public static string EscapeForLike(string str)
2229 {
2230 return str.Replace("_", "\\_").Replace("%", "\\%");
2231 }
2099 } 2232 }
2100 2233
2101 public class DoubleQueue<T> where T:class 2234 public class DoubleQueue<T> where T:class
diff --git a/OpenSim/Framework/WebUtil.cs b/OpenSim/Framework/WebUtil.cs
index b85d93d..bf57fd4 100644
--- a/OpenSim/Framework/WebUtil.cs
+++ b/OpenSim/Framework/WebUtil.cs
@@ -228,8 +228,8 @@ namespace OpenSim.Framework
228 errorMessage = we.Message; 228 errorMessage = we.Message;
229 if (we.Status == WebExceptionStatus.ProtocolError) 229 if (we.Status == WebExceptionStatus.ProtocolError)
230 { 230 {
231 HttpWebResponse webResponse = (HttpWebResponse)we.Response; 231 using (HttpWebResponse webResponse = (HttpWebResponse)we.Response)
232 errorMessage = String.Format("[{0}] {1}",webResponse.StatusCode,webResponse.StatusDescription); 232 errorMessage = String.Format("[{0}] {1}", webResponse.StatusCode, webResponse.StatusDescription);
233 } 233 }
234 } 234 }
235 catch (Exception ex) 235 catch (Exception ex)
@@ -388,8 +388,8 @@ namespace OpenSim.Framework
388 errorMessage = we.Message; 388 errorMessage = we.Message;
389 if (we.Status == WebExceptionStatus.ProtocolError) 389 if (we.Status == WebExceptionStatus.ProtocolError)
390 { 390 {
391 HttpWebResponse webResponse = (HttpWebResponse)we.Response; 391 using (HttpWebResponse webResponse = (HttpWebResponse)we.Response)
392 errorMessage = String.Format("[{0}] {1}",webResponse.StatusCode,webResponse.StatusDescription); 392 errorMessage = String.Format("[{0}] {1}",webResponse.StatusCode,webResponse.StatusDescription);
393 } 393 }
394 } 394 }
395 catch (Exception ex) 395 catch (Exception ex)
@@ -837,15 +837,16 @@ namespace OpenSim.Framework
837 { 837 {
838 if (e.Response is HttpWebResponse) 838 if (e.Response is HttpWebResponse)
839 { 839 {
840 HttpWebResponse httpResponse = (HttpWebResponse)e.Response; 840 using (HttpWebResponse httpResponse = (HttpWebResponse)e.Response)
841 841 {
842 if (httpResponse.StatusCode != HttpStatusCode.NotFound) 842 if (httpResponse.StatusCode != HttpStatusCode.NotFound)
843 { 843 {
844 // We don't appear to be handling any other status codes, so log these feailures to that 844 // We don't appear to be handling any other status codes, so log these feailures to that
845 // people don't spend unnecessary hours hunting phantom bugs. 845 // people don't spend unnecessary hours hunting phantom bugs.
846 m_log.DebugFormat( 846 m_log.DebugFormat(
847 "[ASYNC REQUEST]: Request {0} {1} failed with unexpected status code {2}", 847 "[ASYNC REQUEST]: Request {0} {1} failed with unexpected status code {2}",
848 verb, requestUrl, httpResponse.StatusCode); 848 verb, requestUrl, httpResponse.StatusCode);
849 }
849 } 850 }
850 } 851 }
851 } 852 }
@@ -995,11 +996,9 @@ namespace OpenSim.Framework
995 Stream respStream = null; 996 Stream respStream = null;
996 try 997 try
997 { 998 {
998 respStream = resp.GetResponseStream(); 999 using (respStream = resp.GetResponseStream())
999 using (StreamReader reader = new StreamReader(respStream)) 1000 using (StreamReader reader = new StreamReader(respStream))
1000 { 1001 respstring = reader.ReadToEnd();
1001 respstring = reader.ReadToEnd();
1002 }
1003 } 1002 }
1004 catch (Exception e) 1003 catch (Exception e)
1005 { 1004 {
@@ -1142,10 +1141,11 @@ namespace OpenSim.Framework
1142 { 1141 {
1143 if (resp.ContentLength != 0) 1142 if (resp.ContentLength != 0)
1144 { 1143 {
1145 Stream respStream = resp.GetResponseStream(); 1144 using (Stream respStream = resp.GetResponseStream())
1146 XmlSerializer deserializer = new XmlSerializer(typeof(TResponse)); 1145 {
1147 deserial = (TResponse)deserializer.Deserialize(respStream); 1146 XmlSerializer deserializer = new XmlSerializer(typeof(TResponse));
1148 respStream.Close(); 1147 deserial = (TResponse)deserializer.Deserialize(respStream);
1148 }
1149 } 1149 }
1150 else 1150 else
1151 { 1151 {
@@ -1157,14 +1157,15 @@ namespace OpenSim.Framework
1157 } 1157 }
1158 catch (WebException e) 1158 catch (WebException e)
1159 { 1159 {
1160 HttpWebResponse hwr = (HttpWebResponse)e.Response; 1160 using (HttpWebResponse hwr = (HttpWebResponse)e.Response)
1161 1161 {
1162 if (hwr != null && hwr.StatusCode == HttpStatusCode.NotFound) 1162 if (hwr != null && hwr.StatusCode == HttpStatusCode.NotFound)
1163 return deserial; 1163 return deserial;
1164 else 1164 else
1165 m_log.ErrorFormat( 1165 m_log.ErrorFormat(
1166 "[SynchronousRestObjectRequester]: WebException for {0} {1} {2}: {3} {4}", 1166 "[SynchronousRestObjectRequester]: WebException for {0} {1} {2}: {3} {4}",
1167 verb, requestUrl, typeof(TResponse).ToString(), e.Message, e.StackTrace); 1167 verb, requestUrl, typeof(TResponse).ToString(), e.Message, e.StackTrace);
1168 }
1168 } 1169 }
1169 catch (System.InvalidOperationException) 1170 catch (System.InvalidOperationException)
1170 { 1171 {