Skip to content

Commit c1d284d

Browse files
committed
Change WebClient to HttpClient in pulses, and test cancellation
1 parent e0c0c62 commit c1d284d

2 files changed

Lines changed: 94 additions & 43 deletions

File tree

CodeStats/CodeStatsPackage.cs

Lines changed: 93 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class CodeStatsPackage
3939
public static string ApiKey;
4040
public static string ApiUrl;
4141
public static string Proxy;
42+
public static bool proxyChangePending = false;
4243
public static bool Stats;
4344
public static string Guid;
4445
public static List<Constants.DetectionType> DetectionOrder;
@@ -108,13 +109,12 @@ private static void InitializeAsync()
108109
try
109110
{
110111
currentPulse = new Pulse();
111-
pulseProcessor_httpClientHandler = new HttpClientHandler();
112112

113+
Logger.Info("Initialing settings form...");
113114
// Settings Form
114115
_settingsForm = new CodeStats.Forms.SettingsForm();
115116
_settingsForm.ConfigSaved += SettingsFormOnConfigSaved;
116-
117-
Logger.Info("Initialized settings form");
117+
Logger.Info("Initialized settings form"); // it takes 5 seconds to get here from Initializing Code::Stats message...
118118

119119
// Load config file
120120
_CodeStatsConfigFile = new ConfigFile();
@@ -147,6 +147,9 @@ private static void InitializeAsync()
147147

148148
// setup timer to process queued pulses
149149
pulseProcessor_tokensource = new CancellationTokenSource();
150+
pulseProcessor_httpClientHandler = new HttpClientHandler();
151+
pulseProcessor_httpClientHandler.Proxy = GetProxy();
152+
proxyChangePending = false;
150153
pulseProcessor_client = new HttpClient(pulseProcessor_httpClientHandler);
151154
timer.Interval = pulseFrequency;
152155
timer.Elapsed += ProcessPulses;
@@ -426,7 +429,14 @@ private static Task ProcessPulses(CancellationTokenSource tokenSource)
426429
return;
427430
}
428431

429-
var client = new WebClient { Proxy = CodeStatsPackage.GetProxy() };
432+
if (proxyChangePending)
433+
{
434+
pulseProcessor_httpClientHandler = new HttpClientHandler();
435+
pulseProcessor_httpClientHandler.Proxy = GetProxy();
436+
pulseProcessor_client = new HttpClient(pulseProcessor_httpClientHandler);
437+
proxyChangePending = false;
438+
}
439+
//var client = new WebClient { Proxy = CodeStatsPackage.GetProxy() };
430440
var jsonSerializer = new JavaScriptSerializer();
431441

432442
string URL;
@@ -440,74 +450,85 @@ private static Task ProcessPulses(CancellationTokenSource tokenSource)
440450
URL = ApiUrl;
441451
usesCustomEndpoint = true;
442452
}
443-
client.Headers["User-Agent"] = Constants.PluginUserAgent;
453+
/*client.Headers["User-Agent"] = Constants.PluginUserAgent;
444454
client.Headers["Content-Type"] = "application/json";
445-
client.Headers["Accept"] = "*/*";
446-
client.Headers["X-API-Token"] = ApiKey;
455+
client.Headers["Accept"] = "* /*";
456+
client.Headers["X-API-Token"] = ApiKey;*/
447457

448458
Pulse result;
449459
while (pulseQueue.TryDequeue(out result))
450460
{
451461
if (!result.isEmpty())
452462
{
453463
bool error = false;
464+
HttpResponseMessage response = null;
454465
// Try to pulse to API
455466
try
456467
{
457-
string json = jsonSerializer.Serialize(result);
468+
string json;
469+
var httpRequestMessage = new HttpRequestMessage
470+
{
471+
Method = HttpMethod.Post,
472+
RequestUri = new Uri(URL),
473+
Headers = {
474+
{ "User-Agent", Constants.PluginUserAgent },
475+
{ "Accept", "*/*" },
476+
{ "X-API-Token", ApiKey }
477+
},
478+
Content = new StringContent(json = jsonSerializer.Serialize(result), Encoding.UTF8, "application/json")
479+
};
480+
458481
Logger.Debug("Pulsing " + json);
459-
//string HtmlResult = client.UploadString(URL, json);
460-
string JsonResult = await client.UploadStringTaskAsync(URL, json);
461-
//client.UploadStringAsync()
482+
response = await pulseProcessor_client.SendAsync(httpRequestMessage, tokenSource.Token);
483+
response.EnsureSuccessStatusCode();
484+
string JsonResult = await response.Content.ReadAsStringAsync();
462485
_lastPulse = DateTime.Now;
463486
if (!JsonResult.Contains(@"""ok""") && !JsonResult.Contains(@"success"))
464487
{
465488
error = true;
466489
Logger.Error(@"Error pulsing, response does not contain ""ok"" or ""success"": " + JsonResult);
467490
}
468491
}
469-
catch (WebException ex)
492+
catch (TaskCanceledException)
493+
{
494+
pulseQueue.Enqueue(result); // requeue current pulse
495+
return;
496+
}
497+
catch (HttpRequestException ex)
470498
{
471499
error = true;
472-
if (ex.Status == WebExceptionStatus.ProtocolError)
500+
if (response != null && response.StatusCode != 0)
473501
{
474-
var response = ex.Response as HttpWebResponse;
475-
if (response != null)
502+
if ((int)response.StatusCode == 403)
476503
{
477-
if ((int)response.StatusCode == 403)
478-
{
479-
Logger.Error("Could not pulse (error 403). Please make sure you entered a valid API token in Code::Stats settings.", ex);
480-
if (!_hasAlreadyShownInvalidApiTokenMessage) // we want to inform user only once, and if they do not provide the token, let's not bomb him with error each time after they type something
481-
{
482-
_hasAlreadyShownInvalidApiTokenMessage = true;
483-
MessageBox.Show("Could not pulse. Please make sure you entered a valid API token in Code::Stats settings.\nAll recorded XP from this session will be lost if you do not provide the correct API token!", "Code::Stats – error 403", MessageBoxButtons.OK, MessageBoxIcon.Error);
484-
PromptApiKey();
485-
}
486-
}
487-
else if ((int)response.StatusCode == 404 && usesCustomEndpoint)
504+
Logger.Error("Could not pulse (error 403). Please make sure you entered a valid API token in Code::Stats settings.", ex);
505+
if (!_hasAlreadyShownInvalidApiTokenMessage) // we want to inform user only once, and if they do not provide the token, let's not bomb him with error each time after they type something
488506
{
489-
Logger.Error("Could not pulse (error 404). The entered custom endpoint (" + URL + ") is invalid. ", ex);
490-
MessageBox.Show("Could not pulse. Invalid API endpoint URL. Please make sure you entered a valid API URL in Code::Stats settings or delete the value altogether to restore the default.\nAll recorded XP from this session will be lost if you do not provide the correct API URL path!", "Code::Stats – error 404", MessageBoxButtons.OK, MessageBoxIcon.Error);
491-
492-
//_settingsForm.txtAPIURL.Focus();
493-
//_settingsForm.txtAPIURL.SelectAll();
494-
_settingsForm.FocusTxtAPIURL();
495-
_settingsForm.ShowAPIURLTooltip();
496-
SettingsPopup();
497-
_settingsForm.ShowAPIURLTooltip();
498-
}
499-
else
500-
{
501-
Logger.Error("Could not pulse - HTTP error " + (int)response.StatusCode + ". Server response: " + response.GetResponseStream().ToString(), ex);
507+
_hasAlreadyShownInvalidApiTokenMessage = true;
508+
MessageBox.Show("Could not pulse. Please make sure you entered a valid API token in Code::Stats settings.\nAll recorded XP from this session will be lost if you do not provide the correct API token!", "Code::Stats – error 403", MessageBoxButtons.OK, MessageBoxIcon.Error);
509+
PromptApiKey();
502510
}
503511
}
512+
else if ((int)response.StatusCode == 404 && usesCustomEndpoint)
513+
{
514+
Logger.Error("Could not pulse (error 404). The entered custom endpoint (" + URL + ") is invalid. ", ex);
515+
MessageBox.Show("Could not pulse. Invalid API endpoint URL. Please make sure you entered a valid API URL in Code::Stats settings or delete the value altogether to restore the default.\nAll recorded XP from this session will be lost if you do not provide the correct API URL path!", "Code::Stats – error 404", MessageBoxButtons.OK, MessageBoxIcon.Error);
516+
517+
_settingsForm.FocusTxtAPIURL();
518+
_settingsForm.ShowAPIURLTooltip();
519+
SettingsPopup();
520+
_settingsForm.ShowAPIURLTooltip();
521+
}
504522
else
505523
{
506-
// response==null - no http status code available
507-
Logger.Error("Could not pulse. Are you behind a proxy? Try setting a proxy in Code::Stats settings with format https://user:pass@host:port. Exception Traceback", ex);
524+
Logger.Error("Could not pulse - HTTP error " + (int)response.StatusCode + ". Server response: " + response.Content.ReadAsStringAsync().Result, ex);
508525
}
509526
}
510-
else Logger.Error("Could not pulse. Are you behind a proxy? Try setting a proxy in Code::Stats settings with format https://user:pass@host:port. Exception Traceback", ex);
527+
else
528+
{
529+
// response==null - no http status code available
530+
Logger.Error("Could not pulse. Are you behind a proxy? Try setting a proxy in Code::Stats settings with format https://user:pass@host:port. Exception Traceback", ex);
531+
}
511532
}
512533
catch (Exception ex)
513534
{
@@ -522,6 +543,9 @@ private static Task ProcessPulses(CancellationTokenSource tokenSource)
522543
}
523544

524545
}
546+
547+
if (tokenSource.Token.IsCancellationRequested)
548+
tokenSource.Token.ThrowIfCancellationRequested();
525549
}
526550
}
527551
}, tokenSource.Token);
@@ -699,7 +723,7 @@ public static void GetSettings(bool skipRead = false)
699723
CodeStatsPackage.Guid = _CodeStatsConfigFile.Guid;
700724
DetectionOrder = _CodeStatsConfigFile.DetectionOrder;
701725

702-
pulseProcessor_httpClientHandler.Proxy = GetProxy();
726+
proxyChangePending = true;
703727
}
704728

705729
private static void PromptApiKey()
@@ -823,6 +847,18 @@ internal static void PluginCleanUp()
823847
{
824848
nppStarted = false;
825849

850+
Logger.Debug("Cancelling pulses...");
851+
852+
// Flush the current pulse
853+
if (pulseQueue != null && currentPulse != null && !currentPulse.isEmpty())
854+
{
855+
pulseQueue.Enqueue(currentPulse);
856+
currentPulse = new Pulse();
857+
currentCount = 0;
858+
}
859+
860+
pulseProcessor_tokensource.Cancel();
861+
826862
if (timer != null)
827863
{
828864
timer.Stop();
@@ -833,6 +869,20 @@ internal static void PluginCleanUp()
833869
// make sure the queue is empty
834870
//ProcessPulses();
835871
}
872+
873+
// test if we can cancel and dump pulses
874+
var jsonSerializer = new JavaScriptSerializer();
875+
pulseProcessor.Wait();
876+
Pulse result;
877+
while (pulseQueue.TryDequeue(out result))
878+
{
879+
if (!result.isEmpty())
880+
{
881+
string json = jsonSerializer.Serialize(result);
882+
Logger.Debug("Unsaved pulse: " + json);
883+
}
884+
}
836885
}
886+
837887
}
838888
}

CodeStats/Resources/extension_mapping.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
"shellscript": "Shell Script",
6969
"sql": "SQL",
7070
"swift": "Swift",
71+
"ts": "TypeScript",
7172
"typescript": "TypeScript",
7273
"typescriptreact": "TypeScript (React)",
7374
"vb": "Visual Basic",

0 commit comments

Comments
 (0)