Winify – Diff between revs 39 and 44

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