aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/Servers/HttpServer
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Framework/Servers/HttpServer')
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs383
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BaseOutputStreamHandler.cs60
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BaseRequestHandler.cs23
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs49
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BaseStreamHandlerBasicDOSProtector.cs107
-rw-r--r--OpenSim/Framework/Servers/HttpServer/BinaryStreamHandler.cs2
-rw-r--r--OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs119
-rw-r--r--OpenSim/Framework/Servers/HttpServer/Interfaces/IHttpServer.cs14
-rw-r--r--OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs15
-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/OSHttpRequest.cs13
-rw-r--r--OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs4
-rw-r--r--OpenSim/Framework/Servers/HttpServer/OSHttpServer.cs2
-rw-r--r--OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs31
-rw-r--r--OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs45
-rw-r--r--OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs318
-rw-r--r--OpenSim/Framework/Servers/HttpServer/PollServiceWorkerThread.cs165
-rw-r--r--OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs4
-rw-r--r--OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs4
-rw-r--r--OpenSim/Framework/Servers/HttpServer/RestObjectPoster.cs28
-rw-r--r--OpenSim/Framework/Servers/HttpServer/RestObjectPosterResponse.cs29
-rw-r--r--OpenSim/Framework/Servers/HttpServer/RestSessionService.cs89
-rw-r--r--OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs2
-rw-r--r--OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs1159
-rw-r--r--OpenSim/Framework/Servers/HttpServer/XmlRpcBasicDOSProtector.cs91
27 files changed, 2747 insertions, 383 deletions
diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
index aa49343..f252bd5 100644
--- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
+++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
@@ -46,6 +46,7 @@ using CoolHTTPListener = HttpServer.HttpListener;
46using HttpListener=System.Net.HttpListener; 46using HttpListener=System.Net.HttpListener;
47using LogPrio=HttpServer.LogPrio; 47using LogPrio=HttpServer.LogPrio;
48using OpenSim.Framework.Monitoring; 48using OpenSim.Framework.Monitoring;
49using System.IO.Compression;
49 50
50namespace OpenSim.Framework.Servers.HttpServer 51namespace OpenSim.Framework.Servers.HttpServer
51{ 52{
@@ -53,6 +54,16 @@ namespace OpenSim.Framework.Servers.HttpServer
53 { 54 {
54 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 55 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
55 private HttpServerLogWriter httpserverlog = new HttpServerLogWriter(); 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);
56 67
57 /// <summary> 68 /// <summary>
58 /// Gets or sets the debug level. 69 /// Gets or sets the debug level.
@@ -71,12 +82,18 @@ namespace OpenSim.Framework.Servers.HttpServer
71 /// </remarks> 82 /// </remarks>
72 public int RequestNumber { get; private set; } 83 public int RequestNumber { get; private set; }
73 84
85 /// <summary>
86 /// Statistic for holding number of requests processed.
87 /// </summary>
88 private Stat m_requestsProcessedStat;
89
74 private volatile int NotSocketErrors = 0; 90 private volatile int NotSocketErrors = 0;
75 public volatile bool HTTPDRunning = false; 91 public volatile bool HTTPDRunning = false;
76 92
77 // protected HttpListener m_httpListener; 93 // protected HttpListener m_httpListener;
78 protected CoolHTTPListener m_httpListener2; 94 protected CoolHTTPListener m_httpListener2;
79 protected Dictionary<string, XmlRpcMethod> m_rpcHandlers = new Dictionary<string, XmlRpcMethod>(); 95 protected Dictionary<string, XmlRpcMethod> m_rpcHandlers = new Dictionary<string, XmlRpcMethod>();
96 protected Dictionary<string, JsonRPCMethod> jsonRpcHandlers = new Dictionary<string, JsonRPCMethod>();
80 protected Dictionary<string, bool> m_rpcHandlersKeepAlive = new Dictionary<string, bool>(); 97 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/ 98 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>(); 99 protected Dictionary<string, LLSDMethod> m_llsdHandlers = new Dictionary<string, LLSDMethod>();
@@ -86,6 +103,9 @@ namespace OpenSim.Framework.Servers.HttpServer
86 protected Dictionary<string, PollServiceEventArgs> m_pollHandlers = 103 protected Dictionary<string, PollServiceEventArgs> m_pollHandlers =
87 new Dictionary<string, PollServiceEventArgs>(); 104 new Dictionary<string, PollServiceEventArgs>();
88 105
106 protected Dictionary<string, WebSocketRequestDelegate> m_WebSocketHandlers =
107 new Dictionary<string, WebSocketRequestDelegate>();
108
89 protected uint m_port; 109 protected uint m_port;
90 protected uint m_sslport; 110 protected uint m_sslport;
91 protected bool m_ssl; 111 protected bool m_ssl;
@@ -95,7 +115,7 @@ namespace OpenSim.Framework.Servers.HttpServer
95 115
96 protected IPAddress m_listenIPAddress = IPAddress.Any; 116 protected IPAddress m_listenIPAddress = IPAddress.Any;
97 117
98 private PollServiceRequestManager m_PollServiceManager; 118 public PollServiceRequestManager PollServiceRequestManager { get; private set; }
99 119
100 public uint SSLPort 120 public uint SSLPort
101 { 121 {
@@ -169,6 +189,22 @@ namespace OpenSim.Framework.Servers.HttpServer
169 } 189 }
170 } 190 }
171 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
172 public List<string> GetStreamHandlerKeys() 208 public List<string> GetStreamHandlerKeys()
173 { 209 {
174 lock (m_streamHandlers) 210 lock (m_streamHandlers)
@@ -217,6 +253,37 @@ namespace OpenSim.Framework.Servers.HttpServer
217 return new List<string>(m_rpcHandlers.Keys); 253 return new List<string>(m_rpcHandlers.Keys);
218 } 254 }
219 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
220 public bool AddHTTPHandler(string methodName, GenericHTTPMethod handler) 287 public bool AddHTTPHandler(string methodName, GenericHTTPMethod handler)
221 { 288 {
222 //m_log.DebugFormat("[BASE HTTP SERVER]: Registering {0}", methodName); 289 //m_log.DebugFormat("[BASE HTTP SERVER]: Registering {0}", methodName);
@@ -309,7 +376,7 @@ namespace OpenSim.Framework.Servers.HttpServer
309 return true; 376 return true;
310 } 377 }
311 378
312 private void OnRequest(object source, RequestEventArgs args) 379 public void OnRequest(object source, RequestEventArgs args)
313 { 380 {
314 RequestNumber++; 381 RequestNumber++;
315 382
@@ -322,6 +389,8 @@ namespace OpenSim.Framework.Servers.HttpServer
322 389
323 if (TryGetPollServiceHTTPHandler(request.UriPath.ToString(), out psEvArgs)) 390 if (TryGetPollServiceHTTPHandler(request.UriPath.ToString(), out psEvArgs))
324 { 391 {
392 psEvArgs.RequestsReceived++;
393
325 PollServiceHttpRequest psreq = new PollServiceHttpRequest(psEvArgs, context, request); 394 PollServiceHttpRequest psreq = new PollServiceHttpRequest(psEvArgs, context, request);
326 395
327 if (psEvArgs.Request != null) 396 if (psEvArgs.Request != null)
@@ -362,7 +431,7 @@ namespace OpenSim.Framework.Servers.HttpServer
362 psEvArgs.Request(psreq.RequestID, keysvals); 431 psEvArgs.Request(psreq.RequestID, keysvals);
363 } 432 }
364 433
365 m_PollServiceManager.Enqueue(psreq); 434 PollServiceRequestManager.Enqueue(psreq);
366 } 435 }
367 else 436 else
368 { 437 {
@@ -375,11 +444,24 @@ namespace OpenSim.Framework.Servers.HttpServer
375 } 444 }
376 } 445 }
377 446
378 public void OnHandleRequestIOThread(IHttpClientContext context, IHttpRequest request) 447 private void OnHandleRequestIOThread(IHttpClientContext context, IHttpRequest request)
379 { 448 {
380 OSHttpRequest req = new OSHttpRequest(context, request); 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
381 OSHttpResponse resp = new OSHttpResponse(new HttpResponse(context, request),context); 462 OSHttpResponse resp = new OSHttpResponse(new HttpResponse(context, request),context);
382 HandleRequest(req, resp); 463 resp.ReuseContext = true;
464 HandleRequest(req, resp);
383 465
384 // !!!HACK ALERT!!! 466 // !!!HACK ALERT!!!
385 // There seems to be a bug in the underlying http code that makes subsequent requests 467 // There seems to be a bug in the underlying http code that makes subsequent requests
@@ -410,7 +492,9 @@ namespace OpenSim.Framework.Servers.HttpServer
410 { 492 {
411 try 493 try
412 { 494 {
413 SendHTML500(response); 495 byte[] buffer500 = SendHTML500(response);
496 response.OutputStream.Write(buffer500, 0, buffer500.Length);
497 response.Send();
414 } 498 }
415 catch 499 catch
416 { 500 {
@@ -468,7 +552,7 @@ namespace OpenSim.Framework.Servers.HttpServer
468 LogIncomingToStreamHandler(request, requestHandler); 552 LogIncomingToStreamHandler(request, requestHandler);
469 553
470 response.ContentType = requestHandler.ContentType; // Lets do this defaulting before in case handler has varying content type. 554 response.ContentType = requestHandler.ContentType; // Lets do this defaulting before in case handler has varying content type.
471 555
472 if (requestHandler is IStreamedRequestHandler) 556 if (requestHandler is IStreamedRequestHandler)
473 { 557 {
474 IStreamedRequestHandler streamedRequestHandler = requestHandler as IStreamedRequestHandler; 558 IStreamedRequestHandler streamedRequestHandler = requestHandler as IStreamedRequestHandler;
@@ -556,10 +640,18 @@ namespace OpenSim.Framework.Servers.HttpServer
556 640
557 buffer = HandleLLSDRequests(request, response); 641 buffer = HandleLLSDRequests(request, response);
558 break; 642 break;
643
644 case "application/json-rpc":
645 if (DebugLevel >= 3)
646 LogIncomingToContentTypeHandler(request);
647
648 buffer = HandleJsonRpcRequests(request, response);
649 break;
559 650
560 case "text/xml": 651 case "text/xml":
561 case "application/xml": 652 case "application/xml":
562 case "application/json": 653 case "application/json":
654
563 default: 655 default:
564 //m_log.Info("[Debug BASE HTTP SERVER]: in default handler"); 656 //m_log.Info("[Debug BASE HTTP SERVER]: in default handler");
565 // Point of note.. the DoWeHaveA methods check for an EXACT path 657 // Point of note.. the DoWeHaveA methods check for an EXACT path
@@ -600,7 +692,24 @@ namespace OpenSim.Framework.Servers.HttpServer
600 692
601 if (buffer != null) 693 if (buffer != null)
602 { 694 {
603 if (!response.SendChunked) 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)
604 response.ContentLength64 = buffer.LongLength; 713 response.ContentLength64 = buffer.LongLength;
605 714
606 response.OutputStream.Write(buffer, 0, buffer.Length); 715 response.OutputStream.Write(buffer, 0, buffer.Length);
@@ -630,12 +739,20 @@ namespace OpenSim.Framework.Servers.HttpServer
630 } 739 }
631 catch (IOException e) 740 catch (IOException e)
632 { 741 {
633 m_log.Error(String.Format("[BASE HTTP SERVER]: HandleRequest() threw {0} ", e.StackTrace), e); 742 m_log.Error("[BASE HTTP SERVER]: HandleRequest() threw exception ", e);
634 } 743 }
635 catch (Exception e) 744 catch (Exception e)
636 { 745 {
637 m_log.Error(String.Format("[BASE HTTP SERVER]: HandleRequest() threw {0} ", e.StackTrace), e); 746 m_log.Error("[BASE HTTP SERVER]: HandleRequest() threw exception ", e);
638 SendHTML500(response); 747 try
748 {
749 byte[] buffer500 = SendHTML500(response);
750 response.OutputStream.Write(buffer500, 0, buffer500.Length);
751 response.Send();
752 }
753 catch
754 {
755 }
639 } 756 }
640 finally 757 finally
641 { 758 {
@@ -645,7 +762,7 @@ namespace OpenSim.Framework.Servers.HttpServer
645 if (tickdiff > 3000 && requestHandler != null && requestHandler.Name != "GetTexture") 762 if (tickdiff > 3000 && requestHandler != null && requestHandler.Name != "GetTexture")
646 { 763 {
647 m_log.InfoFormat( 764 m_log.InfoFormat(
648 "[BASE HTTP SERVER]: Slow handling of {0} {1} {2} {3} {4} from {5} took {6}ms", 765 "[LOGHTTP] Slow handling of {0} {1} {2} {3} {4} from {5} took {6}ms",
649 RequestNumber, 766 RequestNumber,
650 requestMethod, 767 requestMethod,
651 uriString, 768 uriString,
@@ -657,7 +774,7 @@ namespace OpenSim.Framework.Servers.HttpServer
657 else if (DebugLevel >= 4) 774 else if (DebugLevel >= 4)
658 { 775 {
659 m_log.DebugFormat( 776 m_log.DebugFormat(
660 "[BASE HTTP SERVER]: HTTP IN {0} :{1} took {2}ms", 777 "[LOGHTTP] HTTP IN {0} :{1} took {2}ms",
661 RequestNumber, 778 RequestNumber,
662 Port, 779 Port,
663 tickdiff); 780 tickdiff);
@@ -668,7 +785,7 @@ namespace OpenSim.Framework.Servers.HttpServer
668 private void LogIncomingToStreamHandler(OSHttpRequest request, IRequestHandler requestHandler) 785 private void LogIncomingToStreamHandler(OSHttpRequest request, IRequestHandler requestHandler)
669 { 786 {
670 m_log.DebugFormat( 787 m_log.DebugFormat(
671 "[BASE HTTP SERVER]: HTTP IN {0} :{1} stream handler {2} {3} {4} {5} from {6}", 788 "[LOGHTTP] HTTP IN {0} :{1} stream handler {2} {3} {4} {5} from {6}",
672 RequestNumber, 789 RequestNumber,
673 Port, 790 Port,
674 request.HttpMethod, 791 request.HttpMethod,
@@ -684,10 +801,10 @@ namespace OpenSim.Framework.Servers.HttpServer
684 private void LogIncomingToContentTypeHandler(OSHttpRequest request) 801 private void LogIncomingToContentTypeHandler(OSHttpRequest request)
685 { 802 {
686 m_log.DebugFormat( 803 m_log.DebugFormat(
687 "[BASE HTTP SERVER]: HTTP IN {0} :{1} {2} content type handler {3} {4} from {5}", 804 "[LOGHTTP] HTTP IN {0} :{1} {2} content type handler {3} {4} from {5}",
688 RequestNumber, 805 RequestNumber,
689 Port, 806 Port,
690 (request.ContentType == null || request.ContentType == "") ? "not set" : request.ContentType, 807 string.IsNullOrEmpty(request.ContentType) ? "not set" : request.ContentType,
691 request.HttpMethod, 808 request.HttpMethod,
692 request.Url.PathAndQuery, 809 request.Url.PathAndQuery,
693 request.RemoteIPEndPoint); 810 request.RemoteIPEndPoint);
@@ -699,7 +816,7 @@ namespace OpenSim.Framework.Servers.HttpServer
699 private void LogIncomingToXmlRpcHandler(OSHttpRequest request) 816 private void LogIncomingToXmlRpcHandler(OSHttpRequest request)
700 { 817 {
701 m_log.DebugFormat( 818 m_log.DebugFormat(
702 "[BASE HTTP SERVER]: HTTP IN {0} :{1} assumed generic XMLRPC request {2} {3} from {4}", 819 "[LOGHTTP] HTTP IN {0} :{1} assumed generic XMLRPC request {2} {3} from {4}",
703 RequestNumber, 820 RequestNumber,
704 Port, 821 Port,
705 request.HttpMethod, 822 request.HttpMethod,
@@ -712,29 +829,49 @@ namespace OpenSim.Framework.Servers.HttpServer
712 829
713 private void LogIncomingInDetail(OSHttpRequest request) 830 private void LogIncomingInDetail(OSHttpRequest request)
714 { 831 {
715 using (StreamReader reader = new StreamReader(Util.Copy(request.InputStream), Encoding.UTF8)) 832 if (request.ContentType == "application/octet-stream")
716 { 833 return; // never log these; they're just binary data
717 string output;
718 834
719 if (DebugLevel == 5) 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"))
720 { 840 {
721 const int sampleLength = 80; 841 innerStream = inputStream;
722 char[] sampleChars = new char[sampleLength + 3]; 842 inputStream = new GZipStream(innerStream, System.IO.Compression.CompressionMode.Decompress);
723 reader.Read(sampleChars, 0, sampleLength);
724 sampleChars[80] = '.';
725 sampleChars[81] = '.';
726 sampleChars[82] = '.';
727 output = new string(sampleChars);
728 } 843 }
729 else 844
845 using (StreamReader reader = new StreamReader(inputStream, Encoding.UTF8))
730 { 846 {
731 output = reader.ReadToEnd(); 847 string output;
732 }
733 848
734 m_log.DebugFormat("[BASE HTTP SERVER]: {0}", output.Replace("\n", @"\n")); 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();
735 } 870 }
736 } 871 }
737 872
873 private readonly string HANDLER_SEPARATORS = "/?&#-";
874
738 private bool TryGetStreamHandler(string handlerKey, out IRequestHandler streamHandler) 875 private bool TryGetStreamHandler(string handlerKey, out IRequestHandler streamHandler)
739 { 876 {
740 string bestMatch = null; 877 string bestMatch = null;
@@ -743,7 +880,8 @@ namespace OpenSim.Framework.Servers.HttpServer
743 { 880 {
744 foreach (string pattern in m_streamHandlers.Keys) 881 foreach (string pattern in m_streamHandlers.Keys)
745 { 882 {
746 if (handlerKey.StartsWith(pattern)) 883 if ((handlerKey == pattern)
884 || (handlerKey.StartsWith(pattern) && (HANDLER_SEPARATORS.IndexOf(handlerKey[pattern.Length]) >= 0)))
747 { 885 {
748 if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length) 886 if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length)
749 { 887 {
@@ -773,7 +911,8 @@ namespace OpenSim.Framework.Servers.HttpServer
773 { 911 {
774 foreach (string pattern in m_pollHandlers.Keys) 912 foreach (string pattern in m_pollHandlers.Keys)
775 { 913 {
776 if (handlerKey.StartsWith(pattern)) 914 if ((handlerKey == pattern)
915 || (handlerKey.StartsWith(pattern) && (HANDLER_SEPARATORS.IndexOf(handlerKey[pattern.Length]) >= 0)))
777 { 916 {
778 if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length) 917 if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length)
779 { 918 {
@@ -805,7 +944,8 @@ namespace OpenSim.Framework.Servers.HttpServer
805 { 944 {
806 foreach (string pattern in m_HTTPHandlers.Keys) 945 foreach (string pattern in m_HTTPHandlers.Keys)
807 { 946 {
808 if (handlerKey.StartsWith(pattern)) 947 if ((handlerKey == pattern)
948 || (handlerKey.StartsWith(pattern) && (HANDLER_SEPARATORS.IndexOf(handlerKey[pattern.Length]) >= 0)))
809 { 949 {
810 if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length) 950 if (String.IsNullOrEmpty(bestMatch) || pattern.Length > bestMatch.Length)
811 { 951 {
@@ -854,16 +994,33 @@ namespace OpenSim.Framework.Servers.HttpServer
854 /// <param name="response"></param> 994 /// <param name="response"></param>
855 private byte[] HandleXmlRpcRequests(OSHttpRequest request, OSHttpResponse response) 995 private byte[] HandleXmlRpcRequests(OSHttpRequest request, OSHttpResponse response)
856 { 996 {
997 String requestBody;
998
857 Stream requestStream = request.InputStream; 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 }
858 1008
859 Encoding encoding = Encoding.UTF8; 1009 using (StreamReader reader = new StreamReader(requestStream, Encoding.UTF8))
860 StreamReader reader = new StreamReader(requestStream, encoding); 1010 {
1011 requestBody = reader.ReadToEnd();
1012 }
1013 }
1014 finally
1015 {
1016 if (innerStream != null)
1017 innerStream.Dispose();
1018 requestStream.Dispose();
1019 }
861 1020
862 string requestBody = reader.ReadToEnd();
863 reader.Close();
864 requestStream.Close();
865 //m_log.Debug(requestBody); 1021 //m_log.Debug(requestBody);
866 requestBody = requestBody.Replace("<base64></base64>", ""); 1022 requestBody = requestBody.Replace("<base64></base64>", "");
1023
867 string responseString = String.Empty; 1024 string responseString = String.Empty;
868 XmlRpcRequest xmlRprcRequest = null; 1025 XmlRpcRequest xmlRprcRequest = null;
869 1026
@@ -958,7 +1115,19 @@ namespace OpenSim.Framework.Servers.HttpServer
958 } 1115 }
959 1116
960 response.ContentType = "text/xml"; 1117 response.ContentType = "text/xml";
961 responseString = XmlRpcResponseSerializer.Singleton.Serialize(xmlRpcResponse); 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 }
962 } 1131 }
963 else 1132 else
964 { 1133 {
@@ -985,6 +1154,93 @@ namespace OpenSim.Framework.Servers.HttpServer
985 return buffer; 1154 return buffer;
986 } 1155 }
987 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
988 private byte[] HandleLLSDRequests(OSHttpRequest request, OSHttpResponse response) 1244 private byte[] HandleLLSDRequests(OSHttpRequest request, OSHttpResponse response)
989 { 1245 {
990 //m_log.Warn("[BASE HTTP SERVER]: We've figured out it's a LLSD Request"); 1246 //m_log.Warn("[BASE HTTP SERVER]: We've figured out it's a LLSD Request");
@@ -1575,16 +1831,24 @@ namespace OpenSim.Framework.Servers.HttpServer
1575 response.SendChunked = false; 1831 response.SendChunked = false;
1576 response.ContentLength64 = buffer.Length; 1832 response.ContentLength64 = buffer.Length;
1577 response.ContentEncoding = Encoding.UTF8; 1833 response.ContentEncoding = Encoding.UTF8;
1578 1834
1835
1579 return buffer; 1836 return buffer;
1580 } 1837 }
1581 1838
1582 public void Start() 1839 public void Start()
1583 { 1840 {
1584 StartHTTP(); 1841 Start(true);
1585 } 1842 }
1586 1843
1587 private void StartHTTP() 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)
1588 { 1852 {
1589 m_log.InfoFormat( 1853 m_log.InfoFormat(
1590 "[BASE HTTP SERVER]: Starting {0} server on port {1}", UseSSL ? "HTTPS" : "HTTP", Port); 1854 "[BASE HTTP SERVER]: Starting {0} server on port {1}", UseSSL ? "HTTPS" : "HTTP", Port);
@@ -1622,8 +1886,9 @@ namespace OpenSim.Framework.Servers.HttpServer
1622 m_httpListener2.Start(64); 1886 m_httpListener2.Start(64);
1623 1887
1624 // Long Poll Service Manager with 3 worker threads a 25 second timeout for no events 1888 // Long Poll Service Manager with 3 worker threads a 25 second timeout for no events
1625 m_PollServiceManager = new PollServiceRequestManager(this, 3, 25000); 1889 PollServiceRequestManager = new PollServiceRequestManager(this, performPollResponsesAsync, 3, 25000);
1626 m_PollServiceManager.Start(); 1890 PollServiceRequestManager.Start();
1891
1627 HTTPDRunning = true; 1892 HTTPDRunning = true;
1628 1893
1629 //HttpListenerContext context; 1894 //HttpListenerContext context;
@@ -1642,6 +1907,21 @@ namespace OpenSim.Framework.Servers.HttpServer
1642 // useful without inbound HTTP. 1907 // useful without inbound HTTP.
1643 throw e; 1908 throw e;
1644 } 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);
1645 } 1925 }
1646 1926
1647 public void httpServerDisconnectMonitor(IHttpClientContext source, SocketError err) 1927 public void httpServerDisconnectMonitor(IHttpClientContext source, SocketError err)
@@ -1672,9 +1952,12 @@ namespace OpenSim.Framework.Servers.HttpServer
1672 public void Stop() 1952 public void Stop()
1673 { 1953 {
1674 HTTPDRunning = false; 1954 HTTPDRunning = false;
1955
1956 StatsManager.DeregisterStat(m_requestsProcessedStat);
1957
1675 try 1958 try
1676 { 1959 {
1677 m_PollServiceManager.Stop(); 1960 PollServiceRequestManager.Stop();
1678 1961
1679 m_httpListener2.ExceptionThrown -= httpServerException; 1962 m_httpListener2.ExceptionThrown -= httpServerException;
1680 //m_httpListener2.DisconnectHandler = null; 1963 //m_httpListener2.DisconnectHandler = null;
@@ -1741,6 +2024,12 @@ namespace OpenSim.Framework.Servers.HttpServer
1741 m_rpcHandlers.Remove(method); 2024 m_rpcHandlers.Remove(method);
1742 } 2025 }
1743 2026
2027 public void RemoveJsonRPCHandler(string method)
2028 {
2029 lock(jsonRpcHandlers)
2030 jsonRpcHandlers.Remove(method);
2031 }
2032
1744 public bool RemoveLLSDHandler(string path, LLSDMethod handler) 2033 public bool RemoveLLSDHandler(string path, LLSDMethod handler)
1745 { 2034 {
1746 lock (m_llsdHandlers) 2035 lock (m_llsdHandlers)
diff --git a/OpenSim/Framework/Servers/HttpServer/BaseOutputStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/BaseOutputStreamHandler.cs
new file mode 100644
index 0000000..72b3065
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/BaseOutputStreamHandler.cs
@@ -0,0 +1,60 @@
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
index ae7aaf2..d4a1ec3 100644
--- a/OpenSim/Framework/Servers/HttpServer/BaseRequestHandler.cs
+++ b/OpenSim/Framework/Servers/HttpServer/BaseRequestHandler.cs
@@ -26,11 +26,16 @@
26 */ 26 */
27 27
28using System; 28using System;
29using OpenSim.Framework.Monitoring;
29 30
30namespace OpenSim.Framework.Servers.HttpServer 31namespace OpenSim.Framework.Servers.HttpServer
31{ 32{
32 public abstract class BaseRequestHandler 33 public abstract class BaseRequestHandler
33 { 34 {
35 public int RequestsReceived { get; protected set; }
36
37 public int RequestsHandled { get; protected set; }
38
34 public virtual string ContentType 39 public virtual string ContentType
35 { 40 {
36 get { return "application/xml"; } 41 get { return "application/xml"; }
@@ -57,6 +62,24 @@ namespace OpenSim.Framework.Servers.HttpServer
57 Description = description; 62 Description = description;
58 m_httpMethod = httpMethod; 63 m_httpMethod = httpMethod;
59 m_path = path; 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 }
60 } 83 }
61 84
62 public virtual string Path 85 public virtual string Path
diff --git a/OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs
index 6342983..41aa19b 100644
--- a/OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs
+++ b/OpenSim/Framework/Servers/HttpServer/BaseStreamHandler.cs
@@ -26,17 +26,60 @@
26 */ 26 */
27 27
28using System.IO; 28using System.IO;
29using System.Net;
30using OpenSim.Framework.ServiceAuth;
29 31
30namespace OpenSim.Framework.Servers.HttpServer 32namespace OpenSim.Framework.Servers.HttpServer
31{ 33{
34 /// <summary>
35 /// Base streamed request handler.
36 /// </summary>
37 /// <remarks>
38 /// Inheriting classes should override ProcessRequest() rather than Handle()
39 /// </remarks>
32 public abstract class BaseStreamHandler : BaseRequestHandler, IStreamedRequestHandler 40 public abstract class BaseStreamHandler : BaseRequestHandler, IStreamedRequestHandler
33 { 41 {
34 public abstract byte[] Handle(string path, Stream request, 42 protected IServiceAuth m_Auth;
35 IOSHttpRequest httpRequest, IOSHttpResponse httpResponse);
36 43
37 protected BaseStreamHandler(string httpMethod, string path) : this(httpMethod, path, null, null) {} 44 protected BaseStreamHandler(string httpMethod, string path) : this(httpMethod, path, null, null) { }
38 45
39 protected BaseStreamHandler(string httpMethod, string path, string name, string description) 46 protected BaseStreamHandler(string httpMethod, string path, string name, string description)
40 : base(httpMethod, path, name, 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 }
41 } 84 }
42} \ No newline at end of file 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
index b94bfb4..1b03f54 100644
--- a/OpenSim/Framework/Servers/HttpServer/BinaryStreamHandler.cs
+++ b/OpenSim/Framework/Servers/HttpServer/BinaryStreamHandler.cs
@@ -45,7 +45,7 @@ namespace OpenSim.Framework.Servers.HttpServer
45 m_method = binaryMethod; 45 m_method = binaryMethod;
46 } 46 }
47 47
48 public override byte[] Handle(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) 48 protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
49 { 49 {
50 byte[] data = ReadFully(request); 50 byte[] data = ReadFully(request);
51 string param = GetParam(path); 51 string param = GetParam(path);
diff --git a/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs b/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs
new file mode 100644
index 0000000..cd4b8ff
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/GenericHTTPBasicDOSProtector.cs
@@ -0,0 +1,119 @@
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/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/Interfaces/IStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs
index cb5cce5..b8541cb 100644
--- a/OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs
+++ b/OpenSim/Framework/Servers/HttpServer/Interfaces/IStreamHandler.cs
@@ -32,7 +32,6 @@ namespace OpenSim.Framework.Servers.HttpServer
32{ 32{
33 public interface IRequestHandler 33 public interface IRequestHandler
34 { 34 {
35
36 /// <summary> 35 /// <summary>
37 /// Name for this handler. 36 /// Name for this handler.
38 /// </summary> 37 /// </summary>
@@ -59,6 +58,19 @@ namespace OpenSim.Framework.Servers.HttpServer
59 58
60 // Return path 59 // Return path
61 string Path { get; } 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; }
62 } 74 }
63 75
64 public interface IStreamedRequestHandler : IRequestHandler 76 public interface IStreamedRequestHandler : IRequestHandler
@@ -69,7 +81,6 @@ namespace OpenSim.Framework.Servers.HttpServer
69 81
70 public interface IStreamHandler : IRequestHandler 82 public interface IStreamHandler : IRequestHandler
71 { 83 {
72 // Handle request stream, return byte array
73 void Handle(string path, Stream request, Stream response, IOSHttpRequest httpReqbuest, IOSHttpResponse httpResponse); 84 void Handle(string path, Stream request, Stream response, IOSHttpRequest httpReqbuest, IOSHttpResponse httpResponse);
74 } 85 }
75 86
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/OSHttpRequest.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpRequest.cs
index 3171759..05ec6dc 100644
--- a/OpenSim/Framework/Servers/HttpServer/OSHttpRequest.cs
+++ b/OpenSim/Framework/Servers/HttpServer/OSHttpRequest.cs
@@ -182,11 +182,22 @@ namespace OpenSim.Framework.Servers.HttpServer
182 _context = context; 182 _context = context;
183 183
184 if (null != req.Headers["content-encoding"]) 184 if (null != req.Headers["content-encoding"])
185 _contentEncoding = Encoding.GetEncoding(_request.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
186 if (null != req.Headers["content-type"]) 196 if (null != req.Headers["content-type"])
187 _contentType = _request.Headers["content-type"]; 197 _contentType = _request.Headers["content-type"];
188 if (null != req.Headers["user-agent"]) 198 if (null != req.Headers["user-agent"])
189 _userAgent = req.Headers["user-agent"]; 199 _userAgent = req.Headers["user-agent"];
200
190 if (null != req.Headers["remote_addr"]) 201 if (null != req.Headers["remote_addr"])
191 { 202 {
192 try 203 try
diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs
index 77cfb7e..bdea278 100644
--- a/OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs
+++ b/OpenSim/Framework/Servers/HttpServer/OSHttpRequestPump.cs
@@ -70,9 +70,9 @@ namespace OpenSim.Framework.Servers.HttpServer
70 _id = id; 70 _id = id;
71 71
72 _engine = new Thread(new ThreadStart(Engine)); 72 _engine = new Thread(new ThreadStart(Engine));
73 _engine.Name = EngineID;
74 _engine.IsBackground = true; 73 _engine.IsBackground = true;
75 _engine.Start(); 74 _engine.Start();
75 _engine.Name = string.Format ("Engine:{0}",EngineID);
76 76
77 ThreadTracker.Add(_engine); 77 ThreadTracker.Add(_engine);
78 } 78 }
@@ -91,9 +91,9 @@ namespace OpenSim.Framework.Servers.HttpServer
91 public void Start() 91 public void Start()
92 { 92 {
93 _engine = new Thread(new ThreadStart(Engine)); 93 _engine = new Thread(new ThreadStart(Engine));
94 _engine.Name = EngineID;
95 _engine.IsBackground = true; 94 _engine.IsBackground = true;
96 _engine.Start(); 95 _engine.Start();
96 _engine.Name = string.Format ("Engine:{0}",EngineID);
97 97
98 ThreadTracker.Add(_engine); 98 ThreadTracker.Add(_engine);
99 } 99 }
diff --git a/OpenSim/Framework/Servers/HttpServer/OSHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/OSHttpServer.cs
index 84aa31b..cd62842 100644
--- a/OpenSim/Framework/Servers/HttpServer/OSHttpServer.cs
+++ b/OpenSim/Framework/Servers/HttpServer/OSHttpServer.cs
@@ -150,9 +150,9 @@ namespace OpenSim.Framework.Servers.HttpServer
150 public void Start() 150 public void Start()
151 { 151 {
152 _engine = new Thread(new ThreadStart(Engine)); 152 _engine = new Thread(new ThreadStart(Engine));
153 _engine.Name = _engineId;
154 _engine.IsBackground = true; 153 _engine.IsBackground = true;
155 _engine.Start(); 154 _engine.Start();
155 _engine.Name = string.Format ("Engine:{0}",_engineId);
156 156
157 ThreadTracker.Add(_engine); 157 ThreadTracker.Add(_engine);
158 158
diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs
index 3089351..9477100 100644
--- a/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs
+++ b/OpenSim/Framework/Servers/HttpServer/PollServiceEventArgs.cs
@@ -34,7 +34,7 @@ namespace OpenSim.Framework.Servers.HttpServer
34 public delegate void RequestMethod(UUID requestID, Hashtable request); 34 public delegate void RequestMethod(UUID requestID, Hashtable request);
35 public delegate bool HasEventsMethod(UUID requestID, UUID pId); 35 public delegate bool HasEventsMethod(UUID requestID, UUID pId);
36 36
37 public delegate Hashtable GetEventsMethod(UUID requestID, UUID pId, string request); 37 public delegate Hashtable GetEventsMethod(UUID requestID, UUID pId);
38 38
39 public delegate Hashtable NoEventsMethod(UUID requestID, UUID pId); 39 public delegate Hashtable NoEventsMethod(UUID requestID, UUID pId);
40 40
@@ -45,17 +45,42 @@ namespace OpenSim.Framework.Servers.HttpServer
45 public NoEventsMethod NoEvents; 45 public NoEventsMethod NoEvents;
46 public RequestMethod Request; 46 public RequestMethod Request;
47 public UUID Id; 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; }
48 69
49 public PollServiceEventArgs( 70 public PollServiceEventArgs(
50 RequestMethod pRequest, 71 RequestMethod pRequest,
72 string pUrl,
51 HasEventsMethod pHasEvents, GetEventsMethod pGetEvents, NoEventsMethod pNoEvents, 73 HasEventsMethod pHasEvents, GetEventsMethod pGetEvents, NoEventsMethod pNoEvents,
52 UUID pId) 74 UUID pId, int pTimeOutms)
53 { 75 {
54 Request = pRequest; 76 Request = pRequest;
77 Url = pUrl;
55 HasEvents = pHasEvents; 78 HasEvents = pHasEvents;
56 GetEvents = pGetEvents; 79 GetEvents = pGetEvents;
57 NoEvents = pNoEvents; 80 NoEvents = pNoEvents;
58 Id = pId; 81 Id = pId;
82 TimeOutms = pTimeOutms;
83 Type = EventType.LongPoll;
59 } 84 }
60 } 85 }
61} \ No newline at end of file 86}
diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs
index 723530a..caf0e98 100644
--- a/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs
+++ b/OpenSim/Framework/Servers/HttpServer/PollServiceHttpRequest.cs
@@ -26,13 +26,19 @@
26 */ 26 */
27 27
28using System; 28using System;
29using System.Collections;
30using System.Reflection;
31using System.Text;
29using HttpServer; 32using HttpServer;
33using log4net;
30using OpenMetaverse; 34using OpenMetaverse;
31 35
32namespace OpenSim.Framework.Servers.HttpServer 36namespace OpenSim.Framework.Servers.HttpServer
33{ 37{
34 public class PollServiceHttpRequest 38 public class PollServiceHttpRequest
35 { 39 {
40 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
41
36 public readonly PollServiceEventArgs PollServiceArgs; 42 public readonly PollServiceEventArgs PollServiceArgs;
37 public readonly IHttpClientContext HttpContext; 43 public readonly IHttpClientContext HttpContext;
38 public readonly IHttpRequest Request; 44 public readonly IHttpRequest Request;
@@ -48,5 +54,44 @@ namespace OpenSim.Framework.Servers.HttpServer
48 RequestTime = System.Environment.TickCount; 54 RequestTime = System.Environment.TickCount;
49 RequestID = UUID.Random(); 55 RequestID = UUID.Random();
50 } 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 }
51 } 96 }
52} \ No newline at end of file 97} \ No newline at end of file
diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
index 3e84c55..28bba70 100644
--- a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
+++ b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs
@@ -33,132 +33,298 @@ using log4net;
33using HttpServer; 33using HttpServer;
34using OpenSim.Framework; 34using OpenSim.Framework;
35using OpenSim.Framework.Monitoring; 35using OpenSim.Framework.Monitoring;
36using Amib.Threading;
37using System.IO;
38using System.Text;
39using System.Collections.Generic;
36 40
37namespace OpenSim.Framework.Servers.HttpServer 41namespace OpenSim.Framework.Servers.HttpServer
38{ 42{
39 public class PollServiceRequestManager 43 public class PollServiceRequestManager
40 { 44 {
41// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 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; }
42 65
43 private readonly BaseHttpServer m_server; 66 private readonly BaseHttpServer m_server;
44 private static Queue m_requests = Queue.Synchronized(new Queue()); 67
68 private BlockingQueue<PollServiceHttpRequest> m_requests = new BlockingQueue<PollServiceHttpRequest>();
69 private static List<PollServiceHttpRequest> m_longPollRequests = new List<PollServiceHttpRequest>();
70
45 private uint m_WorkerThreadCount = 0; 71 private uint m_WorkerThreadCount = 0;
46 private Thread[] m_workerThreads; 72 private Thread[] m_workerThreads;
47 private PollServiceWorkerThread[] m_PollServiceWorkerThreads;
48 private volatile bool m_running = true;
49 private int m_pollTimeout;
50 73
51 public PollServiceRequestManager(BaseHttpServer pSrv, uint pWorkerThreadCount, int pTimeout) 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)
52 { 80 {
53 m_server = pSrv; 81 m_server = pSrv;
82 PerformResponsesAsync = performResponsesAsync;
54 m_WorkerThreadCount = pWorkerThreadCount; 83 m_WorkerThreadCount = pWorkerThreadCount;
55 m_pollTimeout = pTimeout; 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));
56 } 111 }
57 112
58 public void Start() 113 public void Start()
59 { 114 {
60 m_running = true; 115 IsRunning = true;
61 m_workerThreads = new Thread[m_WorkerThreadCount];
62 m_PollServiceWorkerThreads = new PollServiceWorkerThread[m_WorkerThreadCount];
63 116
64 //startup worker threads 117 if (PerformResponsesAsync)
65 for (uint i = 0; i < m_WorkerThreadCount; i++)
66 { 118 {
67 m_PollServiceWorkerThreads[i] = new PollServiceWorkerThread(m_server, m_pollTimeout); 119 //startup worker threads
68 m_PollServiceWorkerThreads[i].ReQueue += ReQueueEvent; 120 for (uint i = 0; i < m_WorkerThreadCount; i++)
69 121 {
70 m_workerThreads[i] 122 m_workerThreads[i]
71 = Watchdog.StartThread( 123 = WorkManager.StartThread(
72 m_PollServiceWorkerThreads[i].ThreadStart, 124 PoolWorkerJob,
73 String.Format("PollServiceWorkerThread{0}", i), 125 string.Format("PollServiceWorkerThread{0}:{1}", i, m_server.Port),
74 ThreadPriority.Normal, 126 ThreadPriority.Normal,
75 false, 127 false,
76 true, 128 false,
77 null, 129 null,
78 int.MaxValue); 130 int.MaxValue);
79 } 131 }
80 132
81 Watchdog.StartThread( 133 WorkManager.StartThread(
82 this.ThreadStart, 134 this.CheckLongPollThreads,
83 "PollServiceWatcherThread", 135 string.Format("LongPollServiceWatcherThread:{0}", m_server.Port),
84 ThreadPriority.Normal, 136 ThreadPriority.Normal,
85 false, 137 false,
86 true, 138 true,
87 null, 139 null,
88 1000 * 60 * 10); 140 1000 * 60 * 10);
141 }
89 } 142 }
90 143
91 internal void ReQueueEvent(PollServiceHttpRequest req) 144 private void ReQueueEvent(PollServiceHttpRequest req)
92 { 145 {
93 // Do accounting stuff here 146 if (IsRunning)
94 Enqueue(req); 147 {
95 } 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 });
96 154
97 public void Enqueue(PollServiceHttpRequest req) 155 t.Change(100, Timeout.Infinite);
98 { 156
99 lock (m_requests) 157 }
100 m_requests.Enqueue(req);
101 } 158 }
102 159
103 public void ThreadStart() 160 public void Enqueue(PollServiceHttpRequest req)
104 { 161 {
105 while (m_running) 162 if (IsRunning)
106 { 163 {
107 Watchdog.UpdateThread(); 164 if (req.PollServiceArgs.Type == PollServiceEventArgs.EventType.LongPoll)
108 ProcessQueuedRequests(); 165 {
109 Thread.Sleep(1000); 166 lock (m_longPollRequests)
167 m_longPollRequests.Add(req);
168 }
169 else
170 m_requests.Enqueue(req);
110 } 171 }
111 } 172 }
112 173
113 private void ProcessQueuedRequests() 174 private void CheckLongPollThreads()
114 { 175 {
115 lock (m_requests) 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)
116 { 183 {
117 if (m_requests.Count == 0) 184 Thread.Sleep(500);
118 return; 185 Watchdog.UpdateThread();
119
120// m_log.DebugFormat("[POLL SERVICE REQUEST MANAGER]: Processing {0} requests", m_requests.Count);
121
122 int reqperthread = (int) (m_requests.Count/m_WorkerThreadCount) + 1;
123 186
124 // For Each WorkerThread 187// List<PollServiceHttpRequest> not_ready = new List<PollServiceHttpRequest>();
125 for (int tc = 0; tc < m_WorkerThreadCount && m_requests.Count > 0; tc++) 188 lock (m_longPollRequests)
126 { 189 {
127 //Loop over number of requests each thread handles. 190 if (m_longPollRequests.Count > 0 && IsRunning)
128 for (int i = 0; i < reqperthread && m_requests.Count > 0; i++)
129 { 191 {
130 try 192 List<PollServiceHttpRequest> ready = m_longPollRequests.FindAll(req =>
131 { 193 (req.PollServiceArgs.HasEvents(req.RequestID, req.PollServiceArgs.Id) || // there are events in this EQ
132 m_PollServiceWorkerThreads[tc].Enqueue((PollServiceHttpRequest)m_requests.Dequeue()); 194 (Environment.TickCount - req.RequestTime) > req.PollServiceArgs.TimeOutms) // no events, but timeout
133 } 195 );
134 catch (InvalidOperationException) 196
135 { 197 ready.ForEach(req =>
136 // The queue is empty, we did our calculations wrong! 198 {
137 return; 199 m_requests.Enqueue(req);
138 } 200 m_longPollRequests.Remove(req);
139 201 });
202
140 } 203 }
204
141 } 205 }
142 } 206 }
143
144 } 207 }
145 208
146 public void Stop() 209 public void Stop()
147 { 210 {
148 m_running = false; 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;
149 219
150 foreach (object o in m_requests) 220 lock (m_longPollRequests)
151 { 221 {
152 PollServiceHttpRequest req = (PollServiceHttpRequest) o; 222 if (m_longPollRequests.Count > 0 && IsRunning)
153 PollServiceWorkerThread.DoHTTPGruntWork( 223 m_longPollRequests.ForEach(req => m_requests.Enqueue(req));
154 m_server, req, req.PollServiceArgs.NoEvents(req.RequestID, req.PollServiceArgs.Id));
155 } 224 }
156 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();
157 m_requests.Clear(); 241 m_requests.Clear();
242 }
158 243
159 foreach (Thread t in m_workerThreads) 244 // work threads
245
246 private void PoolWorkerJob()
247 {
248 while (IsRunning)
160 { 249 {
161 t.Abort(); 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 }
162 } 328 }
163 } 329 }
164 } 330 }
diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceWorkerThread.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceWorkerThread.cs
deleted file mode 100644
index 5adbcd1..0000000
--- a/OpenSim/Framework/Servers/HttpServer/PollServiceWorkerThread.cs
+++ /dev/null
@@ -1,165 +0,0 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections;
30using System.Collections.Generic;
31using System.IO;
32using System.Text;
33using HttpServer;
34using OpenMetaverse;
35using System.Reflection;
36using log4net;
37using OpenSim.Framework.Monitoring;
38
39namespace OpenSim.Framework.Servers.HttpServer
40{
41 public delegate void ReQueuePollServiceItem(PollServiceHttpRequest req);
42
43 public class PollServiceWorkerThread
44 {
45 private static readonly ILog m_log =
46 LogManager.GetLogger(
47 MethodBase.GetCurrentMethod().DeclaringType);
48
49 public event ReQueuePollServiceItem ReQueue;
50
51 private readonly BaseHttpServer m_server;
52 private BlockingQueue<PollServiceHttpRequest> m_request;
53 private bool m_running = true;
54 private int m_timeout = 250;
55
56 public PollServiceWorkerThread(BaseHttpServer pSrv, int pTimeout)
57 {
58 m_request = new BlockingQueue<PollServiceHttpRequest>();
59 m_server = pSrv;
60 m_timeout = pTimeout;
61 }
62
63 public void ThreadStart()
64 {
65 Run();
66 }
67
68 public void Run()
69 {
70 while (m_running)
71 {
72 PollServiceHttpRequest req = m_request.Dequeue();
73
74 Watchdog.UpdateThread();
75
76 try
77 {
78 if (req.PollServiceArgs.HasEvents(req.RequestID, req.PollServiceArgs.Id))
79 {
80 StreamReader str;
81 try
82 {
83 str = new StreamReader(req.Request.Body);
84 }
85 catch (System.ArgumentException)
86 {
87 // Stream was not readable means a child agent
88 // was closed due to logout, leaving the
89 // Event Queue request orphaned.
90 continue;
91 }
92
93 Hashtable responsedata = req.PollServiceArgs.GetEvents(req.RequestID, req.PollServiceArgs.Id, str.ReadToEnd());
94 DoHTTPGruntWork(m_server, req, responsedata);
95 }
96 else
97 {
98 if ((Environment.TickCount - req.RequestTime) > m_timeout)
99 {
100 DoHTTPGruntWork(
101 m_server,
102 req,
103 req.PollServiceArgs.NoEvents(req.RequestID, req.PollServiceArgs.Id));
104 }
105 else
106 {
107 ReQueuePollServiceItem reQueueItem = ReQueue;
108 if (reQueueItem != null)
109 reQueueItem(req);
110 }
111 }
112 }
113 catch (Exception e)
114 {
115 m_log.ErrorFormat("Exception in poll service thread: " + e.ToString());
116 }
117 }
118 }
119
120 internal void Enqueue(PollServiceHttpRequest pPollServiceHttpRequest)
121 {
122 m_request.Enqueue(pPollServiceHttpRequest);
123 }
124
125 /// <summary>
126 /// FIXME: This should be part of BaseHttpServer
127 /// </summary>
128 internal static void DoHTTPGruntWork(BaseHttpServer server, PollServiceHttpRequest req, Hashtable responsedata)
129 {
130 OSHttpResponse response
131 = new OSHttpResponse(new HttpResponse(req.HttpContext, req.Request), req.HttpContext);
132
133 byte[] buffer = server.DoHTTPGruntWork(responsedata, response);
134
135 response.SendChunked = false;
136 response.ContentLength64 = buffer.Length;
137 response.ContentEncoding = Encoding.UTF8;
138
139 try
140 {
141 response.OutputStream.Write(buffer, 0, buffer.Length);
142 }
143 catch (Exception ex)
144 {
145 m_log.Warn(string.Format("[POLL SERVICE WORKER THREAD]: Error ", ex));
146 }
147 finally
148 {
149 //response.OutputStream.Close();
150 try
151 {
152 response.OutputStream.Flush();
153 response.Send();
154
155 //if (!response.KeepAlive && response.ReuseContext)
156 // response.FreeContext();
157 }
158 catch (Exception e)
159 {
160 m_log.Warn(String.Format("[POLL SERVICE WORKER THREAD]: Error ", e));
161 }
162 }
163 }
164 }
165} \ No newline at end of file
diff --git a/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs b/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs
index 02ecc25..5e630dc 100644
--- a/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs
+++ b/OpenSim/Framework/Servers/HttpServer/Properties/AssemblyInfo.cs
@@ -29,5 +29,5 @@ using System.Runtime.InteropServices;
29// Build Number 29// Build Number
30// Revision 30// Revision
31// 31//
32[assembly: AssemblyVersion("0.7.5.*")] 32[assembly: AssemblyVersion("0.8.3.*")]
33[assembly: AssemblyFileVersion("1.0.0.0")] 33
diff --git a/OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs b/OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs
index 07082a8..bd55657 100644
--- a/OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs
+++ b/OpenSim/Framework/Servers/HttpServer/RestDeserialiseHandler.cs
@@ -33,7 +33,7 @@ namespace OpenSim.Framework.Servers.HttpServer
33{ 33{
34 public delegate TResponse RestDeserialiseMethod<TRequest, TResponse>(TRequest request); 34 public delegate TResponse RestDeserialiseMethod<TRequest, TResponse>(TRequest request);
35 35
36 public class RestDeserialiseHandler<TRequest, TResponse> : BaseRequestHandler, IStreamHandler 36 public class RestDeserialiseHandler<TRequest, TResponse> : BaseOutputStreamHandler, IStreamHandler
37 where TRequest : new() 37 where TRequest : new()
38 { 38 {
39 private RestDeserialiseMethod<TRequest, TResponse> m_method; 39 private RestDeserialiseMethod<TRequest, TResponse> m_method;
@@ -48,7 +48,7 @@ namespace OpenSim.Framework.Servers.HttpServer
48 m_method = method; 48 m_method = method;
49 } 49 }
50 50
51 public void Handle(string path, Stream request, Stream responseStream, 51 protected override void ProcessRequest(string path, Stream request, Stream responseStream,
52 IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) 52 IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
53 { 53 {
54 TRequest deserial; 54 TRequest deserial;
diff --git a/OpenSim/Framework/Servers/HttpServer/RestObjectPoster.cs b/OpenSim/Framework/Servers/HttpServer/RestObjectPoster.cs
index 48ced19..afe052f 100644
--- a/OpenSim/Framework/Servers/HttpServer/RestObjectPoster.cs
+++ b/OpenSim/Framework/Servers/HttpServer/RestObjectPoster.cs
@@ -52,23 +52,25 @@ namespace OpenSim.Framework.Servers.HttpServer
52 request.Method = verb; 52 request.Method = verb;
53 request.ContentType = "text/xml"; 53 request.ContentType = "text/xml";
54 54
55 MemoryStream buffer = new MemoryStream(); 55 using (MemoryStream buffer = new MemoryStream())
56 {
57 XmlWriterSettings settings = new XmlWriterSettings();
58 settings.Encoding = Encoding.UTF8;
56 59
57 XmlWriterSettings settings = new XmlWriterSettings(); 60 using (XmlWriter writer = XmlWriter.Create(buffer, settings))
58 settings.Encoding = Encoding.UTF8; 61 {
62 XmlSerializer serializer = new XmlSerializer(type);
63 serializer.Serialize(writer, obj);
64 writer.Flush();
65 }
59 66
60 using (XmlWriter writer = XmlWriter.Create(buffer, settings)) 67 int length = (int)buffer.Length;
61 { 68 request.ContentLength = length;
62 XmlSerializer serializer = new XmlSerializer(type);
63 serializer.Serialize(writer, obj);
64 writer.Flush();
65 }
66 69
67 int length = (int) buffer.Length; 70 using (Stream requestStream = request.GetRequestStream())
68 request.ContentLength = length; 71 requestStream.Write(buffer.ToArray(), 0, length);
72 }
69 73
70 Stream requestStream = request.GetRequestStream();
71 requestStream.Write(buffer.ToArray(), 0, length);
72 // IAsyncResult result = request.BeginGetResponse(AsyncCallback, request); 74 // IAsyncResult result = request.BeginGetResponse(AsyncCallback, request);
73 request.BeginGetResponse(AsyncCallback, request); 75 request.BeginGetResponse(AsyncCallback, request);
74 } 76 }
diff --git a/OpenSim/Framework/Servers/HttpServer/RestObjectPosterResponse.cs b/OpenSim/Framework/Servers/HttpServer/RestObjectPosterResponse.cs
index 451745c..a911b9f 100644
--- a/OpenSim/Framework/Servers/HttpServer/RestObjectPosterResponse.cs
+++ b/OpenSim/Framework/Servers/HttpServer/RestObjectPosterResponse.cs
@@ -60,24 +60,25 @@ namespace OpenSim.Framework.Servers.HttpServer
60 request.ContentType = "text/xml"; 60 request.ContentType = "text/xml";
61 request.Timeout = 10000; 61 request.Timeout = 10000;
62 62
63 MemoryStream buffer = new MemoryStream(); 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 }
64 74
65 XmlWriterSettings settings = new XmlWriterSettings(); 75 int length = (int)buffer.Length;
66 settings.Encoding = Encoding.UTF8; 76 request.ContentLength = length;
67 77
68 using (XmlWriter writer = XmlWriter.Create(buffer, settings)) 78 using (Stream requestStream = request.GetRequestStream())
69 { 79 requestStream.Write(buffer.ToArray(), 0, length);
70 XmlSerializer serializer = new XmlSerializer(type);
71 serializer.Serialize(writer, obj);
72 writer.Flush();
73 } 80 }
74 81
75 int length = (int) buffer.Length;
76 request.ContentLength = length;
77
78 Stream requestStream = request.GetRequestStream();
79 requestStream.Write(buffer.ToArray(), 0, length);
80 requestStream.Close();
81 // IAsyncResult result = request.BeginGetResponse(AsyncCallback, request); 82 // IAsyncResult result = request.BeginGetResponse(AsyncCallback, request);
82 request.BeginGetResponse(AsyncCallback, request); 83 request.BeginGetResponse(AsyncCallback, request);
83 } 84 }
diff --git a/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs b/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs
index 19c03a8..ad69cd2 100644
--- a/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs
+++ b/OpenSim/Framework/Servers/HttpServer/RestSessionService.cs
@@ -77,44 +77,34 @@ namespace OpenSim.Framework.Servers.HttpServer
77 request.ContentType = "text/xml"; 77 request.ContentType = "text/xml";
78 request.Timeout = 20000; 78 request.Timeout = 20000;
79 79
80 MemoryStream buffer = new MemoryStream(); 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 { 81 {
87 XmlSerializer serializer = new XmlSerializer(type); 82 XmlWriterSettings settings = new XmlWriterSettings();
88 serializer.Serialize(writer, sobj); 83 settings.Encoding = Encoding.UTF8;
89 writer.Flush();
90 }
91 84
92 int length = (int)buffer.Length; 85 using (XmlWriter writer = XmlWriter.Create(buffer, settings))
93 request.ContentLength = length; 86 {
87 XmlSerializer serializer = new XmlSerializer(type);
88 serializer.Serialize(writer, sobj);
89 writer.Flush();
90 }
94 91
95 Stream requestStream = request.GetRequestStream(); 92 int length = (int)buffer.Length;
96 requestStream.Write(buffer.ToArray(), 0, length); 93 request.ContentLength = length;
97 buffer.Close(); 94
98 requestStream.Close(); 95 using (Stream requestStream = request.GetRequestStream())
96 requestStream.Write(buffer.ToArray(), 0, length);
97 }
99 98
100 TResponse deserial = default(TResponse); 99 TResponse deserial = default(TResponse);
101 using (WebResponse resp = request.GetResponse()) 100 using (WebResponse resp = request.GetResponse())
102 { 101 {
103 XmlSerializer deserializer = new XmlSerializer(typeof(TResponse)); 102 XmlSerializer deserializer = new XmlSerializer(typeof(TResponse));
104 Stream respStream = null; 103
105 try 104 using (Stream respStream = resp.GetResponseStream())
106 {
107 respStream = resp.GetResponseStream();
108 deserial = (TResponse)deserializer.Deserialize(respStream); 105 deserial = (TResponse)deserializer.Deserialize(respStream);
109 }
110 catch { }
111 finally
112 {
113 if (respStream != null)
114 respStream.Close();
115 resp.Close();
116 }
117 } 106 }
107
118 return deserial; 108 return deserial;
119 } 109 }
120 } 110 }
@@ -142,25 +132,25 @@ namespace OpenSim.Framework.Servers.HttpServer
142 request.ContentType = "text/xml"; 132 request.ContentType = "text/xml";
143 request.Timeout = 10000; 133 request.Timeout = 10000;
144 134
145 MemoryStream buffer = new MemoryStream(); 135 using (MemoryStream buffer = new MemoryStream())
136 {
137 XmlWriterSettings settings = new XmlWriterSettings();
138 settings.Encoding = Encoding.UTF8;
146 139
147 XmlWriterSettings settings = new XmlWriterSettings(); 140 using (XmlWriter writer = XmlWriter.Create(buffer, settings))
148 settings.Encoding = Encoding.UTF8; 141 {
142 XmlSerializer serializer = new XmlSerializer(type);
143 serializer.Serialize(writer, sobj);
144 writer.Flush();
145 }
149 146
150 using (XmlWriter writer = XmlWriter.Create(buffer, settings)) 147 int length = (int)buffer.Length;
151 { 148 request.ContentLength = length;
152 XmlSerializer serializer = new XmlSerializer(type);
153 serializer.Serialize(writer, sobj);
154 writer.Flush();
155 }
156 buffer.Close();
157 149
158 int length = (int)buffer.Length; 150 using (Stream requestStream = request.GetRequestStream())
159 request.ContentLength = length; 151 requestStream.Write(buffer.ToArray(), 0, length);
152 }
160 153
161 Stream requestStream = request.GetRequestStream();
162 requestStream.Write(buffer.ToArray(), 0, length);
163 requestStream.Close();
164 // IAsyncResult result = request.BeginGetResponse(AsyncCallback, request); 154 // IAsyncResult result = request.BeginGetResponse(AsyncCallback, request);
165 request.BeginGetResponse(AsyncCallback, request); 155 request.BeginGetResponse(AsyncCallback, request);
166 } 156 }
@@ -192,7 +182,7 @@ namespace OpenSim.Framework.Servers.HttpServer
192 182
193 public delegate bool CheckIdentityMethod(string sid, string aid); 183 public delegate bool CheckIdentityMethod(string sid, string aid);
194 184
195 public class RestDeserialiseSecureHandler<TRequest, TResponse> : BaseRequestHandler, IStreamHandler 185 public class RestDeserialiseSecureHandler<TRequest, TResponse> : BaseOutputStreamHandler, IStreamHandler
196 where TRequest : new() 186 where TRequest : new()
197 { 187 {
198 private static readonly ILog m_log 188 private static readonly ILog m_log
@@ -210,7 +200,7 @@ namespace OpenSim.Framework.Servers.HttpServer
210 m_method = method; 200 m_method = method;
211 } 201 }
212 202
213 public void Handle(string path, Stream request, Stream responseStream, 203 protected override void ProcessRequest(string path, Stream request, Stream responseStream,
214 IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) 204 IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
215 { 205 {
216 RestSessionObject<TRequest> deserial = default(RestSessionObject<TRequest>); 206 RestSessionObject<TRequest> deserial = default(RestSessionObject<TRequest>);
@@ -246,7 +236,7 @@ namespace OpenSim.Framework.Servers.HttpServer
246 236
247 public delegate bool CheckTrustedSourceMethod(IPEndPoint peer); 237 public delegate bool CheckTrustedSourceMethod(IPEndPoint peer);
248 238
249 public class RestDeserialiseTrustedHandler<TRequest, TResponse> : BaseRequestHandler, IStreamHandler 239 public class RestDeserialiseTrustedHandler<TRequest, TResponse> : BaseOutputStreamHandler, IStreamHandler
250 where TRequest : new() 240 where TRequest : new()
251 { 241 {
252 private static readonly ILog m_log 242 private static readonly ILog m_log
@@ -269,7 +259,7 @@ namespace OpenSim.Framework.Servers.HttpServer
269 m_method = method; 259 m_method = method;
270 } 260 }
271 261
272 public void Handle(string path, Stream request, Stream responseStream, 262 protected override void ProcessRequest(string path, Stream request, Stream responseStream,
273 IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) 263 IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
274 { 264 {
275 TRequest deserial = default(TRequest); 265 TRequest deserial = default(TRequest);
@@ -301,6 +291,5 @@ namespace OpenSim.Framework.Servers.HttpServer
301 serializer.Serialize(xmlWriter, response); 291 serializer.Serialize(xmlWriter, response);
302 } 292 }
303 } 293 }
304 } 294 }
305 295} \ No newline at end of file
306}
diff --git a/OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs b/OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs
index 1f17fee..0305dee 100644
--- a/OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs
+++ b/OpenSim/Framework/Servers/HttpServer/RestStreamHandler.cs
@@ -48,7 +48,7 @@ namespace OpenSim.Framework.Servers.HttpServer
48 m_restMethod = restMethod; 48 m_restMethod = restMethod;
49 } 49 }
50 50
51 public override byte[] Handle(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) 51 protected override byte[] ProcessRequest(string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
52 { 52 {
53 Encoding encoding = Encoding.UTF8; 53 Encoding encoding = Encoding.UTF8;
54 StreamReader streamReader = new StreamReader(request, encoding); 54 StreamReader streamReader = new StreamReader(request, encoding);
diff --git a/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs b/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs
new file mode 100644
index 0000000..c2925e3
--- /dev/null
+++ b/OpenSim/Framework/Servers/HttpServer/WebsocketServerHandler.cs
@@ -0,0 +1,1159 @@
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}