aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/Servers
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Framework/Servers')
-rw-r--r--OpenSim/Framework/Servers/BaseOpenSimServer.cs124
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs253
-rw-r--r--OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs14
-rw-r--r--OpenSim/Framework/Servers/HttpServer/JsonRPCMethod.cs34
-rw-r--r--OpenSim/Framework/Servers/HttpServer/JsonRpcResponse.cs150
-rw-r--r--OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs18
-rw-r--r--OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs4
-rw-r--r--OpenSim/Framework/Servers/HttpServer/RestSessionService.cs15
-rw-r--r--OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs1102
-rw-r--r--OpenSim/Framework/Servers/MainServer.cs11
-rw-r--r--OpenSim/Framework/Servers/Properties/AssemblyInfo.cs2
-rw-r--r--OpenSim/Framework/Servers/ServerBase.cs131
-rw-r--r--OpenSim/Framework/Servers/Tests/OSHttpTests.cs8
-rw-r--r--OpenSim/Framework/Servers/Tests/VersionInfoTests.cs3
-rw-r--r--OpenSim/Framework/Servers/VersionInfo.cs2
15 files changed, 1666 insertions, 205 deletions
diff --git a/OpenSim/Framework/Servers/BaseOpenSimServer.cs b/OpenSim/Framework/Servers/BaseOpenSimServer.cs
index 2c21800..6c04c69 100644
--- a/OpenSim/Framework/Servers/BaseOpenSimServer.cs
+++ b/OpenSim/Framework/Servers/BaseOpenSimServer.cs
@@ -27,7 +27,6 @@
27 27
28using System; 28using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Diagnostics;
31using System.IO; 30using System.IO;
32using System.Reflection; 31using System.Reflection;
33using System.Text; 32using System.Text;
@@ -99,34 +98,6 @@ namespace OpenSim.Framework.Servers
99 m_console.Commands.AddCommand("General", false, "shutdown", 98 m_console.Commands.AddCommand("General", false, "shutdown",
100 "shutdown", 99 "shutdown",
101 "Quit the application", HandleQuit); 100 "Quit the application", HandleQuit);
102
103 m_console.Commands.AddCommand("General", false, "show threads",
104 "show threads",
105 "Show thread status", HandleShow);
106
107 m_console.Commands.AddCommand("General", false, "show version",
108 "show version",
109 "Show server version", HandleShow);
110
111 m_console.Commands.AddCommand("General", false, "threads abort",
112 "threads abort <thread-id>",
113 "Abort a managed thread. Use \"show threads\" to find possible threads.", HandleThreadsAbort);
114
115 m_console.Commands.AddCommand("General", false, "threads show",
116 "threads show",
117 "Show thread status. Synonym for \"show threads\"",
118 (string module, string[] args) => Notice(GetThreadsReport()));
119
120 m_console.Commands.AddCommand("General", false, "force gc",
121 "force gc",
122 "Manually invoke runtime garbage collection. For debugging purposes",
123 HandleForceGc);
124 }
125
126 private void HandleForceGc(string module, string[] args)
127 {
128 MainConsole.Instance.Output("Manually invoking runtime garbage collection");
129 GC.Collect();
130 } 101 }
131 102
132 /// <summary> 103 /// <summary>
@@ -159,54 +130,6 @@ namespace OpenSim.Framework.Servers
159 } 130 }
160 131
161 /// <summary> 132 /// <summary>
162 /// Get a report about the registered threads in this server.
163 /// </summary>
164 protected string GetThreadsReport()
165 {
166 // This should be a constant field.
167 string reportFormat = "{0,6} {1,35} {2,16} {3,13} {4,10} {5,30}";
168
169 StringBuilder sb = new StringBuilder();
170 Watchdog.ThreadWatchdogInfo[] threads = Watchdog.GetThreadsInfo();
171
172 sb.Append(threads.Length + " threads are being tracked:" + Environment.NewLine);
173
174 int timeNow = Environment.TickCount & Int32.MaxValue;
175
176 sb.AppendFormat(reportFormat, "ID", "NAME", "LAST UPDATE (MS)", "LIFETIME (MS)", "PRIORITY", "STATE");
177 sb.Append(Environment.NewLine);
178
179 foreach (Watchdog.ThreadWatchdogInfo twi in threads)
180 {
181 Thread t = twi.Thread;
182
183 sb.AppendFormat(
184 reportFormat,
185 t.ManagedThreadId,
186 t.Name,
187 timeNow - twi.LastTick,
188 timeNow - twi.FirstTick,
189 t.Priority,
190 t.ThreadState);
191
192 sb.Append("\n");
193 }
194
195 sb.Append("\n");
196
197 // For some reason mono 2.6.7 returns an empty threads set! Not going to confuse people by reporting
198 // zero active threads.
199 int totalThreads = Process.GetCurrentProcess().Threads.Count;
200 if (totalThreads > 0)
201 sb.AppendFormat("Total threads active: {0}\n\n", totalThreads);
202
203 sb.Append("Main threadpool (excluding script engine pools)\n");
204 sb.Append(Util.GetThreadPoolReport());
205
206 return sb.ToString();
207 }
208
209 /// <summary>
210 /// Performs initialisation of the scene, such as loading configuration from disk. 133 /// Performs initialisation of the scene, such as loading configuration from disk.
211 /// </summary> 134 /// </summary>
212 public virtual void Startup() 135 public virtual void Startup()
@@ -246,50 +169,7 @@ namespace OpenSim.Framework.Servers
246 private void HandleQuit(string module, string[] args) 169 private void HandleQuit(string module, string[] args)
247 { 170 {
248 Shutdown(); 171 Shutdown();
249 } 172 }
250
251 public override void HandleShow(string module, string[] cmd)
252 {
253 base.HandleShow(module, cmd);
254
255 List<string> args = new List<string>(cmd);
256
257 args.RemoveAt(0);
258
259 string[] showParams = args.ToArray();
260
261 switch (showParams[0])
262 {
263 case "threads":
264 Notice(GetThreadsReport());
265 break;
266
267 case "version":
268 Notice(GetVersionText());
269 break;
270 }
271 }
272
273 public virtual void HandleThreadsAbort(string module, string[] cmd)
274 {
275 if (cmd.Length != 3)
276 {
277 MainConsole.Instance.Output("Usage: threads abort <thread-id>");
278 return;
279 }
280
281 int threadId;
282 if (!int.TryParse(cmd[2], out threadId))
283 {
284 MainConsole.Instance.Output("ERROR: Thread id must be an integer");
285 return;
286 }
287
288 if (Watchdog.AbortThread(threadId))
289 MainConsole.Instance.OutputFormat("Aborted thread with id {0}", threadId);
290 else
291 MainConsole.Instance.OutputFormat("ERROR - Thread with id {0} not found in managed threads", threadId);
292 }
293 173
294 public string osSecret { 174 public string osSecret {
295 // Secret uuid for the simulator 175 // Secret uuid for the simulator
@@ -309,4 +189,4 @@ namespace OpenSim.Framework.Servers
309 } 189 }
310 } 190 }
311 } 191 }
312} \ No newline at end of file 192}
diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
index 77fce9e..97035e3 100644
--- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
+++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
@@ -54,6 +54,16 @@ namespace OpenSim.Framework.Servers.HttpServer
54 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 54 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
55 private HttpServerLogWriter httpserverlog = new HttpServerLogWriter(); 55 private HttpServerLogWriter httpserverlog = new HttpServerLogWriter();
56 56
57
58 /// <summary>
59 /// This is a pending websocket request before it got an sucessful upgrade response.
60 /// The consumer must call handler.HandshakeAndUpgrade() to signal to the handler to
61 /// start the connection and optionally provide an origin authentication method.
62 /// </summary>
63 /// <param name="servicepath"></param>
64 /// <param name="handler"></param>
65 public delegate void WebSocketRequestDelegate(string servicepath, WebSocketHttpServerHandler handler);
66
57 /// <summary> 67 /// <summary>
58 /// Gets or sets the debug level. 68 /// Gets or sets the debug level.
59 /// </summary> 69 /// </summary>
@@ -77,6 +87,7 @@ namespace OpenSim.Framework.Servers.HttpServer
77 // protected HttpListener m_httpListener; 87 // protected HttpListener m_httpListener;
78 protected CoolHTTPListener m_httpListener2; 88 protected CoolHTTPListener m_httpListener2;
79 protected Dictionary<string, XmlRpcMethod> m_rpcHandlers = new Dictionary<string, XmlRpcMethod>(); 89 protected Dictionary<string, XmlRpcMethod> m_rpcHandlers = new Dictionary<string, XmlRpcMethod>();
90 protected Dictionary<string, JsonRPCMethod> jsonRpcHandlers = new Dictionary<string, JsonRPCMethod>();
80 protected Dictionary<string, bool> m_rpcHandlersKeepAlive = new Dictionary<string, bool>(); 91 protected Dictionary<string, bool> m_rpcHandlersKeepAlive = new Dictionary<string, bool>();
81 protected DefaultLLSDMethod m_defaultLlsdHandler = null; // <-- Moving away from the monolithic.. and going to /registered/ 92 protected DefaultLLSDMethod m_defaultLlsdHandler = null; // <-- Moving away from the monolithic.. and going to /registered/
82 protected Dictionary<string, LLSDMethod> m_llsdHandlers = new Dictionary<string, LLSDMethod>(); 93 protected Dictionary<string, LLSDMethod> m_llsdHandlers = new Dictionary<string, LLSDMethod>();
@@ -86,6 +97,9 @@ namespace OpenSim.Framework.Servers.HttpServer
86 protected Dictionary<string, PollServiceEventArgs> m_pollHandlers = 97 protected Dictionary<string, PollServiceEventArgs> m_pollHandlers =
87 new Dictionary<string, PollServiceEventArgs>(); 98 new Dictionary<string, PollServiceEventArgs>();
88 99
100 protected Dictionary<string, WebSocketRequestDelegate> m_WebSocketHandlers =
101 new Dictionary<string, WebSocketRequestDelegate>();
102
89 protected uint m_port; 103 protected uint m_port;
90 protected uint m_sslport; 104 protected uint m_sslport;
91 protected bool m_ssl; 105 protected bool m_ssl;
@@ -169,6 +183,22 @@ namespace OpenSim.Framework.Servers.HttpServer
169 } 183 }
170 } 184 }
171 185
186 public void AddWebSocketHandler(string servicepath, WebSocketRequestDelegate handler)
187 {
188 lock (m_WebSocketHandlers)
189 {
190 if (!m_WebSocketHandlers.ContainsKey(servicepath))
191 m_WebSocketHandlers.Add(servicepath, handler);
192 }
193 }
194
195 public void RemoveWebSocketHandler(string servicepath)
196 {
197 lock (m_WebSocketHandlers)
198 if (m_WebSocketHandlers.ContainsKey(servicepath))
199 m_WebSocketHandlers.Remove(servicepath);
200 }
201
172 public List<string> GetStreamHandlerKeys() 202 public List<string> GetStreamHandlerKeys()
173 { 203 {
174 lock (m_streamHandlers) 204 lock (m_streamHandlers)
@@ -217,6 +247,37 @@ namespace OpenSim.Framework.Servers.HttpServer
217 return new List<string>(m_rpcHandlers.Keys); 247 return new List<string>(m_rpcHandlers.Keys);
218 } 248 }
219 249
250 // JsonRPC
251 public bool AddJsonRPCHandler(string method, JsonRPCMethod handler)
252 {
253 lock(jsonRpcHandlers)
254 {
255 jsonRpcHandlers.Add(method, handler);
256 }
257 return true;
258 }
259
260 public JsonRPCMethod GetJsonRPCHandler(string method)
261 {
262 lock (jsonRpcHandlers)
263 {
264 if (jsonRpcHandlers.ContainsKey(method))
265 {
266 return jsonRpcHandlers[method];
267 }
268 else
269 {
270 return null;
271 }
272 }
273 }
274
275 public List<string> GetJsonRpcHandlerKeys()
276 {
277 lock (jsonRpcHandlers)
278 return new List<string>(jsonRpcHandlers.Keys);
279 }
280
220 public bool AddHTTPHandler(string methodName, GenericHTTPMethod handler) 281 public bool AddHTTPHandler(string methodName, GenericHTTPMethod handler)
221 { 282 {
222 //m_log.DebugFormat("[BASE HTTP SERVER]: Registering {0}", methodName); 283 //m_log.DebugFormat("[BASE HTTP SERVER]: Registering {0}", methodName);
@@ -378,9 +439,24 @@ namespace OpenSim.Framework.Servers.HttpServer
378 439
379 public void OnHandleRequestIOThread(IHttpClientContext context, IHttpRequest request) 440 public void OnHandleRequestIOThread(IHttpClientContext context, IHttpRequest request)
380 { 441 {
442
381 OSHttpRequest req = new OSHttpRequest(context, request); 443 OSHttpRequest req = new OSHttpRequest(context, request);
444 WebSocketRequestDelegate dWebSocketRequestDelegate = null;
445 lock (m_WebSocketHandlers)
446 {
447 if (m_WebSocketHandlers.ContainsKey(req.RawUrl))
448 dWebSocketRequestDelegate = m_WebSocketHandlers[req.RawUrl];
449 }
450 if (dWebSocketRequestDelegate != null)
451 {
452 dWebSocketRequestDelegate(req.Url.AbsolutePath, new WebSocketHttpServerHandler(req, context, 8192));
453 return;
454 }
455
382 OSHttpResponse resp = new OSHttpResponse(new HttpResponse(context, request),context); 456 OSHttpResponse resp = new OSHttpResponse(new HttpResponse(context, request),context);
457
383 HandleRequest(req, resp); 458 HandleRequest(req, resp);
459
384 460
385 // !!!HACK ALERT!!! 461 // !!!HACK ALERT!!!
386 // There seems to be a bug in the underlying http code that makes subsequent requests 462 // There seems to be a bug in the underlying http code that makes subsequent requests
@@ -411,7 +487,9 @@ namespace OpenSim.Framework.Servers.HttpServer
411 { 487 {
412 try 488 try
413 { 489 {
414 SendHTML500(response); 490 byte[] buffer500 = SendHTML500(response);
491 response.Body.Write(buffer500,0,buffer500.Length);
492 response.Body.Close();
415 } 493 }
416 catch 494 catch
417 { 495 {
@@ -437,7 +515,7 @@ namespace OpenSim.Framework.Servers.HttpServer
437// reqnum = String.Format("{0}:{1}",request.RemoteIPEndPoint,request.Headers["opensim-request-id"]); 515// reqnum = String.Format("{0}:{1}",request.RemoteIPEndPoint,request.Headers["opensim-request-id"]);
438 //m_log.DebugFormat("[BASE HTTP SERVER]: <{0}> handle request for {1}",reqnum,request.RawUrl); 516 //m_log.DebugFormat("[BASE HTTP SERVER]: <{0}> handle request for {1}",reqnum,request.RawUrl);
439 517
440 Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US", true); 518 Culture.SetCurrentCulture();
441 519
442// // This is the REST agent interface. We require an agent to properly identify 520// // This is the REST agent interface. We require an agent to properly identify
443// // itself. If the REST handler recognizes the prefix it will attempt to 521// // itself. If the REST handler recognizes the prefix it will attempt to
@@ -469,7 +547,7 @@ namespace OpenSim.Framework.Servers.HttpServer
469 LogIncomingToStreamHandler(request, requestHandler); 547 LogIncomingToStreamHandler(request, requestHandler);
470 548
471 response.ContentType = requestHandler.ContentType; // Lets do this defaulting before in case handler has varying content type. 549 response.ContentType = requestHandler.ContentType; // Lets do this defaulting before in case handler has varying content type.
472 550
473 if (requestHandler is IStreamedRequestHandler) 551 if (requestHandler is IStreamedRequestHandler)
474 { 552 {
475 IStreamedRequestHandler streamedRequestHandler = requestHandler as IStreamedRequestHandler; 553 IStreamedRequestHandler streamedRequestHandler = requestHandler as IStreamedRequestHandler;
@@ -557,10 +635,18 @@ namespace OpenSim.Framework.Servers.HttpServer
557 635
558 buffer = HandleLLSDRequests(request, response); 636 buffer = HandleLLSDRequests(request, response);
559 break; 637 break;
638
639 case "application/json-rpc":
640 if (DebugLevel >= 3)
641 LogIncomingToContentTypeHandler(request);
642
643 buffer = HandleJsonRpcRequests(request, response);
644 break;
560 645
561 case "text/xml": 646 case "text/xml":
562 case "application/xml": 647 case "application/xml":
563 case "application/json": 648 case "application/json":
649
564 default: 650 default:
565 //m_log.Info("[Debug BASE HTTP SERVER]: in default handler"); 651 //m_log.Info("[Debug BASE HTTP SERVER]: in default handler");
566 // Point of note.. the DoWeHaveA methods check for an EXACT path 652 // Point of note.. the DoWeHaveA methods check for an EXACT path
@@ -636,7 +722,15 @@ namespace OpenSim.Framework.Servers.HttpServer
636 catch (Exception e) 722 catch (Exception e)
637 { 723 {
638 m_log.Error(String.Format("[BASE HTTP SERVER]: HandleRequest() threw {0} ", e.StackTrace), e); 724 m_log.Error(String.Format("[BASE HTTP SERVER]: HandleRequest() threw {0} ", e.StackTrace), e);
639 SendHTML500(response); 725 try
726 {
727 byte[] buffer500 = SendHTML500(response);
728 response.Body.Write(buffer500, 0, buffer500.Length);
729 response.Body.Close();
730 }
731 catch
732 {
733 }
640 } 734 }
641 finally 735 finally
642 { 736 {
@@ -986,6 +1080,93 @@ namespace OpenSim.Framework.Servers.HttpServer
986 return buffer; 1080 return buffer;
987 } 1081 }
988 1082
1083 // JsonRpc (v2.0 only)
1084 // Batch requests not yet supported
1085 private byte[] HandleJsonRpcRequests(OSHttpRequest request, OSHttpResponse response)
1086 {
1087 Stream requestStream = request.InputStream;
1088 JsonRpcResponse jsonRpcResponse = new JsonRpcResponse();
1089 OSDMap jsonRpcRequest = null;
1090
1091 try
1092 {
1093 jsonRpcRequest = (OSDMap)OSDParser.DeserializeJson(requestStream);
1094 }
1095 catch (LitJson.JsonException e)
1096 {
1097 jsonRpcResponse.Error.Code = ErrorCode.InternalError;
1098 jsonRpcResponse.Error.Message = e.Message;
1099 }
1100
1101 requestStream.Close();
1102
1103 if (jsonRpcRequest != null)
1104 {
1105 if (jsonRpcRequest.ContainsKey("jsonrpc") || jsonRpcRequest["jsonrpc"].AsString() == "2.0")
1106 {
1107 jsonRpcResponse.JsonRpc = "2.0";
1108
1109 // If we have no id, then it's a "notification"
1110 if (jsonRpcRequest.ContainsKey("id"))
1111 {
1112 jsonRpcResponse.Id = jsonRpcRequest["id"].AsString();
1113 }
1114
1115 string methodname = jsonRpcRequest["method"];
1116 JsonRPCMethod method;
1117
1118 if (jsonRpcHandlers.ContainsKey(methodname))
1119 {
1120 lock(jsonRpcHandlers)
1121 {
1122 jsonRpcHandlers.TryGetValue(methodname, out method);
1123 }
1124 bool res = false;
1125 try
1126 {
1127 res = method(jsonRpcRequest, ref jsonRpcResponse);
1128 if(!res)
1129 {
1130 // The handler sent back an unspecified error
1131 if(jsonRpcResponse.Error.Code == 0)
1132 {
1133 jsonRpcResponse.Error.Code = ErrorCode.InternalError;
1134 }
1135 }
1136 }
1137 catch (Exception e)
1138 {
1139 string ErrorMessage = string.Format("[BASE HTTP SERVER]: Json-Rpc Handler Error method {0} - {1}", methodname, e.Message);
1140 m_log.Error(ErrorMessage);
1141 jsonRpcResponse.Error.Code = ErrorCode.InternalError;
1142 jsonRpcResponse.Error.Message = ErrorMessage;
1143 }
1144 }
1145 else // Error no hanlder defined for requested method
1146 {
1147 jsonRpcResponse.Error.Code = ErrorCode.InvalidRequest;
1148 jsonRpcResponse.Error.Message = string.Format ("No handler defined for {0}", methodname);
1149 }
1150 }
1151 else // not json-rpc 2.0 could be v1
1152 {
1153 jsonRpcResponse.Error.Code = ErrorCode.InvalidRequest;
1154 jsonRpcResponse.Error.Message = "Must be valid json-rpc 2.0 see: http://www.jsonrpc.org/specification";
1155
1156 if (jsonRpcRequest.ContainsKey("id"))
1157 jsonRpcResponse.Id = jsonRpcRequest["id"].AsString();
1158 }
1159 }
1160
1161 response.KeepAlive = true;
1162 string responseData = string.Empty;
1163
1164 responseData = jsonRpcResponse.Serialize();
1165
1166 byte[] buffer = Encoding.UTF8.GetBytes(responseData);
1167 return buffer;
1168 }
1169
989 private byte[] HandleLLSDRequests(OSHttpRequest request, OSHttpResponse response) 1170 private byte[] HandleLLSDRequests(OSHttpRequest request, OSHttpResponse response)
990 { 1171 {
991 //m_log.Warn("[BASE HTTP SERVER]: We've figured out it's a LLSD Request"); 1172 //m_log.Warn("[BASE HTTP SERVER]: We've figured out it's a LLSD Request");
@@ -1283,59 +1464,6 @@ namespace OpenSim.Framework.Servers.HttpServer
1283 map["login"] = OSD.FromString("false"); 1464 map["login"] = OSD.FromString("false");
1284 return map; 1465 return map;
1285 } 1466 }
1286 /// <summary>
1287 /// A specific agent handler was provided. Such a handler is expecetd to have an
1288 /// intimate, and highly specific relationship with the client. Consequently,
1289 /// nothing is done here.
1290 /// </summary>
1291 /// <param name="handler"></param>
1292 /// <param name="request"></param>
1293 /// <param name="response"></param>
1294
1295 private bool HandleAgentRequest(IHttpAgentHandler handler, OSHttpRequest request, OSHttpResponse response)
1296 {
1297 // In the case of REST, then handler is responsible for ALL aspects of
1298 // the request/response handling. Nothing is done here, not even encoding.
1299
1300 try
1301 {
1302 return handler.Handle(request, response);
1303 }
1304 catch (Exception e)
1305 {
1306 // If the handler did in fact close the stream, then this will blow
1307 // chunks. So that that doesn't disturb anybody we throw away any
1308 // and all exceptions raised. We've done our best to release the
1309 // client.
1310 try
1311 {
1312 m_log.Warn("[HTTP-AGENT]: Error - " + e.Message);
1313 response.SendChunked = false;
1314 response.KeepAlive = true;
1315 response.StatusCode = (int)OSHttpStatusCode.ServerErrorInternalError;
1316 //response.OutputStream.Close();
1317 try
1318 {
1319 response.Send();
1320 //response.FreeContext();
1321 }
1322 catch (SocketException f)
1323 {
1324 // This has to be here to prevent a Linux/Mono crash
1325 m_log.Warn(
1326 String.Format("[BASE HTTP SERVER]: XmlRpcRequest issue {0}.\nNOTE: this may be spurious on Linux. ", f.Message), f);
1327 }
1328 }
1329 catch(Exception)
1330 {
1331 }
1332 }
1333
1334 // Indicate that the request has been "handled"
1335
1336 return true;
1337
1338 }
1339 1467
1340 public byte[] HandleHTTPRequest(OSHttpRequest request, OSHttpResponse response) 1468 public byte[] HandleHTTPRequest(OSHttpRequest request, OSHttpResponse response)
1341 { 1469 {
@@ -1674,7 +1802,8 @@ namespace OpenSim.Framework.Servers.HttpServer
1674 response.SendChunked = false; 1802 response.SendChunked = false;
1675 response.ContentLength64 = buffer.Length; 1803 response.ContentLength64 = buffer.Length;
1676 response.ContentEncoding = Encoding.UTF8; 1804 response.ContentEncoding = Encoding.UTF8;
1677 1805
1806
1678 return buffer; 1807 return buffer;
1679 } 1808 }
1680 1809
@@ -1775,6 +1904,8 @@ namespace OpenSim.Framework.Servers.HttpServer
1775 HTTPDRunning = false; 1904 HTTPDRunning = false;
1776 try 1905 try
1777 { 1906 {
1907// m_PollServiceManager.Stop();
1908
1778 m_httpListener2.ExceptionThrown -= httpServerException; 1909 m_httpListener2.ExceptionThrown -= httpServerException;
1779 //m_httpListener2.DisconnectHandler = null; 1910 //m_httpListener2.DisconnectHandler = null;
1780 1911
@@ -1840,6 +1971,12 @@ namespace OpenSim.Framework.Servers.HttpServer
1840 m_rpcHandlers.Remove(method); 1971 m_rpcHandlers.Remove(method);
1841 } 1972 }
1842 1973
1974 public void RemoveJsonRPCHandler(string method)
1975 {
1976 lock(jsonRpcHandlers)
1977 jsonRpcHandlers.Remove(method);
1978 }
1979
1843 public bool RemoveLLSDHandler(string path, LLSDMethod handler) 1980 public bool RemoveLLSDHandler(string path, LLSDMethod handler)
1844 { 1981 {
1845 lock (m_llsdHandlers) 1982 lock (m_llsdHandlers)
diff --git a/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs
index 0bd3aae..d162bc1 100644
--- a/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs
+++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs
@@ -97,6 +97,18 @@ namespace OpenSim.Framework.Servers.HttpServer
97 bool AddXmlRPCHandler(string method, XmlRpcMethod handler); 97 bool AddXmlRPCHandler(string method, XmlRpcMethod handler);
98 bool AddXmlRPCHandler(string method, XmlRpcMethod handler, bool keepAlive); 98 bool AddXmlRPCHandler(string method, XmlRpcMethod handler, bool keepAlive);
99 99
100 bool AddJsonRPCHandler(string method, JsonRPCMethod handler);
101
102 /// <summary>
103 /// Websocket HTTP server handlers.
104 /// </summary>
105 /// <param name="servicepath"></param>
106 /// <param name="handler"></param>
107 void AddWebSocketHandler(string servicepath, BaseHttpServer.WebSocketRequestDelegate handler);
108
109
110 void RemoveWebSocketHandler(string servicepath);
111
100 /// <summary> 112 /// <summary>
101 /// Gets the XML RPC handler for given method name 113 /// Gets the XML RPC handler for given method name
102 /// </summary> 114 /// </summary>
@@ -128,6 +140,8 @@ namespace OpenSim.Framework.Servers.HttpServer
128 void RemoveStreamHandler(string httpMethod, string path); 140 void RemoveStreamHandler(string httpMethod, string path);
129 141
130 void RemoveXmlRPCHandler(string method); 142 void RemoveXmlRPCHandler(string method);
143
144 void RemoveJsonRPCHandler(string method);
131 145
132 string GetHTTP404(string host); 146 string GetHTTP404(string host);
133 147
diff --git a/OpenSim/Framework/Servers/HttpServer/JsonRPCMethod.cs b/OpenSim/Framework/Servers/HttpServer/JsonRPCMethod.cs
new file mode 100644
index 0000000..5bab508
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/JsonRPCMethod.cs
@@ -0,0 +1,34 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System.Net;
29using OpenMetaverse.StructuredData;
30
31namespace OpenSim.Framework.Servers.HttpServer
32{
33 public delegate bool JsonRPCMethod(OSDMap jsonRpcRequest, ref JsonRpcResponse response);
34}
diff --git a/OpenSim/Framework/Servers/HttpServer/JsonRpcResponse.cs b/OpenSim/Framework/Servers/HttpServer/JsonRpcResponse.cs
new file mode 100644
index 0000000..2c50587
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/JsonRpcResponse.cs
@@ -0,0 +1,150 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Net;
29using OpenMetaverse.StructuredData;
30
31namespace OpenSim.Framework.Servers.HttpServer
32{
33 public sealed class ErrorCode
34 {
35 private ErrorCode() {}
36
37 public const int ParseError = -32700;
38 public const int InvalidRequest = -32600;
39 public const int MethodNotFound = -32601;
40 public const int InvalidParams = -32602;
41 public const int InternalError = -32604;
42
43 }
44
45 public class JsonRpcError
46 {
47 internal OSDMap Error = new OSDMap();
48
49 public int Code
50 {
51 get
52 {
53 if (Error.ContainsKey("code"))
54 return Error["code"].AsInteger();
55 else
56 return 0;
57 }
58 set
59 {
60 Error["code"] = OSD.FromInteger(value);
61 }
62 }
63
64 public string Message
65 {
66 get
67 {
68 if (Error.ContainsKey("message"))
69 return Error["message"].AsString();
70 else
71 return null;
72 }
73 set
74 {
75 Error["message"] = OSD.FromString(value);
76 }
77 }
78
79 public OSD Data
80 {
81 get; set;
82 }
83 }
84
85 public class JsonRpcResponse
86 {
87 public string JsonRpc
88 {
89 get
90 {
91 return Reply["jsonrpc"].AsString();
92 }
93 set
94 {
95 Reply["jsonrpc"] = OSD.FromString(value);
96 }
97 }
98
99 public string Id
100 {
101 get
102 {
103 return Reply["id"].AsString();
104 }
105 set
106 {
107 Reply["id"] = OSD.FromString(value);
108 }
109 }
110
111 public OSD Result
112 {
113 get; set;
114 }
115
116 public JsonRpcError Error
117 {
118 get; set;
119 }
120
121 public OSDMap Reply = new OSDMap();
122
123 public JsonRpcResponse()
124 {
125 Error = new JsonRpcError();
126 }
127
128 public string Serialize()
129 {
130 if (Result != null)
131 Reply["result"] = Result;
132
133 if (Error.Code != 0)
134 {
135 Reply["error"] = (OSD)Error.Error;
136 }
137
138 string result = string.Empty;
139 try
140 {
141 result = OSDParser.SerializeJsonString(Reply);
142 }
143 catch
144 {
145
146 }
147 return result;
148 }
149 }
150}
diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
index 4be8bf4..7628e23 100644
--- a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
+++ b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
@@ -50,19 +50,26 @@ namespace OpenSim.Framework.Servers.HttpServer
50 private uint m_WorkerThreadCount = 0; 50 private uint m_WorkerThreadCount = 0;
51 private Thread[] m_workerThreads; 51 private Thread[] m_workerThreads;
52 private PollServiceWorkerThread[] m_PollServiceWorkerThreads; 52 private PollServiceWorkerThread[] m_PollServiceWorkerThreads;
53 private bool m_running = true; 53 private volatile bool m_running = true;
54 private int m_pollTimeout;
54 55
55 public PollServiceRequestManager(BaseHttpServer pSrv, uint pWorkerThreadCount, int pTimeout) 56 public PollServiceRequestManager(BaseHttpServer pSrv, uint pWorkerThreadCount, int pTimeout)
56 { 57 {
57 m_server = pSrv; 58 m_server = pSrv;
58 m_WorkerThreadCount = pWorkerThreadCount; 59 m_WorkerThreadCount = pWorkerThreadCount;
60 m_pollTimeout = pTimeout;
61 }
62
63 public void Start()
64 {
65 m_running = true;
59 m_workerThreads = new Thread[m_WorkerThreadCount]; 66 m_workerThreads = new Thread[m_WorkerThreadCount];
60 m_PollServiceWorkerThreads = new PollServiceWorkerThread[m_WorkerThreadCount]; 67 m_PollServiceWorkerThreads = new PollServiceWorkerThread[m_WorkerThreadCount];
61 68
62 //startup worker threads 69 //startup worker threads
63 for (uint i = 0; i < m_WorkerThreadCount; i++) 70 for (uint i = 0; i < m_WorkerThreadCount; i++)
64 { 71 {
65 m_PollServiceWorkerThreads[i] = new PollServiceWorkerThread(m_server, pTimeout); 72 m_PollServiceWorkerThreads[i] = new PollServiceWorkerThread(m_server, m_pollTimeout);
66 m_PollServiceWorkerThreads[i].ReQueue += ReQueueEvent; 73 m_PollServiceWorkerThreads[i].ReQueue += ReQueueEvent;
67 74
68 m_workerThreads[i] 75 m_workerThreads[i]
@@ -141,8 +148,10 @@ namespace OpenSim.Framework.Servers.HttpServer
141 148
142 } 149 }
143 150
144 ~PollServiceRequestManager() 151 public void Stop()
145 { 152 {
153 m_running = false;
154
146 foreach (object o in m_requests) 155 foreach (object o in m_requests)
147 { 156 {
148 PollServiceHttpRequest req = (PollServiceHttpRequest) o; 157 PollServiceHttpRequest req = (PollServiceHttpRequest) o;
@@ -157,7 +166,6 @@ namespace OpenSim.Framework.Servers.HttpServer
157 { 166 {
158 t.Abort(); 167 t.Abort();
159 } 168 }
160 m_running = false;
161 } 169 }
162 } 170 }
163} 171}
@@ -337,7 +345,7 @@ namespace OpenSim.Framework.Servers.HttpServer
337 if (responsedata == null) 345 if (responsedata == null)
338 continue; 346 continue;
339 347
340 if (req.PollServiceArgs.Type == PollServiceEventArgs.EventType.Normal) 348 if (req.PollServiceArgs.Type == PollServiceEventArgs.EventType.Normal) // This is the event queue
341 { 349 {
342 try 350 try
343 { 351 {
diff --git a/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs b/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs
index 02ecc25..8e592c1 100644
--- a/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs
+++ b/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs
@@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
29// Build Number 29// Build Number
30// Revision 30// Revision
31// 31//
32[assembly: AssemblyVersion("0.7.5.*")] 32[assembly: AssemblyVersion("0.7.6.*")]
33[assembly: AssemblyFileVersion("1.0.0.0")] 33
diff --git a/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs b/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs
index 19c03a8..edcd134 100644
--- a/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs
+++ b/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs
@@ -101,20 +101,11 @@ namespace OpenSim.Framework.Servers.HttpServer
101 using (WebResponse resp = request.GetResponse()) 101 using (WebResponse resp = request.GetResponse())
102 { 102 {
103 XmlSerializer deserializer = new XmlSerializer(typeof(TResponse)); 103 XmlSerializer deserializer = new XmlSerializer(typeof(TResponse));
104 Stream respStream = null; 104
105 try 105 using (Stream respStream = resp.GetResponseStream())
106 {
107 respStream = resp.GetResponseStream();
108 deserial = (TResponse)deserializer.Deserialize(respStream); 106 deserial = (TResponse)deserializer.Deserialize(respStream);
109 }
110 catch { }
111 finally
112 {
113 if (respStream != null)
114 respStream.Close();
115 resp.Close();
116 }
117 } 107 }
108
118 return deserial; 109 return deserial;
119 } 110 }
120 } 111 }
diff --git a/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs b/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs
new file mode 100644
index 0000000..ee96b47
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs
@@ -0,0 +1,1102 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Security.Cryptography;
32using System.Text;
33using HttpServer;
34
35namespace OpenSim.Framework.Servers.HttpServer
36{
37 // Sealed class. If you're going to unseal it, implement IDisposable.
38 /// <summary>
39 /// This class implements websockets. It grabs the network context from C#Webserver and utilizes it directly as a tcp streaming service
40 /// </summary>
41 public sealed class WebSocketHttpServerHandler : BaseRequestHandler
42 {
43
44 private class WebSocketState
45 {
46 public List<byte> ReceivedBytes;
47 public int ExpectedBytes;
48 public WebsocketFrameHeader Header;
49 public bool FrameComplete;
50 public WebSocketFrame ContinuationFrame;
51 }
52
53 /// <summary>
54 /// Binary Data will trigger this event
55 /// </summary>
56 public event DataDelegate OnData;
57
58 /// <summary>
59 /// Textual Data will trigger this event
60 /// </summary>
61 public event TextDelegate OnText;
62
63 /// <summary>
64 /// A ping request form the other side will trigger this event.
65 /// This class responds to the ping automatically. You shouldn't send a pong.
66 /// it's informational.
67 /// </summary>
68 public event PingDelegate OnPing;
69
70 /// <summary>
71 /// This is a response to a ping you sent.
72 /// </summary>
73 public event PongDelegate OnPong;
74
75 /// <summary>
76 /// This is a regular HTTP Request... This may be removed in the future.
77 /// </summary>
78 public event RegularHttpRequestDelegate OnRegularHttpRequest;
79
80 /// <summary>
81 /// When the upgrade from a HTTP request to a Websocket is completed, this will be fired
82 /// </summary>
83 public event UpgradeCompletedDelegate OnUpgradeCompleted;
84
85 /// <summary>
86 /// If the upgrade failed, this will be fired
87 /// </summary>
88 public event UpgradeFailedDelegate OnUpgradeFailed;
89
90 /// <summary>
91 /// When the websocket is closed, this will be fired.
92 /// </summary>
93 public event CloseDelegate OnClose;
94
95 /// <summary>
96 /// Set this delegate to allow your module to validate the origin of the
97 /// Websocket request. Primary line of defense against cross site scripting
98 /// </summary>
99 public ValidateHandshake HandshakeValidateMethodOverride = null;
100
101 private OSHttpRequest _request;
102 private HTTPNetworkContext _networkContext;
103 private IHttpClientContext _clientContext;
104
105 private int _pingtime = 0;
106 private byte[] _buffer;
107 private int _bufferPosition;
108 private int _bufferLength;
109 private bool _closing;
110 private bool _upgraded;
111 private int _maxPayloadBytes = 41943040;
112
113 private const string HandshakeAcceptText =
114 "HTTP/1.1 101 Switching Protocols\r\n" +
115 "upgrade: websocket\r\n" +
116 "Connection: Upgrade\r\n" +
117 "sec-websocket-accept: {0}\r\n\r\n";// +
118 //"{1}";
119
120 private const string HandshakeDeclineText =
121 "HTTP/1.1 {0} {1}\r\n" +
122 "Connection: close\r\n\r\n";
123
124 /// <summary>
125 /// Mysterious constant defined in RFC6455 to append to the client provided security key
126 /// </summary>
127 private const string WebsocketHandshakeAcceptHashConstant = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
128
129 public WebSocketHttpServerHandler(OSHttpRequest preq, IHttpClientContext pContext, int bufferlen)
130 : base(preq.HttpMethod, preq.Url.OriginalString)
131 {
132 _request = preq;
133 _networkContext = pContext.GiveMeTheNetworkStreamIKnowWhatImDoing();
134 _clientContext = pContext;
135 _bufferLength = bufferlen;
136 _buffer = new byte[_bufferLength];
137 }
138
139 // Sealed class implments destructor and an internal dispose method. complies with C# unmanaged resource best practices.
140 ~WebSocketHttpServerHandler()
141 {
142 Dispose();
143
144 }
145
146 /// <summary>
147 /// Sets the length of the stream buffer
148 /// </summary>
149 /// <param name="pChunk">Byte length.</param>
150 public void SetChunksize(int pChunk)
151 {
152 if (!_upgraded)
153 {
154 _buffer = new byte[pChunk];
155 }
156 else
157 {
158 throw new InvalidOperationException("You must set the chunksize before the connection is upgraded");
159 }
160 }
161
162 /// <summary>
163 /// This is the famous nagle.
164 /// </summary>
165 public bool NoDelay_TCP_Nagle
166 {
167 get
168 {
169 if (_networkContext != null && _networkContext.Socket != null)
170 {
171 return _networkContext.Socket.NoDelay;
172 }
173 else
174 {
175 throw new InvalidOperationException("The socket has been shutdown");
176 }
177 }
178 set
179 {
180 if (_networkContext != null && _networkContext.Socket != null)
181 _networkContext.Socket.NoDelay = value;
182 else
183 {
184 throw new InvalidOperationException("The socket has been shutdown");
185 }
186 }
187 }
188
189 /// <summary>
190 /// This triggers the websocket to start the upgrade process...
191 /// This is a Generalized Networking 'common sense' helper method. Some people expect to call Start() instead
192 /// of the more context appropriate HandshakeAndUpgrade()
193 /// </summary>
194 public void Start()
195 {
196 HandshakeAndUpgrade();
197 }
198
199 /// <summary>
200 /// Max Payload Size in bytes. Defaults to 40MB, but could be set upon connection before calling handshake and upgrade.
201 /// </summary>
202 public int MaxPayloadSize
203 {
204 get { return _maxPayloadBytes; }
205 set { _maxPayloadBytes = value; }
206 }
207
208 /// <summary>
209 /// This triggers the websocket start the upgrade process
210 /// </summary>
211 public void HandshakeAndUpgrade()
212 {
213 string webOrigin = string.Empty;
214 string websocketKey = string.Empty;
215 string acceptKey = string.Empty;
216 string accepthost = string.Empty;
217 if (!string.IsNullOrEmpty(_request.Headers["origin"]))
218 webOrigin = _request.Headers["origin"];
219
220 if (!string.IsNullOrEmpty(_request.Headers["sec-websocket-key"]))
221 websocketKey = _request.Headers["sec-websocket-key"];
222
223 if (!string.IsNullOrEmpty(_request.Headers["host"]))
224 accepthost = _request.Headers["host"];
225
226 if (string.IsNullOrEmpty(_request.Headers["upgrade"]))
227 {
228 FailUpgrade(OSHttpStatusCode.ClientErrorBadRequest, "no upgrade request submitted");
229 }
230
231 string connectionheader = _request.Headers["upgrade"];
232 if (connectionheader.ToLower() != "websocket")
233 {
234 FailUpgrade(OSHttpStatusCode.ClientErrorBadRequest, "no connection upgrade request submitted");
235 }
236
237 // If the object consumer provided a method to validate the origin, we should call it and give the client a success or fail.
238 // If not.. we should accept any. The assumption here is that there would be no Websocket handlers registered in baseHTTPServer unless
239 // Something asked for it...
240 if (HandshakeValidateMethodOverride != null)
241 {
242 if (HandshakeValidateMethodOverride(webOrigin, websocketKey, accepthost))
243 {
244 acceptKey = GenerateAcceptKey(websocketKey);
245 string rawaccept = string.Format(HandshakeAcceptText, acceptKey);
246 SendUpgradeSuccess(rawaccept);
247
248 }
249 else
250 {
251 FailUpgrade(OSHttpStatusCode.ClientErrorForbidden, "Origin Validation Failed");
252 }
253 }
254 else
255 {
256 acceptKey = GenerateAcceptKey(websocketKey);
257 string rawaccept = string.Format(HandshakeAcceptText, acceptKey);
258 SendUpgradeSuccess(rawaccept);
259 }
260 }
261
262 /// <summary>
263 /// Generates a handshake response key string based on the client's
264 /// provided key to prove to the client that we're allowing the Websocket
265 /// upgrade of our own free will and we were not coerced into doing it.
266 /// </summary>
267 /// <param name="key">Client provided security key</param>
268 /// <returns></returns>
269 private static string GenerateAcceptKey(string key)
270 {
271 if (string.IsNullOrEmpty(key))
272 return string.Empty;
273
274 string acceptkey = key + WebsocketHandshakeAcceptHashConstant;
275
276 SHA1 hashobj = SHA1.Create();
277 string ret = Convert.ToBase64String(hashobj.ComputeHash(Encoding.UTF8.GetBytes(acceptkey)));
278 hashobj.Clear();
279
280 return ret;
281 }
282
283 /// <summary>
284 /// Informs the otherside that we accepted their upgrade request
285 /// </summary>
286 /// <param name="pHandshakeResponse">The HTTP 1.1 101 response that says Yay \o/ </param>
287 private void SendUpgradeSuccess(string pHandshakeResponse)
288 {
289 // Create a new websocket state so we can keep track of data in between network reads.
290 WebSocketState socketState = new WebSocketState() { ReceivedBytes = new List<byte>(), Header = WebsocketFrameHeader.HeaderDefault(), FrameComplete = true};
291
292 byte[] bhandshakeResponse = Encoding.UTF8.GetBytes(pHandshakeResponse);
293 try
294 {
295
296 // Begin reading the TCP stream before writing the Upgrade success message to the other side of the stream.
297 _networkContext.Stream.BeginRead(_buffer, 0, _bufferLength, OnReceive, socketState);
298
299 // Write the upgrade handshake success message
300 _networkContext.Stream.Write(bhandshakeResponse, 0, bhandshakeResponse.Length);
301 _networkContext.Stream.Flush();
302 _upgraded = true;
303 UpgradeCompletedDelegate d = OnUpgradeCompleted;
304 if (d != null)
305 d(this, new UpgradeCompletedEventArgs());
306 }
307 catch (IOException fail)
308 {
309 Close(string.Empty);
310 }
311 catch (ObjectDisposedException fail)
312 {
313 Close(string.Empty);
314 }
315
316 }
317
318 /// <summary>
319 /// The server has decided not to allow the upgrade to a websocket for some reason. The Http 1.1 response that says Nay >:(
320 /// </summary>
321 /// <param name="pCode">HTTP Status reflecting the reason why</param>
322 /// <param name="pMessage">Textual reason for the upgrade fail</param>
323 private void FailUpgrade(OSHttpStatusCode pCode, string pMessage )
324 {
325 string handshakeResponse = string.Format(HandshakeDeclineText, (int)pCode, pMessage.Replace("\n", string.Empty).Replace("\r", string.Empty));
326 byte[] bhandshakeResponse = Encoding.UTF8.GetBytes(handshakeResponse);
327 _networkContext.Stream.Write(bhandshakeResponse, 0, bhandshakeResponse.Length);
328 _networkContext.Stream.Flush();
329 _networkContext.Stream.Dispose();
330
331 UpgradeFailedDelegate d = OnUpgradeFailed;
332 if (d != null)
333 d(this,new UpgradeFailedEventArgs());
334 }
335
336
337 /// <summary>
338 /// This is our ugly Async OnReceive event handler.
339 /// This chunks the input stream based on the length of the provided buffer and processes out
340 /// as many frames as it can. It then moves the unprocessed data to the beginning of the buffer.
341 /// </summary>
342 /// <param name="ar">Our Async State from beginread</param>
343 private void OnReceive(IAsyncResult ar)
344 {
345 WebSocketState _socketState = ar.AsyncState as WebSocketState;
346 try
347 {
348 int bytesRead = _networkContext.Stream.EndRead(ar);
349 if (bytesRead == 0)
350 {
351 // Do Disconnect
352 _networkContext.Stream.Dispose();
353 _networkContext = null;
354 return;
355 }
356 _bufferPosition += bytesRead;
357
358 if (_bufferPosition > _bufferLength)
359 {
360 // Message too big for chunksize.. not sure how this happened...
361 //Close(string.Empty);
362 }
363
364 int offset = 0;
365 bool headerread = true;
366 int headerforwardposition = 0;
367 while (headerread && offset < bytesRead)
368 {
369 if (_socketState.FrameComplete)
370 {
371 WebsocketFrameHeader pheader = WebsocketFrameHeader.ZeroHeader;
372
373 headerread = WebSocketReader.TryReadHeader(_buffer, offset, _bufferPosition - offset, out pheader,
374 out headerforwardposition);
375 offset += headerforwardposition;
376
377 if (headerread)
378 {
379 _socketState.FrameComplete = false;
380 if (pheader.PayloadLen > (ulong) _maxPayloadBytes)
381 {
382 Close("Invalid Payload size");
383
384 return;
385 }
386 if (pheader.PayloadLen > 0)
387 {
388 if ((int) pheader.PayloadLen > _bufferPosition - offset)
389 {
390 byte[] writebytes = new byte[_bufferPosition - offset];
391
392 Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) _bufferPosition - offset);
393 _socketState.ExpectedBytes = (int) pheader.PayloadLen;
394 _socketState.ReceivedBytes.AddRange(writebytes);
395 _socketState.Header = pheader; // We need to add the header so that we can unmask it
396 offset += (int) _bufferPosition - offset;
397 }
398 else
399 {
400 byte[] writebytes = new byte[pheader.PayloadLen];
401 Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) pheader.PayloadLen);
402 WebSocketReader.Mask(pheader.Mask, writebytes);
403 pheader.IsMasked = false;
404 _socketState.FrameComplete = true;
405 _socketState.ReceivedBytes.AddRange(writebytes);
406 _socketState.Header = pheader;
407 offset += (int) pheader.PayloadLen;
408 }
409 }
410 else
411 {
412 pheader.Mask = 0;
413 _socketState.FrameComplete = true;
414 _socketState.Header = pheader;
415 }
416
417
418
419 if (_socketState.FrameComplete)
420 {
421 ProcessFrame(_socketState);
422 _socketState.Header.SetDefault();
423 _socketState.ReceivedBytes.Clear();
424 _socketState.ExpectedBytes = 0;
425
426 }
427
428 }
429 }
430 else
431 {
432 WebsocketFrameHeader frameHeader = _socketState.Header;
433 int bytesleft = _socketState.ExpectedBytes - _socketState.ReceivedBytes.Count;
434
435 if (bytesleft > _bufferPosition)
436 {
437 byte[] writebytes = new byte[_bufferPosition];
438
439 Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) _bufferPosition);
440 _socketState.ReceivedBytes.AddRange(writebytes);
441 _socketState.Header = frameHeader; // We need to add the header so that we can unmask it
442 offset += (int) _bufferPosition;
443 }
444 else
445 {
446 byte[] writebytes = new byte[_bufferPosition];
447 Buffer.BlockCopy(_buffer, offset, writebytes, 0, (int) _bufferPosition);
448 _socketState.FrameComplete = true;
449 _socketState.ReceivedBytes.AddRange(writebytes);
450 _socketState.Header = frameHeader;
451 offset += (int) _bufferPosition;
452 }
453 if (_socketState.FrameComplete)
454 {
455 ProcessFrame(_socketState);
456 _socketState.Header.SetDefault();
457 _socketState.ReceivedBytes.Clear();
458 _socketState.ExpectedBytes = 0;
459 // do some processing
460 }
461
462 }
463 }
464 if (offset > 0)
465 {
466 // If the buffer is maxed out.. we can just move the cursor. Nothing to move to the beginning.
467 if (offset <_buffer.Length)
468 Buffer.BlockCopy(_buffer, offset, _buffer, 0, _bufferPosition - offset);
469 _bufferPosition -= offset;
470 }
471 if (_networkContext.Stream != null && _networkContext.Stream.CanRead && !_closing)
472 {
473 _networkContext.Stream.BeginRead(_buffer, _bufferPosition, _bufferLength - _bufferPosition, OnReceive,
474 _socketState);
475 }
476 else
477 {
478 // We can't read the stream anymore...
479 }
480
481 }
482 catch (IOException fail)
483 {
484 Close(string.Empty);
485 }
486 catch (ObjectDisposedException fail)
487 {
488 Close(string.Empty);
489 }
490 }
491
492 /// <summary>
493 /// Sends a string to the other side
494 /// </summary>
495 /// <param name="message">the string message that is to be sent</param>
496 public void SendMessage(string message)
497 {
498 byte[] messagedata = Encoding.UTF8.GetBytes(message);
499 WebSocketFrame textMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = messagedata };
500 textMessageFrame.Header.Opcode = WebSocketReader.OpCode.Text;
501 textMessageFrame.Header.IsEnd = true;
502 SendSocket(textMessageFrame.ToBytes());
503
504 }
505
506 public void SendData(byte[] data)
507 {
508 WebSocketFrame dataMessageFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = data};
509 dataMessageFrame.Header.IsEnd = true;
510 dataMessageFrame.Header.Opcode = WebSocketReader.OpCode.Binary;
511 SendSocket(dataMessageFrame.ToBytes());
512
513 }
514
515 /// <summary>
516 /// Writes raw bytes to the websocket. Unframed data will cause disconnection
517 /// </summary>
518 /// <param name="data"></param>
519 private void SendSocket(byte[] data)
520 {
521 if (!_closing)
522 {
523 try
524 {
525
526 _networkContext.Stream.Write(data, 0, data.Length);
527 }
528 catch (IOException)
529 {
530
531 }
532 }
533 }
534
535 /// <summary>
536 /// Sends a Ping check to the other side. The other side SHOULD respond as soon as possible with a pong frame. This interleaves with incoming fragmented frames.
537 /// </summary>
538 public void SendPingCheck()
539 {
540 WebSocketFrame pingFrame = new WebSocketFrame() { Header = WebsocketFrameHeader.HeaderDefault(), WebSocketPayload = new byte[0] };
541 pingFrame.Header.Opcode = WebSocketReader.OpCode.Ping;
542 pingFrame.Header.IsEnd = true;
543 _pingtime = Util.EnvironmentTickCount();
544 SendSocket(pingFrame.ToBytes());
545 }
546
547 /// <summary>
548 /// Closes the websocket connection. Sends a close message to the other side if it hasn't already done so.
549 /// </summary>
550 /// <param name="message"></param>
551 public void Close(string message)
552 {
553 if (_networkContext == null)
554 return;
555 if (_networkContext.Stream != null)
556 {
557 if (_networkContext.Stream.CanWrite)
558 {
559 byte[] messagedata = Encoding.UTF8.GetBytes(message);
560 WebSocketFrame closeResponseFrame = new WebSocketFrame()
561 {
562 Header = WebsocketFrameHeader.HeaderDefault(),
563 WebSocketPayload = messagedata
564 };
565 closeResponseFrame.Header.Opcode = WebSocketReader.OpCode.Close;
566 closeResponseFrame.Header.PayloadLen = (ulong) messagedata.Length;
567 closeResponseFrame.Header.IsEnd = true;
568 SendSocket(closeResponseFrame.ToBytes());
569 }
570 }
571 CloseDelegate closeD = OnClose;
572 if (closeD != null)
573 {
574 closeD(this, new CloseEventArgs());
575 }
576
577 _closing = true;
578 }
579
580 /// <summary>
581 /// Processes a websocket frame and triggers consumer events
582 /// </summary>
583 /// <param name="psocketState">We need to modify the websocket state here depending on the frame</param>
584 private void ProcessFrame(WebSocketState psocketState)
585 {
586 if (psocketState.Header.IsMasked)
587 {
588 byte[] unmask = psocketState.ReceivedBytes.ToArray();
589 WebSocketReader.Mask(psocketState.Header.Mask, unmask);
590 psocketState.ReceivedBytes = new List<byte>(unmask);
591 }
592
593 switch (psocketState.Header.Opcode)
594 {
595 case WebSocketReader.OpCode.Ping:
596 PingDelegate pingD = OnPing;
597 if (pingD != null)
598 {
599 pingD(this, new PingEventArgs());
600 }
601
602 WebSocketFrame pongFrame = new WebSocketFrame(){Header = WebsocketFrameHeader.HeaderDefault(),WebSocketPayload = new byte[0]};
603 pongFrame.Header.Opcode = WebSocketReader.OpCode.Pong;
604 pongFrame.Header.IsEnd = true;
605 SendSocket(pongFrame.ToBytes());
606 break;
607 case WebSocketReader.OpCode.Pong:
608
609 PongDelegate pongD = OnPong;
610 if (pongD != null)
611 {
612 pongD(this, new PongEventArgs(){PingResponseMS = Util.EnvironmentTickCountSubtract(Util.EnvironmentTickCount(),_pingtime)});
613 }
614 break;
615 case WebSocketReader.OpCode.Binary:
616 if (!psocketState.Header.IsEnd) // Not done, so we need to store this and wait for the end frame.
617 {
618 psocketState.ContinuationFrame = new WebSocketFrame
619 {
620 Header = psocketState.Header,
621 WebSocketPayload =
622 psocketState.ReceivedBytes.ToArray()
623 };
624 }
625 else
626 {
627 // Send Done Event!
628 DataDelegate dataD = OnData;
629 if (dataD != null)
630 {
631 dataD(this,new WebsocketDataEventArgs(){Data = psocketState.ReceivedBytes.ToArray()});
632 }
633 }
634 break;
635 case WebSocketReader.OpCode.Text:
636 if (!psocketState.Header.IsEnd) // Not done, so we need to store this and wait for the end frame.
637 {
638 psocketState.ContinuationFrame = new WebSocketFrame
639 {
640 Header = psocketState.Header,
641 WebSocketPayload =
642 psocketState.ReceivedBytes.ToArray()
643 };
644 }
645 else
646 {
647 TextDelegate textD = OnText;
648 if (textD != null)
649 {
650 textD(this, new WebsocketTextEventArgs() { Data = Encoding.UTF8.GetString(psocketState.ReceivedBytes.ToArray()) });
651 }
652
653 // Send Done Event!
654 }
655 break;
656 case WebSocketReader.OpCode.Continue: // Continuation. Multiple frames worth of data for one message. Only valid when not using Control Opcodes
657 //Console.WriteLine("currhead " + psocketState.Header.IsEnd);
658 //Console.WriteLine("Continuation! " + psocketState.ContinuationFrame.Header.IsEnd);
659 byte[] combineddata = new byte[psocketState.ReceivedBytes.Count+psocketState.ContinuationFrame.WebSocketPayload.Length];
660 byte[] newdata = psocketState.ReceivedBytes.ToArray();
661 Buffer.BlockCopy(psocketState.ContinuationFrame.WebSocketPayload, 0, combineddata, 0, psocketState.ContinuationFrame.WebSocketPayload.Length);
662 Buffer.BlockCopy(newdata, 0, combineddata,
663 psocketState.ContinuationFrame.WebSocketPayload.Length, newdata.Length);
664 psocketState.ContinuationFrame.WebSocketPayload = combineddata;
665 psocketState.Header.PayloadLen = (ulong)combineddata.Length;
666 if (psocketState.Header.IsEnd)
667 {
668 if (psocketState.ContinuationFrame.Header.Opcode == WebSocketReader.OpCode.Text)
669 {
670 // Send Done event
671 TextDelegate textD = OnText;
672 if (textD != null)
673 {
674 textD(this, new WebsocketTextEventArgs() { Data = Encoding.UTF8.GetString(combineddata) });
675 }
676 }
677 else if (psocketState.ContinuationFrame.Header.Opcode == WebSocketReader.OpCode.Binary)
678 {
679 // Send Done event
680 DataDelegate dataD = OnData;
681 if (dataD != null)
682 {
683 dataD(this, new WebsocketDataEventArgs() { Data = combineddata });
684 }
685 }
686 else
687 {
688 // protocol violation
689 }
690 psocketState.ContinuationFrame = null;
691 }
692 break;
693 case WebSocketReader.OpCode.Close:
694 Close(string.Empty);
695
696 break;
697
698 }
699 psocketState.Header.SetDefault();
700 psocketState.ReceivedBytes.Clear();
701 psocketState.ExpectedBytes = 0;
702 }
703 public void Dispose()
704 {
705 if (_networkContext != null && _networkContext.Stream != null)
706 {
707 if (_networkContext.Stream.CanWrite)
708 _networkContext.Stream.Flush();
709 _networkContext.Stream.Close();
710 _networkContext.Stream.Dispose();
711 _networkContext.Stream = null;
712 }
713
714 if (_request != null && _request.InputStream != null)
715 {
716 _request.InputStream.Close();
717 _request.InputStream.Dispose();
718 _request = null;
719 }
720
721 if (_clientContext != null)
722 {
723 _clientContext.Close();
724 _clientContext = null;
725 }
726 }
727 }
728
729 /// <summary>
730 /// Reads a byte stream and returns Websocket frames.
731 /// </summary>
732 public class WebSocketReader
733 {
734 /// <summary>
735 /// Bit to determine if the frame read on the stream is the last frame in a sequence of fragmented frames
736 /// </summary>
737 private const byte EndBit = 0x80;
738
739 /// <summary>
740 /// These are the Frame Opcodes
741 /// </summary>
742 public enum OpCode
743 {
744 // Data Opcodes
745 Continue = 0x0,
746 Text = 0x1,
747 Binary = 0x2,
748
749 // Control flow Opcodes
750 Close = 0x8,
751 Ping = 0x9,
752 Pong = 0xA
753 }
754
755 /// <summary>
756 /// Masks and Unmasks data using the frame mask. Mask is applied per octal
757 /// Note: Frames from clients MUST be masked
758 /// Note: Frames from servers MUST NOT be masked
759 /// </summary>
760 /// <param name="pMask">Int representing 32 bytes of mask data. Mask is applied per octal</param>
761 /// <param name="pBuffer"></param>
762 public static void Mask(int pMask, byte[] pBuffer)
763 {
764 byte[] maskKey = BitConverter.GetBytes(pMask);
765 int currentMaskIndex = 0;
766 for (int i = 0; i < pBuffer.Length; i++)
767 {
768 pBuffer[i] = (byte)(pBuffer[i] ^ maskKey[currentMaskIndex]);
769 if (currentMaskIndex == 3)
770 {
771 currentMaskIndex = 0;
772 }
773 else
774 {
775 currentMaskIndex++;
776
777 }
778
779 }
780 }
781
782 /// <summary>
783 /// Attempts to read a header off the provided buffer. Returns true, exports a WebSocketFrameheader,
784 /// and an int to move the buffer forward when it reads a header. False when it can't read a header
785 /// </summary>
786 /// <param name="pBuffer">Bytes read from the stream</param>
787 /// <param name="pOffset">Starting place in the stream to begin trying to read from</param>
788 /// <param name="length">Lenth in the stream to try and read from. Provided for cases where the
789 /// buffer's length is larger then the data in it</param>
790 /// <param name="oHeader">Outputs the read WebSocket frame header</param>
791 /// <param name="moveBuffer">Informs the calling stream to move the buffer forward</param>
792 /// <returns>True if it got a header, False if it didn't get a header</returns>
793 public static bool TryReadHeader(byte[] pBuffer, int pOffset, int length, out WebsocketFrameHeader oHeader,
794 out int moveBuffer)
795 {
796 oHeader = WebsocketFrameHeader.ZeroHeader;
797 int minumheadersize = 2;
798 if (length > pBuffer.Length - pOffset)
799 throw new ArgumentOutOfRangeException("The Length specified was larger the byte array supplied");
800 if (length < minumheadersize)
801 {
802 moveBuffer = 0;
803 return false;
804 }
805
806 byte nibble1 = (byte)(pBuffer[pOffset] & 0xF0); //FIN/RSV1/RSV2/RSV3
807 byte nibble2 = (byte)(pBuffer[pOffset] & 0x0F); // Opcode block
808
809 oHeader = new WebsocketFrameHeader();
810 oHeader.SetDefault();
811
812 if ((nibble1 & WebSocketReader.EndBit) == WebSocketReader.EndBit)
813 {
814 oHeader.IsEnd = true;
815 }
816 else
817 {
818 oHeader.IsEnd = false;
819 }
820 //Opcode
821 oHeader.Opcode = (WebSocketReader.OpCode)nibble2;
822 //Mask
823 oHeader.IsMasked = Convert.ToBoolean((pBuffer[pOffset + 1] & 0x80) >> 7);
824
825 // Payload length
826 oHeader.PayloadLen = (byte)(pBuffer[pOffset + 1] & 0x7F);
827
828 int index = 2; // LargerPayload length starts at byte 3
829
830 switch (oHeader.PayloadLen)
831 {
832 case 126:
833 minumheadersize += 2;
834 if (length < minumheadersize)
835 {
836 moveBuffer = 0;
837 return false;
838 }
839 Array.Reverse(pBuffer, pOffset + index, 2); // two bytes
840 oHeader.PayloadLen = BitConverter.ToUInt16(pBuffer, pOffset + index);
841 index += 2;
842 break;
843 case 127: // we got more this is a bigger frame
844 // 8 bytes - uint64 - most significant bit 0 network byte order
845 minumheadersize += 8;
846 if (length < minumheadersize)
847 {
848 moveBuffer = 0;
849 return false;
850 }
851 Array.Reverse(pBuffer, pOffset + index, 8);
852 oHeader.PayloadLen = BitConverter.ToUInt64(pBuffer, pOffset + index);
853 index += 8;
854 break;
855
856 }
857 //oHeader.PayloadLeft = oHeader.PayloadLen; // Start the count in case it's chunked over the network. This is different then frame fragmentation
858 if (oHeader.IsMasked)
859 {
860 minumheadersize += 4;
861 if (length < minumheadersize)
862 {
863 moveBuffer = 0;
864 return false;
865 }
866 oHeader.Mask = BitConverter.ToInt32(pBuffer, pOffset + index);
867 index += 4;
868 }
869 moveBuffer = index;
870 return true;
871
872 }
873 }
874
875 /// <summary>
876 /// RFC6455 Websocket Frame
877 /// </summary>
878 public class WebSocketFrame
879 {
880 /*
881 * RFC6455
882nib 0 1 2 3 4 5 6 7
883byt 0 1 2 3
884dec 0 1 2 3
885 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
886 +-+-+-+-+-------+-+-------------+-------------------------------+
887 |F|R|R|R| opcode|M| Payload len | Extended payload length |
888 |I|S|S|S| (4) |A| (7) | (16/64) +
889 |N|V|V|V| |S| | (if payload len==126/127) |
890 | |1|2|3| |K| | +
891 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
892 | Extended payload length continued, if payload len == 127 |
893 + - - - - - - - - - - - - - - - +-------------------------------+
894 | |Masking-key, if MASK set to 1 |
895 +-------------------------------+-------------------------------+
896 | Masking-key (continued) | Payload Data |
897 +-------------------------------- - - - - - - - - - - - - - - - +
898 : Payload Data continued ... :
899 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
900 | Payload Data continued ... |
901 +---------------------------------------------------------------+
902
903 * When reading these, the frames are possibly fragmented and interleaved with control frames
904 * the fragmented frames are not interleaved with data frames. Just control frames
905 */
906 public static readonly WebSocketFrame DefaultFrame = new WebSocketFrame(){Header = new WebsocketFrameHeader(),WebSocketPayload = new byte[0]};
907 public WebsocketFrameHeader Header;
908 public byte[] WebSocketPayload;
909
910 public byte[] ToBytes()
911 {
912 Header.PayloadLen = (ulong)WebSocketPayload.Length;
913 return Header.ToBytes(WebSocketPayload);
914 }
915
916 }
917
918 public struct WebsocketFrameHeader
919 {
920 //public byte CurrentMaskIndex;
921 /// <summary>
922 /// The last frame in a sequence of fragmented frames or the one and only frame for this message.
923 /// </summary>
924 public bool IsEnd;
925
926 /// <summary>
927 /// Returns whether the payload data is masked or not. Data from Clients MUST be masked, Data from Servers MUST NOT be masked
928 /// </summary>
929 public bool IsMasked;
930
931 /// <summary>
932 /// A set of cryptologically sound random bytes XoR-ed against the payload octally. Looped
933 /// </summary>
934 public int Mask;
935 /*
936byt 0 1 2 3
937 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
938 +---------------+---------------+---------------+---------------+
939 | Octal 1 | Octal 2 | Octal 3 | Octal 4 |
940 +---------------+---------------+---------------+---------------+
941*/
942
943
944 public WebSocketReader.OpCode Opcode;
945
946 public UInt64 PayloadLen;
947 //public UInt64 PayloadLeft;
948 // Payload is X + Y
949 //public UInt64 ExtensionDataLength;
950 //public UInt64 ApplicationDataLength;
951 public static readonly WebsocketFrameHeader ZeroHeader = WebsocketFrameHeader.HeaderDefault();
952
953 public void SetDefault()
954 {
955
956 //CurrentMaskIndex = 0;
957 IsEnd = true;
958 IsMasked = true;
959 Mask = 0;
960 Opcode = WebSocketReader.OpCode.Close;
961 // PayloadLeft = 0;
962 PayloadLen = 0;
963 // ExtensionDataLength = 0;
964 // ApplicationDataLength = 0;
965
966 }
967
968 /// <summary>
969 /// Returns a byte array representing the Frame header
970 /// </summary>
971 /// <param name="payload">This is the frame data payload. The header describes the size of the payload.
972 /// If payload is null, a Zero sized payload is assumed</param>
973 /// <returns>Returns a byte array representing the frame header</returns>
974 public byte[] ToBytes(byte[] payload)
975 {
976 List<byte> result = new List<byte>();
977
978 // Squeeze in our opcode and our ending bit.
979 result.Add((byte)((byte)Opcode | (IsEnd?0x80:0x00) ));
980
981 // Again with the three different byte interpretations of size..
982
983 //bytesize
984 if (PayloadLen <= 125)
985 {
986 result.Add((byte) PayloadLen);
987 } //Uint16
988 else if (PayloadLen <= ushort.MaxValue)
989 {
990 result.Add(126);
991 byte[] payloadLengthByte = BitConverter.GetBytes(Convert.ToUInt16(PayloadLen));
992 Array.Reverse(payloadLengthByte);
993 result.AddRange(payloadLengthByte);
994 } //UInt64
995 else
996 {
997 result.Add(127);
998 byte[] payloadLengthByte = BitConverter.GetBytes(PayloadLen);
999 Array.Reverse(payloadLengthByte);
1000 result.AddRange(payloadLengthByte);
1001 }
1002
1003 // Only add a payload if it's not null
1004 if (payload != null)
1005 {
1006 result.AddRange(payload);
1007 }
1008 return result.ToArray();
1009 }
1010
1011 /// <summary>
1012 /// A Helper method to define the defaults
1013 /// </summary>
1014 /// <returns></returns>
1015
1016 public static WebsocketFrameHeader HeaderDefault()
1017 {
1018 return new WebsocketFrameHeader
1019 {
1020 //CurrentMaskIndex = 0,
1021 IsEnd = false,
1022 IsMasked = true,
1023 Mask = 0,
1024 Opcode = WebSocketReader.OpCode.Close,
1025 //PayloadLeft = 0,
1026 PayloadLen = 0,
1027 // ExtensionDataLength = 0,
1028 // ApplicationDataLength = 0
1029 };
1030 }
1031 }
1032
1033 public delegate void DataDelegate(object sender, WebsocketDataEventArgs data);
1034
1035 public delegate void TextDelegate(object sender, WebsocketTextEventArgs text);
1036
1037 public delegate void PingDelegate(object sender, PingEventArgs pingdata);
1038
1039 public delegate void PongDelegate(object sender, PongEventArgs pongdata);
1040
1041 public delegate void RegularHttpRequestDelegate(object sender, RegularHttpRequestEvnetArgs request);
1042
1043 public delegate void UpgradeCompletedDelegate(object sender, UpgradeCompletedEventArgs completeddata);
1044
1045 public delegate void UpgradeFailedDelegate(object sender, UpgradeFailedEventArgs faileddata);
1046
1047 public delegate void CloseDelegate(object sender, CloseEventArgs closedata);
1048
1049 public delegate bool ValidateHandshake(string pWebOrigin, string pWebSocketKey, string pHost);
1050
1051
1052 public class WebsocketDataEventArgs : EventArgs
1053 {
1054 public byte[] Data;
1055 }
1056
1057 public class WebsocketTextEventArgs : EventArgs
1058 {
1059 public string Data;
1060 }
1061
1062 public class PingEventArgs : EventArgs
1063 {
1064 /// <summary>
1065 /// The ping event can arbitrarily contain data
1066 /// </summary>
1067 public byte[] Data;
1068 }
1069
1070 public class PongEventArgs : EventArgs
1071 {
1072 /// <summary>
1073 /// The pong event can arbitrarily contain data
1074 /// </summary>
1075 public byte[] Data;
1076
1077 public int PingResponseMS;
1078
1079 }
1080
1081 public class RegularHttpRequestEvnetArgs : EventArgs
1082 {
1083
1084 }
1085
1086 public class UpgradeCompletedEventArgs : EventArgs
1087 {
1088
1089 }
1090
1091 public class UpgradeFailedEventArgs : EventArgs
1092 {
1093
1094 }
1095
1096 public class CloseEventArgs : EventArgs
1097 {
1098
1099 }
1100
1101
1102}
diff --git a/OpenSim/Framework/Servers/MainServer.cs b/OpenSim/Framework/Servers/MainServer.cs
index ae7d515..cfd34bb 100644
--- a/OpenSim/Framework/Servers/MainServer.cs
+++ b/OpenSim/Framework/Servers/MainServer.cs
@@ -227,9 +227,16 @@ namespace OpenSim.Framework.Servers
227 handlers.AppendFormat("\t{0}\n", s); 227 handlers.AppendFormat("\t{0}\n", s);
228 228
229 handlers.AppendFormat("* HTTP:\n"); 229 handlers.AppendFormat("* HTTP:\n");
230 List<String> poll = httpServer.GetPollServiceHandlerKeys();
231 foreach (String s in httpServer.GetHTTPHandlerKeys()) 230 foreach (String s in httpServer.GetHTTPHandlerKeys())
232 handlers.AppendFormat("\t{0} {1}\n", s, (poll.Contains(s) ? "(poll service)" : string.Empty)); 231 handlers.AppendFormat("\t{0}\n", s);
232
233 handlers.AppendFormat("* HTTP (poll):\n");
234 foreach (String s in httpServer.GetPollServiceHandlerKeys())
235 handlers.AppendFormat("\t{0}\n", s);
236
237 handlers.AppendFormat("* JSONRPC:\n");
238 foreach (String s in httpServer.GetJsonRpcHandlerKeys())
239 handlers.AppendFormat("\t{0}\n", s);
233 240
234// handlers.AppendFormat("* Agent:\n"); 241// handlers.AppendFormat("* Agent:\n");
235// foreach (String s in httpServer.GetAgentHandlerKeys()) 242// foreach (String s in httpServer.GetAgentHandlerKeys())
diff --git a/OpenSim/Framework/Servers/Properties/AssemblyInfo.cs b/OpenSim/Framework/Servers/Properties/AssemblyInfo.cs
index 021f63c..792c62e 100644
--- a/OpenSim/Framework/Servers/Properties/AssemblyInfo.cs
+++ b/OpenSim/Framework/Servers/Properties/AssemblyInfo.cs
@@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
29// Build Number 29// Build Number
30// Revision 30// Revision
31// 31//
32[assembly: AssemblyVersion("0.7.5.*")] 32[assembly: AssemblyVersion("0.7.6.*")]
33[assembly: AssemblyFileVersion("1.0.0.0")] 33[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/OpenSim/Framework/Servers/ServerBase.cs b/OpenSim/Framework/Servers/ServerBase.cs
index 9eb2281..1ff8aca 100644
--- a/OpenSim/Framework/Servers/ServerBase.cs
+++ b/OpenSim/Framework/Servers/ServerBase.cs
@@ -27,16 +27,19 @@
27 27
28using System; 28using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Diagnostics;
30using System.IO; 31using System.IO;
31using System.Reflection; 32using System.Reflection;
32using System.Text; 33using System.Text;
33using System.Text.RegularExpressions; 34using System.Text.RegularExpressions;
35using System.Threading;
34using log4net; 36using log4net;
35using log4net.Appender; 37using log4net.Appender;
36using log4net.Core; 38using log4net.Core;
37using log4net.Repository; 39using log4net.Repository;
38using Nini.Config; 40using Nini.Config;
39using OpenSim.Framework.Console; 41using OpenSim.Framework.Console;
42using OpenSim.Framework.Monitoring;
40 43
41namespace OpenSim.Framework.Servers 44namespace OpenSim.Framework.Servers
42{ 45{
@@ -110,6 +113,26 @@ namespace OpenSim.Framework.Servers
110 } 113 }
111 } 114 }
112 115
116 /// <summary>
117 /// Log information about the circumstances in which we're running (OpenSimulator version number, CLR details,
118 /// etc.).
119 /// </summary>
120 public void LogEnvironmentInformation()
121 {
122 // FIXME: This should be done down in ServerBase but we need to sort out and refactor the log4net
123 // XmlConfigurator calls first accross servers.
124 m_log.InfoFormat("[SERVER BASE]: Starting in {0}", m_startupDirectory);
125
126 m_log.InfoFormat("[SERVER BASE]: OpenSimulator version: {0}", m_version);
127
128 // clr version potentially is more confusing than helpful, since it doesn't tell us if we're running under Mono/MS .NET and
129 // the clr version number doesn't match the project version number under Mono.
130 //m_log.Info("[STARTUP]: Virtual machine runtime version: " + Environment.Version + Environment.NewLine);
131 m_log.InfoFormat(
132 "[SERVER BASE]: Operating system version: {0}, .NET platform {1}, {2}-bit",
133 Environment.OSVersion, Environment.OSVersion.Platform, Util.Is64BitProcess() ? "64" : "32");
134 }
135
113 public void RegisterCommonAppenders(IConfig startupConfig) 136 public void RegisterCommonAppenders(IConfig startupConfig)
114 { 137 {
115 ILoggerRepository repository = LogManager.GetRepository(); 138 ILoggerRepository repository = LogManager.GetRepository();
@@ -168,6 +191,9 @@ namespace OpenSim.Framework.Servers
168 "General", false, "show info", "show info", "Show general information about the server", HandleShow); 191 "General", false, "show info", "show info", "Show general information about the server", HandleShow);
169 192
170 m_console.Commands.AddCommand( 193 m_console.Commands.AddCommand(
194 "General", false, "show version", "show version", "Show server version", HandleShow);
195
196 m_console.Commands.AddCommand(
171 "General", false, "show uptime", "show uptime", "Show server uptime", HandleShow); 197 "General", false, "show uptime", "show uptime", "Show server uptime", HandleShow);
172 198
173 m_console.Commands.AddCommand( 199 m_console.Commands.AddCommand(
@@ -206,6 +232,34 @@ namespace OpenSim.Framework.Servers
206 "General", false, "command-script", 232 "General", false, "command-script",
207 "command-script <script>", 233 "command-script <script>",
208 "Run a command script from file", HandleScript); 234 "Run a command script from file", HandleScript);
235
236 m_console.Commands.AddCommand(
237 "General", false, "show threads",
238 "show threads",
239 "Show thread status", HandleShow);
240
241 m_console.Commands.AddCommand(
242 "General", false, "threads abort",
243 "threads abort <thread-id>",
244 "Abort a managed thread. Use \"show threads\" to find possible threads.", HandleThreadsAbort);
245
246 m_console.Commands.AddCommand(
247 "General", false, "threads show",
248 "threads show",
249 "Show thread status. Synonym for \"show threads\"",
250 (string module, string[] args) => Notice(GetThreadsReport()));
251
252 m_console.Commands.AddCommand(
253 "General", false, "force gc",
254 "force gc",
255 "Manually invoke runtime garbage collection. For debugging purposes",
256 HandleForceGc);
257 }
258
259 private void HandleForceGc(string module, string[] args)
260 {
261 Notice("Manually invoking runtime garbage collection");
262 GC.Collect();
209 } 263 }
210 264
211 public virtual void HandleShow(string module, string[] cmd) 265 public virtual void HandleShow(string module, string[] cmd)
@@ -222,9 +276,17 @@ namespace OpenSim.Framework.Servers
222 ShowInfo(); 276 ShowInfo();
223 break; 277 break;
224 278
279 case "version":
280 Notice(GetVersionText());
281 break;
282
225 case "uptime": 283 case "uptime":
226 Notice(GetUptimeReport()); 284 Notice(GetUptimeReport());
227 break; 285 break;
286
287 case "threads":
288 Notice(GetThreadsReport());
289 break;
228 } 290 }
229 } 291 }
230 292
@@ -537,6 +599,75 @@ namespace OpenSim.Framework.Servers
537 } 599 }
538 600
539 /// <summary> 601 /// <summary>
602 /// Get a report about the registered threads in this server.
603 /// </summary>
604 protected string GetThreadsReport()
605 {
606 // This should be a constant field.
607 string reportFormat = "{0,6} {1,35} {2,16} {3,13} {4,10} {5,30}";
608
609 StringBuilder sb = new StringBuilder();
610 Watchdog.ThreadWatchdogInfo[] threads = Watchdog.GetThreadsInfo();
611
612 sb.Append(threads.Length + " threads are being tracked:" + Environment.NewLine);
613
614 int timeNow = Environment.TickCount & Int32.MaxValue;
615
616 sb.AppendFormat(reportFormat, "ID", "NAME", "LAST UPDATE (MS)", "LIFETIME (MS)", "PRIORITY", "STATE");
617 sb.Append(Environment.NewLine);
618
619 foreach (Watchdog.ThreadWatchdogInfo twi in threads)
620 {
621 Thread t = twi.Thread;
622
623 sb.AppendFormat(
624 reportFormat,
625 t.ManagedThreadId,
626 t.Name,
627 timeNow - twi.LastTick,
628 timeNow - twi.FirstTick,
629 t.Priority,
630 t.ThreadState);
631
632 sb.Append("\n");
633 }
634
635 sb.Append("\n");
636
637 // For some reason mono 2.6.7 returns an empty threads set! Not going to confuse people by reporting
638 // zero active threads.
639 int totalThreads = Process.GetCurrentProcess().Threads.Count;
640 if (totalThreads > 0)
641 sb.AppendFormat("Total threads active: {0}\n\n", totalThreads);
642
643 sb.Append("Main threadpool (excluding script engine pools)\n");
644 sb.Append(Util.GetThreadPoolReport());
645
646 return sb.ToString();
647 }
648
649 public virtual void HandleThreadsAbort(string module, string[] cmd)
650 {
651 if (cmd.Length != 3)
652 {
653 MainConsole.Instance.Output("Usage: threads abort <thread-id>");
654 return;
655 }
656
657 int threadId;
658 if (!int.TryParse(cmd[2], out threadId))
659 {
660 MainConsole.Instance.Output("ERROR: Thread id must be an integer");
661 return;
662 }
663
664 if (Watchdog.AbortThread(threadId))
665 MainConsole.Instance.OutputFormat("Aborted thread with id {0}", threadId);
666 else
667 MainConsole.Instance.OutputFormat("ERROR - Thread with id {0} not found in managed threads", threadId);
668 }
669
670 /// <summary>
540 /// Console output is only possible if a console has been established. 671 /// Console output is only possible if a console has been established.
541 /// That is something that cannot be determined within this class. So 672 /// That is something that cannot be determined within this class. So
542 /// all attempts to use the console MUST be verified. 673 /// all attempts to use the console MUST be verified.
diff --git a/OpenSim/Framework/Servers/Tests/OSHttpTests.cs b/OpenSim/Framework/Servers/Tests/OSHttpTests.cs
index 4c2f586..deae45c 100644
--- a/OpenSim/Framework/Servers/Tests/OSHttpTests.cs
+++ b/OpenSim/Framework/Servers/Tests/OSHttpTests.cs
@@ -35,11 +35,12 @@ using HttpServer;
35using HttpServer.FormDecoders; 35using HttpServer.FormDecoders;
36using NUnit.Framework; 36using NUnit.Framework;
37using OpenSim.Framework.Servers.HttpServer; 37using OpenSim.Framework.Servers.HttpServer;
38using OpenSim.Tests.Common;
38 39
39namespace OpenSim.Framework.Servers.Tests 40namespace OpenSim.Framework.Servers.Tests
40{ 41{
41 [TestFixture] 42 [TestFixture]
42 public class OSHttpTests 43 public class OSHttpTests : OpenSimTestCase
43 { 44 {
44 // we need an IHttpClientContext for our tests 45 // we need an IHttpClientContext for our tests
45 public class TestHttpClientContext: IHttpClientContext 46 public class TestHttpClientContext: IHttpClientContext
@@ -69,6 +70,11 @@ namespace OpenSim.Framework.Servers.Tests
69 public void Close() { } 70 public void Close() { }
70 public bool EndWhenDone { get { return false;} set { return;}} 71 public bool EndWhenDone { get { return false;} set { return;}}
71 72
73 public HTTPNetworkContext GiveMeTheNetworkStreamIKnowWhatImDoing()
74 {
75 return new HTTPNetworkContext();
76 }
77
72 public event EventHandler<DisconnectedEventArgs> Disconnected = delegate { }; 78 public event EventHandler<DisconnectedEventArgs> Disconnected = delegate { };
73 /// <summary> 79 /// <summary>
74 /// A request have been received in the context. 80 /// A request have been received in the context.
diff --git a/OpenSim/Framework/Servers/Tests/VersionInfoTests.cs b/OpenSim/Framework/Servers/Tests/VersionInfoTests.cs
index 49e5061..480f2bb 100644
--- a/OpenSim/Framework/Servers/Tests/VersionInfoTests.cs
+++ b/OpenSim/Framework/Servers/Tests/VersionInfoTests.cs
@@ -29,11 +29,12 @@ using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Text; 30using System.Text;
31using NUnit.Framework; 31using NUnit.Framework;
32using OpenSim.Tests.Common;
32 33
33namespace OpenSim.Framework.Servers.Tests 34namespace OpenSim.Framework.Servers.Tests
34{ 35{
35 [TestFixture] 36 [TestFixture]
36 public class VersionInfoTests 37 public class VersionInfoTests : OpenSimTestCase
37 { 38 {
38 [Test] 39 [Test]
39 public void TestVersionLength() 40 public void TestVersionLength()
diff --git a/OpenSim/Framework/Servers/VersionInfo.cs b/OpenSim/Framework/Servers/VersionInfo.cs
index bb094ed..737c14d 100644
--- a/OpenSim/Framework/Servers/VersionInfo.cs
+++ b/OpenSim/Framework/Servers/VersionInfo.cs
@@ -29,7 +29,7 @@ namespace OpenSim
29{ 29{
30 public class VersionInfo 30 public class VersionInfo
31 { 31 {
32 private const string VERSION_NUMBER = "0.7.5CM"; 32 private const string VERSION_NUMBER = "0.7.6CM";
33 private const Flavour VERSION_FLAVOUR = Flavour.Dev; 33 private const Flavour VERSION_FLAVOUR = Flavour.Dev;
34 34
35 public enum Flavour 35 public enum Flavour