diff options
author | David Walter Seikel | 2016-11-03 21:44:39 +1000 |
---|---|---|
committer | David Walter Seikel | 2016-11-03 21:44:39 +1000 |
commit | 134f86e8d5c414409631b25b8c6f0ee45fbd8631 (patch) | |
tree | 216b89d3fb89acfb81be1e440c25c41ab09fa96d /OpenSim/Framework/Servers/HttpServer | |
parent | More changing to production grid. Double oops. (diff) | |
download | opensim-SC-134f86e8d5c414409631b25b8c6f0ee45fbd8631.zip opensim-SC-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.gz opensim-SC-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.bz2 opensim-SC-134f86e8d5c414409631b25b8c6f0ee45fbd8631.tar.xz |
Initial update to OpenSim 0.8.2.1 source code.
Diffstat (limited to '')
27 files changed, 2706 insertions, 411 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; | |||
46 | using HttpListener=System.Net.HttpListener; | 46 | using HttpListener=System.Net.HttpListener; |
47 | using LogPrio=HttpServer.LogPrio; | 47 | using LogPrio=HttpServer.LogPrio; |
48 | using OpenSim.Framework.Monitoring; | 48 | using OpenSim.Framework.Monitoring; |
49 | using System.IO.Compression; | ||
49 | 50 | ||
50 | namespace OpenSim.Framework.Servers.HttpServer | 51 | namespace 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/Communications/XMPP/XmppPresenceStanza.cs b/OpenSim/Framework/Servers/HttpServer/BaseOutputStreamHandler.cs index 4d45ac0..72b3065 100644 --- a/OpenSim/Framework/Communications/XMPP/XmppPresenceStanza.cs +++ b/OpenSim/Framework/Servers/HttpServer/BaseOutputStreamHandler.cs | |||
@@ -25,45 +25,36 @@ | |||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ | 26 | */ |
27 | 27 | ||
28 | using System.Xml.Serialization; | 28 | using System.IO; |
29 | 29 | ||
30 | namespace OpenSim.Framework.Communications.XMPP | 30 | namespace OpenSim.Framework.Servers.HttpServer |
31 | { | 31 | { |
32 | /// <summary> | 32 | /// <summary> |
33 | /// Message types. | 33 | /// Base handler for writing to an output stream |
34 | /// </summary> | 34 | /// </summary> |
35 | public enum XmppPresenceType | 35 | /// <remarks> |
36 | /// Inheriting classes should override ProcessRequest() rather than Handle() | ||
37 | /// </remarks> | ||
38 | public abstract class BaseOutputStreamHandler : BaseRequestHandler, IRequestHandler | ||
36 | { | 39 | { |
37 | [XmlEnum("unavailable")] unavailable, | 40 | protected BaseOutputStreamHandler(string httpMethod, string path) : this(httpMethod, path, null, null) {} |
38 | [XmlEnum("subscribe")] subscribe, | ||
39 | [XmlEnum("subscribed")] subscribed, | ||
40 | [XmlEnum("unsubscribe")] unsubscribe, | ||
41 | [XmlEnum("unsubscribed")] unsubscribed, | ||
42 | [XmlEnum("probe")] probe, | ||
43 | [XmlEnum("error")] error, | ||
44 | } | ||
45 | 41 | ||
42 | protected BaseOutputStreamHandler(string httpMethod, string path, string name, string description) | ||
43 | : base(httpMethod, path, name, description) {} | ||
46 | 44 | ||
47 | [XmlRoot("message")] | 45 | public virtual void Handle( |
48 | public class XmppPresenceStanza: XmppStanza | 46 | string path, Stream request, Stream response, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) |
49 | { | 47 | { |
50 | /// <summary> | 48 | RequestsReceived++; |
51 | /// IQ type: one of set, get, result, error | ||
52 | /// </summary> | ||
53 | [XmlAttribute("type")] | ||
54 | public XmppPresenceType PresenceType; | ||
55 | 49 | ||
56 | // [XmlAttribute("error")] | 50 | ProcessRequest(path, request, response, httpRequest, httpResponse); |
57 | // public XmppError Error; | ||
58 | 51 | ||
59 | public XmppPresenceStanza() : base() | 52 | RequestsHandled++; |
60 | { | ||
61 | } | 53 | } |
62 | 54 | ||
63 | public XmppPresenceStanza(string fromJid, string toJid, XmppPresenceType pType) : | 55 | protected virtual void ProcessRequest( |
64 | base(fromJid, toJid) | 56 | string path, Stream request, Stream response, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) |
65 | { | 57 | { |
66 | PresenceType = pType; | ||
67 | } | 58 | } |
68 | } | 59 | } |
69 | } | 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 | ||
28 | using System; | 28 | using System; |
29 | using OpenSim.Framework.Monitoring; | ||
29 | 30 | ||
30 | namespace OpenSim.Framework.Servers.HttpServer | 31 | namespace 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 | ||
28 | using System.IO; | 28 | using System.IO; |
29 | using System.Net; | ||
30 | using OpenSim.Framework.ServiceAuth; | ||
29 | 31 | ||
30 | namespace OpenSim.Framework.Servers.HttpServer | 32 | namespace 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 | */ | ||
27 | using OpenSim.Framework; | ||
28 | using System.IO; | ||
29 | |||
30 | namespace 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 | |||
28 | using System.Collections; | ||
29 | |||
30 | namespace 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 | |||
28 | using System.Net; | ||
29 | using OpenMetaverse.StructuredData; | ||
30 | |||
31 | namespace 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 | |||
28 | using System; | ||
29 | using System.Net; | ||
30 | using System.Net.Sockets; | ||
31 | using System.Reflection; | ||
32 | using System.Text; | ||
33 | using System.IO; | ||
34 | using OpenMetaverse.StructuredData; | ||
35 | using OpenMetaverse; | ||
36 | using log4net; | ||
37 | |||
38 | namespace 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 | */ | ||
27 | using System; | ||
28 | using System.Net; | ||
29 | using OpenMetaverse.StructuredData; | ||
30 | |||
31 | namespace 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 | ||
28 | using System; | 28 | using System; |
29 | using System.Collections; | ||
30 | using System.Reflection; | ||
31 | using System.Text; | ||
29 | using HttpServer; | 32 | using HttpServer; |
33 | using log4net; | ||
30 | using OpenMetaverse; | 34 | using OpenMetaverse; |
31 | 35 | ||
32 | namespace OpenSim.Framework.Servers.HttpServer | 36 | namespace 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; | |||
33 | using HttpServer; | 33 | using HttpServer; |
34 | using OpenSim.Framework; | 34 | using OpenSim.Framework; |
35 | using OpenSim.Framework.Monitoring; | 35 | using OpenSim.Framework.Monitoring; |
36 | using Amib.Threading; | ||
37 | using System.IO; | ||
38 | using System.Text; | ||
39 | using System.Collections.Generic; | ||
36 | 40 | ||
37 | namespace OpenSim.Framework.Servers.HttpServer | 41 | namespace 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 | |||
28 | using System; | ||
29 | using System.Collections; | ||
30 | using System.Collections.Generic; | ||
31 | using System.IO; | ||
32 | using System.Text; | ||
33 | using HttpServer; | ||
34 | using OpenMetaverse; | ||
35 | using System.Reflection; | ||
36 | using log4net; | ||
37 | using OpenSim.Framework.Monitoring; | ||
38 | |||
39 | namespace 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 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.IO; | ||
31 | using System.Net; | ||
32 | using System.Security.Cryptography; | ||
33 | using System.Text; | ||
34 | using System.Threading; | ||
35 | using HttpServer; | ||
36 | |||
37 | namespace 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 | ||
939 | nib 0 1 2 3 4 5 6 7 | ||
940 | byt 0 1 2 3 | ||
941 | dec 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 | /* | ||
993 | byt 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 | |||
28 | using System.Net; | ||
29 | using Nwc.XmlRpc; | ||
30 | using OpenSim.Framework; | ||
31 | |||
32 | |||
33 | namespace 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 | } | ||