aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/Servers
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Framework/Servers/BaseOpenSimServer.cs178
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BaseHTTPHandler.cs41
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs2147
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BaseOutputStreamHandler.cs60
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BaseRequestHandler.cs117
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs85
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs107
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BinaryStreamHandler.cs76
-rw-r--r--OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs119
-rw-r--r--OpenSim/Framework/Servers/HttpServer/GenericHTTPMethod.cs33
-rw-r--r--OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpAgentHandler.cs35
-rw-r--r--OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs150
-rw-r--r--OpenSim/Framework/Servers/HttpServer/Interfaces/IOSHttpRequest.cs59
-rw-r--r--OpenSim/Framework/Servers/HttpServer/Interfaces/IOSHttpResponse.cs132
-rw-r--r--OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs91
-rw-r--r--OpenSim/Framework/Servers/HttpServer/JsonRPCMethod.cs34
-rw-r--r--OpenSim/Framework/Servers/HttpServer/JsonRpcRequestManager.cs190
-rw-r--r--OpenSim/Framework/Servers/HttpServer/JsonRpcResponse.cs150
-rw-r--r--OpenSim/Framework/Servers/HttpServer/LLSDMethod.cs35
-rw-r--r--OpenSim/Framework/Servers/HttpServer/LLSDMethodString.cs33
-rw-r--r--OpenSim/Framework/Servers/HttpServer/OSHttpHandler.cs183
-rw-r--r--OpenSim/Framework/Servers/HttpServer/OSHttpHttpHandler.cs145
-rw-r--r--OpenSim/Framework/Servers/HttpServer/OSHttpRequest.cs271
-rw-r--r--OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs298
-rw-r--r--OpenSim/Framework/Servers/HttpServer/OSHttpRequestQueue.cs68
-rw-r--r--OpenSim/Framework/Servers/HttpServer/OSHttpResponse.cs332
-rw-r--r--OpenSim/Framework/Servers/HttpServer/OSHttpServer.cs210
-rw-r--r--OpenSim/Framework/Servers/HttpServer/OSHttpStatusCodes.cs279
-rw-r--r--OpenSim/Framework/Servers/HttpServer/OSHttpXmlRpcHandler.cs180
-rw-r--r--OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs86
-rw-r--r--OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs97
-rw-r--r--OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs331
-rw-r--r--OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs33
-rw-r--r--OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs70
-rw-r--r--OpenSim/Framework/Servers/HttpServer/RestHTTPHandler.cs62
-rw-r--r--OpenSim/Framework/Servers/HttpServer/RestMethod.cs32
-rw-r--r--OpenSim/Framework/Servers/HttpServer/RestObjectPoster.cs86
-rw-r--r--OpenSim/Framework/Servers/HttpServer/RestObjectPosterResponse.cs108
-rw-r--r--OpenSim/Framework/Servers/HttpServer/RestSessionService.cs295
-rw-r--r--OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs65
-rw-r--r--OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs1159
-rw-r--r--OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs91
-rw-r--r--OpenSim/Framework/Servers/HttpServer/XmlRpcMethod.cs34
-rw-r--r--OpenSim/Framework/Servers/MainServer.cs357
-rw-r--r--OpenSim/Framework/Servers/Properties/AssemblyInfo.cs33
-rw-r--r--OpenSim/Framework/Servers/ServerBase.cs1047
-rw-r--r--OpenSim/Framework/Servers/Tests/OSHttpTests.cs116
-rw-r--r--OpenSim/Framework/Servers/Tests/VersionInfoTests.cs54
48 files changed, 9994 insertions, 0 deletions
diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs
new file mode 100644
index 0000000..828a852
--- /dev/null
+++ b/OpenSim/Framework/Servers/BaseOpenSimServer.cs
@@ -0,0 +1,178 @@
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.Reflection;
32using System.Text;
33using System.Text.RegularExpressions;
34using System.Threading;
35using System.Timers;
36using log4net;
37using log4net.Appender;
38using log4net.Core;
39using log4net.Repository;
40using OpenMetaverse;
41using OpenMetaverse.StructuredData;
42using OpenSim.Framework;
43using OpenSim.Framework.Console;
44using OpenSim.Framework.Monitoring;
45using OpenSim.Framework.Servers;
46using OpenSim.Framework.Servers.HttpServer;
47using Timer=System.Timers.Timer;
48using Nini.Config;
49
50namespace OpenSim.Framework.Servers
51{
52 /// <summary>
53 /// Common base for the main OpenSimServers (user, grid, inventory, region, etc)
54 /// </summary>
55 public abstract class BaseOpenSimServer : ServerBase
56 {
57 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
58
59 /// <summary>
60 /// Used by tests to suppress Environment.Exit(0) so that post-run operations are possible.
61 /// </summary>
62 public bool SuppressExit { get; set; }
63
64 /// <summary>
65 /// This will control a periodic log printout of the current 'show stats' (if they are active) for this
66 /// server.
67 /// </summary>
68 private int m_periodDiagnosticTimerMS = 60 * 60 * 1000;
69 private Timer m_periodicDiagnosticsTimer = new Timer(60 * 60 * 1000);
70
71 /// <summary>
72 /// Random uuid for private data
73 /// </summary>
74 protected string m_osSecret = String.Empty;
75
76 protected BaseHttpServer m_httpServer;
77 public BaseHttpServer HttpServer
78 {
79 get { return m_httpServer; }
80 }
81
82 public BaseOpenSimServer() : base()
83 {
84 // Random uuid for private data
85 m_osSecret = UUID.Random().ToString();
86
87 }
88
89 /// <summary>
90 /// Must be overriden by child classes for their own server specific startup behaviour.
91 /// </summary>
92 protected virtual void StartupSpecific()
93 {
94 StatsManager.SimExtraStats = new SimExtraStatsCollector();
95 RegisterCommonCommands();
96 RegisterCommonComponents(Config);
97
98 IConfig startupConfig = Config.Configs["Startup"];
99 int logShowStatsSeconds = startupConfig.GetInt("LogShowStatsSeconds", m_periodDiagnosticTimerMS / 1000);
100 m_periodDiagnosticTimerMS = logShowStatsSeconds * 1000;
101 m_periodicDiagnosticsTimer.Elapsed += new ElapsedEventHandler(LogDiagnostics);
102 if (m_periodDiagnosticTimerMS != 0)
103 {
104 m_periodicDiagnosticsTimer.Interval = m_periodDiagnosticTimerMS;
105 m_periodicDiagnosticsTimer.Enabled = true;
106 }
107 }
108
109 protected override void ShutdownSpecific()
110 {
111 m_log.Info("[SHUTDOWN]: Shutdown processing on main thread complete. Exiting...");
112
113 RemovePIDFile();
114
115 base.ShutdownSpecific();
116
117 if (!SuppressExit)
118 Environment.Exit(0);
119 }
120
121 /// <summary>
122 /// Provides a list of help topics that are available. Overriding classes should append their topics to the
123 /// information returned when the base method is called.
124 /// </summary>
125 ///
126 /// <returns>
127 /// A list of strings that represent different help topics on which more information is available
128 /// </returns>
129 protected virtual List<string> GetHelpTopics() { return new List<string>(); }
130
131 /// <summary>
132 /// Print statistics to the logfile, if they are active
133 /// </summary>
134 protected void LogDiagnostics(object source, ElapsedEventArgs e)
135 {
136 StringBuilder sb = new StringBuilder("DIAGNOSTICS\n\n");
137 sb.Append(GetUptimeReport());
138 sb.Append(StatsManager.SimExtraStats.Report());
139 sb.Append(Environment.NewLine);
140 sb.Append(GetThreadsReport());
141
142 m_log.Debug(sb);
143 }
144
145 /// <summary>
146 /// Performs initialisation of the scene, such as loading configuration from disk.
147 /// </summary>
148 public virtual void Startup()
149 {
150 StartupSpecific();
151
152 TimeSpan timeTaken = DateTime.Now - m_startuptime;
153
154 MainConsole.Instance.OutputFormat(
155 "PLEASE WAIT FOR LOGINS TO BE ENABLED ON REGIONS ONCE SCRIPTS HAVE STARTED. Non-script portion of startup took {0}m {1}s.",
156 timeTaken.Minutes, timeTaken.Seconds);
157 }
158
159 public string osSecret
160 {
161 // Secret uuid for the simulator
162 get { return m_osSecret; }
163 }
164
165 public string StatReport(IOSHttpRequest httpRequest)
166 {
167 // If we catch a request for "callback", wrap the response in the value for jsonp
168 if (httpRequest.Query.ContainsKey("callback"))
169 {
170 return httpRequest.Query["callback"].ToString() + "(" + StatsManager.SimExtraStats.XReport((DateTime.Now - m_startuptime).ToString() , m_version) + ");";
171 }
172 else
173 {
174 return StatsManager.SimExtraStats.XReport((DateTime.Now - m_startuptime).ToString() , m_version);
175 }
176 }
177 }
178} \ No newline at end of file
diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHTTPHandler.cs b/OpenSim/Framework/Servers/HttpServer/BaseHTTPHandler.cs
new file mode 100644
index 0000000..9f8f4a8
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/BaseHTTPHandler.cs
@@ -0,0 +1,41 @@
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.Collections;
29
30namespace OpenSim.Framework.Servers.HttpServer
31{
32 public abstract class BaseHTTPHandler : BaseRequestHandler, IGenericHTTPHandler
33 {
34 public abstract Hashtable Handle(string path, Hashtable Request);
35
36 protected BaseHTTPHandler(string httpMethod, string path) : this(httpMethod, path, null, null) {}
37
38 protected BaseHTTPHandler(string httpMethod, string path, string name, string description)
39 : base(httpMethod, path, name, description) {}
40 }
41} \ No newline at end of file
diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
new file mode 100644
index 0000000..f252bd5
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
@@ -0,0 +1,2147 @@
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.Collections.Specialized;
32using System.IO;
33using System.Net;
34using System.Net.Sockets;
35using System.Security.Cryptography.X509Certificates;
36using System.Reflection;
37using System.Globalization;
38using System.Text;
39using System.Threading;
40using System.Xml;
41using HttpServer;
42using log4net;
43using Nwc.XmlRpc;
44using OpenMetaverse.StructuredData;
45using CoolHTTPListener = HttpServer.HttpListener;
46using HttpListener=System.Net.HttpListener;
47using LogPrio=HttpServer.LogPrio;
48using OpenSim.Framework.Monitoring;
49using System.IO.Compression;
50
51namespace OpenSim.Framework.Servers.HttpServer
52{
53 public class BaseHttpServer : IHttpServer
54 {
55 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
56 private HttpServerLogWriter httpserverlog = new HttpServerLogWriter();
57 private static Encoding UTF8NoBOM = new System.Text.UTF8Encoding(false);
58
59 /// <summary>
60 /// This is a pending websocket request before it got an sucessful upgrade response.
61 /// The consumer must call handler.HandshakeAndUpgrade() to signal to the handler to
62 /// start the connection and optionally provide an origin authentication method.
63 /// </summary>
64 /// <param name="servicepath"></param>
65 /// <param name="handler"></param>
66 public delegate void WebSocketRequestDelegate(string servicepath, WebSocketHttpServerHandler handler);
67
68 /// <summary>
69 /// Gets or sets the debug level.
70 /// </summary>
71 /// <value>
72 /// See MainServer.DebugLevel.
73 /// </value>
74 public int DebugLevel { get; set; }
75
76 /// <summary>
77 /// Request number for diagnostic purposes.
78 /// </summary>
79 /// <remarks>
80 /// This is an internal number. In some debug situations an external number may also be supplied in the
81 /// opensim-request-id header but we are not currently logging this.
82 /// </remarks>
83 public int RequestNumber { get; private set; }
84
85 /// <summary>
86 /// Statistic for holding number of requests processed.
87 /// </summary>
88 private Stat m_requestsProcessedStat;
89
90 private volatile int NotSocketErrors = 0;
91 public volatile bool HTTPDRunning = false;
92
93 // protected HttpListener m_httpListener;
94 protected CoolHTTPListener m_httpListener2;
95 protected Dictionary<string, XmlRpcMethod> m_rpcHandlers = new Dictionary<string, XmlRpcMethod>();
96 protected Dictionary<string, JsonRPCMethod> jsonRpcHandlers = new Dictionary<string, JsonRPCMethod>();
97 protected Dictionary<string, bool> m_rpcHandlersKeepAlive = new Dictionary<string, bool>();
98 protected DefaultLLSDMethod m_defaultLlsdHandler = null; // <-- Moving away from the monolithic.. and going to /registered/
99 protected Dictionary<string, LLSDMethod> m_llsdHandlers = new Dictionary<string, LLSDMethod>();
100 protected Dictionary<string, IRequestHandler> m_streamHandlers = new Dictionary<string, IRequestHandler>();
101 protected Dictionary<string, GenericHTTPMethod> m_HTTPHandlers = new Dictionary<string, GenericHTTPMethod>();
102// protected Dictionary<string, IHttpAgentHandler> m_agentHandlers = new Dictionary<string, IHttpAgentHandler>();
103 protected Dictionary<string, PollServiceEventArgs> m_pollHandlers =
104 new Dictionary<string, PollServiceEventArgs>();
105
106 protected Dictionary<string, WebSocketRequestDelegate> m_WebSocketHandlers =
107 new Dictionary<string, WebSocketRequestDelegate>();
108
109 protected uint m_port;
110 protected uint m_sslport;
111 protected bool m_ssl;
112 private X509Certificate2 m_cert;
113 protected bool m_firstcaps = true;
114 protected string m_SSLCommonName = "";
115
116 protected IPAddress m_listenIPAddress = IPAddress.Any;
117
118 public PollServiceRequestManager PollServiceRequestManager { get; private set; }
119
120 public uint SSLPort
121 {
122 get { return m_sslport; }
123 }
124
125 public string SSLCommonName
126 {
127 get { return m_SSLCommonName; }
128 }
129
130 public uint Port
131 {
132 get { return m_port; }
133 }
134
135 public bool UseSSL
136 {
137 get { return m_ssl; }
138 }
139
140 public IPAddress ListenIPAddress
141 {
142 get { return m_listenIPAddress; }
143 set { m_listenIPAddress = value; }
144 }
145
146 public BaseHttpServer(uint port)
147 {
148 m_port = port;
149 }
150
151 public BaseHttpServer(uint port, bool ssl) : this (port)
152 {
153 m_ssl = ssl;
154 }
155
156 public BaseHttpServer(uint port, bool ssl, uint sslport, string CN) : this (port, ssl)
157 {
158 if (m_ssl)
159 {
160 m_sslport = sslport;
161 }
162 }
163
164 public BaseHttpServer(uint port, bool ssl, string CPath, string CPass) : this (port, ssl)
165 {
166 if (m_ssl)
167 {
168 m_cert = new X509Certificate2(CPath, CPass);
169 }
170 }
171
172 /// <summary>
173 /// Add a stream handler to the http server. If the handler already exists, then nothing happens.
174 /// </summary>
175 /// <param name="handler"></param>
176 public void AddStreamHandler(IRequestHandler handler)
177 {
178 string httpMethod = handler.HttpMethod;
179 string path = handler.Path;
180 string handlerKey = GetHandlerKey(httpMethod, path);
181
182 lock (m_streamHandlers)
183 {
184 if (!m_streamHandlers.ContainsKey(handlerKey))
185 {
186 // m_log.DebugFormat("[BASE HTTP SERVER]: Adding handler key {0}", handlerKey);
187 m_streamHandlers.Add(handlerKey, handler);
188 }
189 }
190 }
191
192 public void AddWebSocketHandler(string servicepath, WebSocketRequestDelegate handler)
193 {
194 lock (m_WebSocketHandlers)
195 {
196 if (!m_WebSocketHandlers.ContainsKey(servicepath))
197 m_WebSocketHandlers.Add(servicepath, handler);
198 }
199 }
200
201 public void RemoveWebSocketHandler(string servicepath)
202 {
203 lock (m_WebSocketHandlers)
204 if (m_WebSocketHandlers.ContainsKey(servicepath))
205 m_WebSocketHandlers.Remove(servicepath);
206 }
207
208 public List<string> GetStreamHandlerKeys()
209 {
210 lock (m_streamHandlers)
211 return new List<string>(m_streamHandlers.Keys);
212 }
213
214 private static string GetHandlerKey(string httpMethod, string path)
215 {
216 return httpMethod + ":" + path;
217 }
218
219 public bool AddXmlRPCHandler(string method, XmlRpcMethod handler)
220 {
221 return AddXmlRPCHandler(method, handler, true);
222 }
223
224 public bool AddXmlRPCHandler(string method, XmlRpcMethod handler, bool keepAlive)
225 {
226 lock (m_rpcHandlers)
227 {
228 m_rpcHandlers[method] = handler;
229 m_rpcHandlersKeepAlive[method] = keepAlive; // default
230 }
231
232 return true;
233 }
234
235 public XmlRpcMethod GetXmlRPCHandler(string method)
236 {
237 lock (m_rpcHandlers)
238 {
239 if (m_rpcHandlers.ContainsKey(method))
240 {
241 return m_rpcHandlers[method];
242 }
243 else
244 {
245 return null;
246 }
247 }
248 }
249
250 public List<string> GetXmlRpcHandlerKeys()
251 {
252 lock (m_rpcHandlers)
253 return new List<string>(m_rpcHandlers.Keys);
254 }
255
256 // JsonRPC
257 public bool AddJsonRPCHandler(string method, JsonRPCMethod handler)
258 {
259 lock(jsonRpcHandlers)
260 {
261 jsonRpcHandlers.Add(method, handler);
262 }
263 return true;
264 }
265
266 public JsonRPCMethod GetJsonRPCHandler(string method)
267 {
268 lock (jsonRpcHandlers)
269 {
270 if (jsonRpcHandlers.ContainsKey(method))
271 {
272 return jsonRpcHandlers[method];
273 }
274 else
275 {
276 return null;
277 }
278 }
279 }
280
281 public List<string> GetJsonRpcHandlerKeys()
282 {
283 lock (jsonRpcHandlers)
284 return new List<string>(jsonRpcHandlers.Keys);
285 }
286
287 public bool AddHTTPHandler(string methodName, GenericHTTPMethod handler)
288 {
289 //m_log.DebugFormat("[BASE HTTP SERVER]: Registering {0}", methodName);
290
291 lock (m_HTTPHandlers)
292 {
293 if (!m_HTTPHandlers.ContainsKey(methodName))
294 {
295 m_HTTPHandlers.Add(methodName, handler);
296 return true;
297 }
298 }
299
300 //must already have a handler for that path so return false
301 return false;
302 }
303
304 public List<string> GetHTTPHandlerKeys()
305 {
306 lock (m_HTTPHandlers)
307 return new List<string>(m_HTTPHandlers.Keys);
308 }
309
310 public bool AddPollServiceHTTPHandler(string methodName, PollServiceEventArgs args)
311 {
312 lock (m_pollHandlers)
313 {
314 if (!m_pollHandlers.ContainsKey(methodName))
315 {
316 m_pollHandlers.Add(methodName, args);
317 return true;
318 }
319 }
320
321 return false;
322 }
323
324 public List<string> GetPollServiceHandlerKeys()
325 {
326 lock (m_pollHandlers)
327 return new List<string>(m_pollHandlers.Keys);
328 }
329
330// // Note that the agent string is provided simply to differentiate
331// // the handlers - it is NOT required to be an actual agent header
332// // value.
333// public bool AddAgentHandler(string agent, IHttpAgentHandler handler)
334// {
335// lock (m_agentHandlers)
336// {
337// if (!m_agentHandlers.ContainsKey(agent))
338// {
339// m_agentHandlers.Add(agent, handler);
340// return true;
341// }
342// }
343//
344// //must already have a handler for that path so return false
345// return false;
346// }
347//
348// public List<string> GetAgentHandlerKeys()
349// {
350// lock (m_agentHandlers)
351// return new List<string>(m_agentHandlers.Keys);
352// }
353
354 public bool AddLLSDHandler(string path, LLSDMethod handler)
355 {
356 lock (m_llsdHandlers)
357 {
358 if (!m_llsdHandlers.ContainsKey(path))
359 {
360 m_llsdHandlers.Add(path, handler);
361 return true;
362 }
363 }
364 return false;
365 }
366
367 public List<string> GetLLSDHandlerKeys()
368 {
369 lock (m_llsdHandlers)
370 return new List<string>(m_llsdHandlers.Keys);
371 }
372
373 public bool SetDefaultLLSDHandler(DefaultLLSDMethod handler)
374 {
375 m_defaultLlsdHandler = handler;
376 return true;
377 }
378
379 public void OnRequest(object source, RequestEventArgs args)
380 {
381 RequestNumber++;
382
383 try
384 {
385 IHttpClientContext context = (IHttpClientContext)source;
386 IHttpRequest request = args.Request;
387
388 PollServiceEventArgs psEvArgs;
389
390 if (TryGetPollServiceHTTPHandler(request.UriPath.ToString(), out psEvArgs))
391 {
392 psEvArgs.RequestsReceived++;
393
394 PollServiceHttpRequest psreq = new PollServiceHttpRequest(psEvArgs, context, request);
395
396 if (psEvArgs.Request != null)
397 {
398 OSHttpRequest req = new OSHttpRequest(context, request);
399
400 Stream requestStream = req.InputStream;
401
402 Encoding encoding = Encoding.UTF8;
403 StreamReader reader = new StreamReader(requestStream, encoding);
404
405 string requestBody = reader.ReadToEnd();
406
407 Hashtable keysvals = new Hashtable();
408 Hashtable headervals = new Hashtable();
409
410 string[] querystringkeys = req.QueryString.AllKeys;
411 string[] rHeaders = req.Headers.AllKeys;
412
413 keysvals.Add("body", requestBody);
414 keysvals.Add("uri", req.RawUrl);
415 keysvals.Add("content-type", req.ContentType);
416 keysvals.Add("http-method", req.HttpMethod);
417
418 foreach (string queryname in querystringkeys)
419 {
420 keysvals.Add(queryname, req.QueryString[queryname]);
421 }
422
423 foreach (string headername in rHeaders)
424 {
425 headervals[headername] = req.Headers[headername];
426 }
427
428 keysvals.Add("headers", headervals);
429 keysvals.Add("querystringkeys", querystringkeys);
430
431 psEvArgs.Request(psreq.RequestID, keysvals);
432 }
433
434 PollServiceRequestManager.Enqueue(psreq);
435 }
436 else
437 {
438 OnHandleRequestIOThread(context, request);
439 }
440 }
441 catch (Exception e)
442 {
443 m_log.Error(String.Format("[BASE HTTP SERVER]: OnRequest() failed: {0} ", e.Message), e);
444 }
445 }
446
447 private void OnHandleRequestIOThread(IHttpClientContext context, IHttpRequest request)
448 {
449 OSHttpRequest req = new OSHttpRequest(context, request);
450 WebSocketRequestDelegate dWebSocketRequestDelegate = null;
451 lock (m_WebSocketHandlers)
452 {
453 if (m_WebSocketHandlers.ContainsKey(req.RawUrl))
454 dWebSocketRequestDelegate = m_WebSocketHandlers[req.RawUrl];
455 }
456 if (dWebSocketRequestDelegate != null)
457 {
458 dWebSocketRequestDelegate(req.Url.AbsolutePath, new WebSocketHttpServerHandler(req, context, 8192));
459 return;
460 }
461
462 OSHttpResponse resp = new OSHttpResponse(new HttpResponse(context, request),context);
463 resp.ReuseContext = true;
464 HandleRequest(req, resp);
465
466 // !!!HACK ALERT!!!
467 // There seems to be a bug in the underlying http code that makes subsequent requests
468 // come up with trash in Accept headers. Until that gets fixed, we're cleaning them up here.
469 if (request.AcceptTypes != null)
470 for (int i = 0; i < request.AcceptTypes.Length; i++)
471 request.AcceptTypes[i] = string.Empty;
472 }
473
474 // public void ConvertIHttpClientContextToOSHttp(object stateinfo)
475 // {
476 // HttpServerContextObj objstate = (HttpServerContextObj)stateinfo;
477
478 // OSHttpRequest request = objstate.oreq;
479 // OSHttpResponse resp = objstate.oresp;
480
481 // HandleRequest(request,resp);
482 // }
483
484 /// <summary>
485 /// This methods is the start of incoming HTTP request handling.
486 /// </summary>
487 /// <param name="request"></param>
488 /// <param name="response"></param>
489 public virtual void HandleRequest(OSHttpRequest request, OSHttpResponse response)
490 {
491 if (request.HttpMethod == String.Empty) // Can't handle empty requests, not wasting a thread
492 {
493 try
494 {
495 byte[] buffer500 = SendHTML500(response);
496 response.OutputStream.Write(buffer500, 0, buffer500.Length);
497 response.Send();
498 }
499 catch
500 {
501 }
502
503 return;
504 }
505
506 string requestMethod = request.HttpMethod;
507 string uriString = request.RawUrl;
508
509 int requestStartTick = Environment.TickCount;
510
511 // Will be adjusted later on.
512 int requestEndTick = requestStartTick;
513
514 IRequestHandler requestHandler = null;
515
516 try
517 {
518 // OpenSim.Framework.WebUtil.OSHeaderRequestID
519// if (request.Headers["opensim-request-id"] != null)
520// reqnum = String.Format("{0}:{1}",request.RemoteIPEndPoint,request.Headers["opensim-request-id"]);
521 //m_log.DebugFormat("[BASE HTTP SERVER]: <{0}> handle request for {1}",reqnum,request.RawUrl);
522
523 Culture.SetCurrentCulture();
524
525// // This is the REST agent interface. We require an agent to properly identify
526// // itself. If the REST handler recognizes the prefix it will attempt to
527// // satisfy the request. If it is not recognizable, and no damage has occurred
528// // the request can be passed through to the other handlers. This is a low
529// // probability event; if a request is matched it is normally expected to be
530// // handled
531// IHttpAgentHandler agentHandler;
532//
533// if (TryGetAgentHandler(request, response, out agentHandler))
534// {
535// if (HandleAgentRequest(agentHandler, request, response))
536// {
537// requestEndTick = Environment.TickCount;
538// return;
539// }
540// }
541
542 //response.KeepAlive = true;
543 response.SendChunked = false;
544
545 string path = request.RawUrl;
546 string handlerKey = GetHandlerKey(request.HttpMethod, path);
547 byte[] buffer = null;
548
549 if (TryGetStreamHandler(handlerKey, out requestHandler))
550 {
551 if (DebugLevel >= 3)
552 LogIncomingToStreamHandler(request, requestHandler);
553
554 response.ContentType = requestHandler.ContentType; // Lets do this defaulting before in case handler has varying content type.
555
556 if (requestHandler is IStreamedRequestHandler)
557 {
558 IStreamedRequestHandler streamedRequestHandler = requestHandler as IStreamedRequestHandler;
559
560 buffer = streamedRequestHandler.Handle(path, request.InputStream, request, response);
561 }
562 else if (requestHandler is IGenericHTTPHandler)
563 {
564 //m_log.Debug("[BASE HTTP SERVER]: Found Caps based HTTP Handler");
565 IGenericHTTPHandler HTTPRequestHandler = requestHandler as IGenericHTTPHandler;
566 Stream requestStream = request.InputStream;
567
568 Encoding encoding = Encoding.UTF8;
569 StreamReader reader = new StreamReader(requestStream, encoding);
570
571 string requestBody = reader.ReadToEnd();
572
573 reader.Close();
574 //requestStream.Close();
575
576 Hashtable keysvals = new Hashtable();
577 Hashtable headervals = new Hashtable();
578 //string host = String.Empty;
579
580 string[] querystringkeys = request.QueryString.AllKeys;
581 string[] rHeaders = request.Headers.AllKeys;
582
583 foreach (string queryname in querystringkeys)
584 {
585 keysvals.Add(queryname, request.QueryString[queryname]);
586 }
587
588 foreach (string headername in rHeaders)
589 {
590 //m_log.Warn("[HEADER]: " + headername + "=" + request.Headers[headername]);
591 headervals[headername] = request.Headers[headername];
592 }
593
594 // if (headervals.Contains("Host"))
595 // {
596 // host = (string)headervals["Host"];
597 // }
598
599 keysvals.Add("requestbody", requestBody);
600 keysvals.Add("headers",headervals);
601 if (keysvals.Contains("method"))
602 {
603 //m_log.Warn("[HTTP]: Contains Method");
604 //string method = (string)keysvals["method"];
605 //m_log.Warn("[HTTP]: " + requestBody);
606
607 }
608
609 buffer = DoHTTPGruntWork(HTTPRequestHandler.Handle(path, keysvals), response);
610 }
611 else
612 {
613 IStreamHandler streamHandler = (IStreamHandler)requestHandler;
614
615 using (MemoryStream memoryStream = new MemoryStream())
616 {
617 streamHandler.Handle(path, request.InputStream, memoryStream, request, response);
618 memoryStream.Flush();
619 buffer = memoryStream.ToArray();
620 }
621 }
622 }
623 else
624 {
625 switch (request.ContentType)
626 {
627 case null:
628 case "text/html":
629 if (DebugLevel >= 3)
630 LogIncomingToContentTypeHandler(request);
631
632 buffer = HandleHTTPRequest(request, response);
633 break;
634
635 case "application/llsd+xml":
636 case "application/xml+llsd":
637 case "application/llsd+json":
638 if (DebugLevel >= 3)
639 LogIncomingToContentTypeHandler(request);
640
641 buffer = HandleLLSDRequests(request, response);
642 break;
643
644 case "application/json-rpc":
645 if (DebugLevel >= 3)
646 LogIncomingToContentTypeHandler(request);
647
648 buffer = HandleJsonRpcRequests(request, response);
649 break;
650
651 case "text/xml":
652 case "application/xml":
653 case "application/json":
654
655 default:
656 //m_log.Info("[Debug BASE HTTP SERVER]: in default handler");
657 // Point of note.. the DoWeHaveA methods check for an EXACT path
658 // if (request.RawUrl.Contains("/CAPS/EQG"))
659 // {
660 // int i = 1;
661 // }
662 //m_log.Info("[Debug BASE HTTP SERVER]: Checking for LLSD Handler");
663 if (DoWeHaveALLSDHandler(request.RawUrl))
664 {
665 if (DebugLevel >= 3)
666 LogIncomingToContentTypeHandler(request);
667
668 buffer = HandleLLSDRequests(request, response);
669 }
670 // m_log.DebugFormat("[BASE HTTP SERVER]: Checking for HTTP Handler for request {0}", request.RawUrl);
671 else if (DoWeHaveAHTTPHandler(request.RawUrl))
672 {
673 if (DebugLevel >= 3)
674 LogIncomingToContentTypeHandler(request);
675
676 buffer = HandleHTTPRequest(request, response);
677 }
678 else
679 {
680 if (DebugLevel >= 3)
681 LogIncomingToXmlRpcHandler(request);
682
683 // generic login request.
684 buffer = HandleXmlRpcRequests(request, response);
685 }
686
687 break;
688 }
689 }
690
691 request.InputStream.Close();
692
693 if (buffer != null)
694 {
695 if (WebUtil.DebugLevel >= 5)
696 {
697 string output = System.Text.Encoding.UTF8.GetString(buffer);
698
699 if (WebUtil.DebugLevel >= 6)
700 {
701 // Always truncate binary blobs. We don't have a ContentType, so detect them using the request name.
702 if ((requestHandler != null && requestHandler.Name == "GetMesh"))
703 {
704 if (output.Length > WebUtil.MaxRequestDiagLength)
705 output = output.Substring(0, WebUtil.MaxRequestDiagLength) + "...";
706 }
707 }
708
709 WebUtil.LogResponseDetail(RequestNumber, output);
710 }
711
712 if (!response.SendChunked && response.ContentLength64 <= 0)
713 response.ContentLength64 = buffer.LongLength;
714
715 response.OutputStream.Write(buffer, 0, buffer.Length);
716 }
717
718 // Do not include the time taken to actually send the response to the caller in the measurement
719 // time. This is to avoid logging when it's the client that is slow to process rather than the
720 // server
721 requestEndTick = Environment.TickCount;
722
723 response.Send();
724
725 //response.OutputStream.Close();
726
727 //response.FreeContext();
728 }
729 catch (SocketException e)
730 {
731 // At least on linux, it appears that if the client makes a request without requiring the response,
732 // an unconnected socket exception is thrown when we close the response output stream. There's no
733 // obvious way to tell if the client didn't require the response, so instead we'll catch and ignore
734 // the exception instead.
735 //
736 // An alternative may be to turn off all response write exceptions on the HttpListener, but let's go
737 // with the minimum first
738 m_log.Warn(String.Format("[BASE HTTP SERVER]: HandleRequest threw {0}.\nNOTE: this may be spurious on Linux ", e.Message), e);
739 }
740 catch (IOException e)
741 {
742 m_log.Error("[BASE HTTP SERVER]: HandleRequest() threw exception ", e);
743 }
744 catch (Exception e)
745 {
746 m_log.Error("[BASE HTTP SERVER]: HandleRequest() threw exception ", e);
747 try
748 {
749 byte[] buffer500 = SendHTML500(response);
750 response.OutputStream.Write(buffer500, 0, buffer500.Length);
751 response.Send();
752 }
753 catch
754 {
755 }
756 }
757 finally
758 {
759 // Every month or so this will wrap and give bad numbers, not really a problem
760 // since its just for reporting
761 int tickdiff = requestEndTick - requestStartTick;
762 if (tickdiff > 3000 && requestHandler != null && requestHandler.Name != "GetTexture")
763 {
764 m_log.InfoFormat(
765 "[LOGHTTP] Slow handling of {0} {1} {2} {3} {4} from {5} took {6}ms",
766 RequestNumber,
767 requestMethod,
768 uriString,
769 requestHandler != null ? requestHandler.Name : "",
770 requestHandler != null ? requestHandler.Description : "",
771 request.RemoteIPEndPoint,
772 tickdiff);
773 }
774 else if (DebugLevel >= 4)
775 {
776 m_log.DebugFormat(
777 "[LOGHTTP] HTTP IN {0} :{1} took {2}ms",
778 RequestNumber,
779 Port,
780 tickdiff);
781 }
782 }
783 }
784
785 private void LogIncomingToStreamHandler(OSHttpRequest request, IRequestHandler requestHandler)
786 {
787 m_log.DebugFormat(
788 "[LOGHTTP] HTTP IN {0} :{1} stream handler {2} {3} {4} {5} from {6}",
789 RequestNumber,
790 Port,
791 request.HttpMethod,
792 request.Url.PathAndQuery,
793 requestHandler.Name,
794 requestHandler.Description,
795 request.RemoteIPEndPoint);
796
797 if (DebugLevel >= 5)
798 LogIncomingInDetail(request);
799 }
800
801 private void LogIncomingToContentTypeHandler(OSHttpRequest request)
802 {
803 m_log.DebugFormat(
804 "[LOGHTTP] HTTP IN {0} :{1} {2} content type handler {3} {4} from {5}",
805 RequestNumber,
806 Port,
807 string.IsNullOrEmpty(request.ContentType) ? "not set" : request.ContentType,
808 request.HttpMethod,
809 request.Url.PathAndQuery,
810 request.RemoteIPEndPoint);
811
812 if (DebugLevel >= 5)
813 LogIncomingInDetail(request);
814 }
815
816 private void LogIncomingToXmlRpcHandler(OSHttpRequest request)
817 {
818 m_log.DebugFormat(
819 "[LOGHTTP] HTTP IN {0} :{1} assumed generic XMLRPC request {2} {3} from {4}",
820 RequestNumber,
821 Port,
822 request.HttpMethod,
823 request.Url.PathAndQuery,
824 request.RemoteIPEndPoint);
825
826 if (DebugLevel >= 5)
827 LogIncomingInDetail(request);
828 }
829
830 private void LogIncomingInDetail(OSHttpRequest request)
831 {
832 if (request.ContentType == "application/octet-stream")
833 return; // never log these; they're just binary data
834
835 Stream inputStream = Util.Copy(request.InputStream);
836 Stream innerStream = null;
837 try
838 {
839 if ((request.Headers["Content-Encoding"] == "gzip") || (request.Headers["X-Content-Encoding"] == "gzip"))
840 {
841 innerStream = inputStream;
842 inputStream = new GZipStream(innerStream, System.IO.Compression.CompressionMode.Decompress);
843 }
844
845 using (StreamReader reader = new StreamReader(inputStream, Encoding.UTF8))
846 {
847 string output;
848
849 if (DebugLevel == 5)
850 {
851 char[] chars = new char[WebUtil.MaxRequestDiagLength + 1]; // +1 so we know to add "..." only if needed
852 int len = reader.Read(chars, 0, WebUtil.MaxRequestDiagLength + 1);
853 output = new string(chars, 0, Math.Min(len, WebUtil.MaxRequestDiagLength));
854 if (len > WebUtil.MaxRequestDiagLength)
855 output += "...";
856 }
857 else
858 {
859 output = reader.ReadToEnd();
860 }
861
862 m_log.DebugFormat("[LOGHTTP] {0}", Util.BinaryToASCII(output));
863 }
864 }
865 finally
866 {
867 if (innerStream != null)
868 innerStream.Dispose();
869 inputStream.Dispose();
870 }
871 }
872
873 private readonly string HANDLER_SEPARATORS = "/?&#-";
874
875 private bool TryGetStreamHandler(string handlerKey, out IRequestHandler streamHandler)
876 {
877 string bestMatch = null;
878
879 lock (m_streamHandlers)
880 {
881 foreach (string pattern in m_streamHandlers.Keys)
882 {
883 if ((handlerKey == pattern)
884 || (handlerKey.StartsWith(pattern) && (HANDLER_SEPARATORS.IndexOf(handlerKey[pattern.Length]) >= 0)))
885 {
886 if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length)
887 {
888 bestMatch = pattern;
889 }
890 }
891 }
892
893 if (String.IsNullOrEmpty(bestMatch))
894 {
895 streamHandler = null;
896 return false;
897 }
898 else
899 {
900 streamHandler = m_streamHandlers[bestMatch];
901 return true;
902 }
903 }
904 }
905
906 private bool TryGetPollServiceHTTPHandler(string handlerKey, out PollServiceEventArgs oServiceEventArgs)
907 {
908 string bestMatch = null;
909
910 lock (m_pollHandlers)
911 {
912 foreach (string pattern in m_pollHandlers.Keys)
913 {
914 if ((handlerKey == pattern)
915 || (handlerKey.StartsWith(pattern) && (HANDLER_SEPARATORS.IndexOf(handlerKey[pattern.Length]) >= 0)))
916 {
917 if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length)
918 {
919 bestMatch = pattern;
920 }
921 }
922 }
923
924 if (String.IsNullOrEmpty(bestMatch))
925 {
926 oServiceEventArgs = null;
927 return false;
928 }
929 else
930 {
931 oServiceEventArgs = m_pollHandlers[bestMatch];
932 return true;
933 }
934 }
935 }
936
937 private bool TryGetHTTPHandler(string handlerKey, out GenericHTTPMethod HTTPHandler)
938 {
939// m_log.DebugFormat("[BASE HTTP HANDLER]: Looking for HTTP handler for {0}", handlerKey);
940
941 string bestMatch = null;
942
943 lock (m_HTTPHandlers)
944 {
945 foreach (string pattern in m_HTTPHandlers.Keys)
946 {
947 if ((handlerKey == pattern)
948 || (handlerKey.StartsWith(pattern) && (HANDLER_SEPARATORS.IndexOf(handlerKey[pattern.Length]) >= 0)))
949 {
950 if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length)
951 {
952 bestMatch = pattern;
953 }
954 }
955 }
956
957 if (String.IsNullOrEmpty(bestMatch))
958 {
959 HTTPHandler = null;
960 return false;
961 }
962 else
963 {
964 HTTPHandler = m_HTTPHandlers[bestMatch];
965 return true;
966 }
967 }
968 }
969
970// private bool TryGetAgentHandler(OSHttpRequest request, OSHttpResponse response, out IHttpAgentHandler agentHandler)
971// {
972// agentHandler = null;
973//
974// lock (m_agentHandlers)
975// {
976// foreach (IHttpAgentHandler handler in m_agentHandlers.Values)
977// {
978// if (handler.Match(request, response))
979// {
980// agentHandler = handler;
981// return true;
982// }
983// }
984// }
985//
986// return false;
987// }
988
989 /// <summary>
990 /// Try all the registered xmlrpc handlers when an xmlrpc request is received.
991 /// Sends back an XMLRPC unknown request response if no handler is registered for the requested method.
992 /// </summary>
993 /// <param name="request"></param>
994 /// <param name="response"></param>
995 private byte[] HandleXmlRpcRequests(OSHttpRequest request, OSHttpResponse response)
996 {
997 String requestBody;
998
999 Stream requestStream = request.InputStream;
1000 Stream innerStream = null;
1001 try
1002 {
1003 if ((request.Headers["Content-Encoding"] == "gzip") || (request.Headers["X-Content-Encoding"] == "gzip"))
1004 {
1005 innerStream = requestStream;
1006 requestStream = new GZipStream(innerStream, System.IO.Compression.CompressionMode.Decompress);
1007 }
1008
1009 using (StreamReader reader = new StreamReader(requestStream, Encoding.UTF8))
1010 {
1011 requestBody = reader.ReadToEnd();
1012 }
1013 }
1014 finally
1015 {
1016 if (innerStream != null)
1017 innerStream.Dispose();
1018 requestStream.Dispose();
1019 }
1020
1021 //m_log.Debug(requestBody);
1022 requestBody = requestBody.Replace("<base64></base64>", "");
1023
1024 string responseString = String.Empty;
1025 XmlRpcRequest xmlRprcRequest = null;
1026
1027 try
1028 {
1029 xmlRprcRequest = (XmlRpcRequest) (new XmlRpcRequestDeserializer()).Deserialize(requestBody);
1030 }
1031 catch (XmlException e)
1032 {
1033 if (DebugLevel >= 1)
1034 {
1035 if (DebugLevel >= 2)
1036 m_log.Warn(
1037 string.Format(
1038 "[BASE HTTP SERVER]: Got XMLRPC request with invalid XML from {0}. XML was '{1}'. Sending blank response. Exception ",
1039 request.RemoteIPEndPoint, requestBody),
1040 e);
1041 else
1042 {
1043 m_log.WarnFormat(
1044 "[BASE HTTP SERVER]: Got XMLRPC request with invalid XML from {0}, length {1}. Sending blank response.",
1045 request.RemoteIPEndPoint, requestBody.Length);
1046 }
1047 }
1048 }
1049
1050 if (xmlRprcRequest != null)
1051 {
1052 string methodName = xmlRprcRequest.MethodName;
1053 if (methodName != null)
1054 {
1055 xmlRprcRequest.Params.Add(request.RemoteIPEndPoint); // Param[1]
1056 XmlRpcResponse xmlRpcResponse;
1057
1058 XmlRpcMethod method;
1059 bool methodWasFound;
1060 bool keepAlive = false;
1061 lock (m_rpcHandlers)
1062 {
1063 methodWasFound = m_rpcHandlers.TryGetValue(methodName, out method);
1064 if (methodWasFound)
1065 keepAlive = m_rpcHandlersKeepAlive[methodName];
1066 }
1067
1068 if (methodWasFound)
1069 {
1070 xmlRprcRequest.Params.Add(request.Url); // Param[2]
1071
1072 string xff = "X-Forwarded-For";
1073 string xfflower = xff.ToLower();
1074 foreach (string s in request.Headers.AllKeys)
1075 {
1076 if (s != null && s.Equals(xfflower))
1077 {
1078 xff = xfflower;
1079 break;
1080 }
1081 }
1082 xmlRprcRequest.Params.Add(request.Headers.Get(xff)); // Param[3]
1083
1084 try
1085 {
1086 xmlRpcResponse = method(xmlRprcRequest, request.RemoteIPEndPoint);
1087 }
1088 catch(Exception e)
1089 {
1090 string errorMessage
1091 = String.Format(
1092 "Requested method [{0}] from {1} threw exception: {2} {3}",
1093 methodName, request.RemoteIPEndPoint.Address, e.Message, e.StackTrace);
1094
1095 m_log.ErrorFormat("[BASE HTTP SERVER]: {0}", errorMessage);
1096
1097 // if the registered XmlRpc method threw an exception, we pass a fault-code along
1098 xmlRpcResponse = new XmlRpcResponse();
1099
1100 // Code probably set in accordance with http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php
1101 xmlRpcResponse.SetFault(-32603, errorMessage);
1102 }
1103
1104 // if the method wasn't found, we can't determine KeepAlive state anyway, so lets do it only here
1105 response.KeepAlive = keepAlive;
1106 }
1107 else
1108 {
1109 xmlRpcResponse = new XmlRpcResponse();
1110
1111 // Code set in accordance with http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php
1112 xmlRpcResponse.SetFault(
1113 XmlRpcErrorCodes.SERVER_ERROR_METHOD,
1114 String.Format("Requested method [{0}] not found", methodName));
1115 }
1116
1117 response.ContentType = "text/xml";
1118 using (MemoryStream outs = new MemoryStream())
1119 using (XmlTextWriter writer = new XmlTextWriter(outs, UTF8NoBOM))
1120 {
1121 writer.Formatting = Formatting.None;
1122 XmlRpcResponseSerializer.Singleton.Serialize(writer, xmlRpcResponse);
1123 writer.Flush();
1124 outs.Flush();
1125 outs.Position = 0;
1126 using (StreamReader sr = new StreamReader(outs))
1127 {
1128 responseString = sr.ReadToEnd();
1129 }
1130 }
1131 }
1132 else
1133 {
1134 //HandleLLSDRequests(request, response);
1135 response.ContentType = "text/plain";
1136 response.StatusCode = 404;
1137 response.StatusDescription = "Not Found";
1138 response.ProtocolVersion = "HTTP/1.0";
1139 responseString = "Not found";
1140 response.KeepAlive = false;
1141
1142 m_log.ErrorFormat(
1143 "[BASE HTTP SERVER]: Handler not found for http request {0} {1}",
1144 request.HttpMethod, request.Url.PathAndQuery);
1145 }
1146 }
1147
1148 byte[] buffer = Encoding.UTF8.GetBytes(responseString);
1149
1150 response.SendChunked = false;
1151 response.ContentLength64 = buffer.Length;
1152 response.ContentEncoding = Encoding.UTF8;
1153
1154 return buffer;
1155 }
1156
1157 // JsonRpc (v2.0 only)
1158 // Batch requests not yet supported
1159 private byte[] HandleJsonRpcRequests(OSHttpRequest request, OSHttpResponse response)
1160 {
1161 Stream requestStream = request.InputStream;
1162 JsonRpcResponse jsonRpcResponse = new JsonRpcResponse();
1163 OSDMap jsonRpcRequest = null;
1164
1165 try
1166 {
1167 jsonRpcRequest = (OSDMap)OSDParser.DeserializeJson(requestStream);
1168 }
1169 catch (LitJson.JsonException e)
1170 {
1171 jsonRpcResponse.Error.Code = ErrorCode.InternalError;
1172 jsonRpcResponse.Error.Message = e.Message;
1173 }
1174
1175 requestStream.Close();
1176
1177 if (jsonRpcRequest != null)
1178 {
1179 if (jsonRpcRequest.ContainsKey("jsonrpc") || jsonRpcRequest["jsonrpc"].AsString() == "2.0")
1180 {
1181 jsonRpcResponse.JsonRpc = "2.0";
1182
1183 // If we have no id, then it's a "notification"
1184 if (jsonRpcRequest.ContainsKey("id"))
1185 {
1186 jsonRpcResponse.Id = jsonRpcRequest["id"].AsString();
1187 }
1188
1189 string methodname = jsonRpcRequest["method"];
1190 JsonRPCMethod method;
1191
1192 if (jsonRpcHandlers.ContainsKey(methodname))
1193 {
1194 lock(jsonRpcHandlers)
1195 {
1196 jsonRpcHandlers.TryGetValue(methodname, out method);
1197 }
1198 bool res = false;
1199 try
1200 {
1201 res = method(jsonRpcRequest, ref jsonRpcResponse);
1202 if(!res)
1203 {
1204 // The handler sent back an unspecified error
1205 if(jsonRpcResponse.Error.Code == 0)
1206 {
1207 jsonRpcResponse.Error.Code = ErrorCode.InternalError;
1208 }
1209 }
1210 }
1211 catch (Exception e)
1212 {
1213 string ErrorMessage = string.Format("[BASE HTTP SERVER]: Json-Rpc Handler Error method {0} - {1}", methodname, e.Message);
1214 m_log.Error(ErrorMessage);
1215 jsonRpcResponse.Error.Code = ErrorCode.InternalError;
1216 jsonRpcResponse.Error.Message = ErrorMessage;
1217 }
1218 }
1219 else // Error no hanlder defined for requested method
1220 {
1221 jsonRpcResponse.Error.Code = ErrorCode.InvalidRequest;
1222 jsonRpcResponse.Error.Message = string.Format ("No handler defined for {0}", methodname);
1223 }
1224 }
1225 else // not json-rpc 2.0 could be v1
1226 {
1227 jsonRpcResponse.Error.Code = ErrorCode.InvalidRequest;
1228 jsonRpcResponse.Error.Message = "Must be valid json-rpc 2.0 see: http://www.jsonrpc.org/specification";
1229
1230 if (jsonRpcRequest.ContainsKey("id"))
1231 jsonRpcResponse.Id = jsonRpcRequest["id"].AsString();
1232 }
1233 }
1234
1235 response.KeepAlive = true;
1236 string responseData = string.Empty;
1237
1238 responseData = jsonRpcResponse.Serialize();
1239
1240 byte[] buffer = Encoding.UTF8.GetBytes(responseData);
1241 return buffer;
1242 }
1243
1244 private byte[] HandleLLSDRequests(OSHttpRequest request, OSHttpResponse response)
1245 {
1246 //m_log.Warn("[BASE HTTP SERVER]: We've figured out it's a LLSD Request");
1247 Stream requestStream = request.InputStream;
1248
1249 Encoding encoding = Encoding.UTF8;
1250 StreamReader reader = new StreamReader(requestStream, encoding);
1251
1252 string requestBody = reader.ReadToEnd();
1253 reader.Close();
1254 requestStream.Close();
1255
1256 //m_log.DebugFormat("[OGP]: {0}:{1}", request.RawUrl, requestBody);
1257 response.KeepAlive = true;
1258
1259 OSD llsdRequest = null;
1260 OSD llsdResponse = null;
1261
1262 bool LegacyLLSDLoginLibOMV = (requestBody.Contains("passwd") && requestBody.Contains("mac") && requestBody.Contains("viewer_digest"));
1263
1264 if (requestBody.Length == 0)
1265 // Get Request
1266 {
1267 requestBody = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><llsd><map><key>request</key><string>get</string></map></llsd>";
1268 }
1269 try
1270 {
1271 llsdRequest = OSDParser.Deserialize(requestBody);
1272 }
1273 catch (Exception ex)
1274 {
1275 m_log.Warn("[BASE HTTP SERVER]: Error - " + ex.Message);
1276 }
1277
1278 if (llsdRequest != null)// && m_defaultLlsdHandler != null)
1279 {
1280 LLSDMethod llsdhandler = null;
1281
1282 if (TryGetLLSDHandler(request.RawUrl, out llsdhandler) && !LegacyLLSDLoginLibOMV)
1283 {
1284 // we found a registered llsd handler to service this request
1285 llsdResponse = llsdhandler(request.RawUrl, llsdRequest, request.RemoteIPEndPoint.ToString());
1286 }
1287 else
1288 {
1289 // we didn't find a registered llsd handler to service this request
1290 // check if we have a default llsd handler
1291
1292 if (m_defaultLlsdHandler != null)
1293 {
1294 // LibOMV path
1295 llsdResponse = m_defaultLlsdHandler(llsdRequest, request.RemoteIPEndPoint);
1296 }
1297 else
1298 {
1299 // Oops, no handler for this.. give em the failed message
1300 llsdResponse = GenerateNoLLSDHandlerResponse();
1301 }
1302 }
1303 }
1304 else
1305 {
1306 llsdResponse = GenerateNoLLSDHandlerResponse();
1307 }
1308
1309 byte[] buffer = new byte[0];
1310
1311 if (llsdResponse.ToString() == "shutdown404!")
1312 {
1313 response.ContentType = "text/plain";
1314 response.StatusCode = 404;
1315 response.StatusDescription = "Not Found";
1316 response.ProtocolVersion = "HTTP/1.0";
1317 buffer = Encoding.UTF8.GetBytes("Not found");
1318 }
1319 else
1320 {
1321 // Select an appropriate response format
1322 buffer = BuildLLSDResponse(request, response, llsdResponse);
1323 }
1324
1325 response.SendChunked = false;
1326 response.ContentLength64 = buffer.Length;
1327 response.ContentEncoding = Encoding.UTF8;
1328 response.KeepAlive = true;
1329
1330 return buffer;
1331 }
1332
1333 private byte[] BuildLLSDResponse(OSHttpRequest request, OSHttpResponse response, OSD llsdResponse)
1334 {
1335 if (request.AcceptTypes != null && request.AcceptTypes.Length > 0)
1336 {
1337 foreach (string strAccept in request.AcceptTypes)
1338 {
1339 switch (strAccept)
1340 {
1341 case "application/llsd+xml":
1342 case "application/xml":
1343 case "text/xml":
1344 response.ContentType = strAccept;
1345 return OSDParser.SerializeLLSDXmlBytes(llsdResponse);
1346 case "application/llsd+json":
1347 case "application/json":
1348 response.ContentType = strAccept;
1349 return Encoding.UTF8.GetBytes(OSDParser.SerializeJsonString(llsdResponse));
1350 }
1351 }
1352 }
1353
1354 if (!String.IsNullOrEmpty(request.ContentType))
1355 {
1356 switch (request.ContentType)
1357 {
1358 case "application/llsd+xml":
1359 case "application/xml":
1360 case "text/xml":
1361 response.ContentType = request.ContentType;
1362 return OSDParser.SerializeLLSDXmlBytes(llsdResponse);
1363 case "application/llsd+json":
1364 case "application/json":
1365 response.ContentType = request.ContentType;
1366 return Encoding.UTF8.GetBytes(OSDParser.SerializeJsonString(llsdResponse));
1367 }
1368 }
1369
1370 // response.ContentType = "application/llsd+json";
1371 // return Util.UTF8.GetBytes(OSDParser.SerializeJsonString(llsdResponse));
1372 response.ContentType = "application/llsd+xml";
1373 return OSDParser.SerializeLLSDXmlBytes(llsdResponse);
1374 }
1375
1376 /// <summary>
1377 /// Checks if we have an Exact path in the LLSD handlers for the path provided
1378 /// </summary>
1379 /// <param name="path">URI of the request</param>
1380 /// <returns>true if we have one, false if not</returns>
1381 private bool DoWeHaveALLSDHandler(string path)
1382 {
1383 string[] pathbase = path.Split('/');
1384 string searchquery = "/";
1385
1386 if (pathbase.Length < 1)
1387 return false;
1388
1389 for (int i = 1; i < pathbase.Length; i++)
1390 {
1391 searchquery += pathbase[i];
1392 if (pathbase.Length - 1 != i)
1393 searchquery += "/";
1394 }
1395
1396 string bestMatch = null;
1397
1398 lock (m_llsdHandlers)
1399 {
1400 foreach (string pattern in m_llsdHandlers.Keys)
1401 {
1402 if (searchquery.StartsWith(pattern) && searchquery.Length >= pattern.Length)
1403 bestMatch = pattern;
1404 }
1405 }
1406
1407 // extra kicker to remove the default XMLRPC login case.. just in case..
1408 if (path != "/" && bestMatch == "/" && searchquery != "/")
1409 return false;
1410
1411 if (path == "/")
1412 return false;
1413
1414 if (String.IsNullOrEmpty(bestMatch))
1415 {
1416 return false;
1417 }
1418 else
1419 {
1420 return true;
1421 }
1422 }
1423
1424 /// <summary>
1425 /// Checks if we have an Exact path in the HTTP handlers for the path provided
1426 /// </summary>
1427 /// <param name="path">URI of the request</param>
1428 /// <returns>true if we have one, false if not</returns>
1429 private bool DoWeHaveAHTTPHandler(string path)
1430 {
1431 string[] pathbase = path.Split('/');
1432 string searchquery = "/";
1433
1434 if (pathbase.Length < 1)
1435 return false;
1436
1437 for (int i = 1; i < pathbase.Length; i++)
1438 {
1439 searchquery += pathbase[i];
1440 if (pathbase.Length - 1 != i)
1441 searchquery += "/";
1442 }
1443
1444 string bestMatch = null;
1445
1446 //m_log.DebugFormat("[BASE HTTP HANDLER]: Checking if we have an HTTP handler for {0}", searchquery);
1447
1448 lock (m_HTTPHandlers)
1449 {
1450 foreach (string pattern in m_HTTPHandlers.Keys)
1451 {
1452 if (searchquery.StartsWith(pattern) && searchquery.Length >= pattern.Length)
1453 {
1454 bestMatch = pattern;
1455 }
1456 }
1457
1458 // extra kicker to remove the default XMLRPC login case.. just in case..
1459 if (path == "/")
1460 return false;
1461
1462 if (String.IsNullOrEmpty(bestMatch))
1463 {
1464 return false;
1465 }
1466 else
1467 {
1468 return true;
1469 }
1470 }
1471 }
1472
1473 private bool TryGetLLSDHandler(string path, out LLSDMethod llsdHandler)
1474 {
1475 llsdHandler = null;
1476 // Pull out the first part of the path
1477 // splitting the path by '/' means we'll get the following return..
1478 // {0}/{1}/{2}
1479 // where {0} isn't something we really control 100%
1480
1481 string[] pathbase = path.Split('/');
1482 string searchquery = "/";
1483
1484 if (pathbase.Length < 1)
1485 return false;
1486
1487 for (int i=1; i<pathbase.Length; i++)
1488 {
1489 searchquery += pathbase[i];
1490 if (pathbase.Length-1 != i)
1491 searchquery += "/";
1492 }
1493
1494 // while the matching algorithm below doesn't require it, we're expecting a query in the form
1495 //
1496 // [] = optional
1497 // /resource/UUID/action[/action]
1498 //
1499 // now try to get the closest match to the reigstered path
1500 // at least for OGP, registered path would probably only consist of the /resource/
1501
1502 string bestMatch = null;
1503
1504 lock (m_llsdHandlers)
1505 {
1506 foreach (string pattern in m_llsdHandlers.Keys)
1507 {
1508 if (searchquery.ToLower().StartsWith(pattern.ToLower()))
1509 {
1510 if (String.IsNullOrEmpty(bestMatch) || searchquery.Length > bestMatch.Length)
1511 {
1512 // You have to specifically register for '/' and to get it, you must specificaly request it
1513 //
1514 if (pattern == "/" && searchquery == "/" || pattern != "/")
1515 bestMatch = pattern;
1516 }
1517 }
1518 }
1519
1520 if (String.IsNullOrEmpty(bestMatch))
1521 {
1522 llsdHandler = null;
1523 return false;
1524 }
1525 else
1526 {
1527 llsdHandler = m_llsdHandlers[bestMatch];
1528 return true;
1529 }
1530 }
1531 }
1532
1533 private OSDMap GenerateNoLLSDHandlerResponse()
1534 {
1535 OSDMap map = new OSDMap();
1536 map["reason"] = OSD.FromString("LLSDRequest");
1537 map["message"] = OSD.FromString("No handler registered for LLSD Requests");
1538 map["login"] = OSD.FromString("false");
1539 return map;
1540 }
1541
1542 public byte[] HandleHTTPRequest(OSHttpRequest request, OSHttpResponse response)
1543 {
1544// m_log.DebugFormat(
1545// "[BASE HTTP SERVER]: HandleHTTPRequest for request to {0}, method {1}",
1546// request.RawUrl, request.HttpMethod);
1547
1548 switch (request.HttpMethod)
1549 {
1550 case "OPTIONS":
1551 response.StatusCode = (int)OSHttpStatusCode.SuccessOk;
1552 return null;
1553
1554 default:
1555 return HandleContentVerbs(request, response);
1556 }
1557 }
1558
1559 private byte[] HandleContentVerbs(OSHttpRequest request, OSHttpResponse response)
1560 {
1561// m_log.DebugFormat("[BASE HTTP SERVER]: HandleContentVerbs for request to {0}", request.RawUrl);
1562
1563 // This is a test. There's a workable alternative.. as this way sucks.
1564 // We'd like to put this into a text file parhaps that's easily editable.
1565 //
1566 // For this test to work, I used the following secondlife.exe parameters
1567 // "C:\Program Files\SecondLifeWindLight\SecondLifeWindLight.exe" -settings settings_windlight.xml -channel "Second Life WindLight" -set SystemLanguage en-us -loginpage http://10.1.1.2:8002/?show_login_form=TRUE -loginuri http://10.1.1.2:8002 -user 10.1.1.2
1568 //
1569 // Even after all that, there's still an error, but it's a start.
1570 //
1571 // I depend on show_login_form being in the secondlife.exe parameters to figure out
1572 // to display the form, or process it.
1573 // a better way would be nifty.
1574
1575 byte[] buffer;
1576
1577 Stream requestStream = request.InputStream;
1578
1579 Encoding encoding = Encoding.UTF8;
1580 StreamReader reader = new StreamReader(requestStream, encoding);
1581
1582 string requestBody = reader.ReadToEnd();
1583 // avoid warning for now
1584 reader.ReadToEnd();
1585 reader.Close();
1586 requestStream.Close();
1587
1588 Hashtable keysvals = new Hashtable();
1589 Hashtable headervals = new Hashtable();
1590
1591 Hashtable requestVars = new Hashtable();
1592
1593 string host = String.Empty;
1594
1595 string[] querystringkeys = request.QueryString.AllKeys;
1596 string[] rHeaders = request.Headers.AllKeys;
1597
1598 keysvals.Add("body", requestBody);
1599 keysvals.Add("uri", request.RawUrl);
1600 keysvals.Add("content-type", request.ContentType);
1601 keysvals.Add("http-method", request.HttpMethod);
1602
1603 foreach (string queryname in querystringkeys)
1604 {
1605// m_log.DebugFormat(
1606// "[BASE HTTP SERVER]: Got query paremeter {0}={1}", queryname, request.QueryString[queryname]);
1607 keysvals.Add(queryname, request.QueryString[queryname]);
1608 requestVars.Add(queryname, keysvals[queryname]);
1609 }
1610
1611 foreach (string headername in rHeaders)
1612 {
1613// m_log.Debug("[BASE HTTP SERVER]: " + headername + "=" + request.Headers[headername]);
1614 headervals[headername] = request.Headers[headername];
1615 }
1616
1617 if (headervals.Contains("Host"))
1618 {
1619 host = (string)headervals["Host"];
1620 }
1621
1622 keysvals.Add("headers", headervals);
1623 keysvals.Add("querystringkeys", querystringkeys);
1624 keysvals.Add("requestvars", requestVars);
1625// keysvals.Add("form", request.Form);
1626
1627 if (keysvals.Contains("method"))
1628 {
1629// m_log.Debug("[BASE HTTP SERVER]: Contains Method");
1630 string method = (string) keysvals["method"];
1631// m_log.Debug("[BASE HTTP SERVER]: " + requestBody);
1632 GenericHTTPMethod requestprocessor;
1633 bool foundHandler = TryGetHTTPHandler(method, out requestprocessor);
1634 if (foundHandler)
1635 {
1636 Hashtable responsedata1 = requestprocessor(keysvals);
1637 buffer = DoHTTPGruntWork(responsedata1,response);
1638
1639 //SendHTML500(response);
1640 }
1641 else
1642 {
1643// m_log.Warn("[BASE HTTP SERVER]: Handler Not Found");
1644 buffer = SendHTML404(response, host);
1645 }
1646 }
1647 else
1648 {
1649 GenericHTTPMethod requestprocessor;
1650 bool foundHandler = TryGetHTTPHandlerPathBased(request.RawUrl, out requestprocessor);
1651 if (foundHandler)
1652 {
1653 Hashtable responsedata2 = requestprocessor(keysvals);
1654 buffer = DoHTTPGruntWork(responsedata2, response);
1655
1656 //SendHTML500(response);
1657 }
1658 else
1659 {
1660// m_log.Warn("[BASE HTTP SERVER]: Handler Not Found2");
1661 buffer = SendHTML404(response, host);
1662 }
1663 }
1664
1665 return buffer;
1666 }
1667
1668 private bool TryGetHTTPHandlerPathBased(string path, out GenericHTTPMethod httpHandler)
1669 {
1670 httpHandler = null;
1671 // Pull out the first part of the path
1672 // splitting the path by '/' means we'll get the following return..
1673 // {0}/{1}/{2}
1674 // where {0} isn't something we really control 100%
1675
1676 string[] pathbase = path.Split('/');
1677 string searchquery = "/";
1678
1679 if (pathbase.Length < 1)
1680 return false;
1681
1682 for (int i = 1; i < pathbase.Length; i++)
1683 {
1684 searchquery += pathbase[i];
1685 if (pathbase.Length - 1 != i)
1686 searchquery += "/";
1687 }
1688
1689 // while the matching algorithm below doesn't require it, we're expecting a query in the form
1690 //
1691 // [] = optional
1692 // /resource/UUID/action[/action]
1693 //
1694 // now try to get the closest match to the reigstered path
1695 // at least for OGP, registered path would probably only consist of the /resource/
1696
1697 string bestMatch = null;
1698
1699// m_log.DebugFormat(
1700// "[BASE HTTP HANDLER]: TryGetHTTPHandlerPathBased() looking for HTTP handler to match {0}", searchquery);
1701
1702 lock (m_HTTPHandlers)
1703 {
1704 foreach (string pattern in m_HTTPHandlers.Keys)
1705 {
1706 if (searchquery.ToLower().StartsWith(pattern.ToLower()))
1707 {
1708 if (String.IsNullOrEmpty(bestMatch) || searchquery.Length > bestMatch.Length)
1709 {
1710 // You have to specifically register for '/' and to get it, you must specifically request it
1711 if (pattern == "/" && searchquery == "/" || pattern != "/")
1712 bestMatch = pattern;
1713 }
1714 }
1715 }
1716
1717 if (String.IsNullOrEmpty(bestMatch))
1718 {
1719 httpHandler = null;
1720 return false;
1721 }
1722 else
1723 {
1724 if (bestMatch == "/" && searchquery != "/")
1725 return false;
1726
1727 httpHandler = m_HTTPHandlers[bestMatch];
1728 return true;
1729 }
1730 }
1731 }
1732
1733 internal byte[] DoHTTPGruntWork(Hashtable responsedata, OSHttpResponse response)
1734 {
1735 //m_log.Info("[BASE HTTP SERVER]: Doing HTTP Grunt work with response");
1736 int responsecode = (int)responsedata["int_response_code"];
1737 string responseString = (string)responsedata["str_response_string"];
1738 string contentType = (string)responsedata["content_type"];
1739
1740 if (responsedata.ContainsKey("error_status_text"))
1741 {
1742 response.StatusDescription = (string)responsedata["error_status_text"];
1743 }
1744 if (responsedata.ContainsKey("http_protocol_version"))
1745 {
1746 response.ProtocolVersion = (string)responsedata["http_protocol_version"];
1747 }
1748
1749 if (responsedata.ContainsKey("keepalive"))
1750 {
1751 bool keepalive = (bool)responsedata["keepalive"];
1752 response.KeepAlive = keepalive;
1753
1754 }
1755
1756 if (responsedata.ContainsKey("reusecontext"))
1757 response.ReuseContext = (bool) responsedata["reusecontext"];
1758
1759 // Cross-Origin Resource Sharing with simple requests
1760 if (responsedata.ContainsKey("access_control_allow_origin"))
1761 response.AddHeader("Access-Control-Allow-Origin", (string)responsedata["access_control_allow_origin"]);
1762
1763 //Even though only one other part of the entire code uses HTTPHandlers, we shouldn't expect this
1764 //and should check for NullReferenceExceptions
1765
1766 if (string.IsNullOrEmpty(contentType))
1767 {
1768 contentType = "text/html";
1769 }
1770
1771 // The client ignores anything but 200 here for web login, so ensure that this is 200 for that
1772
1773 response.StatusCode = responsecode;
1774
1775 if (responsecode == (int)OSHttpStatusCode.RedirectMovedPermanently)
1776 {
1777 response.RedirectLocation = (string)responsedata["str_redirect_location"];
1778 response.StatusCode = responsecode;
1779 }
1780
1781 response.AddHeader("Content-Type", contentType);
1782
1783 byte[] buffer;
1784
1785 if (!(contentType.Contains("image")
1786 || contentType.Contains("x-shockwave-flash")
1787 || contentType.Contains("application/x-oar")
1788 || contentType.Contains("application/vnd.ll.mesh")))
1789 {
1790 // Text
1791 buffer = Encoding.UTF8.GetBytes(responseString);
1792 }
1793 else
1794 {
1795 // Binary!
1796 buffer = Convert.FromBase64String(responseString);
1797 }
1798
1799 response.SendChunked = false;
1800 response.ContentLength64 = buffer.Length;
1801 response.ContentEncoding = Encoding.UTF8;
1802
1803 return buffer;
1804 }
1805
1806 public byte[] SendHTML404(OSHttpResponse response, string host)
1807 {
1808 // I know this statuscode is dumb, but the client doesn't respond to 404s and 500s
1809 response.StatusCode = 404;
1810 response.AddHeader("Content-type", "text/html");
1811
1812 string responseString = GetHTTP404(host);
1813 byte[] buffer = Encoding.UTF8.GetBytes(responseString);
1814
1815 response.SendChunked = false;
1816 response.ContentLength64 = buffer.Length;
1817 response.ContentEncoding = Encoding.UTF8;
1818
1819 return buffer;
1820 }
1821
1822 public byte[] SendHTML500(OSHttpResponse response)
1823 {
1824 // I know this statuscode is dumb, but the client doesn't respond to 404s and 500s
1825 response.StatusCode = (int)OSHttpStatusCode.SuccessOk;
1826 response.AddHeader("Content-type", "text/html");
1827
1828 string responseString = GetHTTP500();
1829 byte[] buffer = Encoding.UTF8.GetBytes(responseString);
1830
1831 response.SendChunked = false;
1832 response.ContentLength64 = buffer.Length;
1833 response.ContentEncoding = Encoding.UTF8;
1834
1835
1836 return buffer;
1837 }
1838
1839 public void Start()
1840 {
1841 Start(true);
1842 }
1843
1844 /// <summary>
1845 /// Start the http server
1846 /// </summary>
1847 /// <param name='processPollRequestsAsync'>
1848 /// If true then poll responses are performed asynchronsly.
1849 /// Option exists to allow regression tests to perform processing synchronously.
1850 /// </param>
1851 public void Start(bool performPollResponsesAsync)
1852 {
1853 m_log.InfoFormat(
1854 "[BASE HTTP SERVER]: Starting {0} server on port {1}", UseSSL ? "HTTPS" : "HTTP", Port);
1855
1856 try
1857 {
1858 //m_httpListener = new HttpListener();
1859
1860 NotSocketErrors = 0;
1861 if (!m_ssl)
1862 {
1863 //m_httpListener.Prefixes.Add("http://+:" + m_port + "/");
1864 //m_httpListener.Prefixes.Add("http://10.1.1.5:" + m_port + "/");
1865 m_httpListener2 = CoolHTTPListener.Create(m_listenIPAddress, (int)m_port);
1866 m_httpListener2.ExceptionThrown += httpServerException;
1867 m_httpListener2.LogWriter = httpserverlog;
1868
1869 // Uncomment this line in addition to those in HttpServerLogWriter
1870 // if you want more detailed trace information from the HttpServer
1871 //m_httpListener2.UseTraceLogs = true;
1872
1873 //m_httpListener2.DisconnectHandler = httpServerDisconnectMonitor;
1874 }
1875 else
1876 {
1877 //m_httpListener.Prefixes.Add("https://+:" + (m_sslport) + "/");
1878 //m_httpListener.Prefixes.Add("http://+:" + m_port + "/");
1879 m_httpListener2 = CoolHTTPListener.Create(IPAddress.Any, (int)m_port, m_cert);
1880 m_httpListener2.ExceptionThrown += httpServerException;
1881 m_httpListener2.LogWriter = httpserverlog;
1882 }
1883
1884 m_httpListener2.RequestReceived += OnRequest;
1885 //m_httpListener.Start();
1886 m_httpListener2.Start(64);
1887
1888 // Long Poll Service Manager with 3 worker threads a 25 second timeout for no events
1889 PollServiceRequestManager = new PollServiceRequestManager(this, performPollResponsesAsync, 3, 25000);
1890 PollServiceRequestManager.Start();
1891
1892 HTTPDRunning = true;
1893
1894 //HttpListenerContext context;
1895 //while (true)
1896 //{
1897 // context = m_httpListener.GetContext();
1898 // ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(HandleRequest), context);
1899 // }
1900 }
1901 catch (Exception e)
1902 {
1903 m_log.Error("[BASE HTTP SERVER]: Error - " + e.Message);
1904 m_log.Error("[BASE HTTP SERVER]: Tip: Do you have permission to listen on port " + m_port + ", " + m_sslport + "?");
1905
1906 // We want this exception to halt the entire server since in current configurations we aren't too
1907 // useful without inbound HTTP.
1908 throw e;
1909 }
1910
1911 m_requestsProcessedStat
1912 = new Stat(
1913 "HTTPRequestsServed",
1914 "Number of inbound HTTP requests processed",
1915 "",
1916 "requests",
1917 "httpserver",
1918 Port.ToString(),
1919 StatType.Pull,
1920 MeasuresOfInterest.AverageChangeOverTime,
1921 stat => stat.Value = RequestNumber,
1922 StatVerbosity.Debug);
1923
1924 StatsManager.RegisterStat(m_requestsProcessedStat);
1925 }
1926
1927 public void httpServerDisconnectMonitor(IHttpClientContext source, SocketError err)
1928 {
1929 switch (err)
1930 {
1931 case SocketError.NotSocket:
1932 NotSocketErrors++;
1933
1934 break;
1935 }
1936 }
1937
1938 public void httpServerException(object source, Exception exception)
1939 {
1940 m_log.Error(String.Format("[BASE HTTP SERVER]: {0} had an exception: {1} ", source.ToString(), exception.Message), exception);
1941 /*
1942 if (HTTPDRunning)// && NotSocketErrors > 5)
1943 {
1944 Stop();
1945 Thread.Sleep(200);
1946 StartHTTP();
1947 m_log.Warn("[HTTPSERVER]: Died. Trying to kick.....");
1948 }
1949 */
1950 }
1951
1952 public void Stop()
1953 {
1954 HTTPDRunning = false;
1955
1956 StatsManager.DeregisterStat(m_requestsProcessedStat);
1957
1958 try
1959 {
1960 PollServiceRequestManager.Stop();
1961
1962 m_httpListener2.ExceptionThrown -= httpServerException;
1963 //m_httpListener2.DisconnectHandler = null;
1964
1965 m_httpListener2.LogWriter = null;
1966 m_httpListener2.RequestReceived -= OnRequest;
1967 m_httpListener2.Stop();
1968 }
1969 catch (NullReferenceException)
1970 {
1971 m_log.Warn("[BASE HTTP SERVER]: Null Reference when stopping HttpServer.");
1972 }
1973 }
1974
1975 public void RemoveStreamHandler(string httpMethod, string path)
1976 {
1977 string handlerKey = GetHandlerKey(httpMethod, path);
1978
1979 //m_log.DebugFormat("[BASE HTTP SERVER]: Removing handler key {0}", handlerKey);
1980
1981 lock (m_streamHandlers)
1982 m_streamHandlers.Remove(handlerKey);
1983 }
1984
1985 public void RemoveHTTPHandler(string httpMethod, string path)
1986 {
1987 lock (m_HTTPHandlers)
1988 {
1989 if (httpMethod != null && httpMethod.Length == 0)
1990 {
1991 m_HTTPHandlers.Remove(path);
1992 return;
1993 }
1994
1995 m_HTTPHandlers.Remove(GetHandlerKey(httpMethod, path));
1996 }
1997 }
1998
1999 public void RemovePollServiceHTTPHandler(string httpMethod, string path)
2000 {
2001 lock (m_pollHandlers)
2002 m_pollHandlers.Remove(path);
2003 }
2004
2005// public bool RemoveAgentHandler(string agent, IHttpAgentHandler handler)
2006// {
2007// lock (m_agentHandlers)
2008// {
2009// IHttpAgentHandler foundHandler;
2010//
2011// if (m_agentHandlers.TryGetValue(agent, out foundHandler) && foundHandler == handler)
2012// {
2013// m_agentHandlers.Remove(agent);
2014// return true;
2015// }
2016// }
2017//
2018// return false;
2019// }
2020
2021 public void RemoveXmlRPCHandler(string method)
2022 {
2023 lock (m_rpcHandlers)
2024 m_rpcHandlers.Remove(method);
2025 }
2026
2027 public void RemoveJsonRPCHandler(string method)
2028 {
2029 lock(jsonRpcHandlers)
2030 jsonRpcHandlers.Remove(method);
2031 }
2032
2033 public bool RemoveLLSDHandler(string path, LLSDMethod handler)
2034 {
2035 lock (m_llsdHandlers)
2036 {
2037 LLSDMethod foundHandler;
2038
2039 if (m_llsdHandlers.TryGetValue(path, out foundHandler) && foundHandler == handler)
2040 {
2041 m_llsdHandlers.Remove(path);
2042 return true;
2043 }
2044 }
2045
2046 return false;
2047 }
2048
2049 public string GetHTTP404(string host)
2050 {
2051 string file = Path.Combine(".", "http_404.html");
2052 if (!File.Exists(file))
2053 return getDefaultHTTP404(host);
2054
2055 StreamReader sr = File.OpenText(file);
2056 string result = sr.ReadToEnd();
2057 sr.Close();
2058 return result;
2059 }
2060
2061 public string GetHTTP500()
2062 {
2063 string file = Path.Combine(".", "http_500.html");
2064 if (!File.Exists(file))
2065 return getDefaultHTTP500();
2066
2067 StreamReader sr = File.OpenText(file);
2068 string result = sr.ReadToEnd();
2069 sr.Close();
2070 return result;
2071 }
2072
2073 // Fallback HTTP responses in case the HTTP error response files don't exist
2074 private static string getDefaultHTTP404(string host)
2075 {
2076 return "<HTML><HEAD><TITLE>404 Page not found</TITLE><BODY><BR /><H1>Ooops!</H1><P>The page you requested has been obsconded with by knomes. Find hippos quick!</P><P>If you are trying to log-in, your link parameters should have: &quot;-loginpage http://" + host + "/?method=login -loginuri http://" + host + "/&quot; in your link </P></BODY></HTML>";
2077 }
2078
2079 private static string getDefaultHTTP500()
2080 {
2081 return "<HTML><HEAD><TITLE>500 Internal Server Error</TITLE><BODY><BR /><H1>Ooops!</H1><P>The server you requested is overun by knomes! Find hippos quick!</P></BODY></HTML>";
2082 }
2083 }
2084
2085 public class HttpServerContextObj
2086 {
2087 public IHttpClientContext context = null;
2088 public IHttpRequest req = null;
2089 public OSHttpRequest oreq = null;
2090 public OSHttpResponse oresp = null;
2091
2092 public HttpServerContextObj(IHttpClientContext contxt, IHttpRequest reqs)
2093 {
2094 context = contxt;
2095 req = reqs;
2096 }
2097
2098 public HttpServerContextObj(OSHttpRequest osreq, OSHttpResponse osresp)
2099 {
2100 oreq = osreq;
2101 oresp = osresp;
2102 }
2103 }
2104
2105 /// <summary>
2106 /// Relays HttpServer log messages to our own logging mechanism.
2107 /// </summary>
2108 /// To use this you must uncomment the switch section
2109 ///
2110 /// You may also be able to get additional trace information from HttpServer if you uncomment the UseTraceLogs
2111 /// property in StartHttp() for the HttpListener
2112 public class HttpServerLogWriter : ILogWriter
2113 {
2114// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
2115
2116 public void Write(object source, LogPrio priority, string message)
2117 {
2118 /*
2119 switch (priority)
2120 {
2121 case LogPrio.Trace:
2122 m_log.DebugFormat("[{0}]: {1}", source, message);
2123 break;
2124 case LogPrio.Debug:
2125 m_log.DebugFormat("[{0}]: {1}", source, message);
2126 break;
2127 case LogPrio.Error:
2128 m_log.ErrorFormat("[{0}]: {1}", source, message);
2129 break;
2130 case LogPrio.Info:
2131 m_log.InfoFormat("[{0}]: {1}", source, message);
2132 break;
2133 case LogPrio.Warning:
2134 m_log.WarnFormat("[{0}]: {1}", source, message);
2135 break;
2136 case LogPrio.Fatal:
2137 m_log.ErrorFormat("[{0}]: FATAL! - {1}", source, message);
2138 break;
2139 default:
2140 break;
2141 }
2142 */
2143
2144 return;
2145 }
2146 }
2147}
diff --git a/OpenSim/Framework/Servers/HttpServer/BaseOutputStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/BaseOutputStreamHandler.cs
new file mode 100644
index 0000000..72b3065
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/BaseOutputStreamHandler.cs
@@ -0,0 +1,60 @@
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.IO;
29
30namespace OpenSim.Framework.Servers.HttpServer
31{
32 /// <summary>
33 /// Base handler for writing to an output stream
34 /// </summary>
35 /// <remarks>
36 /// Inheriting classes should override ProcessRequest() rather than Handle()
37 /// </remarks>
38 public abstract class BaseOutputStreamHandler : BaseRequestHandler, IRequestHandler
39 {
40 protected BaseOutputStreamHandler(string httpMethod, string path) : this(httpMethod, path, null, null) {}
41
42 protected BaseOutputStreamHandler(string httpMethod, string path, string name, string description)
43 : base(httpMethod, path, name, description) {}
44
45 public virtual void Handle(
46 string path, Stream request, Stream response, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
47 {
48 RequestsReceived++;
49
50 ProcessRequest(path, request, response, httpRequest, httpResponse);
51
52 RequestsHandled++;
53 }
54
55 protected virtual void ProcessRequest(
56 string path, Stream request, Stream response, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
57 {
58 }
59 }
60} \ No newline at end of file
diff --git a/OpenSim/Framework/Servers/HttpServer/BaseRequestHandler.cs b/OpenSim/Framework/Servers/HttpServer/BaseRequestHandler.cs
new file mode 100644
index 0000000..d4a1ec3
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/BaseRequestHandler.cs
@@ -0,0 +1,117 @@
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 OpenSim.Framework.Monitoring;
30
31namespace OpenSim.Framework.Servers.HttpServer
32{
33 public abstract class BaseRequestHandler
34 {
35 public int RequestsReceived { get; protected set; }
36
37 public int RequestsHandled { get; protected set; }
38
39 public virtual string ContentType
40 {
41 get { return "application/xml"; }
42 }
43
44 private readonly string m_httpMethod;
45
46 public virtual string HttpMethod
47 {
48 get { return m_httpMethod; }
49 }
50
51 private readonly string m_path;
52
53 public string Name { get; private set; }
54
55 public string Description { get; private set; }
56
57 protected BaseRequestHandler(string httpMethod, string path) : this(httpMethod, path, null, null) {}
58
59 protected BaseRequestHandler(string httpMethod, string path, string name, string description)
60 {
61 Name = name;
62 Description = description;
63 m_httpMethod = httpMethod;
64 m_path = path;
65
66 // FIXME: A very temporary measure to stop the simulator stats being overwhelmed with user CAPS info.
67 // Needs to be fixed properly in stats display
68 if (!path.StartsWith("/CAPS/"))
69 {
70 StatsManager.RegisterStat(
71 new Stat(
72 "requests",
73 "requests",
74 "Number of requests received by this service endpoint",
75 "requests",
76 "service",
77 string.Format("{0}:{1}", httpMethod, path),
78 StatType.Pull,
79 MeasuresOfInterest.AverageChangeOverTime,
80 s => s.Value = RequestsReceived,
81 StatVerbosity.Debug));
82 }
83 }
84
85 public virtual string Path
86 {
87 get { return m_path; }
88 }
89
90 public string GetParam(string path)
91 {
92 if (CheckParam(path))
93 {
94 return path.Substring(m_path.Length);
95 }
96
97 return String.Empty;
98 }
99
100 protected bool CheckParam(string path)
101 {
102 if (String.IsNullOrEmpty(path))
103 {
104 return false;
105 }
106
107 return path.StartsWith(Path);
108 }
109
110 public string[] SplitParams(string path)
111 {
112 string param = GetParam(path);
113
114 return param.Split(new char[] { '/', '?', '&' }, StringSplitOptions.RemoveEmptyEntries);
115 }
116 }
117}
diff --git a/OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs
new file mode 100644
index 0000000..41aa19b
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs
@@ -0,0 +1,85 @@
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.IO;
29using System.Net;
30using OpenSim.Framework.ServiceAuth;
31
32namespace OpenSim.Framework.Servers.HttpServer
33{
34 /// <summary>
35 /// Base streamed request handler.
36 /// </summary>
37 /// <remarks>
38 /// Inheriting classes should override ProcessRequest() rather than Handle()
39 /// </remarks>
40 public abstract class BaseStreamHandler : BaseRequestHandler, IStreamedRequestHandler
41 {
42 protected IServiceAuth m_Auth;
43
44 protected BaseStreamHandler(string httpMethod, string path) : this(httpMethod, path, null, null) { }
45
46 protected BaseStreamHandler(string httpMethod, string path, string name, string description)
47 : base(httpMethod, path, name, description) {}
48
49 protected BaseStreamHandler(string httpMethod, string path, IServiceAuth auth)
50 : base(httpMethod, path, null, null)
51 {
52 m_Auth = auth;
53 }
54
55 public virtual byte[] Handle(
56 string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
57 {
58 RequestsReceived++;
59
60 if (m_Auth != null)
61 {
62 HttpStatusCode statusCode;
63
64 if (!m_Auth.Authenticate(httpRequest.Headers, httpResponse.AddHeader, out statusCode))
65 {
66 httpResponse.StatusCode = (int)statusCode;
67 httpResponse.ContentType = "text/plain";
68 return new byte[0];
69 }
70 }
71
72 byte[] result = ProcessRequest(path, request, httpRequest, httpResponse);
73
74 RequestsHandled++;
75
76 return result;
77 }
78
79 protected virtual byte[] ProcessRequest(
80 string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
81 {
82 return null;
83 }
84 }
85} \ No newline at end of file
diff --git a/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs
new file mode 100644
index 0000000..1b88545
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs
@@ -0,0 +1,107 @@
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 OpenSim.Framework;
28using System.IO;
29
30namespace OpenSim.Framework.Servers.HttpServer
31{
32 /// <summary>
33 /// BaseStreamHandlerBasicDOSProtector Base streamed request handler.
34 /// </summary>
35 /// <remarks>
36 /// Inheriting classes should override ProcessRequest() rather than Handle()
37 /// </remarks>
38 public abstract class BaseStreamHandlerBasicDOSProtector : BaseRequestHandler, IStreamedRequestHandler
39 {
40
41 private readonly BasicDosProtectorOptions _options;
42 private readonly BasicDOSProtector _dosProtector;
43
44 protected BaseStreamHandlerBasicDOSProtector(string httpMethod, string path, BasicDosProtectorOptions options) : this(httpMethod, path, null, null, options) {}
45
46 protected BaseStreamHandlerBasicDOSProtector(string httpMethod, string path, string name, string description, BasicDosProtectorOptions options)
47 : base(httpMethod, path, name, description)
48 {
49 _options = options;
50 _dosProtector = new BasicDOSProtector(_options);
51 }
52
53 public virtual byte[] Handle(
54 string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
55 {
56 byte[] result;
57 RequestsReceived++;
58 string clientstring = GetClientString(httpRequest);
59 string endpoint = GetRemoteAddr(httpRequest);
60 if (_dosProtector.Process(clientstring, endpoint))
61 result = ProcessRequest(path, request, httpRequest, httpResponse);
62 else
63 result = ThrottledRequest(path, request, httpRequest, httpResponse);
64 if (_options.MaxConcurrentSessions > 0)
65 _dosProtector.ProcessEnd(clientstring, endpoint);
66
67 RequestsHandled++;
68
69 return result;
70 }
71
72 protected virtual byte[] ProcessRequest(
73 string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
74 {
75 return null;
76 }
77
78 protected virtual byte[] ThrottledRequest(
79 string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
80 {
81 return new byte[0];
82 }
83
84
85 private string GetRemoteAddr(IOSHttpRequest httpRequest)
86 {
87 string remoteaddr = string.Empty;
88 if (httpRequest.Headers["remote_addr"] != null)
89 remoteaddr = httpRequest.Headers["remote_addr"];
90
91 return remoteaddr;
92 }
93
94 private string GetClientString(IOSHttpRequest httpRequest)
95 {
96 string clientstring = string.Empty;
97
98 if (_options.AllowXForwardedFor && httpRequest.Headers["x-forwarded-for"] != null)
99 clientstring = httpRequest.Headers["x-forwarded-for"];
100 else
101 clientstring = GetRemoteAddr(httpRequest);
102
103 return clientstring;
104
105 }
106 }
107}
diff --git a/OpenSim/Framework/Servers/HttpServer/BinaryStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/BinaryStreamHandler.cs
new file mode 100644
index 0000000..1b03f54
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/BinaryStreamHandler.cs
@@ -0,0 +1,76 @@
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.IO;
29using System.Text;
30
31namespace OpenSim.Framework.Servers.HttpServer
32{
33 public delegate string BinaryMethod(byte[] data, string path, string param);
34
35 public class BinaryStreamHandler : BaseStreamHandler
36 {
37 private BinaryMethod m_method;
38
39 public BinaryStreamHandler(string httpMethod, string path, BinaryMethod binaryMethod)
40 : this(httpMethod, path, binaryMethod, null, null) {}
41
42 public BinaryStreamHandler(string httpMethod, string path, BinaryMethod binaryMethod, string name, string description)
43 : base(httpMethod, path, name, description)
44 {
45 m_method = binaryMethod;
46 }
47
48 protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
49 {
50 byte[] data = ReadFully(request);
51 string param = GetParam(path);
52 string responseString = m_method(data, path, param);
53
54 return Encoding.UTF8.GetBytes(responseString);
55 }
56
57 private static byte[] ReadFully(Stream stream)
58 {
59 byte[] buffer = new byte[1024];
60 using (MemoryStream ms = new MemoryStream(1024*256))
61 {
62 while (true)
63 {
64 int read = stream.Read(buffer, 0, buffer.Length);
65
66 if (read <= 0)
67 {
68 return ms.ToArray();
69 }
70
71 ms.Write(buffer, 0, read);
72 }
73 }
74 }
75 }
76} \ No newline at end of file
diff --git a/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs
new file mode 100644
index 0000000..cd4b8ff
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs
@@ -0,0 +1,119 @@
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.Collections;
29
30namespace OpenSim.Framework.Servers.HttpServer
31{
32 public class GenericHTTPDOSProtector
33 {
34 private readonly GenericHTTPMethod _normalMethod;
35 private readonly GenericHTTPMethod _throttledMethod;
36
37 private readonly BasicDosProtectorOptions _options;
38 private readonly BasicDOSProtector _dosProtector;
39
40 public GenericHTTPDOSProtector(GenericHTTPMethod normalMethod, GenericHTTPMethod throttledMethod, BasicDosProtectorOptions options)
41 {
42 _normalMethod = normalMethod;
43 _throttledMethod = throttledMethod;
44
45 _options = options;
46 _dosProtector = new BasicDOSProtector(_options);
47 }
48 public Hashtable Process(Hashtable request)
49 {
50 Hashtable process = null;
51 string clientstring= GetClientString(request);
52 string endpoint = GetRemoteAddr(request);
53 if (_dosProtector.Process(clientstring, endpoint))
54 process = _normalMethod(request);
55 else
56 process = _throttledMethod(request);
57
58 if (_options.MaxConcurrentSessions>0)
59 _dosProtector.ProcessEnd(clientstring, endpoint);
60
61 return process;
62 }
63
64 private string GetRemoteAddr(Hashtable request)
65 {
66 string remoteaddr = "";
67 if (!request.ContainsKey("headers"))
68 return remoteaddr;
69 Hashtable requestinfo = (Hashtable)request["headers"];
70 if (!requestinfo.ContainsKey("remote_addr"))
71 return remoteaddr;
72 object remote_addrobj = requestinfo["remote_addr"];
73 if (remote_addrobj != null)
74 {
75 if (!string.IsNullOrEmpty(remote_addrobj.ToString()))
76 {
77 remoteaddr = remote_addrobj.ToString();
78 }
79
80 }
81 return remoteaddr;
82 }
83
84 private string GetClientString(Hashtable request)
85 {
86 string clientstring = "";
87 if (!request.ContainsKey("headers"))
88 return clientstring;
89
90 Hashtable requestinfo = (Hashtable)request["headers"];
91 if (_options.AllowXForwardedFor && requestinfo.ContainsKey("x-forwarded-for"))
92 {
93 object str = requestinfo["x-forwarded-for"];
94 if (str != null)
95 {
96 if (!string.IsNullOrEmpty(str.ToString()))
97 {
98 return str.ToString();
99 }
100 }
101 }
102 if (!requestinfo.ContainsKey("remote_addr"))
103 return clientstring;
104
105 object remote_addrobj = requestinfo["remote_addr"];
106 if (remote_addrobj != null)
107 {
108 if (!string.IsNullOrEmpty(remote_addrobj.ToString()))
109 {
110 clientstring = remote_addrobj.ToString();
111 }
112 }
113
114 return clientstring;
115
116 }
117
118 }
119}
diff --git a/OpenSim/Framework/Servers/HttpServer/GenericHTTPMethod.cs b/OpenSim/Framework/Servers/HttpServer/GenericHTTPMethod.cs
new file mode 100644
index 0000000..456f3a4
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/GenericHTTPMethod.cs
@@ -0,0 +1,33 @@
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.Collections;
29
30namespace OpenSim.Framework.Servers.HttpServer
31{
32 public delegate Hashtable GenericHTTPMethod(Hashtable request);
33}
diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpAgentHandler.cs b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpAgentHandler.cs
new file mode 100644
index 0000000..7078895
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpAgentHandler.cs
@@ -0,0 +1,35 @@
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
28namespace OpenSim.Framework.Servers.HttpServer
29{
30 public interface IHttpAgentHandler
31 {
32 bool Handle(OSHttpRequest req, OSHttpResponse resp);
33 bool Match(OSHttpRequest req, OSHttpResponse resp);
34 }
35}
diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs
new file mode 100644
index 0000000..d162bc1
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.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 */
27
28using Nwc.XmlRpc;
29
30namespace OpenSim.Framework.Servers.HttpServer
31{
32 /// <summary>
33 /// Interface to OpenSimulator's built in HTTP server. Use this to register handlers (http, llsd, xmlrpc, etc.)
34 /// for given URLs.
35 /// </summary>
36 public interface IHttpServer
37 {
38 uint SSLPort { get; }
39 string SSLCommonName { get; }
40
41 uint Port { get; }
42 bool UseSSL { get; }
43
44// // Note that the agent string is provided simply to differentiate
45// // the handlers - it is NOT required to be an actual agent header
46// // value.
47// bool AddAgentHandler(string agent, IHttpAgentHandler handler);
48
49 /// <summary>
50 /// Add a handler for an HTTP request.
51 /// </summary>
52 /// <remarks>
53 /// This handler can actually be invoked either as
54 ///
55 /// http://<hostname>:<port>/?method=<methodName>
56 ///
57 /// or
58 ///
59 /// http://<hostname>:<port><method>
60 ///
61 /// if the method name starts with a slash. For example, AddHTTPHandler("/object/", ...) on a standalone region
62 /// server will register a handler that can be invoked with either
63 ///
64 /// http://localhost:9000/?method=/object/
65 ///
66 /// or
67 ///
68 /// http://localhost:9000/object/
69 ///
70 /// In addition, the handler invoked by the HTTP server for any request is the one when best matches the request
71 /// URI. So if a handler for "/myapp/" is registered and a request for "/myapp/page" is received, then
72 /// the "/myapp/" handler is invoked if no "/myapp/page" handler exists.
73 /// </remarks>
74 /// <param name="methodName"></param>
75 /// <param name="handler"></param>
76 /// <returns>
77 /// true if the handler was successfully registered, false if a handler with the same name already existed.
78 /// </returns>
79 bool AddHTTPHandler(string methodName, GenericHTTPMethod handler);
80
81 bool AddPollServiceHTTPHandler(string methodName, PollServiceEventArgs args);
82
83 /// <summary>
84 /// Adds a LLSD handler, yay.
85 /// </summary>
86 /// <param name="path">/resource/ path</param>
87 /// <param name="handler">handle the LLSD response</param>
88 /// <returns></returns>
89 bool AddLLSDHandler(string path, LLSDMethod handler);
90
91 /// <summary>
92 /// Add a stream handler to the http server. If the handler already exists, then nothing happens.
93 /// </summary>
94 /// <param name="handler"></param>
95 void AddStreamHandler(IRequestHandler handler);
96
97 bool AddXmlRPCHandler(string method, XmlRpcMethod handler);
98 bool AddXmlRPCHandler(string method, XmlRpcMethod handler, bool keepAlive);
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
112 /// <summary>
113 /// Gets the XML RPC handler for given method name
114 /// </summary>
115 /// <param name="method">Name of the method</param>
116 /// <returns>Returns null if not found</returns>
117 XmlRpcMethod GetXmlRPCHandler(string method);
118
119 bool SetDefaultLLSDHandler(DefaultLLSDMethod handler);
120
121// /// <summary>
122// /// Remove the agent if it is registered.
123// /// </summary>
124// /// <param name="agent"></param>
125// /// <param name="handler"></param>
126// /// <returns></returns>
127// bool RemoveAgentHandler(string agent, IHttpAgentHandler handler);
128
129 /// <summary>
130 /// Remove an HTTP handler
131 /// </summary>
132 /// <param name="httpMethod"></param>
133 /// <param name="path"></param>
134 void RemoveHTTPHandler(string httpMethod, string path);
135
136 void RemovePollServiceHTTPHandler(string httpMethod, string path);
137
138 bool RemoveLLSDHandler(string path, LLSDMethod handler);
139
140 void RemoveStreamHandler(string httpMethod, string path);
141
142 void RemoveXmlRPCHandler(string method);
143
144 void RemoveJsonRPCHandler(string method);
145
146 string GetHTTP404(string host);
147
148 string GetHTTP500();
149 }
150}
diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/IOSHttpRequest.cs b/OpenSim/Framework/Servers/HttpServer/Interfaces/IOSHttpRequest.cs
new file mode 100644
index 0000000..caf0edd
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/IOSHttpRequest.cs
@@ -0,0 +1,59 @@
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.Specialized;
31using System.IO;
32using System.Net;
33using System.Text;
34using System.Web;
35
36namespace OpenSim.Framework.Servers.HttpServer
37{
38 public interface IOSHttpRequest
39 {
40 string[] AcceptTypes { get; }
41 Encoding ContentEncoding { get; }
42 long ContentLength { get; }
43 long ContentLength64 { get; }
44 string ContentType { get; }
45 HttpCookieCollection Cookies { get; }
46 bool HasEntityBody { get; }
47 NameValueCollection Headers { get; }
48 string HttpMethod { get; }
49 Stream InputStream { get; }
50 bool IsSecured { get; }
51 bool KeepAlive { get; }
52 NameValueCollection QueryString { get; }
53 Hashtable Query { get; }
54 string RawUrl { get; }
55 IPEndPoint RemoteIPEndPoint { get; }
56 Uri Url { get; }
57 string UserAgent { get; }
58 }
59} \ No newline at end of file
diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/IOSHttpResponse.cs b/OpenSim/Framework/Servers/HttpServer/Interfaces/IOSHttpResponse.cs
new file mode 100644
index 0000000..f61b090
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/IOSHttpResponse.cs
@@ -0,0 +1,132 @@
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.Specialized;
31using System.IO;
32using System.Net;
33using System.Text;
34using System.Web;
35
36namespace OpenSim.Framework.Servers.HttpServer
37{
38 public interface IOSHttpResponse
39 {
40 /// <summary>
41 /// Content type property.
42 /// </summary>
43 /// <remarks>
44 /// Setting this property will also set IsContentTypeSet to
45 /// true.
46 /// </remarks>
47 string ContentType { get; set; }
48
49 /// <summary>
50 /// Boolean property indicating whether the content type
51 /// property actively has been set.
52 /// </summary>
53 /// <remarks>
54 /// IsContentTypeSet will go away together with .NET base.
55 /// </remarks>
56 // public bool IsContentTypeSet
57 // {
58 // get { return _contentTypeSet; }
59 // }
60 // private bool _contentTypeSet;
61
62 /// <summary>
63 /// Length of the body content; 0 if there is no body.
64 /// </summary>
65 long ContentLength { get; set; }
66
67 /// <summary>
68 /// Alias for ContentLength.
69 /// </summary>
70 long ContentLength64 { get; set; }
71
72 /// <summary>
73 /// Encoding of the body content.
74 /// </summary>
75 Encoding ContentEncoding { get; set; }
76
77 bool KeepAlive { get; set; }
78
79 /// <summary>
80 /// Get or set the keep alive timeout property (default is
81 /// 20). Setting this to 0 also disables KeepAlive. Setting
82 /// this to something else but 0 also enable KeepAlive.
83 /// </summary>
84 int KeepAliveTimeout { get; set; }
85
86 /// <summary>
87 /// Return the output stream feeding the body.
88 /// </summary>
89 /// <remarks>
90 /// On its way out...
91 /// </remarks>
92 Stream OutputStream { get; }
93
94 string ProtocolVersion { get; set; }
95
96 /// <summary>
97 /// Return the output stream feeding the body.
98 /// </summary>
99 Stream Body { get; }
100
101 /// <summary>
102 /// Set a redirct location.
103 /// </summary>
104 string RedirectLocation { set; }
105
106 /// <summary>
107 /// Chunk transfers.
108 /// </summary>
109 bool SendChunked { get; set; }
110
111 /// <summary>
112 /// HTTP status code.
113 /// </summary>
114 int StatusCode { get; set; }
115
116 /// <summary>
117 /// HTTP status description.
118 /// </summary>
119 string StatusDescription { get; set; }
120
121 bool ReuseContext { get; set; }
122
123 /// <summary>
124 /// Add a header field and content to the response.
125 /// </summary>
126 /// <param name="key">string containing the header field
127 /// name</param>
128 /// <param name="value">string containing the header field
129 /// value</param>
130 void AddHeader(string key, string value);
131 }
132} \ No newline at end of file
diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs
new file mode 100644
index 0000000..b8541cb
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs
@@ -0,0 +1,91 @@
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.Collections;
29using System.IO;
30
31namespace OpenSim.Framework.Servers.HttpServer
32{
33 public interface IRequestHandler
34 {
35 /// <summary>
36 /// Name for this handler.
37 /// </summary>
38 /// <remarks>
39 /// Used for diagnostics. The path doesn't always describe what the handler does. Can be null if none
40 /// specified.
41 /// </remarks>
42 string Name { get; }
43
44 /// <summary>
45 /// Description for this handler.
46 /// </summary>
47 /// <remarks>
48 /// Used for diagnostics. The path doesn't always describe what the handler does. Can be null if none
49 /// specified.
50 /// </remarks>
51 string Description { get; }
52
53 // Return response content type
54 string ContentType { get; }
55
56 // Return required http method
57 string HttpMethod { get; }
58
59 // Return path
60 string Path { get; }
61
62 /// <summary>
63 /// Number of requests received by this handler
64 /// </summary>
65 int RequestsReceived { get; }
66
67 /// <summary>
68 /// Number of requests handled.
69 /// </summary>
70 /// <remarks>
71 /// Should be equal to RequestsReceived unless requested are being handled slowly or there is deadlock.
72 /// </remarks>
73 int RequestsHandled { get; }
74 }
75
76 public interface IStreamedRequestHandler : IRequestHandler
77 {
78 // Handle request stream, return byte array
79 byte[] Handle(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse);
80 }
81
82 public interface IStreamHandler : IRequestHandler
83 {
84 void Handle(string path, Stream request, Stream response, IOSHttpRequest httpReqbuest, IOSHttpResponse httpResponse);
85 }
86
87 public interface IGenericHTTPHandler : IRequestHandler
88 {
89 Hashtable Handle(string path, Hashtable request);
90 }
91} \ No newline at end of file
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/JsonRpcRequestManager.cs b/OpenSim/Framework/Servers/HttpServer/JsonRpcRequestManager.cs
new file mode 100644
index 0000000..2fe1a7d
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/JsonRpcRequestManager.cs
@@ -0,0 +1,190 @@
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.Net;
30using System.Net.Sockets;
31using System.Reflection;
32using System.Text;
33using System.IO;
34using OpenMetaverse.StructuredData;
35using OpenMetaverse;
36using log4net;
37
38namespace OpenSim.Framework.Servers.HttpServer
39{
40 /// <summary>
41 /// Json rpc request manager.
42 /// </summary>
43 public class JsonRpcRequestManager
44 {
45 static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
46
47 public JsonRpcRequestManager()
48 {
49 }
50
51 /// <summary>
52 /// Sends json-rpc request with a serializable type.
53 /// </summary>
54 /// <returns>
55 /// OSD Map.
56 /// </returns>
57 /// <param name='parameters'>
58 /// Serializable type .
59 /// </param>
60 /// <param name='method'>
61 /// Json-rpc method to call.
62 /// </param>
63 /// <param name='uri'>
64 /// URI of json-rpc service.
65 /// </param>
66 /// <param name='jsonId'>
67 /// Id for our call.
68 /// </param>
69 public bool JsonRpcRequest(ref object parameters, string method, string uri, string jsonId)
70 {
71 if (jsonId == null)
72 throw new ArgumentNullException("jsonId");
73 if (uri == null)
74 throw new ArgumentNullException("uri");
75 if (method == null)
76 throw new ArgumentNullException("method");
77 if (parameters == null)
78 throw new ArgumentNullException("parameters");
79
80 OSDMap request = new OSDMap();
81 request.Add("jsonrpc", OSD.FromString("2.0"));
82 request.Add("id", OSD.FromString(jsonId));
83 request.Add("method", OSD.FromString(method));
84 request.Add("params", OSD.SerializeMembers(parameters));
85
86 OSDMap response;
87 try
88 {
89 response = WebUtil.PostToService(uri, request, 10000, true);
90 }
91 catch (Exception e)
92 {
93 m_log.Debug(string.Format("JsonRpc request '{0}' to {1} failed", method, uri), e);
94 return false;
95 }
96
97 if (!response.ContainsKey("_Result"))
98 {
99 m_log.DebugFormat("JsonRpc request '{0}' to {1} returned an invalid response: {2}",
100 method, uri, OSDParser.SerializeJsonString(response));
101 return false;
102 }
103 response = (OSDMap)response["_Result"];
104
105 OSD data;
106
107 if (response.ContainsKey("error"))
108 {
109 data = response["error"];
110 m_log.DebugFormat("JsonRpc request '{0}' to {1} returned an error: {2}",
111 method, uri, OSDParser.SerializeJsonString(data));
112 return false;
113 }
114
115 if (!response.ContainsKey("result"))
116 {
117 m_log.DebugFormat("JsonRpc request '{0}' to {1} returned an invalid response: {2}",
118 method, uri, OSDParser.SerializeJsonString(response));
119 return false;
120 }
121
122 data = response["result"];
123 OSD.DeserializeMembers(ref parameters, (OSDMap)data);
124
125 return true;
126 }
127
128 /// <summary>
129 /// Sends json-rpc request with OSD parameter.
130 /// </summary>
131 /// <returns>
132 /// The rpc request.
133 /// </returns>
134 /// <param name='data'>
135 /// data - incoming as parameters, outgoing as result/error
136 /// </param>
137 /// <param name='method'>
138 /// Json-rpc method to call.
139 /// </param>
140 /// <param name='uri'>
141 /// URI of json-rpc service.
142 /// </param>
143 /// <param name='jsonId'>
144 /// If set to <c>true</c> json identifier.
145 /// </param>
146 public bool JsonRpcRequest(ref OSD data, string method, string uri, string jsonId)
147 {
148 if (string.IsNullOrEmpty(jsonId))
149 jsonId = UUID.Random().ToString();
150
151 OSDMap request = new OSDMap();
152 request.Add("jsonrpc", OSD.FromString("2.0"));
153 request.Add("id", OSD.FromString(jsonId));
154 request.Add("method", OSD.FromString(method));
155 request.Add("params", data);
156
157 OSDMap response;
158 try
159 {
160 response = WebUtil.PostToService(uri, request, 10000, true);
161 }
162 catch (Exception e)
163 {
164 m_log.Debug(string.Format("JsonRpc request '{0}' to {1} failed", method, uri), e);
165 return false;
166 }
167
168 if (!response.ContainsKey("_Result"))
169 {
170 m_log.DebugFormat("JsonRpc request '{0}' to {1} returned an invalid response: {2}",
171 method, uri, OSDParser.SerializeJsonString(response));
172 return false;
173 }
174 response = (OSDMap)response["_Result"];
175
176 if (response.ContainsKey("error"))
177 {
178 data = response["error"];
179 m_log.DebugFormat("JsonRpc request '{0}' to {1} returned an error: {2}",
180 method, uri, OSDParser.SerializeJsonString(data));
181 return false;
182 }
183
184 data = response;
185
186 return true;
187 }
188
189 }
190}
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/LLSDMethod.cs b/OpenSim/Framework/Servers/HttpServer/LLSDMethod.cs
new file mode 100644
index 0000000..35655c6
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/LLSDMethod.cs
@@ -0,0 +1,35 @@
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 OSD LLSDMethod(string path, OSD request, string endpoint);
34 public delegate OSD DefaultLLSDMethod(OSD request, IPEndPoint client);
35}
diff --git a/OpenSim/Framework/Servers/HttpServer/LLSDMethodString.cs b/OpenSim/Framework/Servers/HttpServer/LLSDMethodString.cs
new file mode 100644
index 0000000..563b88f
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/LLSDMethodString.cs
@@ -0,0 +1,33 @@
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 OpenMetaverse.StructuredData;
29
30namespace OpenSim.Framework.Servers.HttpServer
31{
32 public delegate OSD LLSDMethodString(OSD request, string thePath);
33}
diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpHandler.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpHandler.cs
new file mode 100644
index 0000000..2c2b47d
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/OSHttpHandler.cs
@@ -0,0 +1,183 @@
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.Text.RegularExpressions;
32
33namespace OpenSim.Framework.Servers.HttpServer
34{
35 /// <sumary>
36 /// Any OSHttpHandler must return one of the following results:
37 /// <list type = "table">
38 /// <listheader>
39 /// <term>result code</term>
40 /// <description>meaning</description>
41 /// </listheader>
42 /// <item>
43 /// <term>Pass</term>
44 /// <description>handler did not process the request</request>
45 /// </item>
46 /// <item>
47 /// <term>Done</term>
48 /// <description>handler did process the request, OSHttpServer
49 /// can clean up and close the request</request>
50 /// </item>
51 /// </list>
52 /// </summary>
53 public enum OSHttpHandlerResult
54 {
55 Unprocessed,
56 Pass,
57 Done,
58 }
59
60 /// <summary>
61 /// An OSHttpHandler that matches on the "content-type" header can
62 /// supply an OSHttpContentTypeChecker delegate which will be
63 /// invoked by the request matcher in OSHttpRequestPump.
64 /// </summary>
65 /// <returns>true if the handler is interested in the content;
66 /// false otherwise</returns>
67 public delegate bool OSHttpContentTypeChecker(OSHttpRequest req);
68
69 public abstract class OSHttpHandler
70 {
71 /// <summary>
72 /// Regular expression used to match against method of
73 /// the incoming HTTP request. If you want to match any string
74 /// either use '.*' or null. To match on the empty string use
75 /// '^$'.
76 /// </summary>
77 public virtual Regex Method
78 {
79 get { return _method; }
80 }
81 protected Regex _method;
82
83 /// <summary>
84 /// Regular expression used to match against path of the
85 /// incoming HTTP request. If you want to match any string
86 /// either use '.*' or null. To match on the empty string use
87 /// '^$'.
88 /// </summary>
89 public virtual Regex Path
90 {
91 get { return _path; }
92 }
93 protected Regex _path;
94
95 /// <summary>
96 /// Dictionary of (query name, regular expression) tuples,
97 /// allowing us to match on URI query fields.
98 /// </summary>
99 public virtual Dictionary<string, Regex> Query
100 {
101 get { return _query; }
102 }
103 protected Dictionary<string, Regex> _query;
104
105 /// <summary>
106 /// Dictionary of (header name, regular expression) tuples,
107 /// allowing us to match on HTTP header fields.
108 /// </summary>
109 public virtual Dictionary<string, Regex> Headers
110 {
111 get { return _headers; }
112 }
113 protected Dictionary<string, Regex> _headers;
114
115 /// <summary>
116 /// Dictionary of (header name, regular expression) tuples,
117 /// allowing us to match on HTTP header fields.
118 /// </summary>
119 /// <remarks>
120 /// This feature is currently not implemented as it requires
121 /// (trivial) changes to HttpServer.HttpListener that have not
122 /// been implemented.
123 /// </remarks>
124 public virtual Regex IPEndPointWhitelist
125 {
126 get { return _ipEndPointRegex; }
127 }
128 protected Regex _ipEndPointRegex;
129
130
131 /// <summary>
132 /// Base class constructor.
133 /// </summary>
134 /// <param name="path">null or path regex</param>
135 /// <param name="headers">null or dictionary of header
136 /// regexs</param>
137 /// <param name="contentType">null or content type
138 /// regex</param>
139 /// <param name="whitelist">null or IP address regex</param>
140 public OSHttpHandler(Regex method, Regex path, Dictionary<string, Regex> query,
141 Dictionary<string, Regex> headers, Regex contentType, Regex whitelist)
142 {
143 _method = method;
144 _path = path;
145 _query = query;
146 _ipEndPointRegex = whitelist;
147
148 if (null == _headers && null != contentType)
149 {
150 _headers = new Dictionary<string, Regex>();
151 _headers.Add("content-type", contentType);
152 }
153 }
154
155
156 /// <summary>
157 /// Process an incoming OSHttpRequest that matched our
158 /// requirements.
159 /// </summary>
160 /// <returns>
161 /// OSHttpHandlerResult.Pass if we are after all not
162 /// interested in the request; OSHttpHandlerResult.Done if we
163 /// did process the request.
164 /// </returns>
165 public abstract OSHttpHandlerResult Process(OSHttpRequest request);
166
167 public override string ToString()
168 {
169 StringWriter sw = new StringWriter();
170 sw.WriteLine("{0}", base.ToString());
171 sw.WriteLine(" method regex {0}", null == Method ? "null" : Method.ToString());
172 sw.WriteLine(" path regex {0}", null == Path ? "null": Path.ToString());
173 foreach (string tag in Headers.Keys)
174 {
175 sw.WriteLine(" header {0} : {1}", tag, Headers[tag].ToString());
176 }
177 sw.WriteLine(" IP whitelist {0}", null == IPEndPointWhitelist ? "null" : IPEndPointWhitelist.ToString());
178 sw.WriteLine();
179 sw.Close();
180 return sw.ToString();
181 }
182 }
183}
diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpHttpHandler.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpHttpHandler.cs
new file mode 100644
index 0000000..95dafcd
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/OSHttpHttpHandler.cs
@@ -0,0 +1,145 @@
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.Net;
33using System.Reflection;
34using System.Text;
35using System.Text.RegularExpressions;
36using System.Xml;
37using log4net;
38using Nwc.XmlRpc;
39
40namespace OpenSim.Framework.Servers.HttpServer
41{
42 public delegate XmlRpcResponse OSHttpHttpProcessor(XmlRpcRequest request);
43
44 public class OSHttpHttpHandler: OSHttpHandler
45 {
46 private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
47
48 // contains handler for processing HTTP Request
49 private GenericHTTPMethod _handler;
50
51 /// <summary>
52 /// Instantiate an HTTP handler.
53 /// </summary>
54 /// <param name="handler">a GenericHTTPMethod</param>
55 /// <param name="method">null or HTTP method regex</param>
56 /// <param name="path">null or path regex</param>
57 /// <param name="query">null or dictionary with query regexs</param>
58 /// <param name="headers">null or dictionary with header
59 /// regexs</param>
60 /// <param name="whitelist">null or IP address whitelist</param>
61 public OSHttpHttpHandler(GenericHTTPMethod handler, Regex method, Regex path,
62 Dictionary<string, Regex> query,
63 Dictionary<string, Regex> headers, Regex whitelist)
64 : base(method, path, query, headers, new Regex(@"^text/html", RegexOptions.IgnoreCase | RegexOptions.Compiled),
65 whitelist)
66 {
67 _handler = handler;
68 }
69
70 /// <summary>
71 /// Instantiate an HTTP handler.
72 /// </summary>
73 /// <param name="handler">a GenericHTTPMethod</param>
74 public OSHttpHttpHandler(GenericHTTPMethod handler)
75 : this(handler, new Regex(@"^GET$", RegexOptions.IgnoreCase | RegexOptions.Compiled), null, null, null, null)
76 {
77 }
78
79 /// <summary>
80 /// Invoked by OSHttpRequestPump.
81 /// </summary>
82 public override OSHttpHandlerResult Process(OSHttpRequest request)
83 {
84 // call handler method
85 Hashtable responseData = _handler(request.Query);
86
87 int responseCode = (int)responseData["int_response_code"];
88 string responseString = (string)responseData["str_response_string"];
89 string contentType = (string)responseData["content_type"];
90
91 //Even though only one other part of the entire code uses HTTPHandlers, we shouldn't expect this
92 //and should check for NullReferenceExceptions
93
94 if (string.IsNullOrEmpty(contentType))
95 {
96 contentType = "text/html";
97 }
98
99 OSHttpResponse response = new OSHttpResponse(request);
100
101 // We're forgoing the usual error status codes here because the client
102 // ignores anything but 200 and 301
103
104 response.StatusCode = (int)OSHttpStatusCode.SuccessOk;
105
106 if (responseCode == (int)OSHttpStatusCode.RedirectMovedPermanently)
107 {
108 response.RedirectLocation = (string)responseData["str_redirect_location"];
109 response.StatusCode = responseCode;
110 }
111
112 response.AddHeader("Content-type", contentType);
113
114 byte[] buffer;
115
116 if (!contentType.Contains("image"))
117 {
118 buffer = Encoding.UTF8.GetBytes(responseString);
119 }
120 else
121 {
122 buffer = Convert.FromBase64String(responseString);
123 }
124
125 response.SendChunked = false;
126 response.ContentLength64 = buffer.Length;
127 response.ContentEncoding = Encoding.UTF8;
128
129 try
130 {
131 response.Body.Write(buffer, 0, buffer.Length);
132 }
133 catch (Exception ex)
134 {
135 _log.ErrorFormat("[OSHttpHttpHandler]: Error: {0}", ex.Message);
136 }
137 finally
138 {
139 response.Send();
140 }
141
142 return OSHttpHandlerResult.Done;
143 }
144 }
145}
diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpRequest.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpRequest.cs
new file mode 100644
index 0000000..05ec6dc
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/OSHttpRequest.cs
@@ -0,0 +1,271 @@
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.Collections.Specialized;
32using System.IO;
33using System.Net;
34using System.Reflection;
35using System.Text;
36using System.Web;
37using HttpServer;
38using log4net;
39
40namespace OpenSim.Framework.Servers.HttpServer
41{
42 public class OSHttpRequest : IOSHttpRequest
43 {
44 private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
45
46 protected IHttpRequest _request = null;
47 protected IHttpClientContext _context = null;
48
49 public string[] AcceptTypes
50 {
51 get { return _request.AcceptTypes; }
52 }
53
54 public Encoding ContentEncoding
55 {
56 get { return _contentEncoding; }
57 }
58 private Encoding _contentEncoding;
59
60 public long ContentLength
61 {
62 get { return _request.ContentLength; }
63 }
64
65 public long ContentLength64
66 {
67 get { return ContentLength; }
68 }
69
70 public string ContentType
71 {
72 get { return _contentType; }
73 }
74 private string _contentType;
75
76 public HttpCookieCollection Cookies
77 {
78 get
79 {
80 RequestCookies cookies = _request.Cookies;
81 HttpCookieCollection httpCookies = new HttpCookieCollection();
82 foreach (RequestCookie cookie in cookies)
83 httpCookies.Add(new HttpCookie(cookie.Name, cookie.Value));
84 return httpCookies;
85 }
86 }
87
88 public bool HasEntityBody
89 {
90 get { return _request.ContentLength != 0; }
91 }
92
93 public NameValueCollection Headers
94 {
95 get { return _request.Headers; }
96 }
97
98 public string HttpMethod
99 {
100 get { return _request.Method; }
101 }
102
103 public Stream InputStream
104 {
105 get { return _request.Body; }
106 }
107
108 public bool IsSecured
109 {
110 get { return _context.IsSecured; }
111 }
112
113 public bool KeepAlive
114 {
115 get { return ConnectionType.KeepAlive == _request.Connection; }
116 }
117
118 public NameValueCollection QueryString
119 {
120 get { return _queryString; }
121 }
122 private NameValueCollection _queryString;
123
124 public Hashtable Query
125 {
126 get { return _query; }
127 }
128 private Hashtable _query;
129
130 /// <value>
131 /// POST request values, if applicable
132 /// </value>
133// public Hashtable Form { get; private set; }
134
135 public string RawUrl
136 {
137 get { return _request.Uri.AbsolutePath; }
138 }
139
140 public IPEndPoint RemoteIPEndPoint
141 {
142 get { return _remoteIPEndPoint; }
143 }
144 private IPEndPoint _remoteIPEndPoint;
145
146 public Uri Url
147 {
148 get { return _request.Uri; }
149 }
150
151 public string UserAgent
152 {
153 get { return _userAgent; }
154 }
155 private string _userAgent;
156
157 internal IHttpRequest IHttpRequest
158 {
159 get { return _request; }
160 }
161
162 internal IHttpClientContext IHttpClientContext
163 {
164 get { return _context; }
165 }
166
167 /// <summary>
168 /// Internal whiteboard for handlers to store temporary stuff
169 /// into.
170 /// </summary>
171 internal Dictionary<string, object> Whiteboard
172 {
173 get { return _whiteboard; }
174 }
175 private Dictionary<string, object> _whiteboard = new Dictionary<string, object>();
176
177 public OSHttpRequest() {}
178
179 public OSHttpRequest(IHttpClientContext context, IHttpRequest req)
180 {
181 _request = req;
182 _context = context;
183
184 if (null != req.Headers["content-encoding"])
185 {
186 try
187 {
188 _contentEncoding = Encoding.GetEncoding(_request.Headers["content-encoding"]);
189 }
190 catch (Exception)
191 {
192 // ignore
193 }
194 }
195
196 if (null != req.Headers["content-type"])
197 _contentType = _request.Headers["content-type"];
198 if (null != req.Headers["user-agent"])
199 _userAgent = req.Headers["user-agent"];
200
201 if (null != req.Headers["remote_addr"])
202 {
203 try
204 {
205 IPAddress addr = IPAddress.Parse(req.Headers["remote_addr"]);
206 // sometimes req.Headers["remote_port"] returns a comma separated list, so use
207 // the first one in the list and log it
208 string[] strPorts = req.Headers["remote_port"].Split(new char[] { ',' });
209 if (strPorts.Length > 1)
210 {
211 _log.ErrorFormat("[OSHttpRequest]: format exception on addr/port {0}:{1}, ignoring",
212 req.Headers["remote_addr"], req.Headers["remote_port"]);
213 }
214 int port = Int32.Parse(strPorts[0]);
215 _remoteIPEndPoint = new IPEndPoint(addr, port);
216 }
217 catch (FormatException)
218 {
219 _log.ErrorFormat("[OSHttpRequest]: format exception on addr/port {0}:{1}, ignoring",
220 req.Headers["remote_addr"], req.Headers["remote_port"]);
221 }
222 }
223
224 _queryString = new NameValueCollection();
225 _query = new Hashtable();
226 try
227 {
228 foreach (HttpInputItem item in req.QueryString)
229 {
230 try
231 {
232 _queryString.Add(item.Name, item.Value);
233 _query[item.Name] = item.Value;
234 }
235 catch (InvalidCastException)
236 {
237 _log.DebugFormat("[OSHttpRequest]: error parsing {0} query item, skipping it", item.Name);
238 continue;
239 }
240 }
241 }
242 catch (Exception)
243 {
244 _log.ErrorFormat("[OSHttpRequest]: Error parsing querystring");
245 }
246
247// Form = new Hashtable();
248// foreach (HttpInputItem item in req.Form)
249// {
250// _log.DebugFormat("[OSHttpRequest]: Got form item {0}={1}", item.Name, item.Value);
251// Form.Add(item.Name, item.Value);
252// }
253 }
254
255 public override string ToString()
256 {
257 StringBuilder me = new StringBuilder();
258 me.Append(String.Format("OSHttpRequest: {0} {1}\n", HttpMethod, RawUrl));
259 foreach (string k in Headers.AllKeys)
260 {
261 me.Append(String.Format(" {0}: {1}\n", k, Headers[k]));
262 }
263 if (null != RemoteIPEndPoint)
264 {
265 me.Append(String.Format(" IP: {0}\n", RemoteIPEndPoint));
266 }
267
268 return me.ToString();
269 }
270 }
271}
diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs
new file mode 100644
index 0000000..bdea278
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs
@@ -0,0 +1,298 @@
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
28// #define DEBUGGING
29
30using System;
31using System.Collections.Generic;
32using System.Collections.Specialized;
33using System.Diagnostics;
34using System.IO;
35using System.Net;
36using System.Reflection;
37using System.Text.RegularExpressions;
38using System.Threading;
39using log4net;
40using HttpServer;
41
42namespace OpenSim.Framework.Servers.HttpServer
43{
44 /// <summary>
45 /// An OSHttpRequestPump fetches incoming OSHttpRequest objects
46 /// from the OSHttpRequestQueue and feeds them to all subscribed
47 /// parties. Each OSHttpRequestPump encapsulates one thread to do
48 /// the work and there is a fixed number of pumps for each
49 /// OSHttpServer object.
50 /// </summary>
51 public class OSHttpRequestPump
52 {
53 private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
54
55 protected OSHttpServer _server;
56 protected OSHttpRequestQueue _queue;
57 protected Thread _engine;
58
59 private int _id;
60
61 public string EngineID
62 {
63 get { return String.Format("{0} pump {1}", _server.EngineID, _id); }
64 }
65
66 public OSHttpRequestPump(OSHttpServer server, OSHttpRequestQueue queue, int id)
67 {
68 _server = server;
69 _queue = queue;
70 _id = id;
71
72 _engine = new Thread(new ThreadStart(Engine));
73 _engine.IsBackground = true;
74 _engine.Start();
75 _engine.Name = string.Format ("Engine:{0}",EngineID);
76
77 ThreadTracker.Add(_engine);
78 }
79
80 public static OSHttpRequestPump[] Pumps(OSHttpServer server, OSHttpRequestQueue queue, int poolSize)
81 {
82 OSHttpRequestPump[] pumps = new OSHttpRequestPump[poolSize];
83 for (int i = 0; i < pumps.Length; i++)
84 {
85 pumps[i] = new OSHttpRequestPump(server, queue, i);
86 }
87
88 return pumps;
89 }
90
91 public void Start()
92 {
93 _engine = new Thread(new ThreadStart(Engine));
94 _engine.IsBackground = true;
95 _engine.Start();
96 _engine.Name = string.Format ("Engine:{0}",EngineID);
97
98 ThreadTracker.Add(_engine);
99 }
100
101 public void Engine()
102 {
103 OSHttpRequest req = null;
104
105 while (true)
106 {
107 try
108 {
109 // dequeue an OSHttpRequest from OSHttpServer's
110 // request queue
111 req = _queue.Dequeue();
112
113 // get a copy of the list of registered handlers
114 List<OSHttpHandler> handlers = _server.OSHttpHandlers;
115
116 // prune list and have it sorted from most
117 // specific to least specific
118 handlers = MatchHandlers(req, handlers);
119
120 // process req: we try each handler in turn until
121 // we are either out of handlers or get back a
122 // Pass or Done
123 OSHttpHandlerResult rc = OSHttpHandlerResult.Unprocessed;
124 foreach (OSHttpHandler h in handlers)
125 {
126 rc = h.Process(req);
127
128 // Pass: handler did not process the request,
129 // try next handler
130 if (OSHttpHandlerResult.Pass == rc) continue;
131
132 // Handled: handler has processed the request
133 if (OSHttpHandlerResult.Done == rc) break;
134
135 // hmm, something went wrong
136 throw new Exception(String.Format("[{0}] got unexpected OSHttpHandlerResult {1}", EngineID, rc));
137 }
138
139 if (OSHttpHandlerResult.Unprocessed == rc)
140 {
141 _log.InfoFormat("[{0}] OSHttpHandler: no handler registered for {1}", EngineID, req);
142
143 // set up response header
144 OSHttpResponse resp = new OSHttpResponse(req);
145 resp.StatusCode = (int)OSHttpStatusCode.ClientErrorNotFound;
146 resp.StatusDescription = String.Format("no handler on call for {0}", req);
147 resp.ContentType = "text/html";
148
149 // add explanatory message
150 StreamWriter body = new StreamWriter(resp.Body);
151 body.WriteLine("<html>");
152 body.WriteLine("<header><title>Ooops...</title><header>");
153 body.WriteLine(String.Format("<body><p>{0}</p></body>", resp.StatusDescription));
154 body.WriteLine("</html>");
155 body.Flush();
156
157 // and ship it back
158 resp.Send();
159 }
160 }
161 catch (Exception e)
162 {
163 _log.DebugFormat("[{0}] OSHttpHandler problem: {1}", EngineID, e.ToString());
164 _log.ErrorFormat("[{0}] OSHttpHandler problem: {1}", EngineID, e.Message);
165 }
166 }
167 }
168
169 protected List<OSHttpHandler> MatchHandlers(OSHttpRequest req, List<OSHttpHandler> handlers)
170 {
171 Dictionary<OSHttpHandler, int> scoredHandlers = new Dictionary<OSHttpHandler, int>();
172
173 _log.DebugFormat("[{0}] MatchHandlers for {1}", EngineID, req);
174 foreach (OSHttpHandler h in handlers)
175 {
176 // initial anchor
177 scoredHandlers[h] = 0;
178
179 // first, check whether IPEndPointWhitelist applies
180 // and, if it does, whether client is on that white
181 // list.
182 if (null != h.IPEndPointWhitelist)
183 {
184 // TODO: following code requires code changes to
185 // HttpServer.HttpRequest to become functional
186
187 IPEndPoint remote = req.RemoteIPEndPoint;
188 if (null != remote)
189 {
190 Match epm = h.IPEndPointWhitelist.Match(remote.ToString());
191 if (!epm.Success)
192 {
193 scoredHandlers.Remove(h);
194 continue;
195 }
196 }
197 }
198
199 if (null != h.Method)
200 {
201 Match m = h.Method.Match(req.HttpMethod);
202 if (!m.Success)
203 {
204 scoredHandlers.Remove(h);
205 continue;
206 }
207 scoredHandlers[h]++;
208 }
209
210 // whitelist ok, now check path
211 if (null != h.Path)
212 {
213 Match m = h.Path.Match(req.RawUrl);
214 if (!m.Success)
215 {
216 scoredHandlers.Remove(h);
217 continue;
218 }
219 scoredHandlers[h] += m.ToString().Length;
220 }
221
222 // whitelist & path ok, now check query string
223 if (null != h.Query)
224 {
225 int queriesMatch = MatchOnNameValueCollection(req.QueryString, h.Query);
226 if (0 == queriesMatch)
227 {
228 _log.DebugFormat("[{0}] request {1}", EngineID, req);
229 _log.DebugFormat("[{0}] dropping handler {1}", EngineID, h);
230
231 scoredHandlers.Remove(h);
232 continue;
233 }
234 scoredHandlers[h] += queriesMatch;
235 }
236
237 // whitelist, path, query string ok, now check headers
238 if (null != h.Headers)
239 {
240 int headersMatch = MatchOnNameValueCollection(req.Headers, h.Headers);
241 if (0 == headersMatch)
242 {
243 _log.DebugFormat("[{0}] request {1}", EngineID, req);
244 _log.DebugFormat("[{0}] dropping handler {1}", EngineID, h);
245
246 scoredHandlers.Remove(h);
247 continue;
248 }
249 scoredHandlers[h] += headersMatch;
250 }
251 }
252
253 List<OSHttpHandler> matchingHandlers = new List<OSHttpHandler>(scoredHandlers.Keys);
254 matchingHandlers.Sort(delegate(OSHttpHandler x, OSHttpHandler y)
255 {
256 return scoredHandlers[x] - scoredHandlers[y];
257 });
258 LogDumpHandlerList(matchingHandlers);
259 return matchingHandlers;
260 }
261
262 protected int MatchOnNameValueCollection(NameValueCollection collection, Dictionary<string, Regex> regexs)
263 {
264 int matched = 0;
265
266 foreach (string tag in regexs.Keys)
267 {
268 // do we have a header "tag"?
269 if (null == collection[tag])
270 {
271 return 0;
272 }
273
274 // does the content of collection[tag] match
275 // the supplied regex?
276 Match cm = regexs[tag].Match(collection[tag]);
277 if (!cm.Success)
278 {
279 return 0;
280 }
281
282 // ok: matches
283 matched++;
284 continue;
285 }
286
287 return matched;
288 }
289
290 [ConditionalAttribute("DEBUGGING")]
291 private void LogDumpHandlerList(List<OSHttpHandler> l)
292 {
293 _log.DebugFormat("[{0}] OSHttpHandlerList dump:", EngineID);
294 foreach (OSHttpHandler h in l)
295 _log.DebugFormat(" ", h.ToString());
296 }
297 }
298}
diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpRequestQueue.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpRequestQueue.cs
new file mode 100644
index 0000000..881c5d1
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/OSHttpRequestQueue.cs
@@ -0,0 +1,68 @@
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.Threading;
31using HttpServer;
32
33namespace OpenSim.Framework.Servers.HttpServer
34{
35 /// <summary>
36 /// OSHttpRequestQueues are used to hand over incoming HTTP
37 /// requests to OSHttpRequestPump objects.
38 /// </summary>
39 public class OSHttpRequestQueue : Queue<OSHttpRequest>
40 {
41 private object _syncObject = new object();
42
43 new public void Enqueue(OSHttpRequest req)
44 {
45 lock (_syncObject)
46 {
47 base.Enqueue(req);
48 Monitor.Pulse(_syncObject);
49 }
50 }
51
52 new public OSHttpRequest Dequeue()
53 {
54 OSHttpRequest req = null;
55
56 lock (_syncObject)
57 {
58 while (null == req)
59 {
60 Monitor.Wait(_syncObject);
61 if (0 != this.Count) req = base.Dequeue();
62 }
63 }
64
65 return req;
66 }
67 }
68}
diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpResponse.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpResponse.cs
new file mode 100644
index 0000000..89fb5d4
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/OSHttpResponse.cs
@@ -0,0 +1,332 @@
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.IO;
29using System.Net;
30using System.Text;
31using HttpServer;
32
33namespace OpenSim.Framework.Servers.HttpServer
34{
35 /// <summary>
36 /// OSHttpResponse is the OpenSim representation of an HTTP
37 /// response.
38 /// </summary>
39 public class OSHttpResponse : IOSHttpResponse
40 {
41 /// <summary>
42 /// Content type property.
43 /// </summary>
44 /// <remarks>
45 /// Setting this property will also set IsContentTypeSet to
46 /// true.
47 /// </remarks>
48 public virtual string ContentType
49 {
50 get
51 {
52 return _httpResponse.ContentType;
53 }
54
55 set
56 {
57 _httpResponse.ContentType = value;
58 }
59 }
60
61 /// <summary>
62 /// Boolean property indicating whether the content type
63 /// property actively has been set.
64 /// </summary>
65 /// <remarks>
66 /// IsContentTypeSet will go away together with .NET base.
67 /// </remarks>
68 // public bool IsContentTypeSet
69 // {
70 // get { return _contentTypeSet; }
71 // }
72 // private bool _contentTypeSet;
73
74
75 /// <summary>
76 /// Length of the body content; 0 if there is no body.
77 /// </summary>
78 public long ContentLength
79 {
80 get
81 {
82 return _httpResponse.ContentLength;
83 }
84
85 set
86 {
87 _httpResponse.ContentLength = value;
88 }
89 }
90
91 /// <summary>
92 /// Alias for ContentLength.
93 /// </summary>
94 public long ContentLength64
95 {
96 get { return ContentLength; }
97 set { ContentLength = value; }
98 }
99
100 /// <summary>
101 /// Encoding of the body content.
102 /// </summary>
103 public Encoding ContentEncoding
104 {
105 get
106 {
107 return _httpResponse.Encoding;
108 }
109
110 set
111 {
112 _httpResponse.Encoding = value;
113 }
114 }
115
116 public bool KeepAlive
117 {
118 get
119 {
120 return _httpResponse.Connection == ConnectionType.KeepAlive;
121 }
122
123 set
124 {
125 if (value)
126 _httpResponse.Connection = ConnectionType.KeepAlive;
127 else
128 _httpResponse.Connection = ConnectionType.Close;
129 }
130 }
131
132 /// <summary>
133 /// Get or set the keep alive timeout property (default is
134 /// 20). Setting this to 0 also disables KeepAlive. Setting
135 /// this to something else but 0 also enable KeepAlive.
136 /// </summary>
137 public int KeepAliveTimeout
138 {
139 get
140 {
141 return _httpResponse.KeepAlive;
142 }
143
144 set
145 {
146 if (value == 0)
147 {
148 _httpResponse.Connection = ConnectionType.Close;
149 _httpResponse.KeepAlive = 0;
150 }
151 else
152 {
153 _httpResponse.Connection = ConnectionType.KeepAlive;
154 _httpResponse.KeepAlive = value;
155 }
156 }
157 }
158
159 /// <summary>
160 /// Return the output stream feeding the body.
161 /// </summary>
162 /// <remarks>
163 /// On its way out...
164 /// </remarks>
165 public Stream OutputStream
166 {
167 get
168 {
169 return _httpResponse.Body;
170 }
171 }
172
173 public string ProtocolVersion
174 {
175 get
176 {
177 return _httpResponse.ProtocolVersion;
178 }
179
180 set
181 {
182 _httpResponse.ProtocolVersion = value;
183 }
184 }
185
186 /// <summary>
187 /// Return the output stream feeding the body.
188 /// </summary>
189 public Stream Body
190 {
191 get
192 {
193 return _httpResponse.Body;
194 }
195 }
196
197 /// <summary>
198 /// Set a redirct location.
199 /// </summary>
200 public string RedirectLocation
201 {
202 // get { return _redirectLocation; }
203 set
204 {
205 _httpResponse.Redirect(value);
206 }
207 }
208
209
210 /// <summary>
211 /// Chunk transfers.
212 /// </summary>
213 public bool SendChunked
214 {
215 get
216 {
217 return _httpResponse.Chunked;
218 }
219
220 set
221 {
222 _httpResponse.Chunked = value;
223 }
224 }
225
226 /// <summary>
227 /// HTTP status code.
228 /// </summary>
229 public virtual int StatusCode
230 {
231 get
232 {
233 return (int)_httpResponse.Status;
234 }
235
236 set
237 {
238 _httpResponse.Status = (HttpStatusCode)value;
239 }
240 }
241
242
243 /// <summary>
244 /// HTTP status description.
245 /// </summary>
246 public string StatusDescription
247 {
248 get
249 {
250 return _httpResponse.Reason;
251 }
252
253 set
254 {
255 _httpResponse.Reason = value;
256 }
257 }
258
259 public bool ReuseContext
260 {
261 get
262 {
263 if (_httpClientContext != null)
264 {
265 return !_httpClientContext.EndWhenDone;
266 }
267 return true;
268 }
269 set
270 {
271 if (_httpClientContext != null)
272 {
273 _httpClientContext.EndWhenDone = !value;
274 }
275 }
276 }
277
278 protected IHttpResponse _httpResponse;
279 private IHttpClientContext _httpClientContext;
280
281 public OSHttpResponse() {}
282
283 public OSHttpResponse(IHttpResponse resp)
284 {
285 _httpResponse = resp;
286 }
287
288 /// <summary>
289 /// Instantiate an OSHttpResponse object from an OSHttpRequest
290 /// object.
291 /// </summary
292 /// <param name="req">Incoming OSHttpRequest to which we are
293 /// replying</param>
294 public OSHttpResponse(OSHttpRequest req)
295 {
296 _httpResponse = new HttpResponse(req.IHttpClientContext, req.IHttpRequest);
297 _httpClientContext = req.IHttpClientContext;
298 }
299 public OSHttpResponse(HttpResponse resp, IHttpClientContext clientContext)
300 {
301 _httpResponse = resp;
302 _httpClientContext = clientContext;
303 }
304
305 /// <summary>
306 /// Add a header field and content to the response.
307 /// </summary>
308 /// <param name="key">string containing the header field
309 /// name</param>
310 /// <param name="value">string containing the header field
311 /// value</param>
312 public void AddHeader(string key, string value)
313 {
314 _httpResponse.AddHeader(key, value);
315 }
316
317 /// <summary>
318 /// Send the response back to the remote client
319 /// </summary>
320 public void Send()
321 {
322 _httpResponse.Body.Flush();
323 _httpResponse.Send();
324 }
325
326 public void FreeContext()
327 {
328 if (_httpClientContext != null)
329 _httpClientContext.Close();
330 }
331 }
332} \ No newline at end of file
diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpServer.cs
new file mode 100644
index 0000000..cd62842
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/OSHttpServer.cs
@@ -0,0 +1,210 @@
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.Net;
31using System.Net.Sockets;
32using System.Reflection;
33using System.Text.RegularExpressions;
34using System.Threading;
35using System.Security.Cryptography.X509Certificates;
36using log4net;
37using HttpServer;
38
39using HttpListener = HttpServer.HttpListener;
40
41namespace OpenSim.Framework.Servers.HttpServer
42{
43 /// <summary>
44 /// OSHttpServer provides an HTTP server bound to a specific
45 /// port. When instantiated with just address and port it uses
46 /// normal HTTP, when instantiated with address, port, and X509
47 /// certificate, it uses HTTPS.
48 /// </summary>
49 public class OSHttpServer
50 {
51 private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
52
53 private object _syncObject = new object();
54
55 // underlying HttpServer.HttpListener
56 protected HttpListener _listener;
57 // underlying core/engine thread
58 protected Thread _engine;
59
60 // Queue containing (OS)HttpRequests
61 protected OSHttpRequestQueue _queue;
62
63 // OSHttpRequestPumps "pumping" incoming OSHttpRequests
64 // upwards
65 protected OSHttpRequestPump[] _pumps;
66
67 // thread identifier
68 protected string _engineId;
69 public string EngineID
70 {
71 get { return _engineId; }
72 }
73
74 /// <summary>
75 /// True if this is an HTTPS connection; false otherwise.
76 /// </summary>
77 protected bool _isSecure;
78 public bool IsSecure
79 {
80 get { return _isSecure; }
81 }
82
83 public int QueueSize
84 {
85 get { return _pumps.Length; }
86 }
87
88 /// <summary>
89 /// List of registered OSHttpHandlers for this OSHttpServer instance.
90 /// </summary>
91 protected List<OSHttpHandler> _httpHandlers = new List<OSHttpHandler>();
92 public List<OSHttpHandler> OSHttpHandlers
93 {
94 get
95 {
96 lock (_httpHandlers)
97 {
98 return new List<OSHttpHandler>(_httpHandlers);
99 }
100 }
101 }
102
103
104 /// <summary>
105 /// Instantiate an HTTP server.
106 /// </summary>
107 public OSHttpServer(IPAddress address, int port, int poolSize)
108 {
109 _engineId = String.Format("OSHttpServer (HTTP:{0})", port);
110 _isSecure = false;
111 _log.DebugFormat("[{0}] HTTP server instantiated", EngineID);
112
113 _listener = new HttpListener(address, port);
114 _queue = new OSHttpRequestQueue();
115 _pumps = OSHttpRequestPump.Pumps(this, _queue, poolSize);
116 }
117
118 /// <summary>
119 /// Instantiate an HTTPS server.
120 /// </summary>
121 public OSHttpServer(IPAddress address, int port, X509Certificate certificate, int poolSize)
122 {
123 _engineId = String.Format("OSHttpServer [HTTPS:{0}/ps:{1}]", port, poolSize);
124 _isSecure = true;
125 _log.DebugFormat("[{0}] HTTPS server instantiated", EngineID);
126
127 _listener = new HttpListener(address, port, certificate);
128 _queue = new OSHttpRequestQueue();
129 _pumps = OSHttpRequestPump.Pumps(this, _queue, poolSize);
130 }
131
132 /// <summary>
133 /// Turn an HttpRequest into an OSHttpRequestItem and place it
134 /// in the queue. The OSHttpRequestQueue object will pulse the
135 /// next available idle pump.
136 /// </summary>
137 protected void OnHttpRequest(HttpClientContext client, HttpRequest request)
138 {
139 // turn request into OSHttpRequest
140 OSHttpRequest req = new OSHttpRequest(client, request);
141
142 // place OSHttpRequest into _httpRequestQueue, will
143 // trigger Pulse to idle waiting pumps
144 _queue.Enqueue(req);
145 }
146
147 /// <summary>
148 /// Start the HTTP server engine.
149 /// </summary>
150 public void Start()
151 {
152 _engine = new Thread(new ThreadStart(Engine));
153 _engine.IsBackground = true;
154 _engine.Start();
155 _engine.Name = string.Format ("Engine:{0}",_engineId);
156
157 ThreadTracker.Add(_engine);
158
159 // start the pumps...
160 for (int i = 0; i < _pumps.Length; i++)
161 _pumps[i].Start();
162 }
163
164 public void Stop()
165 {
166 lock (_syncObject) Monitor.Pulse(_syncObject);
167 }
168
169 /// <summary>
170 /// Engine keeps the HTTP server running.
171 /// </summary>
172 private void Engine()
173 {
174 try {
175 _listener.RequestHandler += OnHttpRequest;
176 _listener.Start(QueueSize);
177 _log.InfoFormat("[{0}] HTTP server started", EngineID);
178
179 lock (_syncObject) Monitor.Wait(_syncObject);
180 }
181 catch (Exception ex)
182 {
183 _log.DebugFormat("[{0}] HTTP server startup failed: {1}", EngineID, ex.ToString());
184 }
185
186 _log.InfoFormat("[{0}] HTTP server terminated", EngineID);
187 }
188
189
190 /// <summary>
191 /// Add an HTTP request handler.
192 /// </summary>
193 /// <param name="handler">OSHttpHandler delegate</param>
194 /// <param name="path">regex object for path matching</parm>
195 /// <param name="headers">dictionary containing header names
196 /// and regular expressions to match against header values</param>
197 public void AddHandler(OSHttpHandler handler)
198 {
199 lock (_httpHandlers)
200 {
201 if (_httpHandlers.Contains(handler))
202 {
203 _log.DebugFormat("[OSHttpServer] attempt to add already existing handler ignored");
204 return;
205 }
206 _httpHandlers.Add(handler);
207 }
208 }
209 }
210}
diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpStatusCodes.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpStatusCodes.cs
new file mode 100644
index 0000000..a736c8b
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/OSHttpStatusCodes.cs
@@ -0,0 +1,279 @@
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
28namespace OpenSim.Framework.Servers.HttpServer
29{
30 /// <summary>
31 /// HTTP status codes (almost) as defined by W3C in http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html and IETF in http://tools.ietf.org/html/rfc6585
32 /// </summary>
33 public enum OSHttpStatusCode : int
34 {
35 #region 1xx Informational status codes providing a provisional response.
36
37 /// <summary>
38 /// 100 Tells client that to keep on going sending its request
39 /// </summary>
40 InfoContinue = 100,
41
42 /// <summary>
43 /// 101 Server understands request, proposes to switch to different application level protocol
44 /// </summary>
45 InfoSwitchingProtocols = 101,
46
47 #endregion
48
49 #region 2xx Success codes
50
51 /// <summary>
52 /// 200 Request successful
53 /// </summary>
54 SuccessOk = 200,
55
56 /// <summary>
57 /// 201 Request successful, new resource created
58 /// </summary>
59 SuccessOkCreated = 201,
60
61 /// <summary>
62 /// 202 Request accepted, processing still on-going
63 /// </summary>
64 SuccessOkAccepted = 202,
65
66 /// <summary>
67 /// 203 Request successful, meta information not authoritative
68 /// </summary>
69 SuccessOkNonAuthoritativeInformation = 203,
70
71 /// <summary>
72 /// 204 Request successful, nothing to return in the body
73 /// </summary>
74 SuccessOkNoContent = 204,
75
76 /// <summary>
77 /// 205 Request successful, reset displayed content
78 /// </summary>
79 SuccessOkResetContent = 205,
80
81 /// <summary>
82 /// 206 Request successful, partial content returned
83 /// </summary>
84 SuccessOkPartialContent = 206,
85
86 #endregion
87
88 #region 3xx Redirect code: user agent needs to go somewhere else
89
90 /// <summary>
91 /// 300 Redirect: different presentation forms available, take a pick
92 /// </summary>
93 RedirectMultipleChoices = 300,
94
95 /// <summary>
96 /// 301 Redirect: requested resource has moved and now lives somewhere else
97 /// </summary>
98 RedirectMovedPermanently = 301,
99
100 /// <summary>
101 /// 302 Redirect: Resource temporarily somewhere else, location might change
102 /// </summary>
103 RedirectFound = 302,
104
105 /// <summary>
106 /// 303 Redirect: See other as result of a POST
107 /// </summary>
108 RedirectSeeOther = 303,
109
110 /// <summary>
111 /// 304 Redirect: Resource still the same as before
112 /// </summary>
113 RedirectNotModified = 304,
114
115 /// <summary>
116 /// 305 Redirect: Resource must be accessed via proxy provided in location field
117 /// </summary>
118 RedirectUseProxy = 305,
119
120 /// <summary>
121 /// 307 Redirect: Resource temporarily somewhere else, location might change
122 /// </summary>
123 RedirectMovedTemporarily = 307,
124
125 #endregion
126
127 #region 4xx Client error: the client borked the request
128
129 /// <summary>
130 /// 400 Client error: bad request, server does not grok what the client wants
131 /// </summary>
132 ClientErrorBadRequest = 400,
133
134 /// <summary>
135 /// 401 Client error: the client is not authorized, response provides WWW-Authenticate header field with a challenge
136 /// </summary>
137 ClientErrorUnauthorized = 401,
138
139 /// <summary>
140 /// 402 Client error: Payment required (reserved for future use)
141 /// </summary>
142 ClientErrorPaymentRequired = 402,
143
144 /// <summary>
145 /// 403 Client error: Server understood request, will not deliver, do not try again.
146 ClientErrorForbidden = 403,
147
148 /// <summary>
149 /// 404 Client error: Server cannot find anything matching the client request.
150 /// </summary>
151 ClientErrorNotFound = 404,
152
153 /// <summary>
154 /// 405 Client error: The method specified by the client in the request is not allowed for the resource requested
155 /// </summary>
156 ClientErrorMethodNotAllowed = 405,
157
158 /// <summary>
159 /// 406 Client error: Server cannot generate suitable response for the resource and content characteristics requested by the client
160 /// </summary>
161 ClientErrorNotAcceptable = 406,
162
163 /// <summary>
164 /// 407 Client error: Similar to 401, Server requests that client authenticate itself with the proxy first
165 /// </summary>
166 ClientErrorProxyAuthRequired = 407,
167
168 /// <summary>
169 /// 408 Client error: Server got impatient with client and decided to give up waiting for the client's request to arrive
170 /// </summary>
171 ClientErrorRequestTimeout = 408,
172
173 /// <summary>
174 /// 409 Client error: Server could not fulfill the request for a resource as there is a conflict with the current state of the resource but thinks client can do something about this
175 /// </summary>
176 ClientErrorConflict = 409,
177
178 /// <summary>
179 /// 410 Client error: The resource has moved somewhere else, but server has no clue where.
180 /// </summary>
181 ClientErrorGone = 410,
182
183 /// <summary>
184 /// 411 Client error: The server is picky again and insists on having a content-length header field in the request
185 /// </summary>
186 ClientErrorLengthRequired = 411,
187
188 /// <summary>
189 /// 412 Client error: one or more preconditions supplied in the client's request is false
190 /// </summary>
191 ClientErrorPreconditionFailed = 412,
192
193 /// <summary>
194 /// 413 Client error: For fear of reflux, the server refuses to swallow that much data.
195 /// </summary>
196 ClientErrorRequestEntityToLarge = 413,
197
198 /// <summary>
199 /// 414 Client error: The server considers the Request-URI to be indecently long and refuses to even look at it.
200 /// </summary>
201 ClientErrorRequestURITooLong = 414,
202
203 /// <summary>
204 /// 415 Client error: The server has no clue about the media type requested by the client (contrary to popular belief it is not a warez server)
205 /// </summary>
206 ClientErrorUnsupportedMediaType = 415,
207
208 /// <summary>
209 /// 416 Client error: The requested range cannot be delivered by the server.
210 /// </summary>
211 ClientErrorRequestRangeNotSatisfiable = 416,
212
213 /// <summary>
214 /// 417 Client error: The expectations of the client as expressed in one or more Expect header fields cannot be met by the server, the server is awfully sorry about this.
215 /// </summary>
216 ClientErrorExpectationFailed = 417,
217
218 /// <summary>
219 /// 428 Client error :The 428 status code indicates that the origin server requires the request to be conditional.
220 /// </summary>
221 ClientErrorPreconditionRequired = 428,
222
223 /// <summary>
224 /// 429 Client error: The 429 status code indicates that the user has sent too many requests in a given amount of time ("rate limiting").
225 /// </summary>
226 ClientErrorTooManyRequests = 429,
227
228 /// <summary>
229 /// 431 Client error: The 431 status code indicates that the server is unwilling to process the request because its header fields are too large. The request MAY be resubmitted after reducing the size of the request header fields.
230 /// </summary>
231 ClientErrorRequestHeaderFieldsTooLarge = 431,
232
233 /// <summary>
234 /// 499 Client error: Wildcard error.
235 /// </summary>
236 ClientErrorJoker = 499,
237
238 #endregion
239
240 #region 5xx Server errors (rare)
241
242 /// <summary>
243 /// 500 Server error: something really strange and unexpected happened
244 /// </summary>
245 ServerErrorInternalError = 500,
246
247 /// <summary>
248 /// 501 Server error: The server does not do the functionality required to carry out the client request. not at all. certainly not before breakfast. but also not after breakfast.
249 /// </summary>
250 ServerErrorNotImplemented = 501,
251
252 /// <summary>
253 /// 502 Server error: While acting as a proxy or a gateway, the server got ditched by the upstream server and as a consequence regretfully cannot fulfill the client's request
254 /// </summary>
255 ServerErrorBadGateway = 502,
256
257 /// <summary>
258 /// 503 Server error: Due to unforseen circumstances the server cannot currently deliver the service requested. Retry-After header might indicate when to try again.
259 /// </summary>
260 ServerErrorServiceUnavailable = 503,
261
262 /// <summary>
263 /// 504 Server error: The server blames the upstream server for not being able to deliver the service requested and claims that the upstream server is too slow delivering the goods.
264 /// </summary>
265 ServerErrorGatewayTimeout = 504,
266
267 /// <summary>
268 /// 505 Server error: The server does not support the HTTP version conveyed in the client's request.
269 /// </summary>
270 ServerErrorHttpVersionNotSupported = 505,
271
272 /// <summary>
273 /// 511 Server error: The 511 status code indicates that the client needs to authenticate to gain network access.
274 /// </summary>
275 ServerErrorNetworkAuthenticationRequired = 511,
276
277 #endregion
278 }
279}
diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpXmlRpcHandler.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpXmlRpcHandler.cs
new file mode 100644
index 0000000..ef573a4
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/OSHttpXmlRpcHandler.cs
@@ -0,0 +1,180 @@
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.Net;
32using System.Reflection;
33using System.Text;
34using System.Text.RegularExpressions;
35using System.Xml;
36using log4net;
37using Nwc.XmlRpc;
38
39namespace OpenSim.Framework.Servers.HttpServer
40{
41 public delegate XmlRpcResponse OSHttpXmlRpcProcessor(XmlRpcRequest request);
42
43 public class OSHttpXmlRpcHandler: OSHttpHandler
44 {
45 private static readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
46
47 /// <summary>
48 /// XmlRpcMethodMatch tries to reify (deserialize) an incoming
49 /// XmlRpc request (and posts it to the "whiteboard") and
50 /// checks whether the method name is one we are interested
51 /// in.
52 /// </summary>
53 /// <returns>true if the handler is interested in the content;
54 /// false otherwise</returns>
55 protected bool XmlRpcMethodMatch(OSHttpRequest req)
56 {
57 XmlRpcRequest xmlRpcRequest = null;
58
59 // check whether req is already reified
60 // if not: reify (and post to whiteboard)
61 try
62 {
63 if (req.Whiteboard.ContainsKey("xmlrequest"))
64 {
65 xmlRpcRequest = req.Whiteboard["xmlrequest"] as XmlRpcRequest;
66 }
67 else
68 {
69 StreamReader body = new StreamReader(req.InputStream);
70 string requestBody = body.ReadToEnd();
71 xmlRpcRequest = (XmlRpcRequest)(new XmlRpcRequestDeserializer()).Deserialize(requestBody);
72 req.Whiteboard["xmlrequest"] = xmlRpcRequest;
73 }
74 }
75 catch (XmlException)
76 {
77 _log.ErrorFormat("[OSHttpXmlRpcHandler] failed to deserialize XmlRpcRequest from {0}", req.ToString());
78 return false;
79 }
80
81 // check against methodName
82 if ((null != xmlRpcRequest)
83 && !String.IsNullOrEmpty(xmlRpcRequest.MethodName)
84 && xmlRpcRequest.MethodName == _methodName)
85 {
86 _log.DebugFormat("[OSHttpXmlRpcHandler] located handler {0} for {1}", _methodName, req.ToString());
87 return true;
88 }
89
90 return false;
91 }
92
93 // contains handler for processing XmlRpc Request
94 private XmlRpcMethod _handler;
95
96 // contains XmlRpc method name
97 private string _methodName;
98
99
100 /// <summary>
101 /// Instantiate an XmlRpc handler.
102 /// </summary>
103 /// <param name="handler">XmlRpcMethod
104 /// delegate</param>
105 /// <param name="methodName">XmlRpc method name</param>
106 /// <param name="path">XmlRpc path prefix (regular expression)</param>
107 /// <param name="headers">Dictionary with header names and
108 /// regular expressions to match content of headers</param>
109 /// <param name="whitelist">IP whitelist of remote end points
110 /// to accept (regular expression)</param>
111 /// <remarks>
112 /// Except for handler and methodName, all other parameters
113 /// can be null, in which case they are not taken into account
114 /// when the handler is being looked up.
115 /// </remarks>
116 public OSHttpXmlRpcHandler(XmlRpcMethod handler, string methodName, Regex path,
117 Dictionary<string, Regex> headers, Regex whitelist)
118 : base(new Regex(@"^POST$", RegexOptions.IgnoreCase | RegexOptions.Compiled), path, null, headers,
119 new Regex(@"^(text|application)/xml", RegexOptions.IgnoreCase | RegexOptions.Compiled),
120 whitelist)
121 {
122 _handler = handler;
123 _methodName = methodName;
124 }
125
126
127 /// <summary>
128 /// Instantiate an XmlRpc handler.
129 /// </summary>
130 /// <param name="handler">XmlRpcMethod
131 /// delegate</param>
132 /// <param name="methodName">XmlRpc method name</param>
133 public OSHttpXmlRpcHandler(XmlRpcMethod handler, string methodName)
134 : this(handler, methodName, null, null, null)
135 {
136 }
137
138
139 /// <summary>
140 /// Invoked by OSHttpRequestPump.
141 /// </summary>
142 public override OSHttpHandlerResult Process(OSHttpRequest request)
143 {
144 XmlRpcResponse xmlRpcResponse;
145 string responseString;
146
147 // check whether we are interested in this request
148 if (!XmlRpcMethodMatch(request)) return OSHttpHandlerResult.Pass;
149
150
151 OSHttpResponse resp = new OSHttpResponse(request);
152 try
153 {
154 // reified XmlRpcRequest must still be on the whiteboard
155 XmlRpcRequest xmlRpcRequest = request.Whiteboard["xmlrequest"] as XmlRpcRequest;
156 xmlRpcResponse = _handler(xmlRpcRequest);
157 responseString = XmlRpcResponseSerializer.Singleton.Serialize(xmlRpcResponse);
158
159 resp.ContentType = "text/xml";
160 byte[] buffer = Encoding.UTF8.GetBytes(responseString);
161
162 resp.SendChunked = false;
163 resp.ContentLength = buffer.Length;
164 resp.ContentEncoding = Encoding.UTF8;
165
166 resp.Body.Write(buffer, 0, buffer.Length);
167 resp.Body.Flush();
168
169 resp.Send();
170
171 }
172 catch (Exception ex)
173 {
174 _log.WarnFormat("[OSHttpXmlRpcHandler]: Error: {0}", ex.Message);
175 return OSHttpHandlerResult.Pass;
176 }
177 return OSHttpHandlerResult.Done;
178 }
179 }
180}
diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs
new file mode 100644
index 0000000..9477100
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs
@@ -0,0 +1,86 @@
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 OpenMetaverse;
31
32namespace OpenSim.Framework.Servers.HttpServer
33{
34 public delegate void RequestMethod(UUID requestID, Hashtable request);
35 public delegate bool HasEventsMethod(UUID requestID, UUID pId);
36
37 public delegate Hashtable GetEventsMethod(UUID requestID, UUID pId);
38
39 public delegate Hashtable NoEventsMethod(UUID requestID, UUID pId);
40
41 public class PollServiceEventArgs : EventArgs
42 {
43 public HasEventsMethod HasEvents;
44 public GetEventsMethod GetEvents;
45 public NoEventsMethod NoEvents;
46 public RequestMethod Request;
47 public UUID Id;
48 public int TimeOutms;
49 public EventType Type;
50
51 public enum EventType : int
52 {
53 LongPoll = 0,
54 LslHttp = 1,
55 Inventory = 2
56 }
57
58 public string Url { get; set; }
59
60 /// <summary>
61 /// Number of requests received for this poll service.
62 /// </summary>
63 public int RequestsReceived { get; set; }
64
65 /// <summary>
66 /// Number of requests handled by this poll service.
67 /// </summary>
68 public int RequestsHandled { get; set; }
69
70 public PollServiceEventArgs(
71 RequestMethod pRequest,
72 string pUrl,
73 HasEventsMethod pHasEvents, GetEventsMethod pGetEvents, NoEventsMethod pNoEvents,
74 UUID pId, int pTimeOutms)
75 {
76 Request = pRequest;
77 Url = pUrl;
78 HasEvents = pHasEvents;
79 GetEvents = pGetEvents;
80 NoEvents = pNoEvents;
81 Id = pId;
82 TimeOutms = pTimeOutms;
83 Type = EventType.LongPoll;
84 }
85 }
86}
diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs
new file mode 100644
index 0000000..caf0e98
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs
@@ -0,0 +1,97 @@
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.Reflection;
31using System.Text;
32using HttpServer;
33using log4net;
34using OpenMetaverse;
35
36namespace OpenSim.Framework.Servers.HttpServer
37{
38 public class PollServiceHttpRequest
39 {
40 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
41
42 public readonly PollServiceEventArgs PollServiceArgs;
43 public readonly IHttpClientContext HttpContext;
44 public readonly IHttpRequest Request;
45 public readonly int RequestTime;
46 public readonly UUID RequestID;
47
48 public PollServiceHttpRequest(
49 PollServiceEventArgs pPollServiceArgs, IHttpClientContext pHttpContext, IHttpRequest pRequest)
50 {
51 PollServiceArgs = pPollServiceArgs;
52 HttpContext = pHttpContext;
53 Request = pRequest;
54 RequestTime = System.Environment.TickCount;
55 RequestID = UUID.Random();
56 }
57
58 internal void DoHTTPGruntWork(BaseHttpServer server, Hashtable responsedata)
59 {
60 OSHttpResponse response
61 = new OSHttpResponse(new HttpResponse(HttpContext, Request), HttpContext);
62
63 byte[] buffer = server.DoHTTPGruntWork(responsedata, response);
64
65 response.SendChunked = false;
66 response.ContentLength64 = buffer.Length;
67 response.ContentEncoding = Encoding.UTF8;
68
69 try
70 {
71 response.OutputStream.Write(buffer, 0, buffer.Length);
72 }
73 catch (Exception ex)
74 {
75 m_log.Warn("[POLL SERVICE WORKER THREAD]: Error ", ex);
76 }
77 finally
78 {
79 //response.OutputStream.Close();
80 try
81 {
82 response.OutputStream.Flush();
83 response.Send();
84
85 //if (!response.KeepAlive && response.ReuseContext)
86 // response.FreeContext();
87 }
88 catch (Exception e)
89 {
90 m_log.Warn("[POLL SERVICE WORKER THREAD]: Error ", e);
91 }
92
93 PollServiceArgs.RequestsHandled++;
94 }
95 }
96 }
97} \ No newline at end of file
diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
new file mode 100644
index 0000000..28bba70
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
@@ -0,0 +1,331 @@
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.Threading;
31using System.Reflection;
32using log4net;
33using HttpServer;
34using OpenSim.Framework;
35using OpenSim.Framework.Monitoring;
36using Amib.Threading;
37using System.IO;
38using System.Text;
39using System.Collections.Generic;
40
41namespace OpenSim.Framework.Servers.HttpServer
42{
43 public class PollServiceRequestManager
44 {
45 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
46
47 /// <summary>
48 /// Is the poll service request manager running?
49 /// </summary>
50 /// <remarks>
51 /// Can be running either synchronously or asynchronously
52 /// </remarks>
53 public bool IsRunning { get; private set; }
54
55 /// <summary>
56 /// Is the poll service performing responses asynchronously (with its own threads) or synchronously (via
57 /// external calls)?
58 /// </summary>
59 public bool PerformResponsesAsync { get; private set; }
60
61 /// <summary>
62 /// Number of responses actually processed and sent to viewer (or aborted due to error).
63 /// </summary>
64 public int ResponsesProcessed { get; private set; }
65
66 private readonly BaseHttpServer m_server;
67
68 private BlockingQueue<PollServiceHttpRequest> m_requests = new BlockingQueue<PollServiceHttpRequest>();
69 private static List<PollServiceHttpRequest> m_longPollRequests = new List<PollServiceHttpRequest>();
70
71 private uint m_WorkerThreadCount = 0;
72 private Thread[] m_workerThreads;
73
74 private SmartThreadPool m_threadPool = new SmartThreadPool(20000, 12, 2);
75
76// private int m_timeout = 1000; // increase timeout 250; now use the event one
77
78 public PollServiceRequestManager(
79 BaseHttpServer pSrv, bool performResponsesAsync, uint pWorkerThreadCount, int pTimeout)
80 {
81 m_server = pSrv;
82 PerformResponsesAsync = performResponsesAsync;
83 m_WorkerThreadCount = pWorkerThreadCount;
84 m_workerThreads = new Thread[m_WorkerThreadCount];
85
86 StatsManager.RegisterStat(
87 new Stat(
88 "QueuedPollResponses",
89 "Number of poll responses queued for processing.",
90 "",
91 "",
92 "httpserver",
93 m_server.Port.ToString(),
94 StatType.Pull,
95 MeasuresOfInterest.AverageChangeOverTime,
96 stat => stat.Value = m_requests.Count(),
97 StatVerbosity.Debug));
98
99 StatsManager.RegisterStat(
100 new Stat(
101 "ProcessedPollResponses",
102 "Number of poll responses processed.",
103 "",
104 "",
105 "httpserver",
106 m_server.Port.ToString(),
107 StatType.Pull,
108 MeasuresOfInterest.AverageChangeOverTime,
109 stat => stat.Value = ResponsesProcessed,
110 StatVerbosity.Debug));
111 }
112
113 public void Start()
114 {
115 IsRunning = true;
116
117 if (PerformResponsesAsync)
118 {
119 //startup worker threads
120 for (uint i = 0; i < m_WorkerThreadCount; i++)
121 {
122 m_workerThreads[i]
123 = WorkManager.StartThread(
124 PoolWorkerJob,
125 string.Format("PollServiceWorkerThread{0}:{1}", i, m_server.Port),
126 ThreadPriority.Normal,
127 false,
128 false,
129 null,
130 int.MaxValue);
131 }
132
133 WorkManager.StartThread(
134 this.CheckLongPollThreads,
135 string.Format("LongPollServiceWatcherThread:{0}", m_server.Port),
136 ThreadPriority.Normal,
137 false,
138 true,
139 null,
140 1000 * 60 * 10);
141 }
142 }
143
144 private void ReQueueEvent(PollServiceHttpRequest req)
145 {
146 if (IsRunning)
147 {
148 // delay the enqueueing for 100ms. There's no need to have the event
149 // actively on the queue
150 Timer t = new Timer(self => {
151 ((Timer)self).Dispose();
152 m_requests.Enqueue(req);
153 });
154
155 t.Change(100, Timeout.Infinite);
156
157 }
158 }
159
160 public void Enqueue(PollServiceHttpRequest req)
161 {
162 if (IsRunning)
163 {
164 if (req.PollServiceArgs.Type == PollServiceEventArgs.EventType.LongPoll)
165 {
166 lock (m_longPollRequests)
167 m_longPollRequests.Add(req);
168 }
169 else
170 m_requests.Enqueue(req);
171 }
172 }
173
174 private void CheckLongPollThreads()
175 {
176 // The only purpose of this thread is to check the EQs for events.
177 // If there are events, that thread will be placed in the "ready-to-serve" queue, m_requests.
178 // If there are no events, that thread will be back to its "waiting" queue, m_longPollRequests.
179 // All other types of tasks (Inventory handlers, http-in, etc) don't have the long-poll nature,
180 // so if they aren't ready to be served by a worker thread (no events), they are placed
181 // directly back in the "ready-to-serve" queue by the worker thread.
182 while (IsRunning)
183 {
184 Thread.Sleep(500);
185 Watchdog.UpdateThread();
186
187// List<PollServiceHttpRequest> not_ready = new List<PollServiceHttpRequest>();
188 lock (m_longPollRequests)
189 {
190 if (m_longPollRequests.Count > 0 && IsRunning)
191 {
192 List<PollServiceHttpRequest> ready = m_longPollRequests.FindAll(req =>
193 (req.PollServiceArgs.HasEvents(req.RequestID, req.PollServiceArgs.Id) || // there are events in this EQ
194 (Environment.TickCount - req.RequestTime) > req.PollServiceArgs.TimeOutms) // no events, but timeout
195 );
196
197 ready.ForEach(req =>
198 {
199 m_requests.Enqueue(req);
200 m_longPollRequests.Remove(req);
201 });
202
203 }
204
205 }
206 }
207 }
208
209 public void Stop()
210 {
211 IsRunning = false;
212// m_timeout = -10000; // cause all to expire
213 Thread.Sleep(1000); // let the world move
214
215 foreach (Thread t in m_workerThreads)
216 Watchdog.AbortThread(t.ManagedThreadId);
217
218 PollServiceHttpRequest wreq;
219
220 lock (m_longPollRequests)
221 {
222 if (m_longPollRequests.Count > 0 && IsRunning)
223 m_longPollRequests.ForEach(req => m_requests.Enqueue(req));
224 }
225
226 while (m_requests.Count() > 0)
227 {
228 try
229 {
230 wreq = m_requests.Dequeue(0);
231 ResponsesProcessed++;
232 wreq.DoHTTPGruntWork(
233 m_server, wreq.PollServiceArgs.NoEvents(wreq.RequestID, wreq.PollServiceArgs.Id));
234 }
235 catch
236 {
237 }
238 }
239
240 m_longPollRequests.Clear();
241 m_requests.Clear();
242 }
243
244 // work threads
245
246 private void PoolWorkerJob()
247 {
248 while (IsRunning)
249 {
250 Watchdog.UpdateThread();
251 WaitPerformResponse();
252 }
253 }
254
255 public void WaitPerformResponse()
256 {
257 PollServiceHttpRequest req = m_requests.Dequeue(5000);
258// m_log.DebugFormat("[YYY]: Dequeued {0}", (req == null ? "null" : req.PollServiceArgs.Type.ToString()));
259
260 if (req != null)
261 {
262 try
263 {
264 if (req.PollServiceArgs.HasEvents(req.RequestID, req.PollServiceArgs.Id))
265 {
266 Hashtable responsedata = req.PollServiceArgs.GetEvents(req.RequestID, req.PollServiceArgs.Id);
267
268 if (responsedata == null)
269 return;
270
271 // This is the event queue.
272 // Even if we're not running we can still perform responses by explicit request.
273 if (req.PollServiceArgs.Type == PollServiceEventArgs.EventType.LongPoll
274 || !PerformResponsesAsync)
275 {
276 try
277 {
278 ResponsesProcessed++;
279 req.DoHTTPGruntWork(m_server, responsedata);
280 }
281 catch (ObjectDisposedException e) // Browser aborted before we could read body, server closed the stream
282 {
283 // Ignore it, no need to reply
284 m_log.Error(e);
285 }
286 }
287 else
288 {
289 m_threadPool.QueueWorkItem(x =>
290 {
291 try
292 {
293 ResponsesProcessed++;
294 req.DoHTTPGruntWork(m_server, responsedata);
295 }
296 catch (ObjectDisposedException e) // Browser aborted before we could read body, server closed the stream
297 {
298 // Ignore it, no need to reply
299 m_log.Error(e);
300 }
301 catch (Exception e)
302 {
303 m_log.Error(e);
304 }
305
306 return null;
307 }, null);
308 }
309 }
310 else
311 {
312 if ((Environment.TickCount - req.RequestTime) > req.PollServiceArgs.TimeOutms)
313 {
314 ResponsesProcessed++;
315 req.DoHTTPGruntWork(
316 m_server, req.PollServiceArgs.NoEvents(req.RequestID, req.PollServiceArgs.Id));
317 }
318 else
319 {
320 ReQueueEvent(req);
321 }
322 }
323 }
324 catch (Exception e)
325 {
326 m_log.ErrorFormat("Exception in poll service thread: " + e.ToString());
327 }
328 }
329 }
330 }
331} \ No newline at end of file
diff --git a/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs b/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..63335bd
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs
@@ -0,0 +1,33 @@
1using System.Reflection;
2using System.Runtime.CompilerServices;
3using System.Runtime.InteropServices;
4
5// General Information about an assembly is controlled through the following
6// set of attributes. Change these attribute values to modify the information
7// associated with an assembly.
8[assembly: AssemblyTitle("OpenSim.Framework.Servers.HttpServer")]
9[assembly: AssemblyDescription("")]
10[assembly: AssemblyConfiguration("")]
11[assembly: AssemblyCompany("http://opensimulator.org")]
12[assembly: AssemblyProduct("OpenSim")]
13[assembly: AssemblyCopyright("OpenSimulator developers")]
14[assembly: AssemblyTrademark("")]
15[assembly: AssemblyCulture("")]
16
17// Setting ComVisible to false makes the types in this assembly not visible
18// to COM components. If you need to access a type in this assembly from
19// COM, set the ComVisible attribute to true on that type.
20[assembly: ComVisible(false)]
21
22// The following GUID is for the ID of the typelib if this project is exposed to COM
23[assembly: Guid("c4ea5baa-81c4-4867-a645-1ec360c1f164")]
24
25// Version information for an assembly consists of the following four values:
26//
27// Major Version
28// Minor Version
29// Build Number
30// Revision
31//
32[assembly: AssemblyVersion("0.8.2.*")]
33
diff --git a/OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs b/OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs
new file mode 100644
index 0000000..bd55657
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs
@@ -0,0 +1,70 @@
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.IO;
29using System.Xml;
30using System.Xml.Serialization;
31
32namespace OpenSim.Framework.Servers.HttpServer
33{
34 public delegate TResponse RestDeserialiseMethod<TRequest, TResponse>(TRequest request);
35
36 public class RestDeserialiseHandler<TRequest, TResponse> : BaseOutputStreamHandler, IStreamHandler
37 where TRequest : new()
38 {
39 private RestDeserialiseMethod<TRequest, TResponse> m_method;
40
41 public RestDeserialiseHandler(string httpMethod, string path, RestDeserialiseMethod<TRequest, TResponse> method)
42 : this(httpMethod, path, method, null, null) {}
43
44 public RestDeserialiseHandler(
45 string httpMethod, string path, RestDeserialiseMethod<TRequest, TResponse> method, string name, string description)
46 : base(httpMethod, path, name, description)
47 {
48 m_method = method;
49 }
50
51 protected override void ProcessRequest(string path, Stream request, Stream responseStream,
52 IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
53 {
54 TRequest deserial;
55 using (XmlTextReader xmlReader = new XmlTextReader(request))
56 {
57 XmlSerializer deserializer = new XmlSerializer(typeof (TRequest));
58 deserial = (TRequest) deserializer.Deserialize(xmlReader);
59 }
60
61 TResponse response = m_method(deserial);
62
63 using (XmlWriter xmlWriter = XmlTextWriter.Create(responseStream))
64 {
65 XmlSerializer serializer = new XmlSerializer(typeof (TResponse));
66 serializer.Serialize(xmlWriter, response);
67 }
68 }
69 }
70}
diff --git a/OpenSim/Framework/Servers/HttpServer/RestHTTPHandler.cs b/OpenSim/Framework/Servers/HttpServer/RestHTTPHandler.cs
new file mode 100644
index 0000000..7f89839
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/RestHTTPHandler.cs
@@ -0,0 +1,62 @@
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.Collections;
29
30namespace OpenSim.Framework.Servers.HttpServer
31{
32 public class RestHTTPHandler : BaseHTTPHandler
33 {
34 private GenericHTTPMethod m_dhttpMethod;
35
36 public GenericHTTPMethod Method
37 {
38 get { return m_dhttpMethod; }
39 }
40
41 public RestHTTPHandler(string httpMethod, string path, GenericHTTPMethod dhttpMethod)
42 : base(httpMethod, path)
43 {
44 m_dhttpMethod = dhttpMethod;
45 }
46
47 public RestHTTPHandler(
48 string httpMethod, string path, GenericHTTPMethod dhttpMethod, string name, string description)
49 : base(httpMethod, path, name, description)
50 {
51 m_dhttpMethod = dhttpMethod;
52 }
53
54 public override Hashtable Handle(string path, Hashtable request)
55 {
56 string param = GetParam(path);
57 request.Add("param", param);
58 request.Add("path", path);
59 return m_dhttpMethod(request);
60 }
61 }
62}
diff --git a/OpenSim/Framework/Servers/HttpServer/RestMethod.cs b/OpenSim/Framework/Servers/HttpServer/RestMethod.cs
new file mode 100644
index 0000000..80bc7ef
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/RestMethod.cs
@@ -0,0 +1,32 @@
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
28namespace OpenSim.Framework.Servers.HttpServer
29{
30 public delegate string RestMethod(string request, string path, string param,
31 IOSHttpRequest httpRequest, IOSHttpResponse httpResponse);
32}
diff --git a/OpenSim/Framework/Servers/HttpServer/RestObjectPoster.cs b/OpenSim/Framework/Servers/HttpServer/RestObjectPoster.cs
new file mode 100644
index 0000000..afe052f
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/RestObjectPoster.cs
@@ -0,0 +1,86 @@
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.IO;
30using System.Net;
31using System.Text;
32using System.Xml;
33using System.Xml.Serialization;
34
35namespace OpenSim.Framework.Servers.HttpServer
36{
37 /// <summary>
38 /// Makes an asynchronous REST request which doesn't require us to do anything with the response.
39 /// </summary>
40 public class RestObjectPoster
41 {
42 public static void BeginPostObject<TRequest>(string requestUrl, TRequest obj)
43 {
44 BeginPostObject("POST", requestUrl, obj);
45 }
46
47 public static void BeginPostObject<TRequest>(string verb, string requestUrl, TRequest obj)
48 {
49 Type type = typeof (TRequest);
50
51 WebRequest request = WebRequest.Create(requestUrl);
52 request.Method = verb;
53 request.ContentType = "text/xml";
54
55 using (MemoryStream buffer = new MemoryStream())
56 {
57 XmlWriterSettings settings = new XmlWriterSettings();
58 settings.Encoding = Encoding.UTF8;
59
60 using (XmlWriter writer = XmlWriter.Create(buffer, settings))
61 {
62 XmlSerializer serializer = new XmlSerializer(type);
63 serializer.Serialize(writer, obj);
64 writer.Flush();
65 }
66
67 int length = (int)buffer.Length;
68 request.ContentLength = length;
69
70 using (Stream requestStream = request.GetRequestStream())
71 requestStream.Write(buffer.ToArray(), 0, length);
72 }
73
74 // IAsyncResult result = request.BeginGetResponse(AsyncCallback, request);
75 request.BeginGetResponse(AsyncCallback, request);
76 }
77
78 private static void AsyncCallback(IAsyncResult result)
79 {
80 WebRequest request = (WebRequest) result.AsyncState;
81 using (WebResponse resp = request.EndGetResponse(result))
82 {
83 }
84 }
85 }
86}
diff --git a/OpenSim/Framework/Servers/HttpServer/RestObjectPosterResponse.cs b/OpenSim/Framework/Servers/HttpServer/RestObjectPosterResponse.cs
new file mode 100644
index 0000000..a911b9f
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/RestObjectPosterResponse.cs
@@ -0,0 +1,108 @@
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.IO;
30using System.Net;
31using System.Text;
32using System.Xml;
33using System.Xml.Serialization;
34
35namespace OpenSim.Framework.Servers.HttpServer
36{
37 public delegate void ReturnResponse<T>(T reponse);
38
39 /// <summary>
40 /// Makes an asynchronous REST request with a callback to invoke with the response.
41 /// </summary>
42 public class RestObjectPosterResponse<TResponse>
43 {
44// private static readonly log4net.ILog m_log
45// = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
46
47 public ReturnResponse<TResponse> ResponseCallback;
48
49 public void BeginPostObject<TRequest>(string requestUrl, TRequest obj)
50 {
51 BeginPostObject("POST", requestUrl, obj);
52 }
53
54 public void BeginPostObject<TRequest>(string verb, string requestUrl, TRequest obj)
55 {
56 Type type = typeof (TRequest);
57
58 WebRequest request = WebRequest.Create(requestUrl);
59 request.Method = verb;
60 request.ContentType = "text/xml";
61 request.Timeout = 10000;
62
63 using (MemoryStream buffer = new MemoryStream())
64 {
65 XmlWriterSettings settings = new XmlWriterSettings();
66 settings.Encoding = Encoding.UTF8;
67
68 using (XmlWriter writer = XmlWriter.Create(buffer, settings))
69 {
70 XmlSerializer serializer = new XmlSerializer(type);
71 serializer.Serialize(writer, obj);
72 writer.Flush();
73 }
74
75 int length = (int)buffer.Length;
76 request.ContentLength = length;
77
78 using (Stream requestStream = request.GetRequestStream())
79 requestStream.Write(buffer.ToArray(), 0, length);
80 }
81
82 // IAsyncResult result = request.BeginGetResponse(AsyncCallback, request);
83 request.BeginGetResponse(AsyncCallback, request);
84 }
85
86 private void AsyncCallback(IAsyncResult result)
87 {
88 WebRequest request = (WebRequest) result.AsyncState;
89 using (WebResponse resp = request.EndGetResponse(result))
90 {
91 TResponse deserial;
92 XmlSerializer deserializer = new XmlSerializer(typeof (TResponse));
93 Stream stream = resp.GetResponseStream();
94
95 // This is currently a bad debug stanza since it gobbles us the response...
96// StreamReader reader = new StreamReader(stream);
97// m_log.DebugFormat("[REST OBJECT POSTER RESPONSE]: Received {0}", reader.ReadToEnd());
98
99 deserial = (TResponse) deserializer.Deserialize(stream);
100
101 if (deserial != null && ResponseCallback != null)
102 {
103 ResponseCallback(deserial);
104 }
105 }
106 }
107 }
108}
diff --git a/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs b/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs
new file mode 100644
index 0000000..ad69cd2
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs
@@ -0,0 +1,295 @@
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.IO;
30using System.Net;
31using System.Reflection;
32using System.Text;
33using System.Xml;
34using System.Xml.Serialization;
35using log4net;
36
37namespace OpenSim.Framework.Servers.HttpServer
38{
39 public class RestSessionObject<TRequest>
40 {
41 private string sid;
42 private string aid;
43 private TRequest request_body;
44
45 public string SessionID
46 {
47 get { return sid; }
48 set { sid = value; }
49 }
50
51 public string AvatarID
52 {
53 get { return aid; }
54 set { aid = value; }
55 }
56
57 public TRequest Body
58 {
59 get { return request_body; }
60 set { request_body = value; }
61 }
62 }
63
64 public class SynchronousRestSessionObjectPoster<TRequest, TResponse>
65 {
66 public static TResponse BeginPostObject(string verb, string requestUrl, TRequest obj, string sid, string aid)
67 {
68 RestSessionObject<TRequest> sobj = new RestSessionObject<TRequest>();
69 sobj.SessionID = sid;
70 sobj.AvatarID = aid;
71 sobj.Body = obj;
72
73 Type type = typeof(RestSessionObject<TRequest>);
74
75 WebRequest request = WebRequest.Create(requestUrl);
76 request.Method = verb;
77 request.ContentType = "text/xml";
78 request.Timeout = 20000;
79
80 using (MemoryStream buffer = new MemoryStream())
81 {
82 XmlWriterSettings settings = new XmlWriterSettings();
83 settings.Encoding = Encoding.UTF8;
84
85 using (XmlWriter writer = XmlWriter.Create(buffer, settings))
86 {
87 XmlSerializer serializer = new XmlSerializer(type);
88 serializer.Serialize(writer, sobj);
89 writer.Flush();
90 }
91
92 int length = (int)buffer.Length;
93 request.ContentLength = length;
94
95 using (Stream requestStream = request.GetRequestStream())
96 requestStream.Write(buffer.ToArray(), 0, length);
97 }
98
99 TResponse deserial = default(TResponse);
100 using (WebResponse resp = request.GetResponse())
101 {
102 XmlSerializer deserializer = new XmlSerializer(typeof(TResponse));
103
104 using (Stream respStream = resp.GetResponseStream())
105 deserial = (TResponse)deserializer.Deserialize(respStream);
106 }
107
108 return deserial;
109 }
110 }
111
112 public class RestSessionObjectPosterResponse<TRequest, TResponse>
113 {
114 public ReturnResponse<TResponse> ResponseCallback;
115
116 public void BeginPostObject(string requestUrl, TRequest obj, string sid, string aid)
117 {
118 BeginPostObject("POST", requestUrl, obj, sid, aid);
119 }
120
121 public void BeginPostObject(string verb, string requestUrl, TRequest obj, string sid, string aid)
122 {
123 RestSessionObject<TRequest> sobj = new RestSessionObject<TRequest>();
124 sobj.SessionID = sid;
125 sobj.AvatarID = aid;
126 sobj.Body = obj;
127
128 Type type = typeof(RestSessionObject<TRequest>);
129
130 WebRequest request = WebRequest.Create(requestUrl);
131 request.Method = verb;
132 request.ContentType = "text/xml";
133 request.Timeout = 10000;
134
135 using (MemoryStream buffer = new MemoryStream())
136 {
137 XmlWriterSettings settings = new XmlWriterSettings();
138 settings.Encoding = Encoding.UTF8;
139
140 using (XmlWriter writer = XmlWriter.Create(buffer, settings))
141 {
142 XmlSerializer serializer = new XmlSerializer(type);
143 serializer.Serialize(writer, sobj);
144 writer.Flush();
145 }
146
147 int length = (int)buffer.Length;
148 request.ContentLength = length;
149
150 using (Stream requestStream = request.GetRequestStream())
151 requestStream.Write(buffer.ToArray(), 0, length);
152 }
153
154 // IAsyncResult result = request.BeginGetResponse(AsyncCallback, request);
155 request.BeginGetResponse(AsyncCallback, request);
156 }
157
158 private void AsyncCallback(IAsyncResult result)
159 {
160 WebRequest request = (WebRequest)result.AsyncState;
161 using (WebResponse resp = request.EndGetResponse(result))
162 {
163 TResponse deserial;
164 XmlSerializer deserializer = new XmlSerializer(typeof(TResponse));
165 Stream stream = resp.GetResponseStream();
166
167 // This is currently a bad debug stanza since it gobbles us the response...
168 // StreamReader reader = new StreamReader(stream);
169 // m_log.DebugFormat("[REST OBJECT POSTER RESPONSE]: Received {0}", reader.ReadToEnd());
170
171 deserial = (TResponse)deserializer.Deserialize(stream);
172 if (stream != null)
173 stream.Close();
174
175 if (deserial != null && ResponseCallback != null)
176 {
177 ResponseCallback(deserial);
178 }
179 }
180 }
181 }
182
183 public delegate bool CheckIdentityMethod(string sid, string aid);
184
185 public class RestDeserialiseSecureHandler<TRequest, TResponse> : BaseOutputStreamHandler, IStreamHandler
186 where TRequest : new()
187 {
188 private static readonly ILog m_log
189 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
190
191 private RestDeserialiseMethod<TRequest, TResponse> m_method;
192 private CheckIdentityMethod m_smethod;
193
194 public RestDeserialiseSecureHandler(
195 string httpMethod, string path,
196 RestDeserialiseMethod<TRequest, TResponse> method, CheckIdentityMethod smethod)
197 : base(httpMethod, path)
198 {
199 m_smethod = smethod;
200 m_method = method;
201 }
202
203 protected override void ProcessRequest(string path, Stream request, Stream responseStream,
204 IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
205 {
206 RestSessionObject<TRequest> deserial = default(RestSessionObject<TRequest>);
207 bool fail = false;
208
209 using (XmlTextReader xmlReader = new XmlTextReader(request))
210 {
211 try
212 {
213 XmlSerializer deserializer = new XmlSerializer(typeof(RestSessionObject<TRequest>));
214 deserial = (RestSessionObject<TRequest>)deserializer.Deserialize(xmlReader);
215 }
216 catch (Exception e)
217 {
218 m_log.Error("[REST]: Deserialization problem. Ignoring request. " + e);
219 fail = true;
220 }
221 }
222
223 TResponse response = default(TResponse);
224 if (!fail && m_smethod(deserial.SessionID, deserial.AvatarID))
225 {
226 response = m_method(deserial.Body);
227 }
228
229 using (XmlWriter xmlWriter = XmlTextWriter.Create(responseStream))
230 {
231 XmlSerializer serializer = new XmlSerializer(typeof(TResponse));
232 serializer.Serialize(xmlWriter, response);
233 }
234 }
235 }
236
237 public delegate bool CheckTrustedSourceMethod(IPEndPoint peer);
238
239 public class RestDeserialiseTrustedHandler<TRequest, TResponse> : BaseOutputStreamHandler, IStreamHandler
240 where TRequest : new()
241 {
242 private static readonly ILog m_log
243 = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
244
245 /// <summary>
246 /// The operation to perform once trust has been established.
247 /// </summary>
248 private RestDeserialiseMethod<TRequest, TResponse> m_method;
249
250 /// <summary>
251 /// The method used to check whether a request is trusted.
252 /// </summary>
253 private CheckTrustedSourceMethod m_tmethod;
254
255 public RestDeserialiseTrustedHandler(string httpMethod, string path, RestDeserialiseMethod<TRequest, TResponse> method, CheckTrustedSourceMethod tmethod)
256 : base(httpMethod, path)
257 {
258 m_tmethod = tmethod;
259 m_method = method;
260 }
261
262 protected override void ProcessRequest(string path, Stream request, Stream responseStream,
263 IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
264 {
265 TRequest deserial = default(TRequest);
266 bool fail = false;
267
268 using (XmlTextReader xmlReader = new XmlTextReader(request))
269 {
270 try
271 {
272 XmlSerializer deserializer = new XmlSerializer(typeof(TRequest));
273 deserial = (TRequest)deserializer.Deserialize(xmlReader);
274 }
275 catch (Exception e)
276 {
277 m_log.Error("[REST]: Deserialization problem. Ignoring request. " + e);
278 fail = true;
279 }
280 }
281
282 TResponse response = default(TResponse);
283 if (!fail && m_tmethod(httpRequest.RemoteIPEndPoint))
284 {
285 response = m_method(deserial);
286 }
287
288 using (XmlWriter xmlWriter = XmlTextWriter.Create(responseStream))
289 {
290 XmlSerializer serializer = new XmlSerializer(typeof(TResponse));
291 serializer.Serialize(xmlWriter, response);
292 }
293 }
294 }
295} \ No newline at end of file
diff --git a/OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs
new file mode 100644
index 0000000..0305dee
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs
@@ -0,0 +1,65 @@
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.IO;
29using System.Text;
30
31namespace OpenSim.Framework.Servers.HttpServer
32{
33 public class RestStreamHandler : BaseStreamHandler
34 {
35 private RestMethod m_restMethod;
36
37 public RestMethod Method
38 {
39 get { return m_restMethod; }
40 }
41
42 public RestStreamHandler(string httpMethod, string path, RestMethod restMethod)
43 : this(httpMethod, path, restMethod, null, null) {}
44
45 public RestStreamHandler(string httpMethod, string path, RestMethod restMethod, string name, string description)
46 : base(httpMethod, path, name, description)
47 {
48 m_restMethod = restMethod;
49 }
50
51 protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
52 {
53 Encoding encoding = Encoding.UTF8;
54 StreamReader streamReader = new StreamReader(request, encoding);
55
56 string requestBody = streamReader.ReadToEnd();
57 streamReader.Close();
58
59 string param = GetParam(path);
60 string responseString = m_restMethod(requestBody, path, param, httpRequest, httpResponse);
61
62 return Encoding.UTF8.GetBytes(responseString);
63 }
64 }
65}
diff --git a/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs b/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs
new file mode 100644
index 0000000..c2925e3
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs
@@ -0,0 +1,1159 @@
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.Net;
32using System.Security.Cryptography;
33using System.Text;
34using System.Threading;
35using HttpServer;
36
37namespace OpenSim.Framework.Servers.HttpServer
38{
39 // Sealed class. If you're going to unseal it, implement IDisposable.
40 /// <summary>
41 /// This class implements websockets. It grabs the network context from C#Webserver and utilizes it directly as a tcp streaming service
42 /// </summary>
43 public sealed class WebSocketHttpServerHandler : BaseRequestHandler
44 {
45
46 private class WebSocketState
47 {
48 public List<byte> ReceivedBytes;
49 public int ExpectedBytes;
50 public WebsocketFrameHeader Header;
51 public bool FrameComplete;
52 public WebSocketFrame ContinuationFrame;
53 }
54
55 /// <summary>
56 /// Binary Data will trigger this event
57 /// </summary>
58 public event DataDelegate OnData;
59
60 /// <summary>
61 /// Textual Data will trigger this event
62 /// </summary>
63 public event TextDelegate OnText;
64
65 /// <summary>
66 /// A ping request form the other side will trigger this event.
67 /// This class responds to the ping automatically. You shouldn't send a pong.
68 /// it's informational.
69 /// </summary>
70 public event PingDelegate OnPing;
71
72 /// <summary>
73 /// This is a response to a ping you sent.
74 /// </summary>
75 public event PongDelegate OnPong;
76
77 /// <summary>
78 /// This is a regular HTTP Request... This may be removed in the future.
79 /// </summary>
80// public event RegularHttpRequestDelegate OnRegularHttpRequest;
81
82 /// <summary>
83 /// When the upgrade from a HTTP request to a Websocket is completed, this will be fired
84 /// </summary>
85 public event UpgradeCompletedDelegate OnUpgradeCompleted;
86
87 /// <summary>
88 /// If the upgrade failed, this will be fired
89 /// </summary>
90 public event UpgradeFailedDelegate OnUpgradeFailed;
91
92 /// <summary>
93 /// When the websocket is closed, this will be fired.
94 /// </summary>
95 public event CloseDelegate OnClose;
96
97 /// <summary>
98 /// Set this delegate to allow your module to validate the origin of the
99 /// Websocket request. Primary line of defense against cross site scripting
100 /// </summary>
101 public ValidateHandshake HandshakeValidateMethodOverride = null;
102
103 private ManualResetEvent _receiveDone = new ManualResetEvent(false);
104
105 private OSHttpRequest _request;
106 private HTTPNetworkContext _networkContext;
107 private IHttpClientContext _clientContext;
108
109 private int _pingtime = 0;
110 private byte[] _buffer;
111 private int _bufferPosition;
112 private int _bufferLength;
113 private bool _closing;
114 private bool _upgraded;
115 private int _maxPayloadBytes = 41943040;
116 private int _initialMsgTimeout = 0;
117 private int _defaultReadTimeout = 10000;
118
119 private const string HandshakeAcceptText =
120 "HTTP/1.1 101 Switching Protocols\r\n" +
121 "upgrade: websocket\r\n" +
122 "Connection: Upgrade\r\n" +
123 "sec-websocket-accept: {0}\r\n\r\n";// +
124 //"{1}";
125
126 private const string HandshakeDeclineText =
127 "HTTP/1.1 {0} {1}\r\n" +
128 "Connection: close\r\n\r\n";
129
130 /// <summary>
131 /// Mysterious constant defined in RFC6455 to append to the client provided security key
132 /// </summary>
133 private const string WebsocketHandshakeAcceptHashConstant = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
134
135 public WebSocketHttpServerHandler(OSHttpRequest preq, IHttpClientContext pContext, int bufferlen)
136 : base(preq.HttpMethod, preq.Url.OriginalString)
137 {
138 _request = preq;
139 _networkContext = pContext.GiveMeTheNetworkStreamIKnowWhatImDoing();
140 _networkContext.Stream.ReadTimeout = _defaultReadTimeout;
141 _clientContext = pContext;
142 _bufferLength = bufferlen;
143 _buffer = new byte[_bufferLength];
144 }
145
146 // Sealed class implments destructor and an internal dispose method. complies with C# unmanaged resource best practices.
147 ~WebSocketHttpServerHandler()
148 {
149 Dispose();
150
151 }
152
153 /// <summary>
154 /// Sets the length of the stream buffer
155 /// </summary>
156 /// <param name="pChunk">Byte length.</param>
157 public void SetChunksize(int pChunk)
158 {
159 if (!_upgraded)
160 {
161 _buffer = new byte[pChunk];
162 }
163 else
164 {
165 throw new InvalidOperationException("You must set the chunksize before the connection is upgraded");
166 }
167 }
168
169 /// <summary>
170 /// This is the famous nagle.
171 /// </summary>
172 public bool NoDelay_TCP_Nagle
173 {
174 get
175 {
176 if (_networkContext != null && _networkContext.Socket != null)
177 {
178 return _networkContext.Socket.NoDelay;
179 }
180 else
181 {
182 throw new InvalidOperationException("The socket has been shutdown");
183 }
184 }
185 set
186 {
187 if (_networkContext != null && _networkContext.Socket != null)
188 _networkContext.Socket.NoDelay = value;
189 else
190 {
191 throw new InvalidOperationException("The socket has been shutdown");
192 }
193 }
194 }
195
196 /// <summary>
197 /// This triggers the websocket to start the upgrade process...
198 /// This is a Generalized Networking 'common sense' helper method. Some people expect to call Start() instead
199 /// of the more context appropriate HandshakeAndUpgrade()
200 /// </summary>
201 public void Start()
202 {
203 HandshakeAndUpgrade();
204 }
205
206 /// <summary>
207 /// Max Payload Size in bytes. Defaults to 40MB, but could be set upon connection before calling handshake and upgrade.
208 /// </summary>
209 public int MaxPayloadSize
210 {
211 get { return _maxPayloadBytes; }
212 set { _maxPayloadBytes = value; }
213 }
214
215 /// <summary>
216 /// Set this to the maximum amount of milliseconds to wait for the first complete message to be sent or received on the websocket after upgrading
217 /// Default, it waits forever. Use this when your Websocket consuming code opens a connection and waits for a message from the other side to avoid a Denial of Service vector.
218 /// </summary>
219 public int InitialMsgTimeout
220 {
221 get { return _initialMsgTimeout; }
222 set { _initialMsgTimeout = value; }
223 }
224
225 /// <summary>
226 /// This triggers the websocket start the upgrade process
227 /// </summary>
228 public void HandshakeAndUpgrade()
229 {
230 string webOrigin = string.Empty;
231 string websocketKey = string.Empty;
232 string acceptKey = string.Empty;
233 string accepthost = string.Empty;
234 if (!string.IsNullOrEmpty(_request.Headers["origin"]))
235 webOrigin = _request.Headers["origin"];
236
237 if (!string.IsNullOrEmpty(_request.Headers["sec-websocket-key"]))
238 websocketKey = _request.Headers["sec-websocket-key"];
239
240 if (!string.IsNullOrEmpty(_request.Headers["host"]))
241 accepthost = _request.Headers["host"];
242
243 if (string.IsNullOrEmpty(_request.Headers["upgrade"]))
244 {
245 FailUpgrade(OSHttpStatusCode.ClientErrorBadRequest, "no upgrade request submitted");
246 }
247
248 string connectionheader = _request.Headers["upgrade"];
249 if (connectionheader.ToLower() != "websocket")
250 {
251 FailUpgrade(OSHttpStatusCode.ClientErrorBadRequest, "no connection upgrade request submitted");
252 }
253
254 // If the object consumer provided a method to validate the origin, we should call it and give the client a success or fail.
255 // If not.. we should accept any. The assumption here is that there would be no Websocket handlers registered in baseHTTPServer unless
256 // Something asked for it...
257 if (HandshakeValidateMethodOverride != null)
258 {
259 if (HandshakeValidateMethodOverride(webOrigin, websocketKey, accepthost))
260 {
261 acceptKey = GenerateAcceptKey(websocketKey);
262 string rawaccept = string.Format(HandshakeAcceptText, acceptKey);
263 SendUpgradeSuccess(rawaccept);
264
265
266 }
267 else
268 {
269 FailUpgrade(OSHttpStatusCode.ClientErrorForbidden, "Origin Validation Failed");
270 }
271 }
272 else
273 {
274 acceptKey = GenerateAcceptKey(websocketKey);
275 string rawaccept = string.Format(HandshakeAcceptText, acceptKey);
276 SendUpgradeSuccess(rawaccept);
277 }
278 }
279 public IPEndPoint GetRemoteIPEndpoint()
280 {
281 return _request.RemoteIPEndPoint;
282 }
283
284 /// <summary>
285 /// Generates a handshake response key string based on the client's
286 /// provided key to prove to the client that we're allowing the Websocket
287 /// upgrade of our own free will and we were not coerced into doing it.
288 /// </summary>
289 /// <param name="key">Client provided security key</param>
290 /// <returns></returns>
291 private static string GenerateAcceptKey(string key)
292 {
293 if (string.IsNullOrEmpty(key))
294 return string.Empty;
295
296 string acceptkey = key + WebsocketHandshakeAcceptHashConstant;
297
298 SHA1 hashobj = SHA1.Create();
299 string ret = Convert.ToBase64String(hashobj.ComputeHash(Encoding.UTF8.GetBytes(acceptkey)));
300 hashobj.Clear();
301
302 return ret;
303 }
304
305 /// <summary>
306 /// Informs the otherside that we accepted their upgrade request
307 /// </summary>
308 /// <param name="pHandshakeResponse">The HTTP 1.1 101 response that says Yay \o/ </param>
309 private void SendUpgradeSuccess(string pHandshakeResponse)
310 {
311 // Create a new websocket state so we can keep track of data in between network reads.
312 WebSocketState socketState = new WebSocketState() { ReceivedBytes = new List<byte>(), Header = WebsocketFrameHeader.HeaderDefault(), FrameComplete = true};
313
314 byte[] bhandshakeResponse = Encoding.UTF8.GetBytes(pHandshakeResponse);
315
316
317
318
319 try
320 {
321 if (_initialMsgTimeout > 0)
322 {
323 _receiveDone.Reset();
324 }
325 // Begin reading the TCP stream before writing the Upgrade success message to the other side of the stream.
326 _networkContext.Stream.BeginRead(_buffer, 0, _bufferLength, OnReceive, socketState);
327
328 // Write the upgrade handshake success message
329 _networkContext.Stream.Write(bhandshakeResponse, 0, bhandshakeResponse.Length);
330 _networkContext.Stream.Flush();
331 _upgraded = true;
332 UpgradeCompletedDelegate d = OnUpgradeCompleted;
333 if (d != null)
334 d(this, new UpgradeCompletedEventArgs());
335 if (_initialMsgTimeout > 0)
336 {
337 if (!_receiveDone.WaitOne(TimeSpan.FromMilliseconds(_initialMsgTimeout)))
338 Close(string.Empty);
339 }
340 }
341 catch (IOException)
342 {
343 Close(string.Empty);
344 }
345 catch (ObjectDisposedException)
346 {
347 Close(string.Empty);
348 }
349 }
350
351 /// <summary>
352 /// The server has decided not to allow the upgrade to a websocket for some reason. The Http 1.1 response that says Nay >:(
353 /// </summary>
354 /// <param name="pCode">HTTP Status reflecting the reason why</param>
355 /// <param name="pMessage">Textual reason for the upgrade fail</param>
356 private void FailUpgrade(OSHttpStatusCode pCode, string pMessage )
357 {
358 string handshakeResponse = string.Format(HandshakeDeclineText, (int)pCode, pMessage.Replace("\n", string.Empty).Replace("\r", string.Empty));
359 byte[] bhandshakeResponse = Encoding.UTF8.GetBytes(handshakeResponse);
360 _networkContext.Stream.Write(bhandshakeResponse, 0, bhandshakeResponse.Length);
361 _networkContext.Stream.Flush();
362 _networkContext.Stream.Dispose();
363
364 UpgradeFailedDelegate d = OnUpgradeFailed;
365 if (d != null)
366 d(this,new UpgradeFailedEventArgs());
367 }
368
369
370 /// <summary>
371 /// This is our ugly Async OnReceive event handler.
372 /// This chunks the input stream based on the length of the provided buffer and processes out
373 /// as many frames as it can. It then moves the unprocessed data to the beginning of the buffer.
374 /// </summary>
375 /// <param name="ar">Our Async State from beginread</param>
376 private void OnReceive(IAsyncResult ar)
377 {
378 WebSocketState _socketState = ar.AsyncState as WebSocketState;
379 try
380 {
381 int bytesRead = _networkContext.Stream.EndRead(ar);
382 if (bytesRead == 0)
383 {
384 // Do Disconnect
385 _networkContext.Stream.Dispose();
386 _networkContext = null;
387 return;
388 }
389 _bufferPosition += bytesRead;
390
391 if (_bufferPosition > _bufferLength)
392 {
393 // Message too big for chunksize.. not sure how this happened...
394 //Close(string.Empty);
395 }
396
397 int offset = 0;
398 bool headerread = true;
399 int headerforwardposition = 0;
400 while (headerread && offset < bytesRead)
401 {
402 if (_socketState.FrameComplete)
403 {
404 WebsocketFrameHeader pheader = WebsocketFrameHeader.ZeroHeader;
405
406 headerread = WebSocketReader.TryReadHeader(_buffer, offset, _bufferPosition - offset, out pheader,
407 out headerforwardposition);
408 offset += headerforwardposition;
409
410 if (headerread)
411 {
412 _socketState.FrameComplete = false;
413 if (pheader.PayloadLen > (ulong) _maxPayloadBytes)
414 {
415 Close("Invalid Payload size");
416
417 return;
418 }
419 if (pheader.PayloadLen > 0)
420 {
421 if ((int) pheader.PayloadLen > _bufferPosition - offset)
422 {
423 byte[] writebytes = new byte[_bufferPosition - offset];
424
425 Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) _bufferPosition - offset);
426 _socketState.ExpectedBytes = (int) pheader.PayloadLen;
427 _socketState.ReceivedBytes.AddRange(writebytes);
428 _socketState.Header = pheader; // We need to add the header so that we can unmask it
429 offset += (int) _bufferPosition - offset;
430 }
431 else
432 {
433 byte[] writebytes = new byte[pheader.PayloadLen];
434 Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) pheader.PayloadLen);
435 WebSocketReader.Mask(pheader.Mask, writebytes);
436 pheader.IsMasked = false;
437 _socketState.FrameComplete = true;
438 _socketState.ReceivedBytes.AddRange(writebytes);
439 _socketState.Header = pheader;
440 offset += (int) pheader.PayloadLen;
441 }
442 }
443 else
444 {
445 pheader.Mask = 0;
446 _socketState.FrameComplete = true;
447 _socketState.Header = pheader;
448 }
449
450 if (_socketState.FrameComplete)
451 {
452 ProcessFrame(_socketState);
453 _socketState.Header.SetDefault();
454 _socketState.ReceivedBytes.Clear();
455 _socketState.ExpectedBytes = 0;
456
457 }
458 }
459 }
460 else
461 {
462 WebsocketFrameHeader frameHeader = _socketState.Header;
463 int bytesleft = _socketState.ExpectedBytes - _socketState.ReceivedBytes.Count;
464
465 if (bytesleft > _bufferPosition)
466 {
467 byte[] writebytes = new byte[_bufferPosition];
468
469 Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) _bufferPosition);
470 _socketState.ReceivedBytes.AddRange(writebytes);
471 _socketState.Header = frameHeader; // We need to add the header so that we can unmask it
472 offset += (int) _bufferPosition;
473 }
474 else
475 {
476 byte[] writebytes = new byte[_bufferPosition];
477 Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) _bufferPosition);
478 _socketState.FrameComplete = true;
479 _socketState.ReceivedBytes.AddRange(writebytes);
480 _socketState.Header = frameHeader;
481 offset += (int) _bufferPosition;
482 }
483 if (_socketState.FrameComplete)
484 {
485 ProcessFrame(_socketState);
486 _socketState.Header.SetDefault();
487 _socketState.ReceivedBytes.Clear();
488 _socketState.ExpectedBytes = 0;
489 // do some processing
490 }
491 }
492 }
493 if (offset > 0)
494 {
495 // If the buffer is maxed out.. we can just move the cursor. Nothing to move to the beginning.
496 if (offset <_buffer.Length)
497 Buffer.BlockCopy(_buffer, offset, _buffer, 0, _bufferPosition - offset);
498 _bufferPosition -= offset;
499 }
500 if (_networkContext.Stream != null && _networkContext.Stream.CanRead && !_closing)
501 {
502 _networkContext.Stream.BeginRead(_buffer, _bufferPosition, _bufferLength - _bufferPosition, OnReceive,
503 _socketState);
504 }
505 else
506 {
507 // We can't read the stream anymore...
508 }
509 }
510 catch (IOException)
511 {
512 Close(string.Empty);
513 }
514 catch (ObjectDisposedException)
515 {
516 Close(string.Empty);
517 }
518 }
519
520 /// <summary>
521 /// Sends a string to the other side
522 /// </summary>
523 /// <param name="message">the string message that is to be sent</param>
524 public void SendMessage(string message)
525 {
526 if (_initialMsgTimeout > 0)
527 {
528 _receiveDone.Set();
529 _initialMsgTimeout = 0;
530 }
531 byte[] messagedata = Encoding.UTF8.GetBytes(message);
532 WebSocketFrame textMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = messagedata };
533 textMessageFrame.Header.Opcode = WebSocketReader.OpCode.Text;
534 textMessageFrame.Header.IsEnd = true;
535 SendSocket(textMessageFrame.ToBytes());
536
537 }
538
539 public void SendData(byte[] data)
540 {
541 if (_initialMsgTimeout > 0)
542 {
543 _receiveDone.Set();
544 _initialMsgTimeout = 0;
545 }
546 WebSocketFrame dataMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = data};
547 dataMessageFrame.Header.IsEnd = true;
548 dataMessageFrame.Header.Opcode = WebSocketReader.OpCode.Binary;
549 SendSocket(dataMessageFrame.ToBytes());
550
551 }
552
553 /// <summary>
554 /// Writes raw bytes to the websocket. Unframed data will cause disconnection
555 /// </summary>
556 /// <param name="data"></param>
557 private void SendSocket(byte[] data)
558 {
559 if (!_closing)
560 {
561 try
562 {
563
564 _networkContext.Stream.Write(data, 0, data.Length);
565 }
566 catch (IOException)
567 {
568
569 }
570 }
571 }
572
573 /// <summary>
574 /// 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.
575 /// </summary>
576 public void SendPingCheck()
577 {
578 if (_initialMsgTimeout > 0)
579 {
580 _receiveDone.Set();
581 _initialMsgTimeout = 0;
582 }
583 WebSocketFrame pingFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = new byte[0] };
584 pingFrame.Header.Opcode = WebSocketReader.OpCode.Ping;
585 pingFrame.Header.IsEnd = true;
586 _pingtime = Util.EnvironmentTickCount();
587 SendSocket(pingFrame.ToBytes());
588 }
589
590 /// <summary>
591 /// Closes the websocket connection. Sends a close message to the other side if it hasn't already done so.
592 /// </summary>
593 /// <param name="message"></param>
594 public void Close(string message)
595 {
596 if (_initialMsgTimeout > 0)
597 {
598 _receiveDone.Set();
599 _initialMsgTimeout = 0;
600 }
601 if (_networkContext == null)
602 return;
603 if (_networkContext.Stream != null)
604 {
605 if (_networkContext.Stream.CanWrite)
606 {
607 byte[] messagedata = Encoding.UTF8.GetBytes(message);
608 WebSocketFrame closeResponseFrame = new WebSocketFrame()
609 {
610 Header = WebsocketFrameHeader.HeaderDefault(),
611 WebSocketPayload = messagedata
612 };
613 closeResponseFrame.Header.Opcode = WebSocketReader.OpCode.Close;
614 closeResponseFrame.Header.PayloadLen = (ulong) messagedata.Length;
615 closeResponseFrame.Header.IsEnd = true;
616 SendSocket(closeResponseFrame.ToBytes());
617 }
618 }
619 CloseDelegate closeD = OnClose;
620 if (closeD != null)
621 {
622 closeD(this, new CloseEventArgs());
623 }
624
625 _closing = true;
626 }
627
628 /// <summary>
629 /// Processes a websocket frame and triggers consumer events
630 /// </summary>
631 /// <param name="psocketState">We need to modify the websocket state here depending on the frame</param>
632 private void ProcessFrame(WebSocketState psocketState)
633 {
634 if (psocketState.Header.IsMasked)
635 {
636 byte[] unmask = psocketState.ReceivedBytes.ToArray();
637 WebSocketReader.Mask(psocketState.Header.Mask, unmask);
638 psocketState.ReceivedBytes = new List<byte>(unmask);
639 }
640 if (psocketState.Header.Opcode != WebSocketReader.OpCode.Continue && _initialMsgTimeout > 0)
641 {
642 _receiveDone.Set();
643 _initialMsgTimeout = 0;
644 }
645 switch (psocketState.Header.Opcode)
646 {
647 case WebSocketReader.OpCode.Ping:
648 PingDelegate pingD = OnPing;
649 if (pingD != null)
650 {
651 pingD(this, new PingEventArgs());
652 }
653
654 WebSocketFrame pongFrame = new WebSocketFrame(){Header = WebsocketFrameHeader.HeaderDefault(),WebSocketPayload = new byte[0]};
655 pongFrame.Header.Opcode = WebSocketReader.OpCode.Pong;
656 pongFrame.Header.IsEnd = true;
657 SendSocket(pongFrame.ToBytes());
658 break;
659 case WebSocketReader.OpCode.Pong:
660
661 PongDelegate pongD = OnPong;
662 if (pongD != null)
663 {
664 pongD(this, new PongEventArgs(){PingResponseMS = Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(),_pingtime)});
665 }
666 break;
667 case WebSocketReader.OpCode.Binary:
668 if (!psocketState.Header.IsEnd) // Not done, so we need to store this and wait for the end frame.
669 {
670 psocketState.ContinuationFrame = new WebSocketFrame
671 {
672 Header = psocketState.Header,
673 WebSocketPayload =
674 psocketState.ReceivedBytes.ToArray()
675 };
676 }
677 else
678 {
679 // Send Done Event!
680 DataDelegate dataD = OnData;
681 if (dataD != null)
682 {
683 dataD(this,new WebsocketDataEventArgs(){Data = psocketState.ReceivedBytes.ToArray()});
684 }
685 }
686 break;
687 case WebSocketReader.OpCode.Text:
688 if (!psocketState.Header.IsEnd) // Not done, so we need to store this and wait for the end frame.
689 {
690 psocketState.ContinuationFrame = new WebSocketFrame
691 {
692 Header = psocketState.Header,
693 WebSocketPayload =
694 psocketState.ReceivedBytes.ToArray()
695 };
696 }
697 else
698 {
699 TextDelegate textD = OnText;
700 if (textD != null)
701 {
702 textD(this, new WebsocketTextEventArgs() { Data = Encoding.UTF8.GetString(psocketState.ReceivedBytes.ToArray()) });
703 }
704
705 // Send Done Event!
706 }
707 break;
708 case WebSocketReader.OpCode.Continue: // Continuation. Multiple frames worth of data for one message. Only valid when not using Control Opcodes
709 //Console.WriteLine("currhead " + psocketState.Header.IsEnd);
710 //Console.WriteLine("Continuation! " + psocketState.ContinuationFrame.Header.IsEnd);
711 byte[] combineddata = new byte[psocketState.ReceivedBytes.Count+psocketState.ContinuationFrame.WebSocketPayload.Length];
712 byte[] newdata = psocketState.ReceivedBytes.ToArray();
713 Buffer.BlockCopy(psocketState.ContinuationFrame.WebSocketPayload, 0, combineddata, 0, psocketState.ContinuationFrame.WebSocketPayload.Length);
714 Buffer.BlockCopy(newdata, 0, combineddata,
715 psocketState.ContinuationFrame.WebSocketPayload.Length, newdata.Length);
716 psocketState.ContinuationFrame.WebSocketPayload = combineddata;
717 psocketState.Header.PayloadLen = (ulong)combineddata.Length;
718 if (psocketState.Header.IsEnd)
719 {
720 if (psocketState.ContinuationFrame.Header.Opcode == WebSocketReader.OpCode.Text)
721 {
722 // Send Done event
723 TextDelegate textD = OnText;
724 if (textD != null)
725 {
726 textD(this, new WebsocketTextEventArgs() { Data = Encoding.UTF8.GetString(combineddata) });
727 }
728 }
729 else if (psocketState.ContinuationFrame.Header.Opcode == WebSocketReader.OpCode.Binary)
730 {
731 // Send Done event
732 DataDelegate dataD = OnData;
733 if (dataD != null)
734 {
735 dataD(this, new WebsocketDataEventArgs() { Data = combineddata });
736 }
737 }
738 else
739 {
740 // protocol violation
741 }
742 psocketState.ContinuationFrame = null;
743 }
744 break;
745 case WebSocketReader.OpCode.Close:
746 Close(string.Empty);
747
748 break;
749
750 }
751 psocketState.Header.SetDefault();
752 psocketState.ReceivedBytes.Clear();
753 psocketState.ExpectedBytes = 0;
754 }
755 public void Dispose()
756 {
757 if (_initialMsgTimeout > 0)
758 {
759 _receiveDone.Set();
760 _initialMsgTimeout = 0;
761 }
762 if (_networkContext != null && _networkContext.Stream != null)
763 {
764 if (_networkContext.Stream.CanWrite)
765 _networkContext.Stream.Flush();
766 _networkContext.Stream.Close();
767 _networkContext.Stream.Dispose();
768 _networkContext.Stream = null;
769 }
770
771 if (_request != null && _request.InputStream != null)
772 {
773 _request.InputStream.Close();
774 _request.InputStream.Dispose();
775 _request = null;
776 }
777
778 if (_clientContext != null)
779 {
780 _clientContext.Close();
781 _clientContext = null;
782 }
783 }
784 }
785
786 /// <summary>
787 /// Reads a byte stream and returns Websocket frames.
788 /// </summary>
789 public class WebSocketReader
790 {
791 /// <summary>
792 /// Bit to determine if the frame read on the stream is the last frame in a sequence of fragmented frames
793 /// </summary>
794 private const byte EndBit = 0x80;
795
796 /// <summary>
797 /// These are the Frame Opcodes
798 /// </summary>
799 public enum OpCode
800 {
801 // Data Opcodes
802 Continue = 0x0,
803 Text = 0x1,
804 Binary = 0x2,
805
806 // Control flow Opcodes
807 Close = 0x8,
808 Ping = 0x9,
809 Pong = 0xA
810 }
811
812 /// <summary>
813 /// Masks and Unmasks data using the frame mask. Mask is applied per octal
814 /// Note: Frames from clients MUST be masked
815 /// Note: Frames from servers MUST NOT be masked
816 /// </summary>
817 /// <param name="pMask">Int representing 32 bytes of mask data. Mask is applied per octal</param>
818 /// <param name="pBuffer"></param>
819 public static void Mask(int pMask, byte[] pBuffer)
820 {
821 byte[] maskKey = BitConverter.GetBytes(pMask);
822 int currentMaskIndex = 0;
823 for (int i = 0; i < pBuffer.Length; i++)
824 {
825 pBuffer[i] = (byte)(pBuffer[i] ^ maskKey[currentMaskIndex]);
826 if (currentMaskIndex == 3)
827 {
828 currentMaskIndex = 0;
829 }
830 else
831 {
832 currentMaskIndex++;
833
834 }
835
836 }
837 }
838
839 /// <summary>
840 /// Attempts to read a header off the provided buffer. Returns true, exports a WebSocketFrameheader,
841 /// and an int to move the buffer forward when it reads a header. False when it can't read a header
842 /// </summary>
843 /// <param name="pBuffer">Bytes read from the stream</param>
844 /// <param name="pOffset">Starting place in the stream to begin trying to read from</param>
845 /// <param name="length">Lenth in the stream to try and read from. Provided for cases where the
846 /// buffer's length is larger then the data in it</param>
847 /// <param name="oHeader">Outputs the read WebSocket frame header</param>
848 /// <param name="moveBuffer">Informs the calling stream to move the buffer forward</param>
849 /// <returns>True if it got a header, False if it didn't get a header</returns>
850 public static bool TryReadHeader(byte[] pBuffer, int pOffset, int length, out WebsocketFrameHeader oHeader,
851 out int moveBuffer)
852 {
853 oHeader = WebsocketFrameHeader.ZeroHeader;
854 int minumheadersize = 2;
855 if (length > pBuffer.Length - pOffset)
856 throw new ArgumentOutOfRangeException("The Length specified was larger the byte array supplied");
857 if (length < minumheadersize)
858 {
859 moveBuffer = 0;
860 return false;
861 }
862
863 byte nibble1 = (byte)(pBuffer[pOffset] & 0xF0); //FIN/RSV1/RSV2/RSV3
864 byte nibble2 = (byte)(pBuffer[pOffset] & 0x0F); // Opcode block
865
866 oHeader = new WebsocketFrameHeader();
867 oHeader.SetDefault();
868
869 if ((nibble1 & WebSocketReader.EndBit) == WebSocketReader.EndBit)
870 {
871 oHeader.IsEnd = true;
872 }
873 else
874 {
875 oHeader.IsEnd = false;
876 }
877 //Opcode
878 oHeader.Opcode = (WebSocketReader.OpCode)nibble2;
879 //Mask
880 oHeader.IsMasked = Convert.ToBoolean((pBuffer[pOffset + 1] & 0x80) >> 7);
881
882 // Payload length
883 oHeader.PayloadLen = (byte)(pBuffer[pOffset + 1] & 0x7F);
884
885 int index = 2; // LargerPayload length starts at byte 3
886
887 switch (oHeader.PayloadLen)
888 {
889 case 126:
890 minumheadersize += 2;
891 if (length < minumheadersize)
892 {
893 moveBuffer = 0;
894 return false;
895 }
896 Array.Reverse(pBuffer, pOffset + index, 2); // two bytes
897 oHeader.PayloadLen = BitConverter.ToUInt16(pBuffer, pOffset + index);
898 index += 2;
899 break;
900 case 127: // we got more this is a bigger frame
901 // 8 bytes - uint64 - most significant bit 0 network byte order
902 minumheadersize += 8;
903 if (length < minumheadersize)
904 {
905 moveBuffer = 0;
906 return false;
907 }
908 Array.Reverse(pBuffer, pOffset + index, 8);
909 oHeader.PayloadLen = BitConverter.ToUInt64(pBuffer, pOffset + index);
910 index += 8;
911 break;
912
913 }
914 //oHeader.PayloadLeft = oHeader.PayloadLen; // Start the count in case it's chunked over the network. This is different then frame fragmentation
915 if (oHeader.IsMasked)
916 {
917 minumheadersize += 4;
918 if (length < minumheadersize)
919 {
920 moveBuffer = 0;
921 return false;
922 }
923 oHeader.Mask = BitConverter.ToInt32(pBuffer, pOffset + index);
924 index += 4;
925 }
926 moveBuffer = index;
927 return true;
928
929 }
930 }
931
932 /// <summary>
933 /// RFC6455 Websocket Frame
934 /// </summary>
935 public class WebSocketFrame
936 {
937 /*
938 * RFC6455
939nib 0 1 2 3 4 5 6 7
940byt 0 1 2 3
941dec 0 1 2 3
942 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
943 +-+-+-+-+-------+-+-------------+-------------------------------+
944 |F|R|R|R| opcode|M| Payload len | Extended payload length |
945 |I|S|S|S| (4) |A| (7) | (16/64) +
946 |N|V|V|V| |S| | (if payload len==126/127) |
947 | |1|2|3| |K| | +
948 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
949 | Extended payload length continued, if payload len == 127 |
950 + - - - - - - - - - - - - - - - +-------------------------------+
951 | |Masking-key, if MASK set to 1 |
952 +-------------------------------+-------------------------------+
953 | Masking-key (continued) | Payload Data |
954 +-------------------------------- - - - - - - - - - - - - - - - +
955 : Payload Data continued ... :
956 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
957 | Payload Data continued ... |
958 +---------------------------------------------------------------+
959
960 * When reading these, the frames are possibly fragmented and interleaved with control frames
961 * the fragmented frames are not interleaved with data frames. Just control frames
962 */
963 public static readonly WebSocketFrame DefaultFrame = new WebSocketFrame(){Header = new WebsocketFrameHeader(),WebSocketPayload = new byte[0]};
964 public WebsocketFrameHeader Header;
965 public byte[] WebSocketPayload;
966
967 public byte[] ToBytes()
968 {
969 Header.PayloadLen = (ulong)WebSocketPayload.Length;
970 return Header.ToBytes(WebSocketPayload);
971 }
972
973 }
974
975 public struct WebsocketFrameHeader
976 {
977 //public byte CurrentMaskIndex;
978 /// <summary>
979 /// The last frame in a sequence of fragmented frames or the one and only frame for this message.
980 /// </summary>
981 public bool IsEnd;
982
983 /// <summary>
984 /// Returns whether the payload data is masked or not. Data from Clients MUST be masked, Data from Servers MUST NOT be masked
985 /// </summary>
986 public bool IsMasked;
987
988 /// <summary>
989 /// A set of cryptologically sound random bytes XoR-ed against the payload octally. Looped
990 /// </summary>
991 public int Mask;
992 /*
993byt 0 1 2 3
994 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
995 +---------------+---------------+---------------+---------------+
996 | Octal 1 | Octal 2 | Octal 3 | Octal 4 |
997 +---------------+---------------+---------------+---------------+
998*/
999
1000
1001 public WebSocketReader.OpCode Opcode;
1002
1003 public UInt64 PayloadLen;
1004 //public UInt64 PayloadLeft;
1005 // Payload is X + Y
1006 //public UInt64 ExtensionDataLength;
1007 //public UInt64 ApplicationDataLength;
1008 public static readonly WebsocketFrameHeader ZeroHeader = WebsocketFrameHeader.HeaderDefault();
1009
1010 public void SetDefault()
1011 {
1012
1013 //CurrentMaskIndex = 0;
1014 IsEnd = true;
1015 IsMasked = true;
1016 Mask = 0;
1017 Opcode = WebSocketReader.OpCode.Close;
1018 // PayloadLeft = 0;
1019 PayloadLen = 0;
1020 // ExtensionDataLength = 0;
1021 // ApplicationDataLength = 0;
1022
1023 }
1024
1025 /// <summary>
1026 /// Returns a byte array representing the Frame header
1027 /// </summary>
1028 /// <param name="payload">This is the frame data payload. The header describes the size of the payload.
1029 /// If payload is null, a Zero sized payload is assumed</param>
1030 /// <returns>Returns a byte array representing the frame header</returns>
1031 public byte[] ToBytes(byte[] payload)
1032 {
1033 List<byte> result = new List<byte>();
1034
1035 // Squeeze in our opcode and our ending bit.
1036 result.Add((byte)((byte)Opcode | (IsEnd?0x80:0x00) ));
1037
1038 // Again with the three different byte interpretations of size..
1039
1040 //bytesize
1041 if (PayloadLen <= 125)
1042 {
1043 result.Add((byte) PayloadLen);
1044 } //Uint16
1045 else if (PayloadLen <= ushort.MaxValue)
1046 {
1047 result.Add(126);
1048 byte[] payloadLengthByte = BitConverter.GetBytes(Convert.ToUInt16(PayloadLen));
1049 Array.Reverse(payloadLengthByte);
1050 result.AddRange(payloadLengthByte);
1051 } //UInt64
1052 else
1053 {
1054 result.Add(127);
1055 byte[] payloadLengthByte = BitConverter.GetBytes(PayloadLen);
1056 Array.Reverse(payloadLengthByte);
1057 result.AddRange(payloadLengthByte);
1058 }
1059
1060 // Only add a payload if it's not null
1061 if (payload != null)
1062 {
1063 result.AddRange(payload);
1064 }
1065 return result.ToArray();
1066 }
1067
1068 /// <summary>
1069 /// A Helper method to define the defaults
1070 /// </summary>
1071 /// <returns></returns>
1072
1073 public static WebsocketFrameHeader HeaderDefault()
1074 {
1075 return new WebsocketFrameHeader
1076 {
1077 //CurrentMaskIndex = 0,
1078 IsEnd = false,
1079 IsMasked = true,
1080 Mask = 0,
1081 Opcode = WebSocketReader.OpCode.Close,
1082 //PayloadLeft = 0,
1083 PayloadLen = 0,
1084 // ExtensionDataLength = 0,
1085 // ApplicationDataLength = 0
1086 };
1087 }
1088 }
1089
1090 public delegate void DataDelegate(object sender, WebsocketDataEventArgs data);
1091
1092 public delegate void TextDelegate(object sender, WebsocketTextEventArgs text);
1093
1094 public delegate void PingDelegate(object sender, PingEventArgs pingdata);
1095
1096 public delegate void PongDelegate(object sender, PongEventArgs pongdata);
1097
1098 public delegate void RegularHttpRequestDelegate(object sender, RegularHttpRequestEvnetArgs request);
1099
1100 public delegate void UpgradeCompletedDelegate(object sender, UpgradeCompletedEventArgs completeddata);
1101
1102 public delegate void UpgradeFailedDelegate(object sender, UpgradeFailedEventArgs faileddata);
1103
1104 public delegate void CloseDelegate(object sender, CloseEventArgs closedata);
1105
1106 public delegate bool ValidateHandshake(string pWebOrigin, string pWebSocketKey, string pHost);
1107
1108
1109 public class WebsocketDataEventArgs : EventArgs
1110 {
1111 public byte[] Data;
1112 }
1113
1114 public class WebsocketTextEventArgs : EventArgs
1115 {
1116 public string Data;
1117 }
1118
1119 public class PingEventArgs : EventArgs
1120 {
1121 /// <summary>
1122 /// The ping event can arbitrarily contain data
1123 /// </summary>
1124 public byte[] Data;
1125 }
1126
1127 public class PongEventArgs : EventArgs
1128 {
1129 /// <summary>
1130 /// The pong event can arbitrarily contain data
1131 /// </summary>
1132 public byte[] Data;
1133
1134 public int PingResponseMS;
1135
1136 }
1137
1138 public class RegularHttpRequestEvnetArgs : EventArgs
1139 {
1140
1141 }
1142
1143 public class UpgradeCompletedEventArgs : EventArgs
1144 {
1145
1146 }
1147
1148 public class UpgradeFailedEventArgs : EventArgs
1149 {
1150
1151 }
1152
1153 public class CloseEventArgs : EventArgs
1154 {
1155
1156 }
1157
1158
1159}
diff --git a/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs
new file mode 100644
index 0000000..f212208
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs
@@ -0,0 +1,91 @@
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 Nwc.XmlRpc;
30using OpenSim.Framework;
31
32
33namespace OpenSim.Framework.Servers.HttpServer
34{
35 public class XmlRpcBasicDOSProtector
36 {
37 private readonly XmlRpcMethod _normalMethod;
38 private readonly XmlRpcMethod _throttledMethod;
39
40 private readonly BasicDosProtectorOptions _options;
41 private readonly BasicDOSProtector _dosProtector;
42
43 public XmlRpcBasicDOSProtector(XmlRpcMethod normalMethod, XmlRpcMethod throttledMethod,BasicDosProtectorOptions options)
44 {
45 _normalMethod = normalMethod;
46 _throttledMethod = throttledMethod;
47
48 _options = options;
49 _dosProtector = new BasicDOSProtector(_options);
50
51 }
52 public XmlRpcResponse Process(XmlRpcRequest request, IPEndPoint client)
53 {
54
55 XmlRpcResponse resp = null;
56 string clientstring = GetClientString(request, client);
57 string endpoint = GetEndPoint(request, client);
58 if (_dosProtector.Process(clientstring, endpoint))
59 resp = _normalMethod(request, client);
60 else
61 resp = _throttledMethod(request, client);
62 if (_options.MaxConcurrentSessions > 0)
63 _dosProtector.ProcessEnd(clientstring, endpoint);
64 return resp;
65 }
66
67 private string GetClientString(XmlRpcRequest request, IPEndPoint client)
68 {
69 string clientstring;
70 if (_options.AllowXForwardedFor && request.Params.Count > 3)
71 {
72 object headerstr = request.Params[3];
73 if (headerstr != null && !string.IsNullOrEmpty(headerstr.ToString()))
74 clientstring = request.Params[3].ToString();
75 else
76 clientstring = client.Address.ToString();
77 }
78 else
79 clientstring = client.Address.ToString();
80 return clientstring;
81 }
82
83 private string GetEndPoint(XmlRpcRequest request, IPEndPoint client)
84 {
85 return client.Address.ToString();
86 }
87
88 }
89
90
91}
diff --git a/OpenSim/Framework/Servers/HttpServer/XmlRpcMethod.cs b/OpenSim/Framework/Servers/HttpServer/XmlRpcMethod.cs
new file mode 100644
index 0000000..27cf3f3
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/XmlRpcMethod.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 Nwc.XmlRpc;
30
31namespace OpenSim.Framework.Servers.HttpServer
32{
33 public delegate XmlRpcResponse XmlRpcMethod(XmlRpcRequest request, IPEndPoint client);
34}
diff --git a/OpenSim/Framework/Servers/MainServer.cs b/OpenSim/Framework/Servers/MainServer.cs
new file mode 100644
index 0000000..57931d4
--- /dev/null
+++ b/OpenSim/Framework/Servers/MainServer.cs
@@ -0,0 +1,357 @@
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.Reflection;
31using System.Net;
32using System.Text;
33using log4net;
34using OpenSim.Framework;
35using OpenSim.Framework.Console;
36using OpenSim.Framework.Servers.HttpServer;
37
38namespace OpenSim.Framework.Servers
39{
40 public class MainServer
41 {
42// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
43
44 private static BaseHttpServer instance = null;
45 private static Dictionary<uint, BaseHttpServer> m_Servers = new Dictionary<uint, BaseHttpServer>();
46
47 /// <summary>
48 /// Control the printing of certain debug messages.
49 /// </summary>
50 /// <remarks>
51 /// If DebugLevel >= 1 then short warnings are logged when receiving bad input data.
52 /// If DebugLevel >= 2 then long warnings are logged when receiving bad input data.
53 /// If DebugLevel >= 3 then short notices about all incoming non-poll HTTP requests are logged.
54 /// If DebugLevel >= 4 then the time taken to fulfill the request is logged.
55 /// If DebugLevel >= 5 then the start of the body of incoming non-poll HTTP requests will be logged.
56 /// If DebugLevel >= 6 then the entire body of incoming non-poll HTTP requests will be logged.
57 /// </remarks>
58 public static int DebugLevel
59 {
60 get { return s_debugLevel; }
61 set
62 {
63 s_debugLevel = value;
64
65 lock (m_Servers)
66 foreach (BaseHttpServer server in m_Servers.Values)
67 server.DebugLevel = s_debugLevel;
68 }
69 }
70
71 private static int s_debugLevel;
72
73 /// <summary>
74 /// Set the main HTTP server instance.
75 /// </summary>
76 /// <remarks>
77 /// This will be used to register all handlers that listen to the default port.
78 /// </remarks>
79 /// <exception cref='Exception'>
80 /// Thrown if the HTTP server has not already been registered via AddHttpServer()
81 /// </exception>
82 public static BaseHttpServer Instance
83 {
84 get { return instance; }
85
86 set
87 {
88 lock (m_Servers)
89 if (!m_Servers.ContainsValue(value))
90 throw new Exception("HTTP server must already have been registered to be set as the main instance");
91
92 instance = value;
93 }
94 }
95
96 /// <summary>
97 /// Get all the registered servers.
98 /// </summary>
99 /// <remarks>
100 /// Returns a copy of the dictionary so this can be iterated through without locking.
101 /// </remarks>
102 /// <value></value>
103 public static Dictionary<uint, BaseHttpServer> Servers
104 {
105 get { return new Dictionary<uint, BaseHttpServer>(m_Servers); }
106 }
107
108 public static void RegisterHttpConsoleCommands(ICommandConsole console)
109 {
110 console.Commands.AddCommand(
111 "Comms", false, "show http-handlers",
112 "show http-handlers",
113 "Show all registered http handlers", HandleShowHttpHandlersCommand);
114
115 console.Commands.AddCommand(
116 "Debug", false, "debug http", "debug http <in|out|all> [<level>]",
117 "Turn on http request logging.",
118 "If in or all and\n"
119 + " level <= 0 then no extra logging is done.\n"
120 + " level >= 1 then short warnings are logged when receiving bad input data.\n"
121 + " level >= 2 then long warnings are logged when receiving bad input data.\n"
122 + " level >= 3 then short notices about all incoming non-poll HTTP requests are logged.\n"
123 + " level >= 4 then the time taken to fulfill the request is logged.\n"
124 + " level >= 5 then a sample from the beginning of the data is logged.\n"
125 + " level >= 6 then the entire data is logged.\n"
126 + " no level is specified then the current level is returned.\n\n"
127 + "If out or all and\n"
128 + " level >= 3 then short notices about all outgoing requests going through WebUtil are logged.\n"
129 + " level >= 4 then the time taken to fulfill the request is logged.\n"
130 + " level >= 5 then a sample from the beginning of the data is logged.\n"
131 + " level >= 6 then the entire data is logged.\n",
132 HandleDebugHttpCommand);
133 }
134
135 /// <summary>
136 /// Turn on some debugging values for OpenSim.
137 /// </summary>
138 /// <param name="args"></param>
139 private static void HandleDebugHttpCommand(string module, string[] cmdparams)
140 {
141 if (cmdparams.Length < 3)
142 {
143 MainConsole.Instance.Output("Usage: debug http <in|out|all> 0..6");
144 return;
145 }
146
147 bool inReqs = false;
148 bool outReqs = false;
149 bool allReqs = false;
150
151 string subCommand = cmdparams[2];
152
153 if (subCommand.ToLower() == "in")
154 {
155 inReqs = true;
156 }
157 else if (subCommand.ToLower() == "out")
158 {
159 outReqs = true;
160 }
161 else if (subCommand.ToLower() == "all")
162 {
163 allReqs = true;
164 }
165 else
166 {
167 MainConsole.Instance.Output("You must specify in, out or all");
168 return;
169 }
170
171 if (cmdparams.Length >= 4)
172 {
173 string rawNewDebug = cmdparams[3];
174 int newDebug;
175
176 if (!int.TryParse(rawNewDebug, out newDebug))
177 {
178 MainConsole.Instance.OutputFormat("{0} is not a valid debug level", rawNewDebug);
179 return;
180 }
181
182 if (newDebug < 0 || newDebug > 6)
183 {
184 MainConsole.Instance.OutputFormat("{0} is outside the valid debug level range of 0..6", newDebug);
185 return;
186 }
187
188 if (allReqs || inReqs)
189 {
190 MainServer.DebugLevel = newDebug;
191 MainConsole.Instance.OutputFormat("IN debug level set to {0}", newDebug);
192 }
193
194 if (allReqs || outReqs)
195 {
196 WebUtil.DebugLevel = newDebug;
197 MainConsole.Instance.OutputFormat("OUT debug level set to {0}", newDebug);
198 }
199 }
200 else
201 {
202 if (allReqs || inReqs)
203 MainConsole.Instance.OutputFormat("Current IN debug level is {0}", MainServer.DebugLevel);
204
205 if (allReqs || outReqs)
206 MainConsole.Instance.OutputFormat("Current OUT debug level is {0}", WebUtil.DebugLevel);
207 }
208 }
209
210 private static void HandleShowHttpHandlersCommand(string module, string[] args)
211 {
212 if (args.Length != 2)
213 {
214 MainConsole.Instance.Output("Usage: show http-handlers");
215 return;
216 }
217
218 StringBuilder handlers = new StringBuilder();
219
220 lock (m_Servers)
221 {
222 foreach (BaseHttpServer httpServer in m_Servers.Values)
223 {
224 handlers.AppendFormat(
225 "Registered HTTP Handlers for server at {0}:{1}\n", httpServer.ListenIPAddress, httpServer.Port);
226
227 handlers.AppendFormat("* XMLRPC:\n");
228 foreach (String s in httpServer.GetXmlRpcHandlerKeys())
229 handlers.AppendFormat("\t{0}\n", s);
230
231 handlers.AppendFormat("* HTTP:\n");
232 foreach (String s in httpServer.GetHTTPHandlerKeys())
233 handlers.AppendFormat("\t{0}\n", s);
234
235 handlers.AppendFormat("* HTTP (poll):\n");
236 foreach (String s in httpServer.GetPollServiceHandlerKeys())
237 handlers.AppendFormat("\t{0}\n", s);
238
239 handlers.AppendFormat("* JSONRPC:\n");
240 foreach (String s in httpServer.GetJsonRpcHandlerKeys())
241 handlers.AppendFormat("\t{0}\n", s);
242
243// handlers.AppendFormat("* Agent:\n");
244// foreach (String s in httpServer.GetAgentHandlerKeys())
245// handlers.AppendFormat("\t{0}\n", s);
246
247 handlers.AppendFormat("* LLSD:\n");
248 foreach (String s in httpServer.GetLLSDHandlerKeys())
249 handlers.AppendFormat("\t{0}\n", s);
250
251 handlers.AppendFormat("* StreamHandlers ({0}):\n", httpServer.GetStreamHandlerKeys().Count);
252 foreach (String s in httpServer.GetStreamHandlerKeys())
253 handlers.AppendFormat("\t{0}\n", s);
254
255 handlers.Append("\n");
256 }
257 }
258
259 MainConsole.Instance.Output(handlers.ToString());
260 }
261
262 /// <summary>
263 /// Register an already started HTTP server to the collection of known servers.
264 /// </summary>
265 /// <param name='server'></param>
266 public static void AddHttpServer(BaseHttpServer server)
267 {
268 lock (m_Servers)
269 {
270 if (m_Servers.ContainsKey(server.Port))
271 throw new Exception(string.Format("HTTP server for port {0} already exists.", server.Port));
272
273 m_Servers.Add(server.Port, server);
274 }
275 }
276
277 /// <summary>
278 /// Removes the http server listening on the given port.
279 /// </summary>
280 /// <remarks>
281 /// It is the responsibility of the caller to do clean up.
282 /// </remarks>
283 /// <param name='port'></param>
284 /// <returns></returns>
285 public static bool RemoveHttpServer(uint port)
286 {
287 lock (m_Servers)
288 {
289 if (instance != null && instance.Port == port)
290 instance = null;
291
292 return m_Servers.Remove(port);
293 }
294 }
295
296 /// <summary>
297 /// Does this collection of servers contain one with the given port?
298 /// </summary>
299 /// <remarks>
300 /// Unlike GetHttpServer, this will not instantiate a server if one does not exist on that port.
301 /// </remarks>
302 /// <param name='port'></param>
303 /// <returns>true if a server with the given port is registered, false otherwise.</returns>
304 public static bool ContainsHttpServer(uint port)
305 {
306 lock (m_Servers)
307 return m_Servers.ContainsKey(port);
308 }
309
310 /// <summary>
311 /// Get the default http server or an http server for a specific port.
312 /// </summary>
313 /// <remarks>
314 /// If the requested HTTP server doesn't already exist then a new one is instantiated and started.
315 /// </remarks>
316 /// <returns></returns>
317 /// <param name='port'>If 0 then the default HTTP server is returned.</param>
318 public static IHttpServer GetHttpServer(uint port)
319 {
320 return GetHttpServer(port, null);
321 }
322
323 /// <summary>
324 /// Get the default http server, an http server for a specific port
325 /// and/or an http server bound to a specific address
326 /// </summary>
327 /// <remarks>
328 /// If the requested HTTP server doesn't already exist then a new one is instantiated and started.
329 /// </remarks>
330 /// <returns></returns>
331 /// <param name='port'>If 0 then the default HTTP server is returned.</param>
332 /// <param name='ipaddr'>A specific IP address to bind to. If null then the default IP address is used.</param>
333 public static IHttpServer GetHttpServer(uint port, IPAddress ipaddr)
334 {
335 if (port == 0)
336 return Instance;
337
338 if (instance != null && port == Instance.Port)
339 return Instance;
340
341 lock (m_Servers)
342 {
343 if (m_Servers.ContainsKey(port))
344 return m_Servers[port];
345
346 m_Servers[port] = new BaseHttpServer(port);
347
348 if (ipaddr != null)
349 m_Servers[port].ListenIPAddress = ipaddr;
350
351 m_Servers[port].Start();
352
353 return m_Servers[port];
354 }
355 }
356 }
357} \ No newline at end of file
diff --git a/OpenSim/Framework/Servers/Properties/AssemblyInfo.cs b/OpenSim/Framework/Servers/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..792c62e
--- /dev/null
+++ b/OpenSim/Framework/Servers/Properties/AssemblyInfo.cs
@@ -0,0 +1,33 @@
1using System.Reflection;
2using System.Runtime.CompilerServices;
3using System.Runtime.InteropServices;
4
5// General Information about an assembly is controlled through the following
6// set of attributes. Change these attribute values to modify the information
7// associated with an assembly.
8[assembly: AssemblyTitle("OpenSim.Framework.Servers")]
9[assembly: AssemblyDescription("")]
10[assembly: AssemblyConfiguration("")]
11[assembly: AssemblyCompany("http://opensimulator.org")]
12[assembly: AssemblyProduct("OpenSim")]
13[assembly: AssemblyCopyright("OpenSimulator developers")]
14[assembly: AssemblyTrademark("")]
15[assembly: AssemblyCulture("")]
16
17// Setting ComVisible to false makes the types in this assembly not visible
18// to COM components. If you need to access a type in this assembly from
19// COM, set the ComVisible attribute to true on that type.
20[assembly: ComVisible(false)]
21
22// The following GUID is for the ID of the typelib if this project is exposed to COM
23[assembly: Guid("b48e8b3e-5c5c-4673-b31f-21e13b8e568b")]
24
25// Version information for an assembly consists of the following four values:
26//
27// Major Version
28// Minor Version
29// Build Number
30// Revision
31//
32[assembly: AssemblyVersion("0.7.6.*")]
33[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/OpenSim/Framework/Servers/ServerBase.cs b/OpenSim/Framework/Servers/ServerBase.cs
new file mode 100644
index 0000000..e403ba0
--- /dev/null
+++ b/OpenSim/Framework/Servers/ServerBase.cs
@@ -0,0 +1,1047 @@
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.Diagnostics;
31using System.IO;
32using System.Linq;
33using System.Reflection;
34using System.Text;
35using System.Text.RegularExpressions;
36using System.Threading;
37using log4net;
38using log4net.Appender;
39using log4net.Core;
40using log4net.Repository;
41using Nini.Config;
42using OpenSim.Framework.Console;
43using OpenSim.Framework.Monitoring;
44
45namespace OpenSim.Framework.Servers
46{
47 public class ServerBase
48 {
49 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
50
51 public IConfigSource Config { get; protected set; }
52
53 /// <summary>
54 /// Console to be used for any command line output. Can be null, in which case there should be no output.
55 /// </summary>
56 protected ICommandConsole m_console;
57
58 protected OpenSimAppender m_consoleAppender;
59 protected FileAppender m_logFileAppender;
60
61 protected DateTime m_startuptime;
62 protected string m_startupDirectory = Environment.CurrentDirectory;
63
64 protected string m_pidFile = String.Empty;
65
66 protected ServerStatsCollector m_serverStatsCollector;
67
68 /// <summary>
69 /// Server version information. Usually VersionInfo + information about git commit, operating system, etc.
70 /// </summary>
71 protected string m_version;
72
73 public ServerBase()
74 {
75 m_startuptime = DateTime.Now;
76 m_version = VersionInfo.Version;
77 EnhanceVersionInformation();
78 }
79
80 protected void CreatePIDFile(string path)
81 {
82 if (File.Exists(path))
83 m_log.ErrorFormat(
84 "[SERVER BASE]: Previous pid file {0} still exists on startup. Possibly previously unclean shutdown.",
85 path);
86
87 try
88 {
89 string pidstring = System.Diagnostics.Process.GetCurrentProcess().Id.ToString();
90
91 using (FileStream fs = File.Create(path))
92 {
93 Byte[] buf = Encoding.ASCII.GetBytes(pidstring);
94 fs.Write(buf, 0, buf.Length);
95 }
96
97 m_pidFile = path;
98
99 m_log.InfoFormat("[SERVER BASE]: Created pid file {0}", m_pidFile);
100 }
101 catch (Exception e)
102 {
103 m_log.Warn(string.Format("[SERVER BASE]: Could not create PID file at {0} ", path), e);
104 }
105 }
106
107 protected void RemovePIDFile()
108 {
109 if (m_pidFile != String.Empty)
110 {
111 try
112 {
113 File.Delete(m_pidFile);
114 }
115 catch (Exception e)
116 {
117 m_log.Error(string.Format("[SERVER BASE]: Error whilst removing {0} ", m_pidFile), e);
118 }
119
120 m_pidFile = String.Empty;
121 }
122 }
123
124 /// <summary>
125 /// Log information about the circumstances in which we're running (OpenSimulator version number, CLR details,
126 /// etc.).
127 /// </summary>
128 public void LogEnvironmentInformation()
129 {
130 // FIXME: This should be done down in ServerBase but we need to sort out and refactor the log4net
131 // XmlConfigurator calls first accross servers.
132 m_log.InfoFormat("[SERVER BASE]: Starting in {0}", m_startupDirectory);
133
134 m_log.InfoFormat("[SERVER BASE]: OpenSimulator version: {0}", m_version);
135
136 // clr version potentially is more confusing than helpful, since it doesn't tell us if we're running under Mono/MS .NET and
137 // the clr version number doesn't match the project version number under Mono.
138 //m_log.Info("[STARTUP]: Virtual machine runtime version: " + Environment.Version + Environment.NewLine);
139 m_log.InfoFormat(
140 "[SERVER BASE]: Operating system version: {0}, .NET platform {1}, {2}-bit",
141 Environment.OSVersion, Environment.OSVersion.Platform, Util.Is64BitProcess() ? "64" : "32");
142 }
143
144 public void RegisterCommonAppenders(IConfig startupConfig)
145 {
146 ILoggerRepository repository = LogManager.GetRepository();
147 IAppender[] appenders = repository.GetAppenders();
148
149 foreach (IAppender appender in appenders)
150 {
151 if (appender.Name == "Console")
152 {
153 m_consoleAppender = (OpenSimAppender)appender;
154 }
155 else if (appender.Name == "LogFileAppender")
156 {
157 m_logFileAppender = (FileAppender)appender;
158 }
159 }
160
161 if (null == m_consoleAppender)
162 {
163 Notice("No appender named Console found (see the log4net config file for this executable)!");
164 }
165 else
166 {
167 // FIXME: This should be done through an interface rather than casting.
168 m_consoleAppender.Console = (ConsoleBase)m_console;
169
170 // If there is no threshold set then the threshold is effectively everything.
171 if (null == m_consoleAppender.Threshold)
172 m_consoleAppender.Threshold = Level.All;
173
174 Notice(String.Format("Console log level is {0}", m_consoleAppender.Threshold));
175 }
176
177 if (m_logFileAppender != null && startupConfig != null)
178 {
179 string cfgFileName = startupConfig.GetString("LogFile", null);
180 if (cfgFileName != null)
181 {
182 m_logFileAppender.File = cfgFileName;
183 m_logFileAppender.ActivateOptions();
184 }
185
186 m_log.InfoFormat("[SERVER BASE]: Logging started to file {0}", m_logFileAppender.File);
187 }
188 }
189
190 /// <summary>
191 /// Register common commands once m_console has been set if it is going to be set
192 /// </summary>
193 public void RegisterCommonCommands()
194 {
195 if (m_console == null)
196 return;
197
198 m_console.Commands.AddCommand(
199 "General", false, "show info", "show info", "Show general information about the server", HandleShow);
200
201 m_console.Commands.AddCommand(
202 "General", false, "show version", "show version", "Show server version", HandleShow);
203
204 m_console.Commands.AddCommand(
205 "General", false, "show uptime", "show uptime", "Show server uptime", HandleShow);
206
207 m_console.Commands.AddCommand(
208 "General", false, "get log level", "get log level", "Get the current console logging level",
209 (mod, cmd) => ShowLogLevel());
210
211 m_console.Commands.AddCommand(
212 "General", false, "set log level", "set log level <level>",
213 "Set the console logging level for this session.", HandleSetLogLevel);
214
215 m_console.Commands.AddCommand(
216 "General", false, "config set",
217 "config set <section> <key> <value>",
218 "Set a config option. In most cases this is not useful since changed parameters are not dynamically reloaded. Neither do changed parameters persist - you will have to change a config file manually and restart.", HandleConfig);
219
220 m_console.Commands.AddCommand(
221 "General", false, "config get",
222 "config get [<section>] [<key>]",
223 "Synonym for config show",
224 HandleConfig);
225
226 m_console.Commands.AddCommand(
227 "General", false, "config show",
228 "config show [<section>] [<key>]",
229 "Show config information",
230 "If neither section nor field are specified, then the whole current configuration is printed." + Environment.NewLine
231 + "If a section is given but not a field, then all fields in that section are printed.",
232 HandleConfig);
233
234 m_console.Commands.AddCommand(
235 "General", false, "config save",
236 "config save <path>",
237 "Save current configuration to a file at the given path", HandleConfig);
238
239 m_console.Commands.AddCommand(
240 "General", false, "command-script",
241 "command-script <script>",
242 "Run a command script from file", HandleScript);
243
244 m_console.Commands.AddCommand(
245 "General", false, "show threads",
246 "show threads",
247 "Show thread status", HandleShow);
248
249 m_console.Commands.AddCommand(
250 "Debug", false, "threads abort",
251 "threads abort <thread-id>",
252 "Abort a managed thread. Use \"show threads\" to find possible threads.", HandleThreadsAbort);
253
254 m_console.Commands.AddCommand(
255 "General", false, "threads show",
256 "threads show",
257 "Show thread status. Synonym for \"show threads\"",
258 (string module, string[] args) => Notice(GetThreadsReport()));
259
260 m_console.Commands.AddCommand (
261 "Debug", false, "debug comms set",
262 "debug comms set serialosdreq true|false",
263 "Set comms parameters. For debug purposes.",
264 HandleDebugCommsSet);
265
266 m_console.Commands.AddCommand (
267 "Debug", false, "debug comms status",
268 "debug comms status",
269 "Show current debug comms parameters.",
270 HandleDebugCommsStatus);
271
272 m_console.Commands.AddCommand (
273 "Debug", false, "debug threadpool set",
274 "debug threadpool set worker|iocp min|max <n>",
275 "Set threadpool parameters. For debug purposes.",
276 HandleDebugThreadpoolSet);
277
278 m_console.Commands.AddCommand (
279 "Debug", false, "debug threadpool status",
280 "debug threadpool status",
281 "Show current debug threadpool parameters.",
282 HandleDebugThreadpoolStatus);
283
284 m_console.Commands.AddCommand(
285 "Debug", false, "debug threadpool level",
286 "debug threadpool level 0.." + Util.MAX_THREADPOOL_LEVEL,
287 "Turn on logging of activity in the main thread pool.",
288 "Log levels:\n"
289 + " 0 = no logging\n"
290 + " 1 = only first line of stack trace; don't log common threads\n"
291 + " 2 = full stack trace; don't log common threads\n"
292 + " 3 = full stack trace, including common threads\n",
293 HandleDebugThreadpoolLevel);
294
295// m_console.Commands.AddCommand(
296// "Debug", false, "show threadpool calls active",
297// "show threadpool calls active",
298// "Show details about threadpool calls that are still active (currently waiting or in progress)",
299// HandleShowThreadpoolCallsActive);
300
301 m_console.Commands.AddCommand(
302 "Debug", false, "show threadpool calls complete",
303 "show threadpool calls complete",
304 "Show details about threadpool calls that have been completed.",
305 HandleShowThreadpoolCallsComplete);
306
307 m_console.Commands.AddCommand(
308 "Debug", false, "force gc",
309 "force gc",
310 "Manually invoke runtime garbage collection. For debugging purposes",
311 HandleForceGc);
312
313 m_console.Commands.AddCommand(
314 "General", false, "quit",
315 "quit",
316 "Quit the application", (mod, args) => Shutdown());
317
318 m_console.Commands.AddCommand(
319 "General", false, "shutdown",
320 "shutdown",
321 "Quit the application", (mod, args) => Shutdown());
322
323 ChecksManager.RegisterConsoleCommands(m_console);
324 StatsManager.RegisterConsoleCommands(m_console);
325 }
326
327 public void RegisterCommonComponents(IConfigSource configSource)
328 {
329 IConfig networkConfig = configSource.Configs["Network"];
330
331 if (networkConfig != null)
332 {
333 WebUtil.SerializeOSDRequestsPerEndpoint = networkConfig.GetBoolean("SerializeOSDRequests", false);
334 }
335
336 m_serverStatsCollector = new ServerStatsCollector();
337 m_serverStatsCollector.Initialise(configSource);
338 m_serverStatsCollector.Start();
339 }
340
341 private void HandleDebugCommsStatus(string module, string[] args)
342 {
343 Notice("serialosdreq is {0}", WebUtil.SerializeOSDRequestsPerEndpoint);
344 }
345
346 private void HandleDebugCommsSet(string module, string[] args)
347 {
348 if (args.Length != 5)
349 {
350 Notice("Usage: debug comms set serialosdreq true|false");
351 return;
352 }
353
354 if (args[3] != "serialosdreq")
355 {
356 Notice("Usage: debug comms set serialosdreq true|false");
357 return;
358 }
359
360 bool setSerializeOsdRequests;
361
362 if (!ConsoleUtil.TryParseConsoleBool(m_console, args[4], out setSerializeOsdRequests))
363 return;
364
365 WebUtil.SerializeOSDRequestsPerEndpoint = setSerializeOsdRequests;
366
367 Notice("serialosdreq is now {0}", setSerializeOsdRequests);
368 }
369
370 private void HandleShowThreadpoolCallsActive(string module, string[] args)
371 {
372 List<KeyValuePair<string, int>> calls = Util.GetFireAndForgetCallsInProgress().ToList();
373 calls.Sort((kvp1, kvp2) => kvp2.Value.CompareTo(kvp1.Value));
374 int namedCalls = 0;
375
376 ConsoleDisplayList cdl = new ConsoleDisplayList();
377 foreach (KeyValuePair<string, int> kvp in calls)
378 {
379 if (kvp.Value > 0)
380 {
381 cdl.AddRow(kvp.Key, kvp.Value);
382 namedCalls += kvp.Value;
383 }
384 }
385
386 cdl.AddRow("TOTAL NAMED", namedCalls);
387
388 long allQueuedCalls = Util.TotalQueuedFireAndForgetCalls;
389 long allRunningCalls = Util.TotalRunningFireAndForgetCalls;
390
391 cdl.AddRow("TOTAL QUEUED", allQueuedCalls);
392 cdl.AddRow("TOTAL RUNNING", allRunningCalls);
393 cdl.AddRow("TOTAL ANONYMOUS", allQueuedCalls + allRunningCalls - namedCalls);
394 cdl.AddRow("TOTAL ALL", allQueuedCalls + allRunningCalls);
395
396 MainConsole.Instance.Output(cdl.ToString());
397 }
398
399 private void HandleShowThreadpoolCallsComplete(string module, string[] args)
400 {
401 List<KeyValuePair<string, int>> calls = Util.GetFireAndForgetCallsMade().ToList();
402 calls.Sort((kvp1, kvp2) => kvp2.Value.CompareTo(kvp1.Value));
403 int namedCallsMade = 0;
404
405 ConsoleDisplayList cdl = new ConsoleDisplayList();
406 foreach (KeyValuePair<string, int> kvp in calls)
407 {
408 cdl.AddRow(kvp.Key, kvp.Value);
409 namedCallsMade += kvp.Value;
410 }
411
412 cdl.AddRow("TOTAL NAMED", namedCallsMade);
413
414 long allCallsMade = Util.TotalFireAndForgetCallsMade;
415 cdl.AddRow("TOTAL ANONYMOUS", allCallsMade - namedCallsMade);
416 cdl.AddRow("TOTAL ALL", allCallsMade);
417
418 MainConsole.Instance.Output(cdl.ToString());
419 }
420
421 private void HandleDebugThreadpoolStatus(string module, string[] args)
422 {
423 int workerThreads, iocpThreads;
424
425 ThreadPool.GetMinThreads(out workerThreads, out iocpThreads);
426 Notice("Min worker threads: {0}", workerThreads);
427 Notice("Min IOCP threads: {0}", iocpThreads);
428
429 ThreadPool.GetMaxThreads(out workerThreads, out iocpThreads);
430 Notice("Max worker threads: {0}", workerThreads);
431 Notice("Max IOCP threads: {0}", iocpThreads);
432
433 ThreadPool.GetAvailableThreads(out workerThreads, out iocpThreads);
434 Notice("Available worker threads: {0}", workerThreads);
435 Notice("Available IOCP threads: {0}", iocpThreads);
436 }
437
438 private void HandleDebugThreadpoolSet(string module, string[] args)
439 {
440 if (args.Length != 6)
441 {
442 Notice("Usage: debug threadpool set worker|iocp min|max <n>");
443 return;
444 }
445
446 int newThreads;
447
448 if (!ConsoleUtil.TryParseConsoleInt(m_console, args[5], out newThreads))
449 return;
450
451 string poolType = args[3];
452 string bound = args[4];
453
454 bool fail = false;
455 int workerThreads, iocpThreads;
456
457 if (poolType == "worker")
458 {
459 if (bound == "min")
460 {
461 ThreadPool.GetMinThreads(out workerThreads, out iocpThreads);
462
463 if (!ThreadPool.SetMinThreads(newThreads, iocpThreads))
464 fail = true;
465 }
466 else
467 {
468 ThreadPool.GetMaxThreads(out workerThreads, out iocpThreads);
469
470 if (!ThreadPool.SetMaxThreads(newThreads, iocpThreads))
471 fail = true;
472 }
473 }
474 else
475 {
476 if (bound == "min")
477 {
478 ThreadPool.GetMinThreads(out workerThreads, out iocpThreads);
479
480 if (!ThreadPool.SetMinThreads(workerThreads, newThreads))
481 fail = true;
482 }
483 else
484 {
485 ThreadPool.GetMaxThreads(out workerThreads, out iocpThreads);
486
487 if (!ThreadPool.SetMaxThreads(workerThreads, newThreads))
488 fail = true;
489 }
490 }
491
492 if (fail)
493 {
494 Notice("ERROR: Could not set {0} {1} threads to {2}", poolType, bound, newThreads);
495 }
496 else
497 {
498 int minWorkerThreads, maxWorkerThreads, minIocpThreads, maxIocpThreads;
499
500 ThreadPool.GetMinThreads(out minWorkerThreads, out minIocpThreads);
501 ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxIocpThreads);
502
503 Notice("Min worker threads now {0}", minWorkerThreads);
504 Notice("Min IOCP threads now {0}", minIocpThreads);
505 Notice("Max worker threads now {0}", maxWorkerThreads);
506 Notice("Max IOCP threads now {0}", maxIocpThreads);
507 }
508 }
509
510 private static void HandleDebugThreadpoolLevel(string module, string[] cmdparams)
511 {
512 if (cmdparams.Length < 4)
513 {
514 MainConsole.Instance.Output("Usage: debug threadpool level 0.." + Util.MAX_THREADPOOL_LEVEL);
515 return;
516 }
517
518 string rawLevel = cmdparams[3];
519 int newLevel;
520
521 if (!int.TryParse(rawLevel, out newLevel))
522 {
523 MainConsole.Instance.OutputFormat("{0} is not a valid debug level", rawLevel);
524 return;
525 }
526
527 if (newLevel < 0 || newLevel > Util.MAX_THREADPOOL_LEVEL)
528 {
529 MainConsole.Instance.OutputFormat("{0} is outside the valid debug level range of 0.." + Util.MAX_THREADPOOL_LEVEL, newLevel);
530 return;
531 }
532
533 Util.LogThreadPool = newLevel;
534 MainConsole.Instance.OutputFormat("LogThreadPool set to {0}", newLevel);
535 }
536
537 private void HandleForceGc(string module, string[] args)
538 {
539 Notice("Manually invoking runtime garbage collection");
540 GC.Collect();
541 }
542
543 public virtual void HandleShow(string module, string[] cmd)
544 {
545 List<string> args = new List<string>(cmd);
546
547 args.RemoveAt(0);
548
549 string[] showParams = args.ToArray();
550
551 switch (showParams[0])
552 {
553 case "info":
554 ShowInfo();
555 break;
556
557 case "version":
558 Notice(GetVersionText());
559 break;
560
561 case "uptime":
562 Notice(GetUptimeReport());
563 break;
564
565 case "threads":
566 Notice(GetThreadsReport());
567 break;
568 }
569 }
570
571 /// <summary>
572 /// Change and load configuration file data.
573 /// </summary>
574 /// <param name="module"></param>
575 /// <param name="cmd"></param>
576 private void HandleConfig(string module, string[] cmd)
577 {
578 List<string> args = new List<string>(cmd);
579 args.RemoveAt(0);
580 string[] cmdparams = args.ToArray();
581
582 if (cmdparams.Length > 0)
583 {
584 string firstParam = cmdparams[0].ToLower();
585
586 switch (firstParam)
587 {
588 case "set":
589 if (cmdparams.Length < 4)
590 {
591 Notice("Syntax: config set <section> <key> <value>");
592 Notice("Example: config set ScriptEngine.DotNetEngine NumberOfScriptThreads 5");
593 }
594 else
595 {
596 IConfig c;
597 IConfigSource source = new IniConfigSource();
598 c = source.AddConfig(cmdparams[1]);
599 if (c != null)
600 {
601 string _value = String.Join(" ", cmdparams, 3, cmdparams.Length - 3);
602 c.Set(cmdparams[2], _value);
603 Config.Merge(source);
604
605 Notice("In section [{0}], set {1} = {2}", c.Name, cmdparams[2], _value);
606 }
607 }
608 break;
609
610 case "get":
611 case "show":
612 if (cmdparams.Length == 1)
613 {
614 foreach (IConfig config in Config.Configs)
615 {
616 Notice("[{0}]", config.Name);
617 string[] keys = config.GetKeys();
618 foreach (string key in keys)
619 Notice(" {0} = {1}", key, config.GetString(key));
620 }
621 }
622 else if (cmdparams.Length == 2 || cmdparams.Length == 3)
623 {
624 IConfig config = Config.Configs[cmdparams[1]];
625 if (config == null)
626 {
627 Notice("Section \"{0}\" does not exist.",cmdparams[1]);
628 break;
629 }
630 else
631 {
632 if (cmdparams.Length == 2)
633 {
634 Notice("[{0}]", config.Name);
635 foreach (string key in config.GetKeys())
636 Notice(" {0} = {1}", key, config.GetString(key));
637 }
638 else
639 {
640 Notice(
641 "config get {0} {1} : {2}",
642 cmdparams[1], cmdparams[2], config.GetString(cmdparams[2]));
643 }
644 }
645 }
646 else
647 {
648 Notice("Syntax: config {0} [<section>] [<key>]", firstParam);
649 Notice("Example: config {0} ScriptEngine.DotNetEngine NumberOfScriptThreads", firstParam);
650 }
651
652 break;
653
654 case "save":
655 if (cmdparams.Length < 2)
656 {
657 Notice("Syntax: config save <path>");
658 return;
659 }
660
661 string path = cmdparams[1];
662 Notice("Saving configuration file: {0}", path);
663
664 if (Config is IniConfigSource)
665 {
666 IniConfigSource iniCon = (IniConfigSource)Config;
667 iniCon.Save(path);
668 }
669 else if (Config is XmlConfigSource)
670 {
671 XmlConfigSource xmlCon = (XmlConfigSource)Config;
672 xmlCon.Save(path);
673 }
674
675 break;
676 }
677 }
678 }
679
680 private void HandleSetLogLevel(string module, string[] cmd)
681 {
682 if (cmd.Length != 4)
683 {
684 Notice("Usage: set log level <level>");
685 return;
686 }
687
688 if (null == m_consoleAppender)
689 {
690 Notice("No appender named Console found (see the log4net config file for this executable)!");
691 return;
692 }
693
694 string rawLevel = cmd[3];
695
696 ILoggerRepository repository = LogManager.GetRepository();
697 Level consoleLevel = repository.LevelMap[rawLevel];
698
699 if (consoleLevel != null)
700 m_consoleAppender.Threshold = consoleLevel;
701 else
702 Notice(
703 "{0} is not a valid logging level. Valid logging levels are ALL, DEBUG, INFO, WARN, ERROR, FATAL, OFF",
704 rawLevel);
705
706 ShowLogLevel();
707 }
708
709 private void ShowLogLevel()
710 {
711 Notice("Console log level is {0}", m_consoleAppender.Threshold);
712 }
713
714 protected virtual void HandleScript(string module, string[] parms)
715 {
716 if (parms.Length != 2)
717 {
718 Notice("Usage: command-script <path-to-script");
719 return;
720 }
721
722 RunCommandScript(parms[1]);
723 }
724
725 /// <summary>
726 /// Run an optional startup list of commands
727 /// </summary>
728 /// <param name="fileName"></param>
729 protected void RunCommandScript(string fileName)
730 {
731 if (m_console == null)
732 return;
733
734 if (File.Exists(fileName))
735 {
736 m_log.Info("[SERVER BASE]: Running " + fileName);
737
738 using (StreamReader readFile = File.OpenText(fileName))
739 {
740 string currentCommand;
741 while ((currentCommand = readFile.ReadLine()) != null)
742 {
743 currentCommand = currentCommand.Trim();
744 if (!(currentCommand == ""
745 || currentCommand.StartsWith(";")
746 || currentCommand.StartsWith("//")
747 || currentCommand.StartsWith("#")))
748 {
749 m_log.Info("[SERVER BASE]: Running '" + currentCommand + "'");
750 m_console.RunCommand(currentCommand);
751 }
752 }
753 }
754 }
755 }
756
757 /// <summary>
758 /// Return a report about the uptime of this server
759 /// </summary>
760 /// <returns></returns>
761 protected string GetUptimeReport()
762 {
763 StringBuilder sb = new StringBuilder(String.Format("Time now is {0}\n", DateTime.Now));
764 sb.Append(String.Format("Server has been running since {0}, {1}\n", m_startuptime.DayOfWeek, m_startuptime));
765 sb.Append(String.Format("That is an elapsed time of {0}\n", DateTime.Now - m_startuptime));
766
767 return sb.ToString();
768 }
769
770 protected void ShowInfo()
771 {
772 Notice(GetVersionText());
773 Notice("Startup directory: " + m_startupDirectory);
774 if (null != m_consoleAppender)
775 Notice(String.Format("Console log level: {0}", m_consoleAppender.Threshold));
776 }
777
778 /// <summary>
779 /// Enhance the version string with extra information if it's available.
780 /// </summary>
781 protected void EnhanceVersionInformation()
782 {
783 string buildVersion = string.Empty;
784
785 // The subversion information is deprecated and will be removed at a later date
786 // Add subversion revision information if available
787 // Try file "svn_revision" in the current directory first, then the .svn info.
788 // This allows to make the revision available in simulators not running from the source tree.
789 // FIXME: Making an assumption about the directory we're currently in - we do this all over the place
790 // elsewhere as well
791 string gitDir = "../.git/";
792 string gitRefPointerPath = gitDir + "HEAD";
793
794 string svnRevisionFileName = "svn_revision";
795 string svnFileName = ".svn/entries";
796 string manualVersionFileName = ".version";
797 string inputLine;
798 int strcmp;
799
800 if (File.Exists(manualVersionFileName))
801 {
802 using (StreamReader CommitFile = File.OpenText(manualVersionFileName))
803 buildVersion = CommitFile.ReadLine();
804
805 m_version += buildVersion ?? "";
806 }
807 else if (File.Exists(gitRefPointerPath))
808 {
809// m_log.DebugFormat("[SERVER BASE]: Found {0}", gitRefPointerPath);
810
811 string rawPointer = "";
812
813 using (StreamReader pointerFile = File.OpenText(gitRefPointerPath))
814 rawPointer = pointerFile.ReadLine();
815
816// m_log.DebugFormat("[SERVER BASE]: rawPointer [{0}]", rawPointer);
817
818 Match m = Regex.Match(rawPointer, "^ref: (.+)$");
819
820 if (m.Success)
821 {
822// m_log.DebugFormat("[SERVER BASE]: Matched [{0}]", m.Groups[1].Value);
823
824 string gitRef = m.Groups[1].Value;
825 string gitRefPath = gitDir + gitRef;
826 if (File.Exists(gitRefPath))
827 {
828// m_log.DebugFormat("[SERVER BASE]: Found gitRefPath [{0}]", gitRefPath);
829
830 using (StreamReader refFile = File.OpenText(gitRefPath))
831 {
832 string gitHash = refFile.ReadLine();
833 m_version += gitHash.Substring(0, 7);
834 }
835 }
836 }
837 }
838 else
839 {
840 // Remove the else logic when subversion mirror is no longer used
841 if (File.Exists(svnRevisionFileName))
842 {
843 StreamReader RevisionFile = File.OpenText(svnRevisionFileName);
844 buildVersion = RevisionFile.ReadLine();
845 buildVersion.Trim();
846 RevisionFile.Close();
847 }
848
849 if (string.IsNullOrEmpty(buildVersion) && File.Exists(svnFileName))
850 {
851 StreamReader EntriesFile = File.OpenText(svnFileName);
852 inputLine = EntriesFile.ReadLine();
853 while (inputLine != null)
854 {
855 // using the dir svn revision at the top of entries file
856 strcmp = String.Compare(inputLine, "dir");
857 if (strcmp == 0)
858 {
859 buildVersion = EntriesFile.ReadLine();
860 break;
861 }
862 else
863 {
864 inputLine = EntriesFile.ReadLine();
865 }
866 }
867 EntriesFile.Close();
868 }
869
870 m_version += string.IsNullOrEmpty(buildVersion) ? " " : ("." + buildVersion + " ").Substring(0, 6);
871 }
872 }
873
874 protected string GetVersionText()
875 {
876 return String.Format("Version: {0} (interface version {1})", m_version, VersionInfo.MajorInterfaceVersion);
877 }
878
879 /// <summary>
880 /// Get a report about the registered threads in this server.
881 /// </summary>
882 protected string GetThreadsReport()
883 {
884 // This should be a constant field.
885 string reportFormat = "{0,6} {1,35} {2,16} {3,13} {4,10} {5,30}";
886
887 StringBuilder sb = new StringBuilder();
888 Watchdog.ThreadWatchdogInfo[] threads = Watchdog.GetThreadsInfo();
889
890 sb.Append(threads.Length + " threads are being tracked:" + Environment.NewLine);
891
892 int timeNow = Environment.TickCount & Int32.MaxValue;
893
894 sb.AppendFormat(reportFormat, "ID", "NAME", "LAST UPDATE (MS)", "LIFETIME (MS)", "PRIORITY", "STATE");
895 sb.Append(Environment.NewLine);
896
897 foreach (Watchdog.ThreadWatchdogInfo twi in threads)
898 {
899 Thread t = twi.Thread;
900
901 sb.AppendFormat(
902 reportFormat,
903 t.ManagedThreadId,
904 t.Name,
905 timeNow - twi.LastTick,
906 timeNow - twi.FirstTick,
907 t.Priority,
908 t.ThreadState);
909
910 sb.Append("\n");
911 }
912
913 sb.Append("\n");
914
915 // For some reason mono 2.6.7 returns an empty threads set! Not going to confuse people by reporting
916 // zero active threads.
917 int totalThreads = Process.GetCurrentProcess().Threads.Count;
918 if (totalThreads > 0)
919 sb.AppendFormat("Total threads active: {0}\n\n", totalThreads);
920
921 sb.Append("Main threadpool (excluding script engine pools)\n");
922 sb.Append(GetThreadPoolReport());
923
924 return sb.ToString();
925 }
926
927 /// <summary>
928 /// Get a thread pool report.
929 /// </summary>
930 /// <returns></returns>
931 public static string GetThreadPoolReport()
932 {
933 string threadPoolUsed = null;
934 int maxThreads = 0;
935 int minThreads = 0;
936 int allocatedThreads = 0;
937 int inUseThreads = 0;
938 int waitingCallbacks = 0;
939 int completionPortThreads = 0;
940
941 StringBuilder sb = new StringBuilder();
942 if (Util.FireAndForgetMethod == FireAndForgetMethod.SmartThreadPool)
943 {
944 STPInfo stpi = Util.GetSmartThreadPoolInfo();
945
946 // ROBUST currently leaves this the FireAndForgetMethod but never actually initializes the threadpool.
947 if (stpi != null)
948 {
949 threadPoolUsed = "SmartThreadPool";
950 maxThreads = stpi.MaxThreads;
951 minThreads = stpi.MinThreads;
952 inUseThreads = stpi.InUseThreads;
953 allocatedThreads = stpi.ActiveThreads;
954 waitingCallbacks = stpi.WaitingCallbacks;
955 }
956 }
957 else if (
958 Util.FireAndForgetMethod == FireAndForgetMethod.QueueUserWorkItem
959 || Util.FireAndForgetMethod == FireAndForgetMethod.UnsafeQueueUserWorkItem)
960 {
961 threadPoolUsed = "BuiltInThreadPool";
962 ThreadPool.GetMaxThreads(out maxThreads, out completionPortThreads);
963 ThreadPool.GetMinThreads(out minThreads, out completionPortThreads);
964 int availableThreads;
965 ThreadPool.GetAvailableThreads(out availableThreads, out completionPortThreads);
966 inUseThreads = maxThreads - availableThreads;
967 allocatedThreads = -1;
968 waitingCallbacks = -1;
969 }
970
971 if (threadPoolUsed != null)
972 {
973 sb.AppendFormat("Thread pool used : {0}\n", threadPoolUsed);
974 sb.AppendFormat("Max threads : {0}\n", maxThreads);
975 sb.AppendFormat("Min threads : {0}\n", minThreads);
976 sb.AppendFormat("Allocated threads : {0}\n", allocatedThreads < 0 ? "not applicable" : allocatedThreads.ToString());
977 sb.AppendFormat("In use threads : {0}\n", inUseThreads);
978 sb.AppendFormat("Work items waiting : {0}\n", waitingCallbacks < 0 ? "not available" : waitingCallbacks.ToString());
979 }
980 else
981 {
982 sb.AppendFormat("Thread pool not used\n");
983 }
984
985 return sb.ToString();
986 }
987
988 public virtual void HandleThreadsAbort(string module, string[] cmd)
989 {
990 if (cmd.Length != 3)
991 {
992 MainConsole.Instance.Output("Usage: threads abort <thread-id>");
993 return;
994 }
995
996 int threadId;
997 if (!int.TryParse(cmd[2], out threadId))
998 {
999 MainConsole.Instance.Output("ERROR: Thread id must be an integer");
1000 return;
1001 }
1002
1003 if (Watchdog.AbortThread(threadId))
1004 MainConsole.Instance.OutputFormat("Aborted thread with id {0}", threadId);
1005 else
1006 MainConsole.Instance.OutputFormat("ERROR - Thread with id {0} not found in managed threads", threadId);
1007 }
1008
1009 /// <summary>
1010 /// Console output is only possible if a console has been established.
1011 /// That is something that cannot be determined within this class. So
1012 /// all attempts to use the console MUST be verified.
1013 /// </summary>
1014 /// <param name="msg"></param>
1015 protected void Notice(string msg)
1016 {
1017 if (m_console != null)
1018 {
1019 m_console.Output(msg);
1020 }
1021 }
1022
1023 /// <summary>
1024 /// Console output is only possible if a console has been established.
1025 /// That is something that cannot be determined within this class. So
1026 /// all attempts to use the console MUST be verified.
1027 /// </summary>
1028 /// <param name="format"></param>
1029 /// <param name="components"></param>
1030 protected void Notice(string format, params object[] components)
1031 {
1032 if (m_console != null)
1033 m_console.OutputFormat(format, components);
1034 }
1035
1036 public virtual void Shutdown()
1037 {
1038 m_serverStatsCollector.Close();
1039 ShutdownSpecific();
1040 }
1041
1042 /// <summary>
1043 /// Should be overriden and referenced by descendents if they need to perform extra shutdown processing
1044 /// </summary>
1045 protected virtual void ShutdownSpecific() {}
1046 }
1047} \ No newline at end of file
diff --git a/OpenSim/Framework/Servers/Tests/OSHttpTests.cs b/OpenSim/Framework/Servers/Tests/OSHttpTests.cs
new file mode 100644
index 0000000..5c0e0df
--- /dev/null
+++ b/OpenSim/Framework/Servers/Tests/OSHttpTests.cs
@@ -0,0 +1,116 @@
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.Specialized;
30using System.IO;
31using System.Net;
32using System.Net.Sockets;
33using System.Text;
34using HttpServer;
35using HttpServer.FormDecoders;
36using NUnit.Framework;
37using OpenSim.Framework.Servers.HttpServer;
38using OpenSim.Tests.Common;
39
40namespace OpenSim.Framework.Servers.Tests
41{
42 [TestFixture]
43 public class OSHttpTests : OpenSimTestCase
44 {
45 public OSHttpRequest req0;
46 public OSHttpRequest req1;
47
48 public OSHttpResponse rsp0;
49
50 public IPEndPoint ipEP0;
51
52 [TestFixtureSetUp]
53 public void Init()
54 {
55 TestHttpRequest threq0 = new TestHttpRequest("utf-8", "text/xml", "OpenSim Test Agent", "192.168.0.1", "4711",
56 new string[] {"text/xml"},
57 ConnectionType.KeepAlive, 4711,
58 new Uri("http://127.0.0.1/admin/inventory/Dr+Who/Tardis"));
59 threq0.Method = "GET";
60 threq0.HttpVersion = HttpHelper.HTTP10;
61
62 TestHttpRequest threq1 = new TestHttpRequest("utf-8", "text/xml", "OpenSim Test Agent", "192.168.0.1", "4711",
63 new string[] {"text/xml"},
64 ConnectionType.KeepAlive, 4711,
65 new Uri("http://127.0.0.1/admin/inventory/Dr+Who/Tardis?a=0&b=1&c=2"));
66 threq1.Method = "POST";
67 threq1.HttpVersion = HttpHelper.HTTP11;
68 threq1.Headers["x-wuff"] = "wuffwuff";
69 threq1.Headers["www-authenticate"] = "go away";
70
71 req0 = new OSHttpRequest(new TestHttpClientContext(false), threq0);
72 req1 = new OSHttpRequest(new TestHttpClientContext(false), threq1);
73
74 rsp0 = new OSHttpResponse(new TestHttpResponse());
75
76 ipEP0 = new IPEndPoint(IPAddress.Parse("192.168.0.1"), 4711);
77
78 }
79
80 [Test]
81 public void T000_OSHttpRequest()
82 {
83 Assert.That(req0.HttpMethod, Is.EqualTo("GET"));
84 Assert.That(req0.ContentType, Is.EqualTo("text/xml"));
85 Assert.That(req0.ContentLength, Is.EqualTo(4711));
86
87 Assert.That(req1.HttpMethod, Is.EqualTo("POST"));
88 }
89
90 [Test]
91 public void T001_OSHttpRequestHeaderAccess()
92 {
93 Assert.That(req1.Headers["x-wuff"], Is.EqualTo("wuffwuff"));
94 Assert.That(req1.Headers.Get("x-wuff"), Is.EqualTo("wuffwuff"));
95
96 Assert.That(req1.Headers["www-authenticate"], Is.EqualTo("go away"));
97 Assert.That(req1.Headers.Get("www-authenticate"), Is.EqualTo("go away"));
98
99 Assert.That(req0.RemoteIPEndPoint, Is.EqualTo(ipEP0));
100 }
101
102 [Test]
103 public void T002_OSHttpRequestUriParsing()
104 {
105 Assert.That(req0.RawUrl, Is.EqualTo("/admin/inventory/Dr+Who/Tardis"));
106 Assert.That(req1.Url.ToString(), Is.EqualTo("http://127.0.0.1/admin/inventory/Dr+Who/Tardis?a=0&b=1&c=2"));
107 }
108
109 [Test]
110 public void T100_OSHttpResponse()
111 {
112 rsp0.ContentType = "text/xml";
113 Assert.That(rsp0.ContentType, Is.EqualTo("text/xml"));
114 }
115 }
116} \ No newline at end of file
diff --git a/OpenSim/Framework/Servers/Tests/VersionInfoTests.cs b/OpenSim/Framework/Servers/Tests/VersionInfoTests.cs
new file mode 100644
index 0000000..480f2bb
--- /dev/null
+++ b/OpenSim/Framework/Servers/Tests/VersionInfoTests.cs
@@ -0,0 +1,54 @@
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.Text;
31using NUnit.Framework;
32using OpenSim.Tests.Common;
33
34namespace OpenSim.Framework.Servers.Tests
35{
36 [TestFixture]
37 public class VersionInfoTests : OpenSimTestCase
38 {
39 [Test]
40 public void TestVersionLength()
41 {
42 Assert.AreEqual(VersionInfo.VERSIONINFO_VERSION_LENGTH, VersionInfo.Version.Length," VersionInfo.Version string not " + VersionInfo.VERSIONINFO_VERSION_LENGTH + " chars.");
43 }
44
45 [Test]
46 public void TestGetVersionStringLength()
47 {
48 foreach (VersionInfo.Flavour flavour in Enum.GetValues(typeof(VersionInfo.Flavour)))
49 {
50 Assert.AreEqual(VersionInfo.VERSIONINFO_VERSION_LENGTH, VersionInfo.GetVersionString("0.0.0", flavour).Length, "0.0.0/" + flavour + " failed");
51 }
52 }
53 }
54}