/trunk/Winify/Gotify/GotifyConnection.cs |
@@ -3,13 +3,10 @@ |
using System.Collections.Generic; |
using System.Diagnostics; |
using System.Drawing; |
using System.Globalization; |
using System.IO; |
using System.Linq; |
using System.Net; |
using System.Net.Http; |
using System.Net.Http.Headers; |
using System.Runtime.Caching; |
using System.Runtime.CompilerServices; |
using System.Security.Authentication; |
using System.Security.Cryptography.X509Certificates; |
@@ -17,9 +14,7 @@ |
using System.Threading; |
using System.Threading.Tasks; |
using System.Threading.Tasks.Dataflow; |
using System.Windows.Forms; |
using Newtonsoft.Json; |
using Org.BouncyCastle.Asn1.Pkcs; |
using Serilog; |
using Servers; |
using WebSocketSharp; |
@@ -34,7 +29,7 @@ |
{ |
#region Public Events & Delegates |
|
public event EventHandler<GotifyNotificationEventArgs> GotifyNotification; |
public event EventHandler<GotifyMessageEventArgs> GotifyMessage; |
|
#endregion |
|
@@ -57,8 +52,6 @@ |
private readonly Configuration.Configuration _configuration; |
private IDisposable _tplRetrievePastMessagesLink; |
private IDisposable _tplWebSocketsBufferBlockTransformLink; |
private IDisposable _tplWebSocketsTransformActionLink; |
private IDisposable _tplWebSocketsTransformActionNullLink; |
private readonly BufferBlock<GotifyConnectionData> _webSocketMessageBufferBlock; |
private readonly Stopwatch _webSocketsClientPingStopWatch; |
private readonly ScheduledContinuation _webSocketsServerResponseScheduledContinuation; |
@@ -82,64 +75,47 @@ |
CancellationToken = _cancellationToken |
}); |
|
var webSocketTransformBlock = new TransformBlock<GotifyConnectionData, GotifyMessage>(data => |
var webSocketActionBlock = new ActionBlock<GotifyConnectionData>(async gotifyConnectionData => |
{ |
if (data.Payload == null || data.Payload.Length == 0) |
try |
{ |
return null; |
} |
using var memoryStream = new MemoryStream(gotifyConnectionData.Payload); |
using var streamReader = new StreamReader(memoryStream); |
using var jsonTextReader = new JsonTextReader(streamReader); |
|
GotifyMessage gotifyNotification; |
var gotifyMessage = _jsonSerializer.Deserialize<GotifyMessage>(jsonTextReader) ?? throw new ArgumentNullException(); |
|
try |
{ |
var message = Encoding.UTF8.GetString(data.Payload, 0, data.Payload.Length); |
gotifyMessage.Server = gotifyConnectionData.Server; |
|
gotifyNotification = JsonConvert.DeserializeObject<GotifyMessage>(message); |
using var imageStream = await RetrieveGotifyApplicationImage(gotifyMessage.AppId, _cancellationToken); |
|
if (gotifyNotification == null) |
if (imageStream == null || imageStream.Length == 0) |
{ |
throw new ArgumentNullException(); |
Log.Warning("Could not find any application image for notification."); |
|
return; |
} |
|
gotifyNotification.Server = data.Server; |
var image = new Bitmap(imageStream); |
|
GotifyMessage?.Invoke(this, |
new GotifyMessageEventArgs(gotifyMessage, image)); |
|
Log.Debug($"Notification message received: {gotifyMessage.Message}"); |
} |
catch (Exception exception) |
catch (JsonSerializationException exception) |
{ |
Log.Warning(exception, "Could not deserialize notification."); |
|
return null; |
Log.Warning(exception, "Could not deserialize notification message."); |
} |
|
return gotifyNotification; |
|
}, new ExecutionDataflowBlockOptions { CancellationToken = _cancellationToken }); |
|
var webSocketActionBlock = new ActionBlock<GotifyMessage>(async message => |
{ |
using var imageStream = await RetrieveGotifyApplicationImage(message.AppId, _cancellationToken); |
if (imageStream == null || imageStream.Length == 0) |
catch (Exception exception) |
{ |
Log.Warning("Could not find any application image for notification."); |
|
return; |
Log.Warning(exception, "Generic failure."); |
} |
|
var image = new Bitmap(imageStream); |
|
GotifyNotification?.Invoke(this, |
new GotifyNotificationEventArgs(message, image)); |
|
Log.Debug($"Notification message received: {message.Message}"); |
|
}, new ExecutionDataflowBlockOptions { CancellationToken = _cancellationToken }); |
|
_tplWebSocketsBufferBlockTransformLink = _webSocketMessageBufferBlock.LinkTo(webSocketTransformBlock, |
_tplWebSocketsBufferBlockTransformLink = _webSocketMessageBufferBlock.LinkTo(webSocketActionBlock, |
new DataflowLinkOptions { PropagateCompletion = true }); |
_tplWebSocketsTransformActionLink = webSocketTransformBlock.LinkTo(webSocketActionBlock, |
new DataflowLinkOptions { PropagateCompletion = true }, message => message != null); |
_tplWebSocketsTransformActionNullLink = webSocketTransformBlock.LinkTo(DataflowBlock.NullTarget<GotifyMessage>(), |
new DataflowLinkOptions() { PropagateCompletion = true }); |
} |
|
public GotifyConnection(Server server, Configuration.Configuration configuration) : this() |
@@ -208,18 +184,6 @@ |
_tplWebSocketsBufferBlockTransformLink = null; |
} |
|
if (_tplWebSocketsTransformActionLink != null) |
{ |
_tplWebSocketsTransformActionLink.Dispose(); |
_tplWebSocketsTransformActionLink = null; |
} |
|
if (_tplWebSocketsTransformActionNullLink != null) |
{ |
_tplWebSocketsTransformActionNullLink.Dispose(); |
_tplWebSocketsTransformActionNullLink = null; |
} |
|
if (_tplRetrievePastMessagesLink != null) |
{ |
_tplRetrievePastMessagesLink.Dispose(); |
@@ -309,7 +273,7 @@ |
|
private void OnUnresponsiveServer() |
{ |
Log.Warning($"Server {_server} has not responded in a long while..."); |
Log.Warning($"Server {_server.Name} has not responded in a long while..."); |
} |
|
private async void WebSocketSharp_OnError(object sender, ErrorEventArgs e) |
@@ -329,7 +293,7 @@ |
{ |
if (e.IsPing) |
{ |
Log.Information($"Server {_server} sent PING message"); |
Log.Information($"Server {_server.Name} sent PING message"); |
|
_webSocketsServerResponseScheduledContinuation.Schedule(TimeSpan.FromMinutes(1), OnUnresponsiveServer, _cancellationToken); |
return; |
@@ -382,7 +346,6 @@ |
return; |
} |
|
GotifyMessageQuery gotifyMessageQuery; |
try |
{ |
using var messageStream = await _httpClient.GetStreamAsync(combinedUri); |
@@ -389,50 +352,52 @@ |
using var streamReader = new StreamReader(messageStream); |
using var jsonTextReader = new JsonTextReader(streamReader); |
|
gotifyMessageQuery = _jsonSerializer.Deserialize<GotifyMessageQuery>(jsonTextReader); |
var gotifyMessageQuery = _jsonSerializer.Deserialize<GotifyMessageQuery>(jsonTextReader) ?? |
throw new ArgumentNullException(); |
|
if (gotifyMessageQuery.Messages == null) |
{ |
Log.Warning("Invalid application messages deserialized deserialized."); |
|
return; |
} |
|
foreach (var message in gotifyMessageQuery.Messages) |
{ |
if (message.Date < |
DateTime.Now - TimeSpan.FromHours(_configuration.RetrievePastNotificationHours)) |
{ |
continue; |
} |
|
using var imageStream = await RetrieveGotifyApplicationImage(message.AppId, _cancellationToken); |
if (imageStream == null || imageStream.Length == 0) |
{ |
Log.Warning("Could not find any application image for notification."); |
|
continue; |
} |
|
var image = new Bitmap(imageStream); |
message.Server = gotifyConnectionApplication.Server; |
|
GotifyMessage?.Invoke(this, |
new GotifyMessageEventArgs(message, image)); |
} |
} |
catch (HttpRequestException exception) |
{ |
Log.Warning(exception, $"Could not get application {gotifyConnectionApplication.Application.Id}."); |
|
return; |
} |
catch (JsonSerializationException exception) |
{ |
Log.Warning(exception,$"Could not deserialize the message response for application {gotifyConnectionApplication.Application.Id}."); |
|
return; |
Log.Warning(exception, |
$"Could not deserialize the message response for application {gotifyConnectionApplication.Application.Id}."); |
} |
|
if (gotifyMessageQuery == null || gotifyMessageQuery.Messages == null) |
catch (Exception exception) |
{ |
Log.Warning("Invalid application messages deserialized deserialized."); |
|
return; |
Log.Warning(exception, "Generic failure."); |
} |
|
foreach (var message in gotifyMessageQuery.Messages) |
{ |
if (message.Date < DateTime.Now - TimeSpan.FromHours(_configuration.RetrievePastNotificationHours)) |
{ |
continue; |
} |
|
using var imageStream = await RetrieveGotifyApplicationImage(message.AppId, _cancellationToken); |
if (imageStream == null || imageStream.Length == 0) |
{ |
Log.Warning("Could not find any application image for notification."); |
|
continue; |
} |
|
var image = new Bitmap(imageStream); |
message.Server = gotifyConnectionApplication.Server; |
|
GotifyNotification?.Invoke(this, |
new GotifyNotificationEventArgs(message, image)); |
} |
|
}, new ExecutionDataflowBlockOptions { CancellationToken = cancellationToken }); |
|
gotifyApplicationBufferBlock.LinkTo(gotifyApplicationActionBlock, |
@@ -448,7 +413,6 @@ |
await Task.WhenAll(tasks); |
gotifyApplicationBufferBlock.Complete(); |
await gotifyApplicationActionBlock.Completion; |
|
} |
|
private async Task HeartBeat(CancellationToken cancellationToken) |
@@ -462,13 +426,13 @@ |
_webSocketsClientPingStopWatch.Restart(); |
if (!_webSocketSharp.Ping()) |
{ |
Log.Warning($"Server {_server} did not respond to PING message."); |
Log.Warning($"Server {_server.Name} did not respond to PING message."); |
continue; |
} |
|
var delta = _webSocketsClientPingStopWatch.ElapsedMilliseconds; |
|
Log.Information($"PING response latency for {_server} is {delta}ms"); |
Log.Information($"PING response latency for {_server.Name} is {delta}ms"); |
|
_webSocketsServerResponseScheduledContinuation.Schedule(TimeSpan.FromMinutes(1), OnUnresponsiveServer, _cancellationToken); |
} while (!cancellationToken.IsCancellationRequested); |
@@ -479,7 +443,7 @@ |
} |
catch (Exception exception) |
{ |
Log.Warning(exception, $"Heartbeat for server {_server} has failed due to {exception.Message}"); |
Log.Warning(exception, $"Heartbeat for server {_server.Name} has failed."); |
} |
} |
|
@@ -537,17 +501,17 @@ |
|
try |
{ |
var imageResponse = await _httpClient.GetAsync(applicationImageUri, cancellationToken); |
var imageResponse = await _httpClient.GetStreamAsync(applicationImageUri); |
|
var memoryStream = new MemoryStream(); |
|
await imageResponse.Content.CopyToAsync(memoryStream); |
await imageResponse.CopyToAsync(memoryStream); |
|
return memoryStream; |
} |
catch (Exception exception) |
{ |
Log.Error($"Could not retrieve application image: {exception.Message}"); |
Log.Error(exception,"Could not retrieve application image."); |
} |
} |
|