From 27a0b3ecbdcf248b331742c7b2771d2a87dc8c3a Mon Sep 17 00:00:00 2001 From: teravus Date: Sun, 3 Feb 2013 06:49:17 -0500 Subject: [PATCH] Commit 1 in of this branch feature. This is one of many... --- .../TCPJSONStream/ClientAcceptedEventArgs.cs | 50 ++++++ .../TCPJSONStream/ClientNetworkContext.cs | 143 +++++++++++++++ .../TCPJSONStream/DisconnectedEventArgs.cs | 17 ++ .../TCPJSONStream/OpenSimWebSocketBase.cs | 73 ++++++++ .../TCPJSONStream/TCPJsonWebSocketServer.cs | 163 ++++++++++++++++++ prebuild.xml | 45 +++++ 6 files changed, 491 insertions(+) create mode 100644 OpenSim/Region/ClientStack/TCPJSONStream/ClientAcceptedEventArgs.cs create mode 100644 OpenSim/Region/ClientStack/TCPJSONStream/ClientNetworkContext.cs create mode 100644 OpenSim/Region/ClientStack/TCPJSONStream/DisconnectedEventArgs.cs create mode 100644 OpenSim/Region/ClientStack/TCPJSONStream/OpenSimWebSocketBase.cs create mode 100644 OpenSim/Region/ClientStack/TCPJSONStream/TCPJsonWebSocketServer.cs diff --git a/OpenSim/Region/ClientStack/TCPJSONStream/ClientAcceptedEventArgs.cs b/OpenSim/Region/ClientStack/TCPJSONStream/ClientAcceptedEventArgs.cs new file mode 100644 index 0000000000..a58eab13cc --- /dev/null +++ b/OpenSim/Region/ClientStack/TCPJSONStream/ClientAcceptedEventArgs.cs @@ -0,0 +1,50 @@ +using System; +using System.Net.Sockets; + +namespace OpenSim.Region.ClientStack.TCPJSONStream +{ + /// + /// Invoked when a client have been accepted by the + /// + /// + /// Can be used to revoke incoming connections + /// + public class ClientAcceptedEventArgs : EventArgs + { + private readonly Socket _socket; + private bool _revoke; + + /// + /// Initializes a new instance of the class. + /// + /// The socket. + public ClientAcceptedEventArgs(Socket socket) + { + _socket = socket; + } + + /// + /// Accepted socket. + /// + public Socket Socket + { + get { return _socket; } + } + + /// + /// Client should be revoked. + /// + public bool Revoked + { + get { return _revoke; } + } + + /// + /// Client may not be handled. + /// + public void Revoke() + { + _revoke = true; + } + } +} \ No newline at end of file diff --git a/OpenSim/Region/ClientStack/TCPJSONStream/ClientNetworkContext.cs b/OpenSim/Region/ClientStack/TCPJSONStream/ClientNetworkContext.cs new file mode 100644 index 0000000000..591f817b8f --- /dev/null +++ b/OpenSim/Region/ClientStack/TCPJSONStream/ClientNetworkContext.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Text; + +namespace OpenSim.Region.ClientStack.TCPJSONStream +{ + public class ClientNetworkContext + { + private Socket _socket; + private string _remoteAddress; + private string _remotePort; + private WebSocketConnectionStage _wsConnectionStatus = WebSocketConnectionStage.Accept; + private int _bytesLeft; + private NetworkStream _stream; + private byte[] _buffer; + public event EventHandler Disconnected = delegate { }; + + public ClientNetworkContext(IPEndPoint endPoint, int port, Stream stream, int buffersize, Socket sock) + { + _socket = sock; + _remoteAddress = endPoint.Address.ToString(); + _remotePort = port.ToString(); + _stream = stream as NetworkStream; + _buffer = new byte[buffersize]; + + + } + + public void BeginRead() + { + _wsConnectionStatus = WebSocketConnectionStage.Http; + try + { + _stream.BeginRead(_buffer, 0, _buffer.Length, OnReceive, _wsConnectionStatus); + } + catch (IOException err) + { + //m_log.Debug(err.ToString()); + } + } + + private void OnReceive(IAsyncResult ar) + { + try + { + int bytesRead = _stream.EndRead(ar); + if (bytesRead == 0) + { + + Disconnected(this, new DisconnectedEventArgs(SocketError.ConnectionReset)); + return; + } + + } + } + /// + /// send a whole buffer + /// + /// buffer to send + /// + public void Send(byte[] buffer) + { + if (buffer == null) + throw new ArgumentNullException("buffer"); + Send(buffer, 0, buffer.Length); + } + + /// + /// Send data using the stream + /// + /// Contains data to send + /// Start position in buffer + /// number of bytes to send + /// + /// + public void Send(byte[] buffer, int offset, int size) + { + + if (offset + size > buffer.Length) + throw new ArgumentOutOfRangeException("offset", offset, "offset + size is beyond end of buffer."); + + if (_stream != null && _stream.CanWrite) + { + try + { + _stream.Write(buffer, offset, size); + } + catch (IOException) + { + + } + } + + } + private void Reset() + { + if (_stream == null) + return; + _stream.Dispose(); + _stream = null; + if (_socket == null) + return; + if (_socket.Connected) + _socket.Disconnect(true); + _socket = null; + } + } + + public enum WebSocketConnectionStage + { + Reuse, + Accept, + Http, + WebSocket, + Closed + } + + public enum FrameOpCodesRFC6455 + { + Continue = 0x0, + Text = 0x1, + Binary = 0x2, + Close = 0x8, + Ping = 0x9, + Pong = 0xA + } + + public enum DataState + { + Empty = 0, + Waiting = 1, + Receiving = 2, + Complete = 3, + Closed = 4, + Ping = 5, + Pong = 6 + } + + +} diff --git a/OpenSim/Region/ClientStack/TCPJSONStream/DisconnectedEventArgs.cs b/OpenSim/Region/ClientStack/TCPJSONStream/DisconnectedEventArgs.cs new file mode 100644 index 0000000000..32880cc399 --- /dev/null +++ b/OpenSim/Region/ClientStack/TCPJSONStream/DisconnectedEventArgs.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Sockets; +using System.Text; + +namespace OpenSim.Region.ClientStack.TCPJSONStream +{ + public class DisconnectedEventArgs:EventArgs + { + public SocketError Error { get; private set; } + public DisconnectedEventArgs(SocketError err) + { + Error = err; + } + } +} diff --git a/OpenSim/Region/ClientStack/TCPJSONStream/OpenSimWebSocketBase.cs b/OpenSim/Region/ClientStack/TCPJSONStream/OpenSimWebSocketBase.cs new file mode 100644 index 0000000000..379438d384 --- /dev/null +++ b/OpenSim/Region/ClientStack/TCPJSONStream/OpenSimWebSocketBase.cs @@ -0,0 +1,73 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Net; +using Nini.Config; +using OpenSim.Framework; + +namespace OpenSim.Region.ClientStack.TCPJSONStream +{ + public sealed class TCPJsonWebSocketBase : IClientNetworkServer + { + private TCPJsonWebSocketServer m_tcpServer; + + public TCPJsonWebSocketBase() + { + } + + public void Initialise(IPAddress _listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager authenticateClass) + { + m_tcpServer = new TCPJsonWebSocketServer(_listenIP,ref port, proxyPortOffsetParm, allow_alternate_port,configSource,authenticateClass); + } + + public void NetworkStop() + { + m_tcpServer.Stop(); + } + + public bool HandlesRegion(Location x) + { + return m_tcpServer.HandlesRegion(x); + } + + public void AddScene(IScene x) + { + m_tcpServer.AddScene(x); + } + + public void Start() + { + m_tcpServer.Start(); + } + + public void Stop() + { + m_tcpServer.Stop(); + } + } +} \ No newline at end of file diff --git a/OpenSim/Region/ClientStack/TCPJSONStream/TCPJsonWebSocketServer.cs b/OpenSim/Region/ClientStack/TCPJSONStream/TCPJsonWebSocketServer.cs new file mode 100644 index 0000000000..0713bf4403 --- /dev/null +++ b/OpenSim/Region/ClientStack/TCPJSONStream/TCPJsonWebSocketServer.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Text; +using System.Threading; +using Nini.Config; +using OpenSim.Framework; +using OpenSim.Region.Framework.Scenes; +using log4net; + +namespace OpenSim.Region.ClientStack.TCPJSONStream +{ + public delegate void ExceptionHandler(object source, Exception exception); + + public class TCPJsonWebSocketServer + { + private readonly IPAddress _address; + private readonly int _port; + private readonly ManualResetEvent _shutdownEvent = new ManualResetEvent(false); + private TcpListener _listener; + private int _pendingAccepts; + private bool _shutdown; + private int _backlogAcceptQueueLength = 5; + private Scene m_scene; + private Location m_location; + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public event EventHandler Accepted = delegate { }; + + + public TCPJsonWebSocketServer(IPAddress _listenIP, ref uint port, int proxyPortOffsetParm, + bool allow_alternate_port, IConfigSource configSource, + AgentCircuitManager authenticateClass) + { + _address = _listenIP; + _port = (int)port; //Why is a uint passed in? + } + public void Stop() + { + _shutdown = true; + _listener.Stop(); + if (!_shutdownEvent.WaitOne()) + m_log.Error("[WEBSOCKETSERVER]: Failed to shutdown listener properly."); + _listener = null; + } + + public bool HandlesRegion(Location x) + { + return x == m_location; + } + + public void AddScene(IScene scene) + { + if (m_scene != null) + { + m_log.Debug("[WEBSOCKETSERVER]: AddScene() called but I already have a scene."); + return; + } + if (!(scene is Scene)) + { + m_log.Error("[WEBSOCKETSERVER]: AddScene() called with an unrecognized scene type " + scene.GetType()); + return; + } + + m_scene = (Scene)scene; + m_location = new Location(m_scene.RegionInfo.RegionHandle); + } + + public void Start() + { + _listener = new TcpListener(_address, _port); + _listener.Start(_backlogAcceptQueueLength); + Interlocked.Increment(ref _pendingAccepts); + _listener.BeginAcceptSocket(OnAccept, null); + } + + private void OnAccept(IAsyncResult ar) + { + bool beginAcceptCalled = false; + try + { + int count = Interlocked.Decrement(ref _pendingAccepts); + if (_shutdown) + { + if (count == 0) + _shutdownEvent.Set(); + return; + } + Interlocked.Increment(ref _pendingAccepts); + _listener.BeginAcceptSocket(OnAccept, null); + beginAcceptCalled = true; + Socket socket = _listener.EndAcceptSocket(ar); + if (!OnAcceptingSocket(socket)) + { + socket.Disconnect(true); + return; + } + ClientNetworkContext context = new ClientNetworkContext((IPEndPoint) socket.RemoteEndPoint, _port, + new NetworkStream(socket), 16384, socket); + HttpRequestParser parser; + context.BeginRead(); + + } + catch (Exception err) + { + if (ExceptionThrown == null) +#if DEBUG + throw; +#else + _logWriter.Write(this, LogPrio.Fatal, err.Message); + // we can't really do anything but close the connection +#endif + if (ExceptionThrown != null) + ExceptionThrown(this, err); + + if (!beginAcceptCalled) + RetryBeginAccept(); + + } + } + + private void RetryBeginAccept() + { + try + { + + _listener.BeginAcceptSocket(OnAccept, null); + } + catch (Exception err) + { + + if (ExceptionThrown == null) +#if DEBUG + throw; +#else + // we can't really do anything but close the connection +#endif + if (ExceptionThrown != null) + ExceptionThrown(this, err); + } + } + + private bool OnAcceptingSocket(Socket sock) + { + ClientAcceptedEventArgs args = new ClientAcceptedEventArgs(sock); + Accepted(this, args); + return !args.Revoked; + } + /// + /// Catch exceptions not handled by the listener. + /// + /// + /// Exceptions will be thrown during debug mode if this event is not used, + /// exceptions will be printed to console and suppressed during release mode. + /// + public event ExceptionHandler ExceptionThrown = delegate { }; + + + + } +} diff --git a/prebuild.xml b/prebuild.xml index 329ff73550..d8b4145698 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -1466,6 +1466,8 @@ + + @@ -1508,6 +1510,49 @@ + + + + ../../../../bin/ + + + + + ../../../../bin/ + + + + ../../../../bin/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +