asternet/Asterisk.2013/Asterisk.NET/FastAGI/AsteriskFastAGI.cs
Boris Bendovsky 53e521c7ee Fix typo
2017-09-27 15:08:18 +03:00

325 lines
9.7 KiB
C#

using System;
using System.IO;
using System.Net;
using System.Text;
using AsterNET.FastAGI.MappingStrategies;
using AsterNET.IO;
using AsterNET.Util;
namespace AsterNET.FastAGI
{
public class AsteriskFastAGI
{
#region Flags
/// <summary>
/// If set to true, causes the AGIChannel to throw an exception when a status code of 511 (Channel Dead) is returned.
/// This is set to false by default to maintain backwards compatibility
/// </summary>
public bool SC511_CAUSES_EXCEPTION = false;
/// <summary>
/// If set to true, causes the AGIChannel to throw an exception when return status is 0 and reply is HANGUP.
/// This is set to false by default to maintain backwards compatibility
/// </summary>
public bool SCHANGUP_CAUSES_EXCEPTION = false;
#endregion
#region Variables
#if LOGGER
private readonly Logger logger = Logger.Instance();
#endif
private ServerSocket serverSocket;
/// <summary> The port to listen on.</summary>
private int port;
/// <summary> The address to listen on.</summary>
private readonly string address;
/// <summary>The thread pool that contains the worker threads to process incoming requests.</summary>
private ThreadPool pool;
/// <summary>
/// The number of worker threads in the thread pool. This equals the maximum number of concurrent requests this
/// AGIServer can serve.
/// </summary>
private int poolSize;
/// <summary> True while this server is shut down. </summary>
private bool stopped;
/// <summary>
/// The strategy to use for bind AGIRequests to AGIScripts that serve them.
/// </summary>
private IMappingStrategy mappingStrategy;
private Encoding socketEncoding = Encoding.ASCII;
#endregion
#region PoolSize
/// <summary>
/// Sets the number of worker threads in the thread pool.<br />
/// This equals the maximum number of concurrent requests this AGIServer can serve.<br />
/// The default pool size is 10.
/// </summary>
public int PoolSize
{
set { poolSize = value; }
}
#endregion
#region BindPort
/// <summary>
/// Sets the TCP port to listen on for new connections.<br />
/// The default bind port is 4573.
/// </summary>
public int BindPort
{
set { port = value; }
}
#endregion
#region MappingStrategy
/// <summary>
/// Sets the strategy to use for mapping AGIRequests to AGIScripts that serve them.<br />
/// The default mapping is a MappingStrategy.
/// </summary>
/// <seealso cref="MappingStrategy" />
public IMappingStrategy MappingStrategy
{
set { mappingStrategy = value; }
}
#endregion
#region SocketEncoding
public Encoding SocketEncoding
{
get { return socketEncoding; }
set { socketEncoding = value; }
}
#endregion
#region Constructor - AsteriskFastAGI()
/// <summary>
/// Creates a new AsteriskFastAGI.
/// </summary>
public AsteriskFastAGI()
{
address = Common.AGI_BIND_ADDRESS;
port = Common.AGI_BIND_PORT;
poolSize = Common.AGI_POOL_SIZE;
mappingStrategy = new ResourceMappingStrategy();
}
#endregion
#region Constructor - AsteriskFastAGI()
/// <summary>
/// Creates a new AsteriskFastAGI.
/// </summary>
public AsteriskFastAGI(string mappingStrategy)
{
address = Common.AGI_BIND_ADDRESS;
port = Common.AGI_BIND_PORT;
poolSize = Common.AGI_POOL_SIZE;
this.mappingStrategy = new ResourceMappingStrategy(mappingStrategy);
}
#endregion
#region Constructor - AsteriskFastAGI()
/// <summary>
/// Creates a new AsteriskFastAGI.
/// </summary>
public AsteriskFastAGI(IMappingStrategy mappingStrategy)
{
address = Common.AGI_BIND_ADDRESS;
port = Common.AGI_BIND_PORT;
poolSize = Common.AGI_POOL_SIZE;
this.mappingStrategy = mappingStrategy;
}
public AsteriskFastAGI(IMappingStrategy mappingStrategy, string ipaddress, int port, int poolSize)
{
address = ipaddress;
this.port = port;
this.poolSize = poolSize;
this.mappingStrategy = mappingStrategy;
}
#endregion
#region Constructor - AsteriskFastAGI(int port, int poolSize)
/// <summary>
/// Creates a new AsteriskFastAGI.
/// </summary>
/// <param name="port">The port to listen on.</param>
/// <param name="poolSize">
/// The number of worker threads in the thread pool.
/// This equals the maximum number of concurrent requests this AGIServer can serve.
/// </param>
public AsteriskFastAGI(int port, int poolSize)
{
address = Common.AGI_BIND_ADDRESS;
this.port = port;
this.poolSize = poolSize;
mappingStrategy = new ResourceMappingStrategy();
}
#endregion
#region Constructor - AsteriskFastAGI(string address, int port, int poolSize)
/// <summary>
/// Creates a new AsteriskFastAGI.
/// </summary>
/// <param name="ipaddress">The address to listen on.</param>
/// <param name="port">The port to listen on.</param>
/// <param name="poolSize">
/// The number of worker threads in the thread pool.
/// This equals the maximum number of concurrent requests this AGIServer can serve.
/// </param>
public AsteriskFastAGI(string ipaddress, int port, int poolSize)
{
address = ipaddress;
this.port = port;
this.poolSize = poolSize;
mappingStrategy = new ResourceMappingStrategy();
}
#endregion
public AsteriskFastAGI(string ipaddress = Common.AGI_BIND_ADDRESS,
int port = Common.AGI_BIND_PORT,
int poolSize = Common.AGI_POOL_SIZE,
bool sc511_CausesException = false,
bool scHangUp_CausesException = false)
{
address = ipaddress;
this.port = port;
this.poolSize = poolSize;
mappingStrategy = new ResourceMappingStrategy();
SC511_CAUSES_EXCEPTION = sc511_CausesException;
SCHANGUP_CAUSES_EXCEPTION = scHangUp_CausesException;
}
#region Start()
public void Start()
{
stopped = false;
mappingStrategy.Load();
pool = new ThreadPool("AGIServer", poolSize);
#if LOGGER
logger.Info("Thread pool started.");
#endif
try
{
var ipAddress = IPAddress.Parse(address);
serverSocket = new ServerSocket(port, ipAddress, SocketEncoding);
}
catch (Exception ex)
{
#if LOGGER
if (ex is IOException)
{
logger.Error("Unable start AGI Server: cannot to bind to " + address + ":" + port + ".", ex);
}
#endif
if (serverSocket != null)
{
serverSocket.Close();
serverSocket = null;
}
pool.Shutdown();
#if LOGGER
logger.Info("AGI Server shut down.");
#endif
throw ex;
}
#if LOGGER
logger.Info("Listening on " + address + ":" + port + ".");
#endif
try
{
SocketConnection socket;
while ((socket = serverSocket.Accept()) != null)
{
#if LOGGER
logger.Info("Received connection.");
#endif
var connectionHandler = new AGIConnectionHandler(socket, mappingStrategy, SC511_CAUSES_EXCEPTION,
SCHANGUP_CAUSES_EXCEPTION);
pool.AddJob(connectionHandler);
}
}
catch (IOException ex)
{
if (!stopped)
{
#if LOGGER
logger.Error("IOException while waiting for connections (1).", ex);
#endif
throw ex;
}
}
finally
{
if (serverSocket != null)
{
try
{
serverSocket.Close();
}
#if LOGGER
catch (IOException ex)
{
logger.Error("IOException while waiting for connections (2).", ex);
}
#else
catch { }
#endif
}
serverSocket = null;
pool.Shutdown();
#if LOGGER
logger.Info("AGI Server shut down.");
#endif
}
}
#endregion
#region Stop()
public void Stop()
{
stopped = true;
if (serverSocket != null)
serverSocket.Close();
}
#endregion
}
}