2014-01-08 14:16:39 +00:00
using AsterNET.FastAGI.MappingStrategies ;
2013-01-18 15:55:50 +00:00
using System.IO ;
using System.Net ;
using System.Text ;
2014-01-08 14:16:39 +00:00
namespace AsterNET.FastAGI
2013-01-18 15:55:50 +00:00
{
public class AsteriskFastAGI
2013-08-21 10:31:26 +00:00
{
#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
2013-01-18 15:55:50 +00:00
#if LOGGER
2013-08-21 10:31:26 +00:00
private Logger logger = Logger . Instance ( ) ;
2013-01-18 15:55:50 +00:00
#endif
private IO . ServerSocket serverSocket ;
/// <summary> The port to listen on.</summary>
private int port ;
/// <summary> The address to listen on.</summary>
private string address ;
/// <summary>The thread pool that contains the worker threads to process incoming requests.</summary>
private Util . 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>
2013-08-21 10:31:26 +00:00
private IMappingStrategy mappingStrategy ;
2013-01-18 15:55:50 +00:00
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 { this . 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
{
this . 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" />
2013-08-21 10:31:26 +00:00
public IMappingStrategy MappingStrategy
2013-01-18 15:55:50 +00:00
{
set { this . mappingStrategy = value ; }
}
#endregion
#region SocketEncoding
public Encoding SocketEncoding
{
get { return this . socketEncoding ; }
set { this . socketEncoding = value ; }
}
#endregion
#region Constructor - AsteriskFastAGI ( )
/// <summary>
/// Creates a new AsteriskFastAGI.
/// </summary>
public AsteriskFastAGI ( )
{
this . address = Common . AGI_BIND_ADDRESS ;
this . port = Common . AGI_BIND_PORT ;
this . poolSize = Common . AGI_POOL_SIZE ;
2013-08-21 10:31:26 +00:00
this . mappingStrategy = new ResourceMappingStrategy ( ) ;
2013-01-18 15:55:50 +00:00
}
#endregion
#region Constructor - AsteriskFastAGI ( )
/// <summary>
/// Creates a new AsteriskFastAGI.
/// </summary>
public AsteriskFastAGI ( string mappingStrategy )
{
this . address = Common . AGI_BIND_ADDRESS ;
this . port = Common . AGI_BIND_PORT ;
this . poolSize = Common . AGI_POOL_SIZE ;
2013-08-21 10:31:26 +00:00
this . mappingStrategy = new ResourceMappingStrategy ( mappingStrategy ) ;
2013-01-18 15:55:50 +00:00
}
#endregion
2013-08-21 10:31:26 +00:00
#region Constructor - AsteriskFastAGI ( )
/// <summary>
/// Creates a new AsteriskFastAGI.
/// </summary>
public AsteriskFastAGI ( IMappingStrategy mappingStrategy )
{
this . address = Common . AGI_BIND_ADDRESS ;
this . port = Common . AGI_BIND_PORT ;
this . poolSize = Common . AGI_POOL_SIZE ;
this . mappingStrategy = mappingStrategy ;
}
public AsteriskFastAGI ( IMappingStrategy mappingStrategy , string ipaddress , int port , int poolSize )
{
this . address = ipaddress ;
this . port = port ;
this . poolSize = poolSize ;
this . mappingStrategy = mappingStrategy ;
}
#endregion
2013-01-18 15:55:50 +00:00
#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 )
{
this . address = Common . AGI_BIND_ADDRESS ;
this . port = port ;
this . poolSize = poolSize ;
2013-08-21 10:31:26 +00:00
this . mappingStrategy = new ResourceMappingStrategy ( ) ;
2013-01-18 15:55:50 +00:00
}
#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 )
{
this . address = ipaddress ;
this . port = port ;
this . poolSize = poolSize ;
2013-08-21 10:31:26 +00:00
this . mappingStrategy = new ResourceMappingStrategy ( ) ;
2013-01-18 15:55:50 +00:00
}
#endregion
2014-03-05 16:42:56 +00:00
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 )
{
this . address = ipaddress ;
this . port = port ;
this . poolSize = poolSize ;
this . mappingStrategy = new ResourceMappingStrategy ( ) ;
SC511_CAUSES_EXCEPTION = sc511_CausesException ;
SCHANGUP_CAUSES_EXCEPTION = scHangUp_CausesException ;
}
2013-01-18 15:55:50 +00:00
#region Start ( )
public void Start ( )
{
stopped = false ;
mappingStrategy . Load ( ) ;
pool = new Util . ThreadPool ( "AGIServer" , poolSize ) ;
#if LOGGER
logger . Info ( "Thread pool started." ) ;
#endif
try
{
IPAddress ipAddress = IPAddress . Parse ( address ) ;
serverSocket = new IO . ServerSocket ( port , ipAddress , this . SocketEncoding ) ;
}
catch ( IOException ex )
{
#if LOGGER
logger . Error ( "Unable start AGI Server: cannot to bind to " + address + ":" + port + "." , ex ) ;
#endif
throw ex ;
}
#if LOGGER
logger . Info ( "Listening on " + address + ":" + port + "." ) ;
#endif
AGIConnectionHandler connectionHandler ;
IO . SocketConnection socket ;
try
{
while ( ( socket = serverSocket . Accept ( ) ) ! = null )
{
#if LOGGER
logger . Info ( "Received connection." ) ;
#endif
2013-08-21 10:31:26 +00:00
connectionHandler = new AGIConnectionHandler ( socket , mappingStrategy , this . SC511_CAUSES_EXCEPTION , this . SCHANGUP_CAUSES_EXCEPTION ) ;
2013-01-18 15:55:50 +00:00
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 ( "AGIServer shut down." ) ;
#endif
}
}
#endregion
#region Stop ( )
public void Stop ( )
{
stopped = true ;
if ( serverSocket ! = null )
serverSocket . Close ( ) ;
}
#endregion
}
}