Winify – Diff between revs 66 and 67

Subversion Repositories:
Rev:
Show entire fileIgnore whitespace
Rev 66 Rev 67
Line 1... Line 1...
1 using System; 1 using System;
2 using System.Collections.Concurrent; 2 using System.Collections.Concurrent;
3 using System.Collections.Generic; 3 using System.Collections.Generic;
4 using System.Diagnostics; 4 using System.Diagnostics;
5 using System.Drawing; 5 using System.Drawing;
6 using System.Globalization; -  
7 using System.IO; 6 using System.IO;
8 using System.Linq; -  
9 using System.Net; 7 using System.Net;
10 using System.Net.Http; 8 using System.Net.Http;
11 using System.Net.Http.Headers; 9 using System.Net.Http.Headers;
12 using System.Runtime.Caching; -  
13 using System.Runtime.CompilerServices; 10 using System.Runtime.CompilerServices;
14 using System.Security.Authentication; 11 using System.Security.Authentication;
15 using System.Security.Cryptography.X509Certificates; 12 using System.Security.Cryptography.X509Certificates;
16 using System.Text; 13 using System.Text;
17 using System.Threading; 14 using System.Threading;
18 using System.Threading.Tasks; 15 using System.Threading.Tasks;
19 using System.Threading.Tasks.Dataflow; 16 using System.Threading.Tasks.Dataflow;
20 using System.Windows.Forms; -  
21 using Newtonsoft.Json; 17 using Newtonsoft.Json;
22 using Org.BouncyCastle.Asn1.Pkcs; -  
23 using Serilog; 18 using Serilog;
24 using Servers; 19 using Servers;
25 using WebSocketSharp; 20 using WebSocketSharp;
26 using WebSocketSharp.Net; 21 using WebSocketSharp.Net;
27 using Winify.Utilities; 22 using Winify.Utilities;
Line 32... Line 27...
32 { 27 {
33 public class GotifyConnection : IDisposable 28 public class GotifyConnection : IDisposable
34 { 29 {
35 #region Public Events & Delegates 30 #region Public Events & Delegates
Line 36... Line 31...
36   31  
Line 37... Line 32...
37 public event EventHandler<GotifyNotificationEventArgs> GotifyNotification; 32 public event EventHandler<GotifyMessageEventArgs> GotifyMessage;
Line 38... Line 33...
38   33  
Line 55... Line 50...
55 private readonly Uri _httpUri; 50 private readonly Uri _httpUri;
56 private WebSocket _webSocketSharp; 51 private WebSocket _webSocketSharp;
57 private readonly Configuration.Configuration _configuration; 52 private readonly Configuration.Configuration _configuration;
58 private IDisposable _tplRetrievePastMessagesLink; 53 private IDisposable _tplRetrievePastMessagesLink;
59 private IDisposable _tplWebSocketsBufferBlockTransformLink; 54 private IDisposable _tplWebSocketsBufferBlockTransformLink;
60 private IDisposable _tplWebSocketsTransformActionLink; -  
61 private IDisposable _tplWebSocketsTransformActionNullLink; -  
62 private readonly BufferBlock<GotifyConnectionData> _webSocketMessageBufferBlock; 55 private readonly BufferBlock<GotifyConnectionData> _webSocketMessageBufferBlock;
63 private readonly Stopwatch _webSocketsClientPingStopWatch; 56 private readonly Stopwatch _webSocketsClientPingStopWatch;
64 private readonly ScheduledContinuation _webSocketsServerResponseScheduledContinuation; 57 private readonly ScheduledContinuation _webSocketsServerResponseScheduledContinuation;
Line 65... Line 58...
65 58
Line 80... Line 73...
80 new DataflowBlockOptions 73 new DataflowBlockOptions
81 { 74 {
82 CancellationToken = _cancellationToken 75 CancellationToken = _cancellationToken
83 }); 76 });
Line 84... Line 77...
84   77  
85 var webSocketTransformBlock = new TransformBlock<GotifyConnectionData, GotifyMessage>(data => 78 var webSocketActionBlock = new ActionBlock<GotifyConnectionData>(async gotifyConnectionData =>
86 { 79 {
87 if (data.Payload == null || data.Payload.Length == 0) 80 try
-   81 {
88 { 82 using var memoryStream = new MemoryStream(gotifyConnectionData.Payload);
89 return null; 83 using var streamReader = new StreamReader(memoryStream);
Line 90... Line 84...
90 } 84 using var jsonTextReader = new JsonTextReader(streamReader);
Line 91... Line -...
91   -  
92 GotifyMessage gotifyNotification; -  
93   85  
Line 94... Line 86...
94 try 86 var gotifyMessage = _jsonSerializer.Deserialize<GotifyMessage>(jsonTextReader) ?? throw new ArgumentNullException();
Line 95... Line 87...
95 { 87  
96 var message = Encoding.UTF8.GetString(data.Payload, 0, data.Payload.Length); 88 gotifyMessage.Server = gotifyConnectionData.Server;
-   89  
-   90 using var imageStream = await RetrieveGotifyApplicationImage(gotifyMessage.AppId, _cancellationToken);
97   91  
98 gotifyNotification = JsonConvert.DeserializeObject<GotifyMessage>(message); 92 if (imageStream == null || imageStream.Length == 0)
Line -... Line 93...
-   93 {
-   94 Log.Warning("Could not find any application image for notification.");
99   95  
-   96 return;
-   97 }
-   98  
100 if (gotifyNotification == null) 99 var image = new Bitmap(imageStream);
101 { 100  
102 throw new ArgumentNullException(); 101 GotifyMessage?.Invoke(this,
103 } 102 new GotifyMessageEventArgs(gotifyMessage, image));
104   -  
105 gotifyNotification.Server = data.Server; -  
106 } 103  
107 catch (Exception exception) -  
108 { 104 Log.Debug($"Notification message received: {gotifyMessage.Message}");
109 Log.Warning(exception, "Could not deserialize notification."); -  
110   -  
111 return null; -  
112 } -  
113   -  
114 return gotifyNotification; -  
115   -  
116 }, new ExecutionDataflowBlockOptions { CancellationToken = _cancellationToken }); 105 }
117   106 catch (JsonSerializationException exception)
118 var webSocketActionBlock = new ActionBlock<GotifyMessage>(async message => -  
119 { -  
120 using var imageStream = await RetrieveGotifyApplicationImage(message.AppId, _cancellationToken); 107 {
Line 121... Line -...
121 if (imageStream == null || imageStream.Length == 0) -  
122 { -  
123 Log.Warning("Could not find any application image for notification."); -  
124   -  
125 return; -  
126 } -  
127   -  
128 var image = new Bitmap(imageStream); 108 Log.Warning(exception, "Could not deserialize notification message.");
Line 129... Line 109...
129   109 }
130 GotifyNotification?.Invoke(this, 110 catch (Exception exception)
131 new GotifyNotificationEventArgs(message, image)); -  
132   -  
133 Log.Debug($"Notification message received: {message.Message}"); -  
134   -  
135 }, new ExecutionDataflowBlockOptions { CancellationToken = _cancellationToken }); 111 {
Line 136... Line 112...
136   112 Log.Warning(exception, "Generic failure.");
137 _tplWebSocketsBufferBlockTransformLink = _webSocketMessageBufferBlock.LinkTo(webSocketTransformBlock, 113 }
138 new DataflowLinkOptions { PropagateCompletion = true }); 114  
Line 206... Line 182...
206 { 182 {
207 _tplWebSocketsBufferBlockTransformLink.Dispose(); 183 _tplWebSocketsBufferBlockTransformLink.Dispose();
208 _tplWebSocketsBufferBlockTransformLink = null; 184 _tplWebSocketsBufferBlockTransformLink = null;
209 } 185 }
Line 210... Line -...
210   -  
211 if (_tplWebSocketsTransformActionLink != null) -  
212 { -  
213 _tplWebSocketsTransformActionLink.Dispose(); -  
214 _tplWebSocketsTransformActionLink = null; -  
215 } -  
216   -  
217 if (_tplWebSocketsTransformActionNullLink != null) -  
218 { -  
219 _tplWebSocketsTransformActionNullLink.Dispose(); -  
220 _tplWebSocketsTransformActionNullLink = null; -  
221 } -  
222   186  
223 if (_tplRetrievePastMessagesLink != null) 187 if (_tplRetrievePastMessagesLink != null)
224 { 188 {
225 _tplRetrievePastMessagesLink.Dispose(); 189 _tplRetrievePastMessagesLink.Dispose();
226 _tplRetrievePastMessagesLink = null; 190 _tplRetrievePastMessagesLink = null;
Line 307... Line 271...
307 _webSocketsServerResponseScheduledContinuation.Schedule(TimeSpan.FromMinutes(1), OnUnresponsiveServer, _cancellationToken); 271 _webSocketsServerResponseScheduledContinuation.Schedule(TimeSpan.FromMinutes(1), OnUnresponsiveServer, _cancellationToken);
308 } 272 }
Line 309... Line 273...
309   273  
310 private void OnUnresponsiveServer() 274 private void OnUnresponsiveServer()
311 { 275 {
312 Log.Warning($"Server {_server} has not responded in a long while..."); 276 Log.Warning($"Server {_server.Name} has not responded in a long while...");
Line 313... Line 277...
313 } 277 }
314   278  
315 private async void WebSocketSharp_OnError(object sender, ErrorEventArgs e) 279 private async void WebSocketSharp_OnError(object sender, ErrorEventArgs e)
Line 327... Line 291...
327   291  
328 private async void WebSocketSharp_OnMessage(object sender, MessageEventArgs e) 292 private async void WebSocketSharp_OnMessage(object sender, MessageEventArgs e)
329 { 293 {
330 if (e.IsPing) 294 if (e.IsPing)
331 { 295 {
Line 332... Line 296...
332 Log.Information($"Server {_server} sent PING message"); 296 Log.Information($"Server {_server.Name} sent PING message");
333   297  
334 _webSocketsServerResponseScheduledContinuation.Schedule(TimeSpan.FromMinutes(1), OnUnresponsiveServer, _cancellationToken); 298 _webSocketsServerResponseScheduledContinuation.Schedule(TimeSpan.FromMinutes(1), OnUnresponsiveServer, _cancellationToken);
Line 380... Line 344...
380 Log.Error($"Could not get application message Uri {gotifyConnectionApplication.Application.Id}."); 344 Log.Error($"Could not get application message Uri {gotifyConnectionApplication.Application.Id}.");
Line 381... Line 345...
381   345  
382 return; 346 return;
Line 383... Line -...
383 } -  
384   347 }
385 GotifyMessageQuery gotifyMessageQuery; 348  
386 try 349 try
387 { 350 {
388 using var messageStream = await _httpClient.GetStreamAsync(combinedUri); 351 using var messageStream = await _httpClient.GetStreamAsync(combinedUri);
Line 389... Line 352...
389 using var streamReader = new StreamReader(messageStream); 352 using var streamReader = new StreamReader(messageStream);
-   353 using var jsonTextReader = new JsonTextReader(streamReader);
-   354  
-   355 var gotifyMessageQuery = _jsonSerializer.Deserialize<GotifyMessageQuery>(jsonTextReader) ??
-   356 throw new ArgumentNullException();
-   357  
-   358 if (gotifyMessageQuery.Messages == null)
-   359 {
-   360 Log.Warning("Invalid application messages deserialized deserialized.");
-   361  
-   362 return;
-   363 }
-   364  
-   365 foreach (var message in gotifyMessageQuery.Messages)
-   366 {
-   367 if (message.Date <
-   368 DateTime.Now - TimeSpan.FromHours(_configuration.RetrievePastNotificationHours))
-   369 {
-   370 continue;
-   371 }
-   372  
-   373 using var imageStream = await RetrieveGotifyApplicationImage(message.AppId, _cancellationToken);
-   374 if (imageStream == null || imageStream.Length == 0)
-   375 {
-   376 Log.Warning("Could not find any application image for notification.");
-   377  
-   378 continue;
-   379 }
-   380  
-   381 var image = new Bitmap(imageStream);
-   382 message.Server = gotifyConnectionApplication.Server;
-   383  
390 using var jsonTextReader = new JsonTextReader(streamReader); 384 GotifyMessage?.Invoke(this,
391   385 new GotifyMessageEventArgs(message, image));
392 gotifyMessageQuery = _jsonSerializer.Deserialize<GotifyMessageQuery>(jsonTextReader); 386 }
393 } 387 }
394 catch (HttpRequestException exception) -  
395 { -  
396 Log.Warning(exception, $"Could not get application {gotifyConnectionApplication.Application.Id}."); 388 catch (HttpRequestException exception)
397   389 {
398 return; 390 Log.Warning(exception, $"Could not get application {gotifyConnectionApplication.Application.Id}.");
399 } -  
400 catch (JsonSerializationException exception) -  
401 { 391 }
402 Log.Warning(exception,$"Could not deserialize the message response for application {gotifyConnectionApplication.Application.Id}."); -  
403   -  
404 return; -  
405 } -  
406   392 catch (JsonSerializationException exception)
407 if (gotifyMessageQuery == null || gotifyMessageQuery.Messages == null) -  
408 { -  
409 Log.Warning("Invalid application messages deserialized deserialized."); 393 {
410   -  
411 return; 394 Log.Warning(exception,
412 } 395 $"Could not deserialize the message response for application {gotifyConnectionApplication.Application.Id}.");
413   -  
414 foreach (var message in gotifyMessageQuery.Messages) -  
415 { -  
416 if (message.Date < DateTime.Now - TimeSpan.FromHours(_configuration.RetrievePastNotificationHours)) -  
417 { -  
418 continue; -  
419 } -  
420 -  
421 using var imageStream = await RetrieveGotifyApplicationImage(message.AppId, _cancellationToken); -  
422 if (imageStream == null || imageStream.Length == 0) -  
423 { -  
424 Log.Warning("Could not find any application image for notification."); -  
425   -  
426 continue; 396 }
427 } -  
428   -  
429 var image = new Bitmap(imageStream); -  
430 message.Server = gotifyConnectionApplication.Server; -  
431   397 catch (Exception exception)
432 GotifyNotification?.Invoke(this, -  
433 new GotifyNotificationEventArgs(message, image)); 398 {
Line 434... Line 399...
434 } 399 Log.Warning(exception, "Generic failure.");
435   400 }
Line 446... Line 411...
446 } 411 }
Line 447... Line 412...
447   412  
448 await Task.WhenAll(tasks); 413 await Task.WhenAll(tasks);
449 gotifyApplicationBufferBlock.Complete(); 414 gotifyApplicationBufferBlock.Complete();
450 await gotifyApplicationActionBlock.Completion; -  
451   415 await gotifyApplicationActionBlock.Completion;
Line 452... Line 416...
452 } 416 }
453   417  
454 private async Task HeartBeat(CancellationToken cancellationToken) 418 private async Task HeartBeat(CancellationToken cancellationToken)
Line 460... Line 424...
460 await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken); 424 await Task.Delay(TimeSpan.FromMinutes(1), cancellationToken);
Line 461... Line 425...
461   425  
462 _webSocketsClientPingStopWatch.Restart(); 426 _webSocketsClientPingStopWatch.Restart();
463 if (!_webSocketSharp.Ping()) 427 if (!_webSocketSharp.Ping())
464 { 428 {
465 Log.Warning($"Server {_server} did not respond to PING message."); 429 Log.Warning($"Server {_server.Name} did not respond to PING message.");
466 continue; 430 continue;
Line 467... Line 431...
467 } 431 }
Line 468... Line 432...
468   432  
Line 469... Line 433...
469 var delta = _webSocketsClientPingStopWatch.ElapsedMilliseconds; 433 var delta = _webSocketsClientPingStopWatch.ElapsedMilliseconds;
470   434  
471 Log.Information($"PING response latency for {_server} is {delta}ms"); 435 Log.Information($"PING response latency for {_server.Name} is {delta}ms");
472   436  
473 _webSocketsServerResponseScheduledContinuation.Schedule(TimeSpan.FromMinutes(1), OnUnresponsiveServer, _cancellationToken); 437 _webSocketsServerResponseScheduledContinuation.Schedule(TimeSpan.FromMinutes(1), OnUnresponsiveServer, _cancellationToken);
474 } while (!cancellationToken.IsCancellationRequested); 438 } while (!cancellationToken.IsCancellationRequested);
475 } 439 }
476 catch (Exception exception) when (exception is OperationCanceledException || 440 catch (Exception exception) when (exception is OperationCanceledException ||
477 exception is ObjectDisposedException) 441 exception is ObjectDisposedException)
478 { 442 {
479 } 443 }
480 catch (Exception exception) 444 catch (Exception exception)
Line 481... Line 445...
481 { 445 {
482 Log.Warning(exception, $"Heartbeat for server {_server} has failed due to {exception.Message}"); 446 Log.Warning(exception, $"Heartbeat for server {_server.Name} has failed.");
Line 535... Line 499...
535 continue; 499 continue;
536 } 500 }
Line 537... Line 501...
537   501  
538 try 502 try
539 { 503 {
Line 540... Line 504...
540 var imageResponse = await _httpClient.GetAsync(applicationImageUri, cancellationToken); 504 var imageResponse = await _httpClient.GetStreamAsync(applicationImageUri);
Line 541... Line 505...
541   505  
Line 542... Line 506...
542 var memoryStream = new MemoryStream(); 506 var memoryStream = new MemoryStream();
543   507  
544 await imageResponse.Content.CopyToAsync(memoryStream); 508 await imageResponse.CopyToAsync(memoryStream);
545   509  
546 return memoryStream; 510 return memoryStream;
547 } 511 }
548 catch (Exception exception) 512 catch (Exception exception)
Line 549... Line 513...
549 { 513 {
550 Log.Error($"Could not retrieve application image: {exception.Message}"); 514 Log.Error(exception,"Could not retrieve application image.");