2020-08-02 20:12:45 +00:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* (c) Copyright Ascensio System Limited 2010-2018
|
|
|
|
*
|
|
|
|
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
|
|
|
|
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
|
|
|
|
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
|
|
|
|
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
|
|
|
|
*
|
|
|
|
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
|
|
|
|
*
|
|
|
|
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
|
|
|
|
*
|
|
|
|
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
|
|
|
|
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
|
|
|
|
*
|
|
|
|
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
|
|
|
|
* relevant author attributions when distributing the software. If the display of the logo in its graphic
|
|
|
|
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
|
|
|
|
* in every copy of the program you distribute.
|
|
|
|
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
using System;
|
2022-01-11 16:09:44 +00:00
|
|
|
using System.Collections.Generic;
|
2020-08-02 20:12:45 +00:00
|
|
|
using System.Diagnostics;
|
|
|
|
using System.IO;
|
|
|
|
using System.Threading;
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
2020-08-27 14:01:37 +00:00
|
|
|
using ASC.Common;
|
2020-08-02 20:12:45 +00:00
|
|
|
using ASC.Common.Logging;
|
2020-08-27 14:01:37 +00:00
|
|
|
using ASC.Common.Utils;
|
2020-08-02 20:12:45 +00:00
|
|
|
using ASC.Core;
|
2021-06-03 13:13:22 +00:00
|
|
|
using ASC.Core.Notify.Signalr;
|
|
|
|
|
2020-08-02 20:12:45 +00:00
|
|
|
using Microsoft.Extensions.Configuration;
|
|
|
|
using Microsoft.Extensions.Hosting;
|
2021-06-03 13:13:22 +00:00
|
|
|
using Microsoft.Extensions.Options;
|
|
|
|
|
2022-01-11 16:09:44 +00:00
|
|
|
using SocketIOClient;
|
2021-06-03 13:13:22 +00:00
|
|
|
|
2020-08-02 20:12:45 +00:00
|
|
|
namespace ASC.Socket.IO.Svc
|
2020-10-19 15:53:15 +00:00
|
|
|
{
|
2021-05-31 08:35:42 +00:00
|
|
|
[Singletone]
|
2020-08-02 20:12:45 +00:00
|
|
|
public class SocketServiceLauncher : IHostedService
|
|
|
|
{
|
2022-02-09 09:11:39 +00:00
|
|
|
private int PingInterval { get; set; }
|
|
|
|
private int ReconnectAttempts { get; set; }
|
2020-08-02 20:12:45 +00:00
|
|
|
private Process Proc { get; set; }
|
2020-10-20 09:33:38 +00:00
|
|
|
private ProcessStartInfo StartInfo { get; set; }
|
2022-01-11 16:09:44 +00:00
|
|
|
private SocketIO SocketClient { get; set; }
|
2021-06-03 13:13:22 +00:00
|
|
|
private CancellationTokenSource CancellationTokenSource { get; set; }
|
2020-08-02 20:12:45 +00:00
|
|
|
private ILog Logger { get; set; }
|
|
|
|
private string LogDir { get; set; }
|
2020-11-24 10:15:11 +00:00
|
|
|
private IConfiguration Configuration { get; set; }
|
|
|
|
private ConfigurationExtension ConfigurationExtension { get; }
|
2020-08-02 20:12:45 +00:00
|
|
|
private CoreBaseSettings CoreBaseSettings { get; set; }
|
2021-06-03 13:13:22 +00:00
|
|
|
private SignalrServiceClient SignalrServiceClient { get; set; }
|
2020-08-02 20:12:45 +00:00
|
|
|
private IHostEnvironment HostEnvironment { get; set; }
|
|
|
|
|
2020-10-28 12:26:27 +00:00
|
|
|
public SocketServiceLauncher(
|
|
|
|
IOptionsMonitor<ILog> options,
|
|
|
|
IConfiguration configuration,
|
2020-11-24 10:15:11 +00:00
|
|
|
ConfigurationExtension configurationExtension,
|
2020-10-28 12:26:27 +00:00
|
|
|
CoreBaseSettings coreBaseSettings,
|
2021-06-03 13:13:22 +00:00
|
|
|
IOptionsSnapshot<SignalrServiceClient> signalrServiceClient,
|
2020-10-28 12:26:27 +00:00
|
|
|
IHostEnvironment hostEnvironment)
|
2020-08-02 20:12:45 +00:00
|
|
|
{
|
|
|
|
Logger = options.CurrentValue;
|
2020-11-24 10:15:11 +00:00
|
|
|
Configuration = configuration;
|
|
|
|
ConfigurationExtension = configurationExtension;
|
2020-08-02 20:12:45 +00:00
|
|
|
CoreBaseSettings = coreBaseSettings;
|
2021-06-03 13:13:22 +00:00
|
|
|
SignalrServiceClient = signalrServiceClient.Value;
|
2020-08-02 20:12:45 +00:00
|
|
|
HostEnvironment = hostEnvironment;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Task StartAsync(CancellationToken cancellationToken)
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2020-11-24 10:15:11 +00:00
|
|
|
var settings = ConfigurationExtension.GetSetting<SocketSettings>("socket");
|
2022-02-24 10:47:18 +00:00
|
|
|
|
|
|
|
var path = settings.Path;
|
|
|
|
if (!Path.IsPathRooted(settings.Path))
|
|
|
|
{
|
|
|
|
path = Path.GetFullPath(CrossPlatform.PathCombine(HostEnvironment.ContentRootPath, settings.Path));
|
|
|
|
}
|
2020-08-02 20:12:45 +00:00
|
|
|
|
2022-02-09 09:11:39 +00:00
|
|
|
PingInterval = settings.PingInterval.GetValueOrDefault(10000);
|
|
|
|
ReconnectAttempts = settings.ReconnectAttempts.GetValueOrDefault(5);
|
2020-08-02 20:12:45 +00:00
|
|
|
|
|
|
|
StartInfo = new ProcessStartInfo
|
|
|
|
{
|
|
|
|
CreateNoWindow = false,
|
|
|
|
UseShellExecute = false,
|
|
|
|
FileName = "node",
|
|
|
|
WindowStyle = ProcessWindowStyle.Hidden,
|
2022-02-24 10:47:18 +00:00
|
|
|
Arguments = $"\"{Path.Combine(path, "server.js")}\"",
|
2020-08-02 20:12:45 +00:00
|
|
|
WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory
|
|
|
|
};
|
2022-01-11 16:09:44 +00:00
|
|
|
|
2020-08-08 19:40:40 +00:00
|
|
|
StartInfo.EnvironmentVariables.Add("core.machinekey", Configuration["core:machinekey"]);
|
2020-08-02 20:12:45 +00:00
|
|
|
StartInfo.EnvironmentVariables.Add("port", settings.Port);
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(settings.RedisHost) && !string.IsNullOrEmpty(settings.RedisPort))
|
|
|
|
{
|
|
|
|
StartInfo.EnvironmentVariables.Add("redis:host", settings.RedisHost);
|
|
|
|
StartInfo.EnvironmentVariables.Add("redis:port", settings.RedisPort);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CoreBaseSettings.Standalone)
|
|
|
|
{
|
|
|
|
StartInfo.EnvironmentVariables.Add("portal.internal.url", "http://localhost");
|
|
|
|
}
|
|
|
|
|
|
|
|
LogDir = Logger.LogDirectory;
|
2022-02-03 15:57:26 +00:00
|
|
|
StartInfo.EnvironmentVariables.Add("logPath", CrossPlatform.PathCombine(LogDir, "socket-io.%DATE%.log"));
|
2020-08-02 20:12:45 +00:00
|
|
|
StartNode();
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
Logger.Error(e);
|
|
|
|
}
|
2022-01-11 16:09:44 +00:00
|
|
|
|
2020-08-02 20:12:45 +00:00
|
|
|
return Task.CompletedTask;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Task StopAsync(CancellationToken cancellationToken)
|
|
|
|
{
|
2021-06-03 13:13:22 +00:00
|
|
|
StopPing();
|
2020-08-02 20:12:45 +00:00
|
|
|
StopNode();
|
|
|
|
return Task.CompletedTask;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void StartNode()
|
|
|
|
{
|
|
|
|
StopNode();
|
2020-08-27 14:01:37 +00:00
|
|
|
Proc = Process.Start(StartInfo);
|
2020-08-02 20:12:45 +00:00
|
|
|
|
2022-01-11 16:09:44 +00:00
|
|
|
CancellationTokenSource = new CancellationTokenSource();
|
|
|
|
|
2021-06-03 13:13:22 +00:00
|
|
|
var task = new Task(StartPing, CancellationTokenSource.Token, TaskCreationOptions.LongRunning);
|
|
|
|
task.Start(TaskScheduler.Default);
|
2020-08-02 20:12:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private void StopNode()
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if (Proc != null && !Proc.HasExited)
|
|
|
|
{
|
|
|
|
Proc.Kill();
|
|
|
|
if (!Proc.WaitForExit(10000)) /* wait 10 seconds */
|
|
|
|
{
|
|
|
|
Logger.Warn("The process does not wait for completion.");
|
|
|
|
}
|
|
|
|
Proc.Close();
|
|
|
|
Proc.Dispose();
|
|
|
|
Proc = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (Exception e)
|
|
|
|
{
|
|
|
|
Logger.Error("SocketIO failed stop", e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-11 16:09:44 +00:00
|
|
|
private async void StartPing()
|
2021-06-03 13:13:22 +00:00
|
|
|
{
|
2022-01-11 16:09:44 +00:00
|
|
|
try
|
|
|
|
{
|
|
|
|
var settings = ConfigurationExtension.GetSetting<SocketSettings>("socket");
|
2021-06-03 13:13:22 +00:00
|
|
|
|
2022-02-21 19:22:38 +00:00
|
|
|
var uri = new Uri($"ws://localhost:{settings.Port}"); //TODO: replace localhost to variable
|
2021-06-03 13:13:22 +00:00
|
|
|
|
2022-01-11 16:09:44 +00:00
|
|
|
var token = SignalrServiceClient.CreateAuthToken();
|
2021-06-03 13:13:22 +00:00
|
|
|
|
2022-01-11 16:09:44 +00:00
|
|
|
SocketClient = new SocketIO(uri, new SocketIOOptions
|
2021-06-03 13:13:22 +00:00
|
|
|
{
|
2022-01-11 16:09:44 +00:00
|
|
|
ExtraHeaders = new Dictionary<string, string>
|
|
|
|
{
|
|
|
|
{ "Authorization", token }
|
|
|
|
},
|
|
|
|
ConnectionTimeout = TimeSpan.FromSeconds(30),
|
|
|
|
Reconnection = true,
|
|
|
|
ReconnectionAttempts = ReconnectAttempts,
|
|
|
|
EIO = 4,
|
|
|
|
Path = "/socket.io",
|
|
|
|
Transport = SocketIOClient.Transport.TransportProtocol.WebSocket,
|
|
|
|
RandomizationFactor = 0.5
|
2021-06-03 13:13:22 +00:00
|
|
|
|
2022-01-11 16:09:44 +00:00
|
|
|
});
|
2021-06-03 13:13:22 +00:00
|
|
|
|
2022-01-11 16:09:44 +00:00
|
|
|
SocketClient.OnConnected += IOClient_OnConnected;
|
|
|
|
SocketClient.OnDisconnected += IOClient_OnDisconnected;
|
|
|
|
SocketClient.OnReconnectAttempt += IOClient_OnReconnectAttempt;
|
|
|
|
SocketClient.OnError += IOClient_OnError;
|
|
|
|
SocketClient.On("pong", response =>
|
|
|
|
{
|
|
|
|
Logger.Debug($"pong (server) at {response}");
|
|
|
|
});
|
|
|
|
|
|
|
|
Logger.Debug("Try to connect...");
|
|
|
|
|
|
|
|
await SocketClient.ConnectAsync();
|
|
|
|
}
|
|
|
|
catch (Exception ex)
|
2021-06-03 13:13:22 +00:00
|
|
|
{
|
2022-01-11 16:09:44 +00:00
|
|
|
if (CancellationTokenSource.IsCancellationRequested) return;
|
2021-06-03 13:13:22 +00:00
|
|
|
|
2022-01-11 16:09:44 +00:00
|
|
|
Logger.Error(ex.Message);
|
2021-06-03 13:13:22 +00:00
|
|
|
|
2022-01-11 16:09:44 +00:00
|
|
|
StopNode();
|
|
|
|
Process.GetCurrentProcess().Kill();
|
|
|
|
}
|
|
|
|
}
|
2021-06-03 13:13:22 +00:00
|
|
|
|
2022-01-11 16:09:44 +00:00
|
|
|
private async void IOClient_OnConnected(object sender, EventArgs e)
|
|
|
|
{
|
|
|
|
var socket = sender as SocketIO;
|
2021-06-03 13:13:22 +00:00
|
|
|
|
2022-01-11 16:09:44 +00:00
|
|
|
Logger.Info($"Socket_OnConnected Socket.Id: {socket.Id}");
|
|
|
|
|
|
|
|
while (SocketClient.Connected)
|
2021-06-03 13:13:22 +00:00
|
|
|
{
|
|
|
|
if (CancellationTokenSource.IsCancellationRequested) return;
|
|
|
|
|
2022-01-11 16:09:44 +00:00
|
|
|
await Task.Delay(PingInterval);
|
2021-06-03 13:13:22 +00:00
|
|
|
|
2022-01-11 16:09:44 +00:00
|
|
|
if (!SocketClient.Connected)
|
|
|
|
break;
|
2021-06-03 13:13:22 +00:00
|
|
|
|
2022-01-11 16:09:44 +00:00
|
|
|
await SocketClient.EmitAsync("ping", DateTime.UtcNow.ToString());
|
|
|
|
}
|
|
|
|
}
|
2021-06-03 13:13:22 +00:00
|
|
|
|
2022-01-11 16:09:44 +00:00
|
|
|
private void IOClient_OnDisconnected(object sender, string e)
|
|
|
|
{
|
|
|
|
Logger.Debug($"Socket_OnDisconnected {e}");
|
|
|
|
}
|
|
|
|
|
|
|
|
private void IOClient_OnReconnectAttempt(object sender, int attempt)
|
|
|
|
{
|
|
|
|
Logger.Debug($"Try to reconnect... attempt {attempt}");
|
|
|
|
|
|
|
|
if (attempt >= ReconnectAttempts)
|
2021-06-03 13:13:22 +00:00
|
|
|
{
|
2022-01-11 16:09:44 +00:00
|
|
|
StopPing();
|
2022-02-03 16:12:29 +00:00
|
|
|
Process.GetCurrentProcess().Kill();
|
2022-01-11 16:09:44 +00:00
|
|
|
}
|
|
|
|
}
|
2021-06-03 13:13:22 +00:00
|
|
|
|
2022-01-11 16:09:44 +00:00
|
|
|
private void IOClient_OnError(object sender, string e)
|
|
|
|
{
|
|
|
|
Logger.Error($"IOClient_OnError {e}");
|
2021-06-03 13:13:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private void StopPing()
|
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2022-01-11 16:09:44 +00:00
|
|
|
if (SocketClient != null)
|
2021-06-03 13:13:22 +00:00
|
|
|
{
|
2022-01-11 16:09:44 +00:00
|
|
|
SocketClient.OnConnected -= IOClient_OnConnected;
|
|
|
|
SocketClient.OnDisconnected -= IOClient_OnDisconnected;
|
|
|
|
SocketClient.OnReconnectAttempt -= IOClient_OnReconnectAttempt;
|
|
|
|
SocketClient.OnError -= IOClient_OnError;
|
|
|
|
|
|
|
|
SocketClient.Dispose();
|
|
|
|
SocketClient = null;
|
2021-06-03 13:13:22 +00:00
|
|
|
}
|
2022-01-11 16:09:44 +00:00
|
|
|
|
2021-06-03 13:13:22 +00:00
|
|
|
CancellationTokenSource.Cancel();
|
|
|
|
}
|
2022-01-11 16:09:44 +00:00
|
|
|
catch (Exception ex)
|
2021-06-03 13:13:22 +00:00
|
|
|
{
|
2022-01-11 16:09:44 +00:00
|
|
|
Logger.Error($"Ping failed stop {ex.Message}");
|
|
|
|
StopNode();
|
|
|
|
Process.GetCurrentProcess().Kill();
|
2021-06-03 13:13:22 +00:00
|
|
|
}
|
|
|
|
}
|
2020-08-02 20:12:45 +00:00
|
|
|
}
|
|
|
|
}
|