Winify – Diff between revs 46 and 47

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