Winify – Blame information for rev 18

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