Winify

Subversion Repositories:
Compare Path: Rev
With Path: Rev
?path1? @ 43  →  ?path2? @ 44
/trunk/Winify/Gotify/GotifyConnection.cs
@@ -3,7 +3,6 @@
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@@ -10,7 +9,9 @@
using Newtonsoft.Json;
using Serilog;
using Servers;
using ClientWebSocket = System.Net.WebSockets.Managed.ClientWebSocket;
using WebSocketSharp;
using Configuration = Configuration.Configuration;
using ErrorEventArgs = WebSocketSharp.ErrorEventArgs;
 
namespace Winify.Gotify
{
@@ -34,34 +35,57 @@
 
private HttpClient _httpClient;
 
private readonly string _auth;
 
private readonly Uri _webSocketsUri;
 
private readonly Uri _httpUri;
private WebSocket _webSocketSharp;
private readonly global::Configuration.Configuration _configuration;
 
#endregion
 
#region Constructors, Destructors and Finalizers
 
public GotifyConnection()
private GotifyConnection()
{
_httpClient = new HttpClient();
 
}
 
public GotifyConnection(Server server) : this()
public GotifyConnection(Server server, global::Configuration.Configuration configuration) : this()
{
_server = server;
_configuration = configuration;
 
_auth = Convert.ToBase64String(Encoding.Default.GetBytes($"{_server.Username}:{_server.Password}"));
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Basic", _auth);
var httpClientHandler = new HttpClientHandler();
_httpClient = new HttpClient(httpClientHandler);
if (_configuration.IgnoreSelfSignedCertificates)
{
httpClientHandler.ServerCertificateCustomValidationCallback =
HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
}
 
_httpClient = new HttpClient(httpClientHandler)
{
DefaultRequestHeaders =
{
Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(Encoding.Default.GetBytes($"{_server.Username}:{_server.Password}")))
}
};
 
if (Uri.TryCreate(_server.Url, UriKind.Absolute, out _httpUri))
{
// Build the web sockets URI.
var webSocketsUriBuilder = new UriBuilder(_httpUri);
webSocketsUriBuilder.Scheme = "ws";
switch (webSocketsUriBuilder.Scheme.ToUpperInvariant())
{
case "HTTP":
webSocketsUriBuilder.Scheme = "ws";
break;
case "HTTPS":
webSocketsUriBuilder.Scheme = "wss";
break;
}
 
webSocketsUriBuilder.Path = Path.Combine(webSocketsUriBuilder.Path, "stream");
_webSocketsUri = webSocketsUriBuilder.Uri;
}
@@ -75,6 +99,9 @@
_cancellationTokenSource = null;
}
 
_webSocketSharp.Close();
_webSocketSharp = null;
 
_httpClient.Dispose();
_httpClient = null;
}
@@ -88,99 +115,136 @@
_cancellationTokenSource = new CancellationTokenSource();
_cancellationToken = _cancellationTokenSource.Token;
 
Connect();
 
_runTask = Run(_cancellationToken);
}
 
public void Stop()
private void Connect()
{
if (_cancellationTokenSource != null)
_webSocketSharp = new WebSocket(_webSocketsUri.AbsoluteUri);
_webSocketSharp.SetCredentials(_server.Username, _server.Password, true);
if (_configuration.IgnoreSelfSignedCertificates)
{
_cancellationTokenSource.Cancel();
_webSocketSharp.SslConfiguration.ServerCertificateValidationCallback +=
(sender, certificate, chain, errors) => true;
}
 
_webSocketSharp.OnMessage += WebSocketSharp_OnMessage;
_webSocketSharp.OnError += WebSocketSharp_OnError;
_webSocketSharp.OnOpen += WebSocketSharp_OnOpen;
_webSocketSharp.OnClose += WebSocketSharp_OnClose;
_webSocketSharp.ConnectAsync();
}
 
#endregion
private void WebSocketSharp_OnClose(object sender, CloseEventArgs e)
{
Log.Information($"Connection to server closed with reason: {e.Reason}");
}
 
#region Private Methods
private void WebSocketSharp_OnOpen(object sender, EventArgs e)
{
Log.Information("Connection to server is now open.");
}
 
private async Task Run(CancellationToken cancellationToken)
private async void WebSocketSharp_OnError(object sender, ErrorEventArgs e)
{
try
if (_cancellationToken.IsCancellationRequested)
{
do
{
try
{
using (var webSocketClient = new ClientWebSocket())
{
webSocketClient.Options.SetRequestHeader("Authorization", $"Basic {_auth}");
Stop();
return;
}
 
await webSocketClient.ConnectAsync(_webSocketsUri, cancellationToken);
await Task.Delay(TimeSpan.FromSeconds(1), _cancellationToken);
Log.Information("Reconnecting to websocket server.");
 
do
{
var payload = new ArraySegment<byte>(new byte[1024]);
Connect();
}
 
var result = await webSocketClient.ReceiveAsync(payload, cancellationToken);
private async void WebSocketSharp_OnMessage(object sender, MessageEventArgs e)
{
if (e.RawData.Length == 0)
{
Log.Warning($"Empty message received from server.");
return;
}
 
if (result.Count == 0)
{
continue;
}
var message = Encoding.UTF8.GetString(e.RawData, 0, e.RawData.Length);
 
if (payload.Array == null || payload.Count == 0)
{
continue;
}
GotifyNotification gotifyNotification;
 
var message = Encoding.UTF8.GetString(payload.Array, 0, payload.Count);
try
{
gotifyNotification = JsonConvert.DeserializeObject<GotifyNotification>(message);
}
catch (JsonSerializationException exception)
{
Log.Warning($"Could not deserialize notification: {exception.Message}");
return;
}
 
var gotifyNotification = JsonConvert.DeserializeObject<GotifyNotification>(message);
if (gotifyNotification == null)
{
Log.Warning($"Could not deserialize gotify notification: {message}");
 
if (gotifyNotification == null)
{
Log.Warning($"Could not deserialize gotify notification: {message}");
return;
}
 
continue;
}
gotifyNotification.Server = _server;
 
gotifyNotification.Server = _server;
if (!Uri.TryCreate(Path.Combine($"{_httpUri}", "application"), UriKind.Absolute,
out var applicationUri))
{
Log.Warning($"Could not build an URI to an application.");
return;
}
 
if (!Uri.TryCreate(Path.Combine($"{_httpUri}", "application"), UriKind.Absolute,
out var applicationUri))
{
continue;
}
using (var imageStream =
await RetrieveGotifyApplicationImage(gotifyNotification.AppId, applicationUri, _cancellationToken))
{
if (imageStream == null)
{
Log.Warning("Could not find any application image for notification.");
return;
}
 
var image = await RetrieveGotifyApplicationImage(gotifyNotification.AppId,
applicationUri, cancellationToken);
var image = Image.FromStream(imageStream);
 
GotifyNotification?.Invoke(this,
new GotifyNotificationEventArgs(gotifyNotification, image));
GotifyNotification?.Invoke(this,
new GotifyNotificationEventArgs(gotifyNotification, image));
}
 
Log.Debug($"Notification message received: {gotifyNotification.Message}");
} while (!cancellationToken.IsCancellationRequested);
}
}
catch (Exception ex) when (ex is WebSocketException || ex is HttpRequestException)
{
// Reconnect
Log.Warning($"Unable to connect to gotify server: {ex.Message}");
Log.Debug($"Notification message received: {gotifyNotification.Message}");
}
 
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
}
public void Stop()
{
if (_cancellationTokenSource != null) _cancellationTokenSource.Cancel();
}
 
#endregion
 
#region Private Methods
 
private async Task Run(CancellationToken cancellationToken)
{
try
{
do
{
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
} while (!cancellationToken.IsCancellationRequested);
}
catch (Exception ex) when (ex is OperationCanceledException || ex is ObjectDisposedException)
catch (Exception exception) when (exception is OperationCanceledException || exception is ObjectDisposedException)
{
}
catch (Exception ex)
catch (Exception exception)
{
Log.Warning(ex, "Failure running connection loop.");
Log.Warning(exception, "Failure running connection loop.");
}
}
 
private async Task<Image> RetrieveGotifyApplicationImage(int appId, Uri applicationUri,
private async Task<Stream> RetrieveGotifyApplicationImage(int appId, Uri applicationUri,
CancellationToken cancellationToken)
{
var applicationResponse = await _httpClient.GetAsync(applicationUri, cancellationToken);
@@ -187,35 +251,37 @@
 
var applications = await applicationResponse.Content.ReadAsStringAsync();
 
var gotifyApplications =
JsonConvert.DeserializeObject<GotifyApplication[]>(applications);
GotifyApplication[] gotifyApplications;
try
{
gotifyApplications =
JsonConvert.DeserializeObject<GotifyApplication[]>(applications);
}
catch (JsonSerializationException exception)
{
Log.Warning($"Could not deserialize the list of applications from the server: {exception.Message}");
 
if (gotifyApplications == null)
{
return null;
}
 
foreach (var application in gotifyApplications)
{
if (application.Id != appId)
{
continue;
}
if (application.Id != appId) continue;
 
if (!Uri.TryCreate(Path.Combine($"{_httpUri}", $"{application.Image}"), UriKind.Absolute,
out var applicationImageUri))
{
Log.Warning("Could not build URL path to application icon.");
continue;
}
 
var imageResponse = await _httpClient.GetAsync(applicationImageUri, cancellationToken);
 
using (var memoryStream = new MemoryStream())
{
await imageResponse.Content.CopyToAsync(memoryStream);
var memoryStream = new MemoryStream();
 
return Image.FromStream(memoryStream);
}
await imageResponse.Content.CopyToAsync(memoryStream);
 
return memoryStream;
}
 
return null;