Winify – Blame information for rev 26

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.Net.WebSockets;
7 using System.Text;
8 using System.Threading;
9 using System.Threading.Tasks;
10 using Newtonsoft.Json;
18 office 11 using Serilog;
24 office 12 using Servers;
1 office 13 using ClientWebSocket = System.Net.WebSockets.Managed.ClientWebSocket;
14  
15 namespace Winify.Gotify
16 {
17 public class GotifyConnection : IDisposable
18 {
19 #region Public Events & Delegates
20  
21 public event EventHandler<GotifyNotificationEventArgs> GotifyNotification;
22  
23 #endregion
24  
25 #region Private Delegates, Events, Enums, Properties, Indexers and Fields
26  
25 office 27 private readonly Server _server;
28  
1 office 29 private CancellationToken _cancellationToken;
30  
31 private CancellationTokenSource _cancellationTokenSource;
32  
33 private Task _runTask;
34  
25 office 35 #endregion
1 office 36  
25 office 37 #region Constructors, Destructors and Finalizers
24 office 38  
39 public GotifyConnection(Server server)
40 {
41 _server = server;
42 }
43  
1 office 44 public void Dispose()
45 {
46 if (_cancellationTokenSource != null)
47 {
48 _cancellationTokenSource.Dispose();
49 _cancellationTokenSource = null;
50 }
51 }
52  
53 #endregion
54  
55 #region Public Methods
56  
25 office 57 public void Start()
1 office 58 {
25 office 59 if (!Uri.TryCreate(_server.Url, UriKind.Absolute, out var httpUri))
1 office 60 {
61 return;
62 }
63  
12 office 64 // Build the web sockets URI.
65 var webSocketsUriBuilder = new UriBuilder(httpUri);
66 webSocketsUriBuilder.Scheme = "ws";
67 webSocketsUriBuilder.Path = $"{webSocketsUriBuilder.Path}/stream";
68 var webSocketsUri = webSocketsUriBuilder.Uri;
1 office 69  
70 _cancellationTokenSource = new CancellationTokenSource();
71 _cancellationToken = _cancellationTokenSource.Token;
72  
25 office 73 _runTask = Run(webSocketsUri, httpUri, _server.Username, _server.Password, _cancellationToken);
1 office 74 }
75  
76 public void Stop()
77 {
78 if (_cancellationTokenSource != null)
79 {
80 _cancellationTokenSource.Cancel();
81 }
82 }
83  
84 #endregion
85  
86 #region Private Methods
87  
12 office 88 private async Task Run(Uri webSocketsUri, Uri httpUri, string username, string password,
89 CancellationToken cancellationToken)
1 office 90 {
91 try
92 {
93 do
94 {
95 try
96 {
25 office 97 using (var webSocketClient = new ClientWebSocket())
21 office 98 {
99 var auth = Convert.ToBase64String(Encoding.Default.GetBytes($"{username}:{password}"));
18 office 100  
25 office 101 webSocketClient.Options.SetRequestHeader("Authorization", $"Basic {auth}");
18 office 102  
25 office 103 await webSocketClient.ConnectAsync(webSocketsUri, cancellationToken);
1 office 104  
21 office 105 do
1 office 106 {
21 office 107 var payload = new ArraySegment<byte>(new byte[1024]);
1 office 108  
25 office 109 var result = await webSocketClient.ReceiveAsync(payload, cancellationToken);
1 office 110  
21 office 111 if (result.Count == 0)
112 {
113 continue;
114 }
1 office 115  
21 office 116 if (payload.Array == null || payload.Count == 0)
117 {
118 continue;
119 }
1 office 120  
21 office 121 var message = Encoding.UTF8.GetString(payload.Array, 0, payload.Count);
12 office 122  
25 office 123 if (!(JsonConvert.DeserializeObject<GotifyNotification>(message) is GotifyNotification
124 gotifyNotification))
1 office 125 {
25 office 126 Log.Warning($"Could not deserialize gotify notification: {message}");
127  
1 office 128 continue;
129 }
130  
24 office 131 gotifyNotification.Server = _server;
132  
25 office 133 if (!Uri.TryCreate($"{httpUri}/application", UriKind.Absolute,
134 out var applicationUri))
12 office 135 {
136 continue;
137 }
3 office 138  
25 office 139 var image = await RetrieveGotifyApplicationImage(gotifyNotification.AppId, httpUri,
140 applicationUri, auth, cancellationToken);
12 office 141  
25 office 142 GotifyNotification?.Invoke(this,
143 new GotifyNotificationEventArgs(gotifyNotification, image));
3 office 144  
21 office 145 Log.Debug($"Notification message received: {gotifyNotification.Message}");
146 } while (!cancellationToken.IsCancellationRequested);
147 }
1 office 148 }
12 office 149 catch (Exception ex) when (ex is WebSocketException || ex is HttpRequestException)
1 office 150 {
25 office 151 // Reconnect
18 office 152 Log.Warning($"Unable to connect to gotify server: {ex.Message}");
12 office 153  
1 office 154 await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
155 }
156 } while (!cancellationToken.IsCancellationRequested);
157 }
158 catch (Exception ex) when (ex is OperationCanceledException || ex is ObjectDisposedException)
159 {
160 }
161 catch (Exception ex)
162 {
18 office 163 Log.Warning(ex, "Failure running connection loop.");
1 office 164 }
165 }
166  
25 office 167 private static async Task<Image> RetrieveGotifyApplicationImage(int appId, Uri httpUri, Uri applicationUri,
168 string auth,
169 CancellationToken cancellationToken)
170 {
171 using (var httpClient = new HttpClient())
172 {
173 httpClient.DefaultRequestHeaders.Authorization =
174 new AuthenticationHeaderValue("Basic", auth);
175  
176 var applicationResponse = await httpClient.GetAsync(applicationUri, cancellationToken);
177  
178 var applications = await applicationResponse.Content.ReadAsStringAsync();
179  
180 var gotifyApplications =
181 JsonConvert.DeserializeObject<GotifyApplication[]>(applications);
182  
183 if (gotifyApplications == null)
184 {
185 return null;
186 }
187  
188 foreach (var application in gotifyApplications)
189 {
190 if (application.Id != appId)
191 {
192 continue;
193 }
194  
195 if (!Uri.TryCreate($"{httpUri}/{application.Image}", UriKind.Absolute,
196 out var applicationImageUri))
197 {
198 continue;
199 }
200  
201 var imageResponse = await httpClient.GetAsync(applicationImageUri, cancellationToken);
202  
203 using (var memoryStream = new MemoryStream())
204 {
205 await imageResponse.Content.CopyToAsync(memoryStream);
206  
207 return Image.FromStream(memoryStream);
208 }
209 }
210 }
211  
212 return null;
213 }
214  
1 office 215 #endregion
216 }
217 }