using AsterNET.FastAGI.MappingStrategies; using System.IO; using System.Net; using System.Text; namespace AsterNET.FastAGI { public class AsteriskFastAGI { #region Flags /// /// 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 /// public bool SC511_CAUSES_EXCEPTION = false; /// /// 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 /// public bool SCHANGUP_CAUSES_EXCEPTION = false; #endregion #region Variables #if LOGGER private Logger logger = Logger.Instance(); #endif private IO.ServerSocket serverSocket; /// The port to listen on. private int port; /// The address to listen on. private string address; /// The thread pool that contains the worker threads to process incoming requests. private Util.ThreadPool pool; /// The number of worker threads in the thread pool. This equals the maximum number of concurrent requests this AGIServer can serve. private int poolSize; /// True while this server is shut down. private bool stopped; /// /// The strategy to use for bind AGIRequests to AGIScripts that serve them. /// private IMappingStrategy mappingStrategy; private Encoding socketEncoding = Encoding.ASCII; #endregion #region PoolSize /// /// Sets the number of worker threads in the thread pool.
/// This equals the maximum number of concurrent requests this AGIServer can serve.
/// The default pool size is 10. ///
public int PoolSize { set { this.poolSize = value; } } #endregion #region BindPort /// /// Sets the TCP port to listen on for new connections.
/// The default bind port is 4573. ///
public int BindPort { set { this.port = value; } } #endregion #region MappingStrategy /// /// Sets the strategy to use for mapping AGIRequests to AGIScripts that serve them.
/// The default mapping is a MappingStrategy. ///
/// public IMappingStrategy MappingStrategy { set { this.mappingStrategy = value; } } #endregion #region SocketEncoding public Encoding SocketEncoding { get { return this.socketEncoding; } set { this.socketEncoding = value; } } #endregion #region Constructor - AsteriskFastAGI() /// /// Creates a new AsteriskFastAGI. /// public AsteriskFastAGI() { this.address = Common.AGI_BIND_ADDRESS; this.port = Common.AGI_BIND_PORT; this.poolSize = Common.AGI_POOL_SIZE; this.mappingStrategy = new ResourceMappingStrategy(); } #endregion #region Constructor - AsteriskFastAGI() /// /// Creates a new AsteriskFastAGI. /// public AsteriskFastAGI(string mappingStrategy) { this.address = Common.AGI_BIND_ADDRESS; this.port = Common.AGI_BIND_PORT; this.poolSize = Common.AGI_POOL_SIZE; this.mappingStrategy = new ResourceMappingStrategy(mappingStrategy); } #endregion #region Constructor - AsteriskFastAGI() /// /// Creates a new AsteriskFastAGI. /// 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 #region Constructor - AsteriskFastAGI(int port, int poolSize) /// /// Creates a new AsteriskFastAGI. /// /// The port to listen on. /// The number of worker threads in the thread pool. /// This equals the maximum number of concurrent requests this AGIServer can serve. public AsteriskFastAGI(int port, int poolSize) { this.address = Common.AGI_BIND_ADDRESS; this.port = port; this.poolSize = poolSize; this.mappingStrategy = new ResourceMappingStrategy(); } #endregion #region Constructor - AsteriskFastAGI(string address, int port, int poolSize) /// /// Creates a new AsteriskFastAGI. /// /// The address to listen on. /// The port to listen on. /// The number of worker threads in the thread pool. /// This equals the maximum number of concurrent requests this AGIServer can serve. public AsteriskFastAGI(string ipaddress, int port, int poolSize) { this.address = ipaddress; this.port = port; this.poolSize = poolSize; this.mappingStrategy = new ResourceMappingStrategy(); } #endregion #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 connectionHandler = new AGIConnectionHandler(socket, mappingStrategy, this.SC511_CAUSES_EXCEPTION, this.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("AGIServer shut down."); #endif } } #endregion #region Stop() public void Stop() { stopped = true; if (serverSocket != null) serverSocket.Close(); } #endregion } }