Winify – Blame information for rev 44

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 using System;
2 using System.Drawing;
3 using System.IO;
4 using System.Net.Http;
5 using System.Net.Http.Headers;
6 using System.Text;
7 using System.Threading;
8 using System.Threading.Tasks;
9 using Newtonsoft.Json;
18 office 10 using Serilog;
24 office 11 using Servers;
44 office 12 using WebSocketSharp;
13 using Configuration = Configuration.Configuration;
14 using ErrorEventArgs = WebSocketSharp.ErrorEventArgs;
1 office 15  
16 namespace Winify.Gotify
17 {
18 public class GotifyConnection : IDisposable
19 {
20 #region Public Events & Delegates
21  
22 public event EventHandler<GotifyNotificationEventArgs> GotifyNotification;
23  
24 #endregion
25  
26 #region Private Delegates, Events, Enums, Properties, Indexers and Fields
27  
25 office 28 private readonly Server _server;
29  
1 office 30 private CancellationToken _cancellationToken;
31  
32 private CancellationTokenSource _cancellationTokenSource;
33  
34 private Task _runTask;
35  
39 office 36 private HttpClient _httpClient;
37  
38 private readonly Uri _webSocketsUri;
39  
40 private readonly Uri _httpUri;
44 office 41 private WebSocket _webSocketSharp;
42 private readonly global::Configuration.Configuration _configuration;
39 office 43  
25 office 44 #endregion
1 office 45  
25 office 46 #region Constructors, Destructors and Finalizers
24 office 47  
44 office 48 private GotifyConnection()
24 office 49 {
44 office 50  
39 office 51 }
52  
44 office 53 public GotifyConnection(Server server, global::Configuration.Configuration configuration) : this()
39 office 54 {
24 office 55 _server = server;
44 office 56 _configuration = configuration;
39 office 57  
44 office 58 var httpClientHandler = new HttpClientHandler();
59 _httpClient = new HttpClient(httpClientHandler);
60 if (_configuration.IgnoreSelfSignedCertificates)
61 {
62 httpClientHandler.ServerCertificateCustomValidationCallback =
63 HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
64 }
39 office 65  
44 office 66 _httpClient = new HttpClient(httpClientHandler)
67 {
68 DefaultRequestHeaders =
69 {
70 Authorization = new AuthenticationHeaderValue("Basic",
71 Convert.ToBase64String(Encoding.Default.GetBytes($"{_server.Username}:{_server.Password}")))
72 }
73 };
74  
39 office 75 if (Uri.TryCreate(_server.Url, UriKind.Absolute, out _httpUri))
76 {
77 // Build the web sockets URI.
78 var webSocketsUriBuilder = new UriBuilder(_httpUri);
44 office 79 switch (webSocketsUriBuilder.Scheme.ToUpperInvariant())
80 {
81 case "HTTP":
82 webSocketsUriBuilder.Scheme = "ws";
83 break;
84 case "HTTPS":
85 webSocketsUriBuilder.Scheme = "wss";
86 break;
87 }
88  
39 office 89 webSocketsUriBuilder.Path = Path.Combine(webSocketsUriBuilder.Path, "stream");
90 _webSocketsUri = webSocketsUriBuilder.Uri;
91 }
24 office 92 }
93  
1 office 94 public void Dispose()
95 {
96 if (_cancellationTokenSource != null)
97 {
98 _cancellationTokenSource.Dispose();
99 _cancellationTokenSource = null;
100 }
39 office 101  
44 office 102 _webSocketSharp.Close();
103 _webSocketSharp = null;
104  
39 office 105 _httpClient.Dispose();
106 _httpClient = null;
1 office 107 }
108  
109 #endregion
110  
111 #region Public Methods
112  
25 office 113 public void Start()
1 office 114 {
115 _cancellationTokenSource = new CancellationTokenSource();
116 _cancellationToken = _cancellationTokenSource.Token;
117  
44 office 118 Connect();
119  
39 office 120 _runTask = Run(_cancellationToken);
1 office 121 }
122  
44 office 123 private void Connect()
1 office 124 {
44 office 125 _webSocketSharp = new WebSocket(_webSocketsUri.AbsoluteUri);
126 _webSocketSharp.SetCredentials(_server.Username, _server.Password, true);
127 if (_configuration.IgnoreSelfSignedCertificates)
39 office 128 {
44 office 129 _webSocketSharp.SslConfiguration.ServerCertificateValidationCallback +=
130 (sender, certificate, chain, errors) => true;
39 office 131 }
44 office 132  
133 _webSocketSharp.OnMessage += WebSocketSharp_OnMessage;
134 _webSocketSharp.OnError += WebSocketSharp_OnError;
135 _webSocketSharp.OnOpen += WebSocketSharp_OnOpen;
136 _webSocketSharp.OnClose += WebSocketSharp_OnClose;
137 _webSocketSharp.ConnectAsync();
1 office 138 }
139  
44 office 140 private void WebSocketSharp_OnClose(object sender, CloseEventArgs e)
141 {
142 Log.Information($"Connection to server closed with reason: {e.Reason}");
143 }
1 office 144  
44 office 145 private void WebSocketSharp_OnOpen(object sender, EventArgs e)
146 {
147 Log.Information("Connection to server is now open.");
148 }
1 office 149  
44 office 150 private async void WebSocketSharp_OnError(object sender, ErrorEventArgs e)
1 office 151 {
44 office 152 if (_cancellationToken.IsCancellationRequested)
1 office 153 {
44 office 154 Stop();
155 return;
156 }
18 office 157  
44 office 158 await Task.Delay(TimeSpan.FromSeconds(1), _cancellationToken);
159 Log.Information("Reconnecting to websocket server.");
18 office 160  
44 office 161 Connect();
162 }
1 office 163  
44 office 164 private async void WebSocketSharp_OnMessage(object sender, MessageEventArgs e)
165 {
166 if (e.RawData.Length == 0)
167 {
168 Log.Warning($"Empty message received from server.");
169 return;
170 }
1 office 171  
44 office 172 var message = Encoding.UTF8.GetString(e.RawData, 0, e.RawData.Length);
1 office 173  
44 office 174 GotifyNotification gotifyNotification;
1 office 175  
44 office 176 try
177 {
178 gotifyNotification = JsonConvert.DeserializeObject<GotifyNotification>(message);
179 }
180 catch (JsonSerializationException exception)
181 {
182 Log.Warning($"Could not deserialize notification: {exception.Message}");
183 return;
184 }
12 office 185  
44 office 186 if (gotifyNotification == null)
187 {
188 Log.Warning($"Could not deserialize gotify notification: {message}");
28 office 189  
44 office 190 return;
191 }
25 office 192  
44 office 193 gotifyNotification.Server = _server;
1 office 194  
44 office 195 if (!Uri.TryCreate(Path.Combine($"{_httpUri}", "application"), UriKind.Absolute,
196 out var applicationUri))
197 {
198 Log.Warning($"Could not build an URI to an application.");
199 return;
200 }
24 office 201  
44 office 202 using (var imageStream =
203 await RetrieveGotifyApplicationImage(gotifyNotification.AppId, applicationUri, _cancellationToken))
204 {
205 if (imageStream == null)
206 {
207 Log.Warning("Could not find any application image for notification.");
208 return;
209 }
3 office 210  
44 office 211 var image = Image.FromStream(imageStream);
12 office 212  
44 office 213 GotifyNotification?.Invoke(this,
214 new GotifyNotificationEventArgs(gotifyNotification, image));
215 }
3 office 216  
44 office 217 Log.Debug($"Notification message received: {gotifyNotification.Message}");
218 }
12 office 219  
44 office 220 public void Stop()
221 {
222 if (_cancellationTokenSource != null) _cancellationTokenSource.Cancel();
223 }
224  
225 #endregion
226  
227 #region Private Methods
228  
229 private async Task Run(CancellationToken cancellationToken)
230 {
231 try
232 {
233 do
234 {
235 await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
1 office 236 } while (!cancellationToken.IsCancellationRequested);
237 }
44 office 238 catch (Exception exception) when (exception is OperationCanceledException || exception is ObjectDisposedException)
1 office 239 {
240 }
44 office 241 catch (Exception exception)
1 office 242 {
44 office 243 Log.Warning(exception, "Failure running connection loop.");
1 office 244 }
245 }
246  
44 office 247 private async Task<Stream> RetrieveGotifyApplicationImage(int appId, Uri applicationUri,
28 office 248 CancellationToken cancellationToken)
25 office 249 {
39 office 250 var applicationResponse = await _httpClient.GetAsync(applicationUri, cancellationToken);
25 office 251  
39 office 252 var applications = await applicationResponse.Content.ReadAsStringAsync();
25 office 253  
44 office 254 GotifyApplication[] gotifyApplications;
255 try
256 {
257 gotifyApplications =
258 JsonConvert.DeserializeObject<GotifyApplication[]>(applications);
259 }
260 catch (JsonSerializationException exception)
261 {
262 Log.Warning($"Could not deserialize the list of applications from the server: {exception.Message}");
25 office 263  
39 office 264 return null;
265 }
25 office 266  
39 office 267 foreach (var application in gotifyApplications)
268 {
44 office 269 if (application.Id != appId) continue;
25 office 270  
39 office 271 if (!Uri.TryCreate(Path.Combine($"{_httpUri}", $"{application.Image}"), UriKind.Absolute,
272 out var applicationImageUri))
25 office 273 {
44 office 274 Log.Warning("Could not build URL path to application icon.");
39 office 275 continue;
276 }
25 office 277  
39 office 278 var imageResponse = await _httpClient.GetAsync(applicationImageUri, cancellationToken);
25 office 279  
44 office 280 var memoryStream = new MemoryStream();
25 office 281  
44 office 282 await imageResponse.Content.CopyToAsync(memoryStream);
283  
284 return memoryStream;
25 office 285 }
286  
287 return null;
288 }
289  
1 office 290 #endregion
291 }
292 }