Skip to content

Commit 76d23ab

Browse files
committed
Suppress JsonRPC Empty Response Exception and refactor code
1 parent b122dc3 commit 76d23ab

File tree

1 file changed

+124
-120
lines changed

1 file changed

+124
-120
lines changed

Flow.Launcher.Core/Plugin/JsonRPCPlugin.cs

Lines changed: 124 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public List<Result> LoadContextMenus(Result selectedResult)
6969
private static readonly JsonSerializerOptions options = new()
7070
{
7171
PropertyNameCaseInsensitive = true,
72-
#pragma warning disable SYSLIB0020
72+
#pragma warning disable SYSLIB0020
7373
// IgnoreNullValues is obsolete, but the replacement JsonIgnoreCondition.WhenWritingNull still
7474
// deserializes null, instead of ignoring it and leaving the default (empty list). We can change the behaviour
7575
// to accept null and fallback to a default etc, or just keep IgnoreNullValues for now
@@ -247,74 +247,75 @@ protected string Execute(ProcessStartInfo startInfo)
247247

248248
protected async Task<Stream> ExecuteAsync(ProcessStartInfo startInfo, CancellationToken token = default)
249249
{
250-
Process process = null;
251-
using var exitTokenSource = new CancellationTokenSource();
252-
try
250+
using var process = Process.Start(startInfo);
251+
if (process == null)
253252
{
254-
process = Process.Start(startInfo);
255-
if (process == null)
256-
{
257-
Log.Error("|JsonRPCPlugin.ExecuteAsync|Can't start new process");
258-
return Stream.Null;
259-
}
260-
261-
262-
await using var source = process.StandardOutput.BaseStream;
263-
264-
var buffer = BufferManager.GetStream();
265-
266-
token.Register(() =>
267-
{
268-
// ReSharper disable once AccessToModifiedClosure
269-
// Manually Check whether disposed
270-
if (!exitTokenSource.IsCancellationRequested && !process.HasExited)
271-
process.Kill();
272-
});
253+
Log.Error("|JsonRPCPlugin.ExecuteAsync|Can't start new process");
254+
return Stream.Null;
255+
}
273256

257+
token.Register(() =>
258+
{
274259
try
275260
{
276-
// token expire won't instantly trigger the exception,
277-
// manually kill process at before
278-
await source.CopyToAsync(buffer, token);
261+
if (!process.HasExited)
262+
process.Kill();
279263
}
280-
catch (OperationCanceledException)
264+
catch (InvalidOperationException)
281265
{
282-
await buffer.DisposeAsync();
283-
return Stream.Null;
284266
}
267+
});
285268

286-
buffer.Seek(0, SeekOrigin.Begin);
287269

288-
token.ThrowIfCancellationRequested();
270+
var sourceBuffer = BufferManager.GetStream();
271+
var errorBuffer = BufferManager.GetStream();
289272

290-
if (buffer.Length == 0)
291-
{
292-
var errorMessage = process.StandardError.EndOfStream ?
293-
"Empty JSONRPC Response" :
294-
await process.StandardError.ReadToEndAsync();
295-
throw new InvalidDataException($"{context.CurrentPluginMetadata.Name}|{errorMessage}");
296-
}
273+
var sourceCopyTask = process.StandardOutput.BaseStream.CopyToAsync(sourceBuffer, token);
274+
var errorCopyTask = process.StandardError.BaseStream.CopyToAsync(errorBuffer, token);
297275

298-
if (!process.StandardError.EndOfStream)
299-
{
300-
using var standardError = process.StandardError;
301-
var error = await standardError.ReadToEndAsync();
302276

303-
if (!string.IsNullOrEmpty(error))
304-
{
305-
Log.Error($"|{context.CurrentPluginMetadata.Name}.{nameof(ExecuteAsync)}|{error}");
306-
}
307-
}
277+
try
278+
{
279+
// token expire won't instantly trigger the exception,
280+
// manually kill process at before
281+
await process.WaitForExitAsync(token);
282+
await Task.WhenAll(sourceCopyTask, errorCopyTask);
283+
}
284+
catch (OperationCanceledException)
285+
{
286+
await sourceBuffer.DisposeAsync();
287+
return Stream.Null;
288+
}
289+
290+
sourceBuffer.Seek(0, SeekOrigin.Begin);
291+
292+
token.ThrowIfCancellationRequested();
308293

309-
return buffer;
294+
if (sourceBuffer.Length == 0)
295+
{
296+
var errorMessage = errorBuffer.Length == 0 ?
297+
"Empty JSONRPC Response" :
298+
await process.StandardError.ReadToEndAsync();
299+
300+
Log.Error($"{context.CurrentPluginMetadata.Name}|{errorMessage}");
310301
}
311-
finally
302+
303+
if (errorBuffer.Length != 0)
312304
{
313-
exitTokenSource.Cancel();
314-
process?.Dispose();
305+
using var error = new StreamReader(errorBuffer);
306+
307+
var errorMessage = await error.ReadToEndAsync();
308+
309+
if (!string.IsNullOrEmpty(errorMessage))
310+
{
311+
Log.Error($"|{context.CurrentPluginMetadata.Name}.{nameof(ExecuteAsync)}|{errorMessage}");
312+
}
315313
}
314+
315+
return sourceBuffer;
316316
}
317317

318+
318319
public async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
319320
{
320321
var request = new JsonRPCRequestModel
@@ -366,6 +367,7 @@ public virtual async Task InitAsync(PluginInitContext context)
366367
private static readonly Thickness settingPanelMargin = new(15, 20, 15, 20);
367368
private static readonly Thickness settingTextBlockMargin = new(10, 4, 10, 4);
368369
private JsonRpcConfigurationModel _settingsTemplate;
370+
369371
public Control CreateSettingPanel()
370372
{
371373
if (Settings == null)
@@ -397,84 +399,84 @@ public Control CreateSettingPanel()
397399
switch (type)
398400
{
399401
case "textBlock":
402+
{
403+
contentControl = new TextBlock
400404
{
401-
contentControl = new TextBlock
402-
{
403-
Text = attribute.Description.Replace("\\r\\n", "\r\n"),
404-
Margin = settingTextBlockMargin,
405-
MaxWidth = 500,
406-
TextWrapping = TextWrapping.WrapWithOverflow
407-
};
408-
break;
409-
}
405+
Text = attribute.Description.Replace("\\r\\n", "\r\n"),
406+
Margin = settingTextBlockMargin,
407+
MaxWidth = 500,
408+
TextWrapping = TextWrapping.WrapWithOverflow
409+
};
410+
break;
411+
}
410412
case "input":
413+
{
414+
var textBox = new TextBox()
411415
{
412-
var textBox = new TextBox()
413-
{
414-
Width = 300,
415-
Text = Settings[attribute.Name] as string ?? string.Empty,
416-
Margin = settingControlMargin,
417-
ToolTip = attribute.Description
418-
};
419-
textBox.TextChanged += (_, _) =>
420-
{
421-
Settings[attribute.Name] = textBox.Text;
422-
};
423-
contentControl = textBox;
424-
break;
425-
}
416+
Width = 300,
417+
Text = Settings[attribute.Name] as string ?? string.Empty,
418+
Margin = settingControlMargin,
419+
ToolTip = attribute.Description
420+
};
421+
textBox.TextChanged += (_, _) =>
422+
{
423+
Settings[attribute.Name] = textBox.Text;
424+
};
425+
contentControl = textBox;
426+
break;
427+
}
426428
case "textarea":
429+
{
430+
var textBox = new TextBox()
427431
{
428-
var textBox = new TextBox()
429-
{
430-
Width = 300,
431-
Height = 120,
432-
Margin = settingControlMargin,
433-
TextWrapping = TextWrapping.WrapWithOverflow,
434-
AcceptsReturn = true,
435-
Text = Settings[attribute.Name] as string ?? string.Empty,
436-
ToolTip = attribute.Description
437-
};
438-
textBox.TextChanged += (sender, _) =>
439-
{
440-
Settings[attribute.Name] = ((TextBox)sender).Text;
441-
};
442-
contentControl = textBox;
443-
break;
444-
}
432+
Width = 300,
433+
Height = 120,
434+
Margin = settingControlMargin,
435+
TextWrapping = TextWrapping.WrapWithOverflow,
436+
AcceptsReturn = true,
437+
Text = Settings[attribute.Name] as string ?? string.Empty,
438+
ToolTip = attribute.Description
439+
};
440+
textBox.TextChanged += (sender, _) =>
441+
{
442+
Settings[attribute.Name] = ((TextBox)sender).Text;
443+
};
444+
contentControl = textBox;
445+
break;
446+
}
445447
case "passwordBox":
448+
{
449+
var passwordBox = new PasswordBox()
446450
{
447-
var passwordBox = new PasswordBox()
448-
{
449-
Width = 300,
450-
Margin = settingControlMargin,
451-
Password = Settings[attribute.Name] as string ?? string.Empty,
452-
PasswordChar = attribute.passwordChar == default ? '*' : attribute.passwordChar,
453-
ToolTip = attribute.Description
454-
};
455-
passwordBox.PasswordChanged += (sender, _) =>
456-
{
457-
Settings[attribute.Name] = ((PasswordBox)sender).Password;
458-
};
459-
contentControl = passwordBox;
460-
break;
461-
}
451+
Width = 300,
452+
Margin = settingControlMargin,
453+
Password = Settings[attribute.Name] as string ?? string.Empty,
454+
PasswordChar = attribute.passwordChar == default ? '*' : attribute.passwordChar,
455+
ToolTip = attribute.Description
456+
};
457+
passwordBox.PasswordChanged += (sender, _) =>
458+
{
459+
Settings[attribute.Name] = ((PasswordBox)sender).Password;
460+
};
461+
contentControl = passwordBox;
462+
break;
463+
}
462464
case "dropdown":
465+
{
466+
var comboBox = new ComboBox()
463467
{
464-
var comboBox = new ComboBox()
465-
{
466-
ItemsSource = attribute.Options,
467-
SelectedItem = Settings[attribute.Name],
468-
Margin = settingControlMargin,
469-
ToolTip = attribute.Description
470-
};
471-
comboBox.SelectionChanged += (sender, _) =>
472-
{
473-
Settings[attribute.Name] = (string)((ComboBox)sender).SelectedItem;
474-
};
475-
contentControl = comboBox;
476-
break;
477-
}
468+
ItemsSource = attribute.Options,
469+
SelectedItem = Settings[attribute.Name],
470+
Margin = settingControlMargin,
471+
ToolTip = attribute.Description
472+
};
473+
comboBox.SelectionChanged += (sender, _) =>
474+
{
475+
Settings[attribute.Name] = (string)((ComboBox)sender).SelectedItem;
476+
};
477+
contentControl = comboBox;
478+
break;
479+
}
478480
case "checkbox":
479481
var checkBox = new CheckBox
480482
{
@@ -499,6 +501,7 @@ public Control CreateSettingPanel()
499501
}
500502
return settingWindow;
501503
}
504+
502505
public void Save()
503506
{
504507
if (Settings != null)
@@ -541,4 +544,5 @@ public void UpdateSettings(Dictionary<string, object> settings)
541544
}
542545
}
543546
}
547+
544548
}

0 commit comments

Comments
 (0)