using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Security; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading; using System.Threading.Tasks; using SuperSocket.Common; using SuperSocket.SocketBase.Command; using SuperSocket.SocketBase.Config; using SuperSocket.SocketBase.Logging; using SuperSocket.SocketBase.Metadata; using SuperSocket.SocketBase.Protocol; using SuperSocket.SocketBase.Provider; using SuperSocket.SocketBase.Security; namespace SuperSocket.SocketBase { /// /// AppServer base class /// /// The type of the app session. /// The type of the request info. [AppServerMetadataType(typeof(DefaultAppServerMetadata))] public abstract partial class AppServerBase : IAppServer, IRawDataProcessor, IRequestHandler, ISocketServerAccessor, IStatusInfoSource, IRemoteCertificateValidator, IActiveConnector, ISystemEndPoint, IDisposable where TRequestInfo : class, IRequestInfo where TAppSession : AppSession, IAppSession, new() { /// /// Null appSession instance /// protected readonly TAppSession NullAppSession = default(TAppSession); /// /// Gets the server's config. /// public IServerConfig Config { get; private set; } //Server instance name private string m_Name; /// /// the current state's code /// private int m_StateCode = ServerStateConst.NotInitialized; /// /// Gets the current state of the work item. /// /// /// The state. /// public ServerState State { get { return (ServerState)m_StateCode; } } /// /// Gets the certificate of current server. /// public X509Certificate Certificate { get; private set; } /// /// Gets or sets the receive filter factory. /// /// /// The receive filter factory. /// public virtual IReceiveFilterFactory ReceiveFilterFactory { get; protected set; } /// /// Gets the Receive filter factory. /// object IAppServer.ReceiveFilterFactory { get { return this.ReceiveFilterFactory; } } private List>> m_CommandLoaders = new List>>(); private Dictionary>> m_CommandContainer; private CommandFilterAttribute[] m_GlobalCommandFilters; private ISocketServerFactory m_SocketServerFactory; /// /// Gets the basic transfer layer security protocol. /// public SslProtocols BasicSecurity { get; private set; } /// /// Gets the root config. /// protected IRootConfig RootConfig { get; private set; } /// /// Gets the logger assosiated with this object. /// public ILog Logger { get; private set; } /// /// Gets the bootstrap of this appServer instance. /// protected IBootstrap Bootstrap { get; private set; } private static bool m_ThreadPoolConfigured = false; private List m_ConnectionFilters; private long m_TotalHandledRequests = 0; /// /// Gets the total handled requests number. /// protected long TotalHandledRequests { get { return m_TotalHandledRequests; } } private ListenerInfo[] m_Listeners; /// /// Gets or sets the listeners inforamtion. /// /// /// The listeners. /// public ListenerInfo[] Listeners { get { return m_Listeners; } } /// /// Gets the started time of this server instance. /// /// /// The started time. /// public DateTime StartedTime { get; private set; } /// /// Gets or sets the log factory. /// /// /// The log factory. /// public ILogFactory LogFactory { get; private set; } /// /// Gets the default text encoding. /// /// /// The text encoding. /// public Encoding TextEncoding { get; private set; } /// /// Initializes a new instance of the class. /// public AppServerBase() { } /// /// Initializes a new instance of the class. /// /// The Receive filter factory. public AppServerBase(IReceiveFilterFactory receiveFilterFactory) { this.ReceiveFilterFactory = receiveFilterFactory; } /// /// Gets the filter attributes. /// /// The type. /// internal static CommandFilterAttribute[] GetCommandFilterAttributes(Type type) { var attrs = type.GetCustomAttributes(true); return attrs.OfType().ToArray(); } /// /// Setups the command into command dictionary /// /// The discovered commands. /// protected virtual bool SetupCommands(Dictionary> discoveredCommands) { foreach (var loader in m_CommandLoaders) { loader.Error += new EventHandler(CommandLoaderOnError); loader.Updated += new EventHandler>>(CommandLoaderOnCommandsUpdated); if (!loader.Initialize(RootConfig, this)) { if (Logger.IsErrorEnabled) Logger.ErrorFormat("Failed initialize the command loader {0}.", loader.ToString()); return false; } IEnumerable> commands; if (!loader.TryLoadCommands(out commands)) { if (Logger.IsErrorEnabled) Logger.ErrorFormat("Failed load commands from the command loader {0}.", loader.ToString()); return false; } if (commands != null && commands.Any()) { foreach (var c in commands) { if (discoveredCommands.ContainsKey(c.Name)) { if (Logger.IsErrorEnabled) Logger.Error("Duplicated name command has been found! Command name: " + c.Name); return false; } var castedCommand = c as ICommand; if (castedCommand == null) { if (Logger.IsErrorEnabled) Logger.Error("Invalid command has been found! Command name: " + c.Name); return false; } if (Logger.IsDebugEnabled) Logger.DebugFormat("The command {0}({1}) has been discovered", castedCommand.Name, castedCommand.ToString()); discoveredCommands.Add(c.Name, castedCommand); } } } return true; } void CommandLoaderOnCommandsUpdated(object sender, CommandUpdateEventArgs> e) { var workingDict = m_CommandContainer.Values.ToDictionary(c => c.Command.Name, c => c.Command, StringComparer.OrdinalIgnoreCase); var updatedCommands = 0; foreach (var c in e.Commands) { if (c == null) continue; if (c.UpdateAction == CommandUpdateAction.Remove) { workingDict.Remove(c.Command.Name); if (Logger.IsInfoEnabled) Logger.InfoFormat("The command '{0}' has been removed from this server!", c.Command.Name); } else if (c.UpdateAction == CommandUpdateAction.Add) { workingDict.Add(c.Command.Name, c.Command); if (Logger.IsInfoEnabled) Logger.InfoFormat("The command '{0}' has been added into this server!", c.Command.Name); } else { workingDict[c.Command.Name] = c.Command; if (Logger.IsInfoEnabled) Logger.InfoFormat("The command '{0}' has been updated!", c.Command.Name); } updatedCommands++; } if (updatedCommands > 0) { OnCommandSetup(workingDict); } } void CommandLoaderOnError(object sender, ErrorEventArgs e) { if (!Logger.IsErrorEnabled) return; Logger.Error(e.Exception); } /// /// Setups the specified root config. /// /// The root config. /// The config. /// protected virtual bool Setup(IRootConfig rootConfig, IServerConfig config) { return true; } partial void SetDefaultCulture(IRootConfig rootConfig, IServerConfig config); private void SetupBasic(IRootConfig rootConfig, IServerConfig config, ISocketServerFactory socketServerFactory) { if (rootConfig == null) throw new ArgumentNullException("rootConfig"); RootConfig = rootConfig; if (config == null) throw new ArgumentNullException("config"); if (!string.IsNullOrEmpty(config.Name)) m_Name = config.Name; else m_Name = string.Format("{0}-{1}", this.GetType().Name, Math.Abs(this.GetHashCode())); Config = config; SetDefaultCulture(rootConfig, config); if (!m_ThreadPoolConfigured) { if (!TheadPoolEx.ResetThreadPool(rootConfig.MaxWorkingThreads >= 0 ? rootConfig.MaxWorkingThreads : new Nullable(), rootConfig.MaxCompletionPortThreads >= 0 ? rootConfig.MaxCompletionPortThreads : new Nullable(), rootConfig.MinWorkingThreads >= 0 ? rootConfig.MinWorkingThreads : new Nullable(), rootConfig.MinCompletionPortThreads >= 0 ? rootConfig.MinCompletionPortThreads : new Nullable())) { throw new Exception("Failed to configure thread pool!"); } m_ThreadPoolConfigured = true; } if (socketServerFactory == null) { var socketServerFactoryType = Type.GetType("SuperSocket.SocketEngine.SocketServerFactory, SuperSocket.SocketEngine", true); socketServerFactory = (ISocketServerFactory)Activator.CreateInstance(socketServerFactoryType); } m_SocketServerFactory = socketServerFactory; //Read text encoding from the configuration if (!string.IsNullOrEmpty(config.TextEncoding)) TextEncoding = Encoding.GetEncoding(config.TextEncoding); else TextEncoding = new ASCIIEncoding(); } private bool SetupMedium(IReceiveFilterFactory receiveFilterFactory, IEnumerable connectionFilters, IEnumerable>> commandLoaders) { if (receiveFilterFactory != null) ReceiveFilterFactory = receiveFilterFactory; if (connectionFilters != null && connectionFilters.Any()) { if (m_ConnectionFilters == null) m_ConnectionFilters = new List(); m_ConnectionFilters.AddRange(connectionFilters); } if (commandLoaders != null && commandLoaders.Any()) m_CommandLoaders.AddRange(commandLoaders); return SetupCommandLoaders(m_CommandLoaders); } private bool SetupAdvanced(IServerConfig config) { if (!SetupSecurity(config)) return false; if (!SetupListeners(config)) return false; m_GlobalCommandFilters = GetCommandFilterAttributes(this.GetType()); var discoveredCommands = new Dictionary>(StringComparer.OrdinalIgnoreCase); if (!SetupCommands(discoveredCommands)) return false; OnCommandSetup(discoveredCommands); return true; } private void OnCommandSetup(IDictionary> discoveredCommands) { var commandContainer = new Dictionary>>(StringComparer.OrdinalIgnoreCase); foreach (var command in discoveredCommands.Values) { commandContainer.Add(command.Name, new CommandInfo>(command, m_GlobalCommandFilters)); } Interlocked.Exchange(ref m_CommandContainer, commandContainer); } internal abstract IReceiveFilterFactory CreateDefaultReceiveFilterFactory(); private bool SetupFinal() { //Check receiveFilterFactory if (ReceiveFilterFactory == null) { ReceiveFilterFactory = CreateDefaultReceiveFilterFactory(); if (ReceiveFilterFactory == null) { if (Logger.IsErrorEnabled) Logger.Error("receiveFilterFactory is required!"); return false; } } var plainConfig = Config as ServerConfig; if (plainConfig == null) { //Using plain config model instead of .NET configuration element to improve performance plainConfig = new ServerConfig(Config); if (string.IsNullOrEmpty(plainConfig.Name)) plainConfig.Name = Name; Config = plainConfig; } try { m_ServerStatus = new StatusInfoCollection(); m_ServerStatus.Name = Name; m_ServerStatus.Tag = Name; m_ServerStatus[StatusInfoKeys.MaxConnectionNumber] = Config.MaxConnectionNumber; m_ServerStatus[StatusInfoKeys.Listeners] = m_Listeners; } catch (Exception e) { if (Logger.IsErrorEnabled) Logger.Error("Failed to create ServerSummary instance!", e); return false; } return SetupSocketServer(); } /// /// Setups with the specified port. /// /// The port. /// return setup result public bool Setup(int port) { return Setup("Any", port); } private void TrySetInitializedState() { if (Interlocked.CompareExchange(ref m_StateCode, ServerStateConst.Initializing, ServerStateConst.NotInitialized) != ServerStateConst.NotInitialized) { throw new Exception("The server has been initialized already, you cannot initialize it again!"); } } #if NET_35 /// /// Setups with the specified ip and port. /// /// The ip. /// The port. /// The providers. /// public bool Setup(string ip, int port, params object[] providers) { return Setup(new ServerConfig { Name = string.Format("{0}-{1}", this.GetType().Name, Math.Abs(this.GetHashCode())), Ip = ip, Port = port }, providers); } /// /// Setups with the specified config, used for programming setup /// /// The server config. /// The providers. /// public bool Setup(IServerConfig config, params object[] providers) { return Setup(new RootConfig(), config, providers); } /// /// Setups with the specified root config, used for programming setup /// /// The root config. /// The server config. /// The providers. /// public bool Setup(IRootConfig rootConfig, IServerConfig config, params object[] providers) { TrySetInitializedState(); SetupBasic(rootConfig, config, GetProviderInstance(providers)); if (!SetupLogFactory(GetProviderInstance(providers))) return false; Logger = CreateLogger(this.Name); if (!SetupMedium(GetProviderInstance>(providers), GetProviderInstance>(providers), GetProviderInstance>>>(providers))) return false; if (!SetupAdvanced(config)) return false; if (!Setup(rootConfig, config)) return false; if(!SetupFinal()) return false; m_StateCode = ServerStateConst.NotStarted; return true; } private T GetProviderInstance(object[] providers) { if (providers == null || !providers.Any()) return default(T); var providerType = typeof(T); return (T)providers.FirstOrDefault(p => p != null && providerType.IsAssignableFrom(p.GetType())); } #else /// /// Setups with the specified config. /// /// The server config. /// The socket server factory. /// The receive filter factory. /// The log factory. /// The connection filters. /// The command loaders. /// public bool Setup(IServerConfig config, ISocketServerFactory socketServerFactory = null, IReceiveFilterFactory receiveFilterFactory = null, ILogFactory logFactory = null, IEnumerable connectionFilters = null, IEnumerable>> commandLoaders = null) { return Setup(new RootConfig(), config, socketServerFactory, receiveFilterFactory, logFactory, connectionFilters, commandLoaders); } /// /// Setups the specified root config, this method used for programming setup /// /// The root config. /// The server config. /// The socket server factory. /// The Receive filter factory. /// The log factory. /// The connection filters. /// The command loaders. /// public bool Setup(IRootConfig rootConfig, IServerConfig config, ISocketServerFactory socketServerFactory = null, IReceiveFilterFactory receiveFilterFactory = null, ILogFactory logFactory = null, IEnumerable connectionFilters = null, IEnumerable>> commandLoaders = null) { TrySetInitializedState(); SetupBasic(rootConfig, config, socketServerFactory); if (!SetupLogFactory(logFactory)) return false; Logger = CreateLogger(this.Name); if (!SetupMedium(receiveFilterFactory, connectionFilters, commandLoaders)) return false; if (!SetupAdvanced(config)) return false; if (!Setup(rootConfig, config)) return false; if (!SetupFinal()) return false; m_StateCode = ServerStateConst.NotStarted; return true; } /// /// Setups with the specified ip and port. /// /// The ip. /// The port. /// The socket server factory. /// The Receive filter factory. /// The log factory. /// The connection filters. /// The command loaders. /// return setup result public bool Setup(string ip, int port, ISocketServerFactory socketServerFactory = null, IReceiveFilterFactory receiveFilterFactory = null, ILogFactory logFactory = null, IEnumerable connectionFilters = null, IEnumerable>> commandLoaders = null) { return Setup(new ServerConfig { Ip = ip, Port = port }, socketServerFactory, receiveFilterFactory, logFactory, connectionFilters, commandLoaders); } #endif /// /// Setups the specified root config. /// /// The bootstrap. /// The socket server instance config. /// The factories. /// bool IWorkItem.Setup(IBootstrap bootstrap, IServerConfig config, ProviderFactoryInfo[] factories) { if (bootstrap == null) throw new ArgumentNullException("bootstrap"); Bootstrap = bootstrap; if (factories == null) throw new ArgumentNullException("factories"); TrySetInitializedState(); var rootConfig = bootstrap.Config; SetupBasic(rootConfig, config, GetSingleProviderInstance(factories, ProviderKey.SocketServerFactory)); if (!SetupLogFactory(GetSingleProviderInstance(factories, ProviderKey.LogFactory))) return false; Logger = CreateLogger(this.Name); IEnumerable connectionFilters = null; if (!TryGetProviderInstances(factories, ProviderKey.ConnectionFilter, null, (p, f) => { var ret = p.Initialize(f.Name, this); if(!ret) { Logger.ErrorFormat("Failed to initialize the connection filter: {0}.", f.Name); } return ret; }, out connectionFilters)) { return false; } if (!SetupMedium( GetSingleProviderInstance>(factories, ProviderKey.ReceiveFilterFactory), connectionFilters, GetProviderInstances>>( factories, ProviderKey.CommandLoader, (t) => Activator.CreateInstance(t.MakeGenericType(typeof(ICommand)))))) { return false; } if (!SetupAdvanced(config)) return false; if (!Setup(rootConfig, config)) return false; if (!SetupFinal()) return false; m_StateCode = ServerStateConst.NotStarted; return true; } private TProvider GetSingleProviderInstance(ProviderFactoryInfo[] factories, ProviderKey key) { var factory = factories.FirstOrDefault(p => p.Key.Name == key.Name); if (factory == null) return default(TProvider); return factory.ExportFactory.CreateExport(); } private bool TryGetProviderInstances(ProviderFactoryInfo[] factories, ProviderKey key, Func creator, Func initializer, out IEnumerable providers) where TProvider : class { IEnumerable selectedFactories = factories.Where(p => p.Key.Name == key.Name); if (!selectedFactories.Any()) { providers = null; return true; } providers = new List(); var list = (List)providers; foreach (var f in selectedFactories) { var provider = creator == null ? f.ExportFactory.CreateExport() : f.ExportFactory.CreateExport(creator); if (!initializer(provider, f)) return false; list.Add(provider); } return true; } private IEnumerable GetProviderInstances(ProviderFactoryInfo[] factories, ProviderKey key) where TProvider : class { return GetProviderInstances(factories, key, null); } private IEnumerable GetProviderInstances(ProviderFactoryInfo[] factories, ProviderKey key, Func creator) where TProvider : class { IEnumerable providers; TryGetProviderInstances(factories, key, creator, (p, f) => true, out providers); return providers; } private bool SetupLogFactory(ILogFactory logFactory) { if (logFactory != null) { LogFactory = logFactory; return true; } //Log4NetLogFactory is default log factory if (LogFactory == null) LogFactory = new Log4NetLogFactory(); return true; } /// /// Setups the command loaders. /// /// The command loaders. /// protected virtual bool SetupCommandLoaders(List>> commandLoaders) { commandLoaders.Add(new ReflectCommandLoader>()); return true; } /// /// Creates the logger for the AppServer. /// /// Name of the logger. /// protected virtual ILog CreateLogger(string loggerName) { return LogFactory.GetLog(loggerName); } /// /// Setups the security option of socket communications. /// /// The config of the server instance. /// private bool SetupSecurity(IServerConfig config) { if (!string.IsNullOrEmpty(config.Security)) { SslProtocols configProtocol; if (!config.Security.TryParseEnum(true, out configProtocol)) { if (Logger.IsErrorEnabled) Logger.ErrorFormat("Failed to parse '{0}' to SslProtocol!", config.Security); return false; } BasicSecurity = configProtocol; } else { BasicSecurity = SslProtocols.None; } try { var certificate = GetCertificate(config.Certificate); if (certificate != null) { Certificate = certificate; } else if(BasicSecurity != SslProtocols.None) { if (Logger.IsErrorEnabled) Logger.Error("Certificate is required in this security mode!"); return false; } } catch (Exception e) { if (Logger.IsErrorEnabled) Logger.Error("Failed to initialize certificate!", e); return false; } return true; } /// /// Gets the certificate from server configuguration. /// /// The certificate config. /// protected virtual X509Certificate GetCertificate(ICertificateConfig certificate) { if (certificate == null) { if (BasicSecurity != SslProtocols.None && Logger.IsErrorEnabled) Logger.Error("There is no certificate configured!"); return null; } if (string.IsNullOrEmpty(certificate.FilePath) && string.IsNullOrEmpty(certificate.Thumbprint)) { if (BasicSecurity != SslProtocols.None && Logger.IsErrorEnabled) Logger.Error("You should define certificate node and either attribute 'filePath' or 'thumbprint' is required!"); return null; } return CertificateManager.Initialize(certificate, GetFilePath); } bool IRemoteCertificateValidator.Validate(IAppSession session, object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return ValidateClientCertificate((TAppSession)session, sender, certificate, chain, sslPolicyErrors); } /// /// Validates the client certificate. This method is only used if the certificate configuration attribute "clientCertificateRequired" is true. /// /// The session. /// The sender. /// The certificate. /// The chain. /// The SSL policy errors. /// return the validation result protected virtual bool ValidateClientCertificate(TAppSession session, object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { return sslPolicyErrors == SslPolicyErrors.None; } /// /// Setups the socket server.instance /// /// private bool SetupSocketServer() { try { m_SocketServer = m_SocketServerFactory.CreateSocketServer(this, m_Listeners, Config); return m_SocketServer != null; } catch (Exception e) { if (Logger.IsErrorEnabled) Logger.Error(e); return false; } } private IPAddress ParseIPAddress(string ip) { if (string.IsNullOrEmpty(ip) || "Any".Equals(ip, StringComparison.OrdinalIgnoreCase)) return IPAddress.Any; else if ("IPv6Any".Equals(ip, StringComparison.OrdinalIgnoreCase)) return IPAddress.IPv6Any; else return IPAddress.Parse(ip); } /// /// Setups the listeners base on server configuration /// /// The config. /// private bool SetupListeners(IServerConfig config) { var listeners = new List(); try { if (config.Port > 0) { listeners.Add(new ListenerInfo { EndPoint = new IPEndPoint(ParseIPAddress(config.Ip), config.Port), BackLog = config.ListenBacklog, Security = BasicSecurity }); } else { //Port is not configured, but ip is configured if (!string.IsNullOrEmpty(config.Ip)) { if (Logger.IsErrorEnabled) Logger.Error("Port is required in config!"); return false; } } //There are listener defined if (config.Listeners != null && config.Listeners.Any()) { //But ip and port were configured in server node //We don't allow this case if (listeners.Any()) { if (Logger.IsErrorEnabled) Logger.Error("If you configured Ip and Port in server node, you cannot defined listener in listeners node any more!"); return false; } foreach (var l in config.Listeners) { SslProtocols configProtocol; if (string.IsNullOrEmpty(l.Security)) { configProtocol = BasicSecurity; } else if (!l.Security.TryParseEnum(true, out configProtocol)) { if (Logger.IsErrorEnabled) Logger.ErrorFormat("Failed to parse '{0}' to SslProtocol!", config.Security); return false; } if (configProtocol != SslProtocols.None && (Certificate == null)) { if (Logger.IsErrorEnabled) Logger.Error("There is no certificate loaded, but there is a secure listener defined!"); return false; } listeners.Add(new ListenerInfo { EndPoint = new IPEndPoint(ParseIPAddress(l.Ip), l.Port), BackLog = l.Backlog, Security = configProtocol }); } } if (!listeners.Any()) { if (Logger.IsErrorEnabled) Logger.Error("No listener defined!"); return false; } m_Listeners = listeners.ToArray(); return true; } catch (Exception e) { if (Logger.IsErrorEnabled) Logger.Error(e); return false; } } /// /// Gets the name of the server instance. /// public string Name { get { return m_Name; } } private ISocketServer m_SocketServer; /// /// Gets the socket server. /// /// /// The socket server. /// ISocketServer ISocketServerAccessor.SocketServer { get { return m_SocketServer; } } /// /// Starts this server instance. /// /// /// return true if start successfull, else false /// public virtual bool Start() { var origStateCode = Interlocked.CompareExchange(ref m_StateCode, ServerStateConst.Starting, ServerStateConst.NotStarted); if (origStateCode != ServerStateConst.NotStarted) { if (origStateCode < ServerStateConst.NotStarted) throw new Exception("You cannot start a server instance which has not been setup yet."); if (Logger.IsErrorEnabled) Logger.ErrorFormat("This server instance is in the state {0}, you cannot start it now.", (ServerState)origStateCode); return false; } if (!m_SocketServer.Start()) { m_StateCode = ServerStateConst.NotStarted; return false; } StartedTime = DateTime.Now; m_StateCode = ServerStateConst.Running; m_ServerStatus[StatusInfoKeys.IsRunning] = true; m_ServerStatus[StatusInfoKeys.StartedTime] = StartedTime; try { //Will be removed in the next version #pragma warning disable 0612, 618 OnStartup(); #pragma warning restore 0612, 618 OnStarted(); } catch (Exception e) { if (Logger.IsErrorEnabled) { Logger.Error("One exception wa thrown in the method 'OnStartup()'.", e); } } finally { if (Logger.IsInfoEnabled) Logger.Info(string.Format("The server instance {0} has been started!", Name)); } return true; } /// /// Called when [startup]. /// [Obsolete("Use OnStarted() instead")] protected virtual void OnStartup() { } /// /// Called when [started]. /// protected virtual void OnStarted() { } /// /// Called when [stopped]. /// protected virtual void OnStopped() { } /// /// Stops this server instance. /// public virtual void Stop() { if (Interlocked.CompareExchange(ref m_StateCode, ServerStateConst.Stopping, ServerStateConst.Running) != ServerStateConst.Running) { return; } m_SocketServer.Stop(); m_StateCode = ServerStateConst.NotStarted; OnStopped(); m_ServerStatus[StatusInfoKeys.IsRunning] = false; m_ServerStatus[StatusInfoKeys.StartedTime] = null; if (Logger.IsInfoEnabled) Logger.Info(string.Format("The server instance {0} has been stopped!", Name)); } /// /// Gets command by command name. /// /// Name of the command. /// private CommandInfo> GetCommandByName(string commandName) { CommandInfo> commandProxy; if (m_CommandContainer.TryGetValue(commandName, out commandProxy)) return commandProxy; else return null; } private Func m_RawDataReceivedHandler; /// /// Gets or sets the raw binary data received event handler. /// TAppSession: session /// byte[]: receive buffer /// int: receive buffer offset /// int: receive lenght /// bool: whether process the received data further /// event Func IRawDataProcessor.RawDataReceived { add { m_RawDataReceivedHandler += value; } remove { m_RawDataReceivedHandler -= value; } } /// /// Called when [raw data received]. /// /// The session. /// The buffer. /// The offset. /// The length. internal bool OnRawDataReceived(IAppSession session, byte[] buffer, int offset, int length) { var handler = m_RawDataReceivedHandler; if (handler == null) return true; return handler((TAppSession)session, buffer, offset, length); } private RequestHandler m_RequestHandler; /// /// Occurs when a full request item received. /// public virtual event RequestHandler NewRequestReceived { add { m_RequestHandler += value; } remove { m_RequestHandler -= value; } } /// /// Executes the command. /// /// The session. /// The request info. protected virtual void ExecuteCommand(TAppSession session, TRequestInfo requestInfo) { if (m_RequestHandler == null) { var commandProxy = GetCommandByName(requestInfo.Key); if (commandProxy != null) { var command = commandProxy.Command; var commandFilters = commandProxy.Filters; session.CurrentCommand = requestInfo.Key; var cancelled = false; if (commandFilters == null) { command.ExecuteCommand(session, requestInfo); } else { var commandContext = new CommandExecutingContext(); commandContext.Initialize(session, requestInfo, command); for (var i = 0; i < commandFilters.Length; i++) { var filter = commandFilters[i]; filter.OnCommandExecuting(commandContext); if (commandContext.Cancel) { cancelled = true; if(Logger.IsInfoEnabled) Logger.Info(session, string.Format("The executing of the command {0} was cancelled by the command filter {1}.", command.Name, filter.GetType().ToString())); break; } } if (!cancelled) { try { command.ExecuteCommand(session, requestInfo); } catch (Exception exc) { commandContext.Exception = exc; } for (var i = 0; i < commandFilters.Length; i++) { var filter = commandFilters[i]; filter.OnCommandExecuted(commandContext); } if (commandContext.Exception != null && !commandContext.ExceptionHandled) { try { session.InternalHandleExcetion(commandContext.Exception); } catch { } } } } if(!cancelled) { session.PrevCommand = requestInfo.Key; if (Config.LogCommand && Logger.IsInfoEnabled) Logger.Info(session, string.Format("Command - {0}", requestInfo.Key)); } } else { session.InternalHandleUnknownRequest(requestInfo); } session.LastActiveTime = DateTime.Now; } else { session.CurrentCommand = requestInfo.Key; try { m_RequestHandler(session, requestInfo); } catch (Exception e) { session.InternalHandleExcetion(e); } session.PrevCommand = requestInfo.Key; session.LastActiveTime = DateTime.Now; if (Config.LogCommand && Logger.IsInfoEnabled) Logger.Info(session, string.Format("Command - {0}", requestInfo.Key)); } Interlocked.Increment(ref m_TotalHandledRequests); } /// /// Executes the command for the session. /// /// The session. /// The request info. internal void ExecuteCommand(IAppSession session, TRequestInfo requestInfo) { this.ExecuteCommand((TAppSession)session, requestInfo); } /// /// Executes the command. /// /// The session. /// The request info. void IRequestHandler.ExecuteCommand(IAppSession session, TRequestInfo requestInfo) { this.ExecuteCommand((TAppSession)session, requestInfo); } /// /// Gets or sets the server's connection filter /// /// /// The server's connection filters /// public IEnumerable ConnectionFilters { get { return m_ConnectionFilters; } } /// /// Executes the connection filters. /// /// The remote address. /// private bool ExecuteConnectionFilters(IPEndPoint remoteAddress) { if (m_ConnectionFilters == null) return true; for (var i = 0; i < m_ConnectionFilters.Count; i++) { var currentFilter = m_ConnectionFilters[i]; if (!currentFilter.AllowConnect(remoteAddress)) { if (Logger.IsInfoEnabled) Logger.InfoFormat("A connection from {0} has been refused by filter {1}!", remoteAddress, currentFilter.Name); return false; } } return true; } /// /// Creates the app session. /// /// The socket session. /// IAppSession IAppServer.CreateAppSession(ISocketSession socketSession) { if (!ExecuteConnectionFilters(socketSession.RemoteEndPoint)) return NullAppSession; var appSession = CreateAppSession(socketSession); appSession.Initialize(this, socketSession); return appSession; } /// /// create a new TAppSession instance, you can override it to create the session instance in your own way /// /// the socket session. /// the new created session instance protected virtual TAppSession CreateAppSession(ISocketSession socketSession) { return new TAppSession(); } /// /// Registers the new created app session into the appserver's session container. /// /// The session. /// bool IAppServer.RegisterSession(IAppSession session) { var appSession = session as TAppSession; if (!RegisterSession(appSession.SessionID, appSession)) return false; appSession.SocketSession.Closed += OnSocketSessionClosed; if (Config.LogBasicSessionActivity && Logger.IsInfoEnabled) Logger.Info(session, "A new session connected! "); OnNewSessionConnected(appSession); return true; } /// /// Registers the session into session container. /// /// The session ID. /// The app session. /// protected virtual bool RegisterSession(string sessionID, TAppSession appSession) { return true; } private SessionHandler m_NewSessionConnected; /// /// The action which will be executed after a new session connect /// public event SessionHandler NewSessionConnected { add { m_NewSessionConnected += value; } remove { m_NewSessionConnected -= value; } } /// /// Called when [new session connected]. /// /// The session. protected virtual void OnNewSessionConnected(TAppSession session) { var handler = m_NewSessionConnected; if (handler == null) return; handler.BeginInvoke(session, OnNewSessionConnectedCallback, handler); } private void OnNewSessionConnectedCallback(IAsyncResult result) { try { var handler = (SessionHandler)result.AsyncState; handler.EndInvoke(result); } catch (Exception e) { Logger.Error(e); } } /// /// Resets the session's security protocol. /// /// The session. /// The security protocol. public void ResetSessionSecurity(IAppSession session, SslProtocols security) { m_SocketServer.ResetSessionSecurity(session, security); } /// /// Called when [socket session closed]. /// /// The socket session. /// The reason. private void OnSocketSessionClosed(ISocketSession session, CloseReason reason) { //Even if LogBasicSessionActivity is false, we also log the unexpected closing because the close reason probably be useful if (Logger.IsInfoEnabled && (Config.LogBasicSessionActivity || (reason != CloseReason.ServerClosing && reason != CloseReason.ClientClosing && reason != CloseReason.ServerShutdown && reason != CloseReason.SocketError))) Logger.Info(session, string.Format("This session was closed for {0}!", reason)); var appSession = session.AppSession as TAppSession; appSession.Connected = false; OnSessionClosed(appSession, reason); } private SessionHandler m_SessionClosed; /// /// Gets/sets the session closed event handler. /// public event SessionHandler SessionClosed { add { m_SessionClosed += value; } remove { m_SessionClosed -= value; } } /// /// Called when [session closed]. /// /// The appSession. /// The reason. protected virtual void OnSessionClosed(TAppSession session, CloseReason reason) { var handler = m_SessionClosed; if (handler != null) { handler.BeginInvoke(session, reason, OnSessionClosedCallback, handler); } session.OnSessionClosed(reason); } private void OnSessionClosedCallback(IAsyncResult result) { try { var handler = (SessionHandler)result.AsyncState; handler.EndInvoke(result); } catch (Exception e) { Logger.Error(e); } } /// /// Gets the app session by ID. /// /// The session ID. /// public abstract TAppSession GetSessionByID(string sessionID); /// /// Gets the app session by ID. /// /// /// IAppSession IAppServer.GetSessionByID(string sessionID) { return this.GetSessionByID(sessionID); } /// /// Gets the matched sessions from sessions snapshot. /// /// The prediction critera. public virtual IEnumerable GetSessions(Func critera) { throw new NotSupportedException(); } /// /// Gets all sessions in sessions snapshot. /// public virtual IEnumerable GetAllSessions() { throw new NotSupportedException(); } /// /// Gets the total session count. /// public abstract int SessionCount { get; } /// /// Gets the physical file path by the relative file path, /// search both in the appserver's root and in the supersocket root dir if the isolation level has been set other than 'None'. /// /// The relative file path. /// public string GetFilePath(string relativeFilePath) { var filePath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, relativeFilePath); if (!System.IO.File.Exists(filePath) && RootConfig != null && RootConfig.Isolation != IsolationMode.None) { var rootDir = System.IO.Directory.GetParent(AppDomain.CurrentDomain.BaseDirectory).Parent.FullName; var rootFilePath = System.IO.Path.Combine(rootDir, relativeFilePath); if (System.IO.File.Exists(rootFilePath)) return rootFilePath; } return filePath; } #region IActiveConnector /// /// Connect the remote endpoint actively. /// /// The target end point. /// The local end point. /// /// This server cannot support active connect. Task IActiveConnector.ActiveConnect(EndPoint targetEndPoint, EndPoint localEndPoint) { var activeConnector = m_SocketServer as IActiveConnector; if (activeConnector == null) throw new Exception("This server cannot support active connect."); return activeConnector.ActiveConnect(targetEndPoint, localEndPoint); } /// /// Connect the remote endpoint actively. /// /// The target end point. /// /// This server cannot support active connect. Task IActiveConnector.ActiveConnect(EndPoint targetEndPoint) { return ((IActiveConnector)this).ActiveConnect(targetEndPoint, null); } #endregion IActiveConnector #region ISystemEndPoint /// /// Transfers the system message /// /// Type of the message. /// The message data. void ISystemEndPoint.TransferSystemMessage(string messageType, object messageData) { OnSystemMessageReceived(messageType, messageData); } /// /// Called when [system message received]. /// /// Type of the message. /// The message data. protected virtual void OnSystemMessageReceived(string messageType, object messageData) { } #endregion ISystemEndPoint #region IStatusInfoSource private StatusInfoCollection m_ServerStatus; StatusInfoAttribute[] IStatusInfoSource.GetServerStatusMetadata() { return this.GetType().GetStatusInfoMetadata(); } StatusInfoCollection IStatusInfoSource.CollectServerStatus(StatusInfoCollection bootstrapStatus) { UpdateServerStatus(m_ServerStatus); this.AsyncRun(() => OnServerStatusCollected(bootstrapStatus, m_ServerStatus), e => Logger.Error(e)); return m_ServerStatus; } /// /// Updates the summary of the server. /// /// The server status. protected virtual void UpdateServerStatus(StatusInfoCollection serverStatus) { DateTime now = DateTime.Now; serverStatus[StatusInfoKeys.IsRunning] = m_StateCode == ServerStateConst.Running; serverStatus[StatusInfoKeys.TotalConnections] = this.SessionCount; var totalHandledRequests0 = serverStatus.GetValue(StatusInfoKeys.TotalHandledRequests, 0); var totalHandledRequests = this.TotalHandledRequests; serverStatus[StatusInfoKeys.RequestHandlingSpeed] = ((totalHandledRequests - totalHandledRequests0) / now.Subtract(serverStatus.CollectedTime).TotalSeconds); serverStatus[StatusInfoKeys.TotalHandledRequests] = totalHandledRequests; if (State == ServerState.Running) { var sendingQueuePool = m_SocketServer.SendingQueuePool; serverStatus[StatusInfoKeys.AvialableSendingQueueItems] = sendingQueuePool.AvialableItemsCount; serverStatus[StatusInfoKeys.TotalSendingQueueItems] = sendingQueuePool.TotalItemsCount; } else { serverStatus[StatusInfoKeys.AvialableSendingQueueItems] = 0; serverStatus[StatusInfoKeys.TotalSendingQueueItems] = 0; } serverStatus.CollectedTime = now; } /// /// Called when [server status collected]. /// /// The bootstrapStatus status. /// The server status. protected virtual void OnServerStatusCollected(StatusInfoCollection bootstrapStatus, StatusInfoCollection serverStatus) { } #endregion IStatusInfoSource #region IDisposable Members /// /// Releases unmanaged and - optionally - managed resources /// public void Dispose() { if (m_StateCode == ServerStateConst.Running) Stop(); } #endregion } }