OpenSimMirror/OpenSim/Region/ClientStack/TCPJSONStream/TCPJsonWebSocketServer.cs

164 lines
5.2 KiB
C#

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<ClientAcceptedEventArgs> 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;
}
/// <summary>
/// Catch exceptions not handled by the listener.
/// </summary>
/// <remarks>
/// Exceptions will be thrown during debug mode if this event is not used,
/// exceptions will be printed to console and suppressed during release mode.
/// </remarks>
public event ExceptionHandler ExceptionThrown = delegate { };
}
}