mirror of
https://github.com/duplicati/duplicati.git
synced 2026-05-06 07:16:38 -04:00
Changed logic for reconnect to not rely on serverstate API.
Changed websocket to send current state on initial connect.
This commit is contained in:
@@ -82,6 +82,10 @@ backupApp.service('AppService', function ($http, $cookies, $q, $cookies, DialogS
|
||||
return deferred.promise;
|
||||
};
|
||||
|
||||
this.clearAccessToken = function () {
|
||||
self.access_token = null;
|
||||
self.access_token_promise = null;
|
||||
}
|
||||
|
||||
// Returns a promise that resolves to the access token
|
||||
this.getAccessToken = function () {
|
||||
|
||||
@@ -252,23 +252,15 @@ backupApp.service('ServerStatus', function ($rootScope, $timeout, AppService, Ap
|
||||
|
||||
function handleConnectionError(response) {
|
||||
state.failedConnectionAttempts++;
|
||||
var errorMessage = AppService.responseErrorMessage(response);
|
||||
|
||||
// First failure, we ignore
|
||||
if (state.connectionState == 'connected' && state.failedConnectionAttempts == 1) {
|
||||
// Try again
|
||||
countdownForReconnect(function () {
|
||||
updateServerState();
|
||||
});
|
||||
updateServerState();
|
||||
} else if (response.status == 401) {
|
||||
// Change state to connected to hide the connecting message, which is on top of the login message from the AppService
|
||||
state.connectionState = 'unauthorized';
|
||||
// Notify
|
||||
$rootScope.$broadcast('serverstatechanged');
|
||||
} else {
|
||||
state.connectionState = 'disconnected';
|
||||
|
||||
// Notify
|
||||
$rootScope.$broadcast('serverstatechanged');
|
||||
countdownForReconnect(function () {
|
||||
updateServerState();
|
||||
@@ -282,37 +274,35 @@ backupApp.service('ServerStatus', function ($rootScope, $timeout, AppService, Ap
|
||||
$rootScope.$broadcast('serverstatechanged');
|
||||
}
|
||||
|
||||
const url = window.location.origin + '/api/v1/serverstate';
|
||||
AppService.get(url, {timeout: state.lastEventId > 0 ? longpolltime : 5000}).then(
|
||||
handleServerState, handleConnectionError
|
||||
);
|
||||
self.reconnect();
|
||||
};
|
||||
|
||||
updateServerState();
|
||||
|
||||
this.reconnect = function () {
|
||||
var reconnect_websocket = function () {
|
||||
window.clearInterval(longPollRetryTimer);
|
||||
const w = new WebSocket('ws://' + window.location.host + '/notifications?token=' + AppService.access_token)
|
||||
w.addEventListener("open", (event) => {
|
||||
state.connectionState = 'connected';
|
||||
$rootScope.$broadcast('serverstatechanged.connectionState', state.connectionState);
|
||||
$rootScope.$broadcast('serverstatechanged');
|
||||
});
|
||||
w.addEventListener("message", (event) => {
|
||||
const status = JSON.parse(event.data);
|
||||
handleServerState({data: status});
|
||||
});
|
||||
w.addEventListener("close", (event) => {
|
||||
if (event.code === 4401) {
|
||||
state.connectionState = 'unauthorized';
|
||||
$rootScope.$broadcast('serverstatechanged');
|
||||
}
|
||||
window.websocket = null;
|
||||
if (event.code === 4401)
|
||||
AppService.clearAccessToken();
|
||||
|
||||
handleConnectionError({status: event.code});
|
||||
});
|
||||
return w;
|
||||
};
|
||||
|
||||
AppService.access_token_promise.then(() => {
|
||||
let websocket = this.reconnect()
|
||||
window.websocket = websocket;
|
||||
});
|
||||
this.reconnect = function () {
|
||||
AppService.getAccessToken().then(() => {
|
||||
let websocket = reconnect_websocket()
|
||||
window.websocket = websocket;
|
||||
}, resp => {
|
||||
handleConnectionError(resp);
|
||||
});
|
||||
}
|
||||
|
||||
this.reconnect();
|
||||
|
||||
});
|
||||
|
||||
@@ -4,8 +4,7 @@ namespace Duplicati.WebserverCore.Abstractions.Notifications;
|
||||
|
||||
public interface IWebsocketAccessor
|
||||
{
|
||||
void AddConnection(WebSocket newConnection);
|
||||
WebSocket[] OpenConnections { get; }
|
||||
Task AddConnection(WebSocket newConnection);
|
||||
Task Send<T>(T data);
|
||||
Task HandleClientMessage(string message);
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using Duplicati.WebserverCore.Abstractions;
|
||||
using Duplicati.WebserverCore.Abstractions.Notifications;
|
||||
using Duplicati.WebserverCore.Exceptions;
|
||||
|
||||
namespace Duplicati.WebserverCore.Middlewares;
|
||||
|
||||
@@ -37,8 +35,9 @@ public static class WebsocketExtensions
|
||||
if (context.WebSockets.IsWebSocketRequest)
|
||||
{
|
||||
using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
|
||||
websocketAccessor.AddConnection(webSocket);
|
||||
var initial = websocketAccessor.AddConnection(webSocket);
|
||||
await HandleClientData(webSocket, websocketAccessor);
|
||||
await initial;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -9,34 +9,41 @@ namespace Duplicati.WebserverCore.Notifications;
|
||||
|
||||
public class WebsocketAccessor : IWebsocketAccessor
|
||||
{
|
||||
private object _lock = new object();
|
||||
private List<WebSocket> _connections = new();
|
||||
private readonly JsonSerializerSettings m_jsonSettings;
|
||||
private readonly IServiceProvider m_serviceProvider;
|
||||
|
||||
public WebsocketAccessor(JsonSerializerSettings jsonSettings, EventPollNotify eventPollNotify, IServiceProvider serviceProvider)
|
||||
{
|
||||
m_jsonSettings = jsonSettings;
|
||||
|
||||
m_serviceProvider = serviceProvider;
|
||||
|
||||
eventPollNotify.NewEvent += async (_, _) =>
|
||||
{
|
||||
using var scope = serviceProvider.CreateScope();
|
||||
var statusService = scope.ServiceProvider.GetRequiredService<IStatusService>();
|
||||
await Send(statusService.GetStatus());
|
||||
await Send(StatusService.GetStatus());
|
||||
};
|
||||
}
|
||||
|
||||
public void AddConnection(WebSocket newConnection)
|
||||
private IStatusService StatusService => m_serviceProvider.GetService<IStatusService>() ?? throw new Exception("StatusService not found");
|
||||
|
||||
public async Task AddConnection(WebSocket newConnection)
|
||||
{
|
||||
_connections.Add(newConnection);
|
||||
await SendInitialStatus(newConnection);
|
||||
lock (_lock)
|
||||
_connections.Add(newConnection);
|
||||
ClearClosed();
|
||||
}
|
||||
|
||||
private async Task SendInitialStatus(WebSocket connection)
|
||||
=> await Send(StatusService.GetStatus(), [connection]);
|
||||
|
||||
private void ClearClosed()
|
||||
{
|
||||
_connections = _connections.Where(c => c.State == WebSocketState.Open).ToList();
|
||||
lock (_lock)
|
||||
_connections = _connections.Where(c => c.State == WebSocketState.Open).ToList();
|
||||
}
|
||||
|
||||
public WebSocket[] OpenConnections => Connections.ToArray();
|
||||
|
||||
private IEnumerable<WebSocket> Connections
|
||||
{
|
||||
get
|
||||
@@ -46,19 +53,28 @@ public class WebsocketAccessor : IWebsocketAccessor
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Send<T>(T data)
|
||||
private Task Send<T>(T data, IEnumerable<WebSocket> connections)
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(data, m_jsonSettings);
|
||||
var bytes = json.GetBytes();
|
||||
|
||||
foreach (var webSocket in Connections)
|
||||
{
|
||||
await webSocket.SendAsync(bytes, WebSocketMessageType.Text, true, CancellationToken.None);
|
||||
}
|
||||
lock (_lock)
|
||||
connections = connections.ToList();
|
||||
|
||||
return Task.WhenAll(
|
||||
connections
|
||||
.Where(c => c.State == WebSocketState.Open)
|
||||
.Select(c => c.SendAsync(bytes, WebSocketMessageType.Text, true, CancellationToken.None))
|
||||
);
|
||||
}
|
||||
|
||||
public async Task HandleClientMessage(string message)
|
||||
|
||||
public Task Send<T>(T data)
|
||||
=> Send(data, Connections);
|
||||
|
||||
public Task HandleClientMessage(string message)
|
||||
{
|
||||
//TODO: handle client message
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user