11
11
using System . Windows . Forms ;
12
12
using Flow . Launcher . Infrastructure . Logger ;
13
13
using Flow . Launcher . Plugin ;
14
+ using ICSharpCode . SharpZipLib . Zip ;
14
15
using JetBrains . Annotations ;
16
+ using Microsoft . IO ;
15
17
16
18
namespace Flow . Launcher . Core . Plugin
17
19
{
@@ -33,9 +35,11 @@ internal abstract class JsonRPCPlugin : IAsyncPlugin, IContextMenu
33
35
protected abstract string ExecuteCallback ( JsonRPCRequestModel rpcRequest ) ;
34
36
protected abstract string ExecuteContextMenu ( Result selectedResult ) ;
35
37
38
+ private static readonly RecyclableMemoryStreamManager BufferManager = new ( ) ;
39
+
36
40
public List < Result > LoadContextMenus ( Result selectedResult )
37
41
{
38
- string output = ExecuteContextMenu ( selectedResult ) ;
42
+ var output = ExecuteContextMenu ( selectedResult ) ;
39
43
try
40
44
{
41
45
return DeserializedResult ( output ) ;
@@ -61,12 +65,23 @@ private async Task<List<Result>> DeserializedResultAsync(Stream output)
61
65
{
62
66
if ( output == Stream . Null ) return null ;
63
67
64
- var queryResponseModel = await
65
- JsonSerializer . DeserializeAsync < JsonRPCQueryResponseModel > ( output , options ) ;
68
+ try
69
+ {
70
+ var queryResponseModel =
71
+ await JsonSerializer . DeserializeAsync < JsonRPCQueryResponseModel > ( output , options ) ;
66
72
67
- await output . DisposeAsync ( ) ;
68
-
69
- return ParseResults ( queryResponseModel ) ;
73
+ return ParseResults ( queryResponseModel ) ;
74
+ }
75
+ catch ( JsonException e )
76
+ {
77
+ Log . Exception ( GetType ( ) . FullName , "Unexpected Json Input" , e ) ;
78
+ }
79
+ finally
80
+ {
81
+ await output . DisposeAsync ( ) ;
82
+ }
83
+
84
+ return null ;
70
85
}
71
86
72
87
private List < Result > DeserializedResult ( string output )
@@ -81,15 +96,14 @@ private List<Result> DeserializedResult(string output)
81
96
82
97
private List < Result > ParseResults ( JsonRPCQueryResponseModel queryResponseModel )
83
98
{
84
- var results = new List < Result > ( ) ;
85
99
if ( queryResponseModel . Result == null ) return null ;
86
100
87
101
if ( ! string . IsNullOrEmpty ( queryResponseModel . DebugMessage ) )
88
102
{
89
103
context . API . ShowMsg ( queryResponseModel . DebugMessage ) ;
90
104
}
91
105
92
- foreach ( JsonRPCResult result in queryResponseModel . Result )
106
+ foreach ( var result in queryResponseModel . Result )
93
107
{
94
108
result . Action = c =>
95
109
{
@@ -114,7 +128,8 @@ private List<Result> ParseResults(JsonRPCQueryResponseModel queryResponseModel)
114
128
return ! result . JsonRPCAction . DontHideAfterAction ;
115
129
}
116
130
117
- var jsonRpcRequestModel = JsonSerializer . Deserialize < JsonRPCRequestModel > ( actionResponse , options ) ;
131
+ var jsonRpcRequestModel =
132
+ JsonSerializer . Deserialize < JsonRPCRequestModel > ( actionResponse , options ) ;
118
133
119
134
if ( jsonRpcRequestModel ? . Method ? . StartsWith ( "Flow.Launcher." ) ?? false )
120
135
{
@@ -125,9 +140,12 @@ private List<Result> ParseResults(JsonRPCQueryResponseModel queryResponseModel)
125
140
126
141
return ! result . JsonRPCAction . DontHideAfterAction ;
127
142
} ;
128
- results . Add ( result ) ;
129
143
}
130
144
145
+ var results = new List < Result > ( ) ;
146
+
147
+ results . AddRange ( queryResponseModel . Result ) ;
148
+
131
149
return results ;
132
150
}
133
151
@@ -217,16 +235,42 @@ protected string Execute(ProcessStartInfo startInfo)
217
235
218
236
protected async Task < Stream > ExecuteAsync ( ProcessStartInfo startInfo , CancellationToken token = default )
219
237
{
238
+ Process process = null ;
239
+ bool disposed = false ;
220
240
try
221
241
{
222
- using var process = Process . Start ( startInfo ) ;
242
+ process = Process . Start ( startInfo ) ;
223
243
if ( process == null )
224
244
{
225
245
Log . Error ( "|JsonRPCPlugin.ExecuteAsync|Can't start new process" ) ;
226
246
return Stream . Null ;
227
247
}
228
248
229
- var result = process . StandardOutput . BaseStream ;
249
+ await using var source = process . StandardOutput . BaseStream ;
250
+
251
+ var buffer = BufferManager . GetStream ( ) ;
252
+
253
+ token . Register ( ( ) =>
254
+ {
255
+ // ReSharper disable once AccessToModifiedClosure
256
+ // Manually Check whether disposed
257
+ if ( ! disposed && ! process . HasExited )
258
+ process . Kill ( ) ;
259
+ } ) ;
260
+
261
+ try
262
+ {
263
+ // token expire won't instantly trigger the exception,
264
+ // manually kill process at before
265
+ await source . CopyToAsync ( buffer , token ) ;
266
+ }
267
+ catch ( OperationCanceledException )
268
+ {
269
+ await buffer . DisposeAsync ( ) ;
270
+ return Stream . Null ;
271
+ }
272
+
273
+ buffer . Seek ( 0 , SeekOrigin . Begin ) ;
230
274
231
275
token . ThrowIfCancellationRequested ( ) ;
232
276
@@ -245,7 +289,7 @@ protected async Task<Stream> ExecuteAsync(ProcessStartInfo startInfo, Cancellati
245
289
return Stream . Null ;
246
290
}
247
291
248
- return result ;
292
+ return buffer ;
249
293
}
250
294
catch ( Exception e )
251
295
{
@@ -254,15 +298,24 @@ protected async Task<Stream> ExecuteAsync(ProcessStartInfo startInfo, Cancellati
254
298
e ) ;
255
299
return Stream . Null ;
256
300
}
301
+ finally
302
+ {
303
+ process ? . Dispose ( ) ;
304
+ disposed = true ;
305
+ }
257
306
}
258
307
259
308
public async Task < List < Result > > QueryAsync ( Query query , CancellationToken token )
260
309
{
261
- var output = await ExecuteQueryAsync ( query , token ) ;
262
310
try
263
311
{
312
+ var output = await ExecuteQueryAsync ( query , token ) ;
264
313
return await DeserializedResultAsync ( output ) ;
265
314
}
315
+ catch ( OperationCanceledException )
316
+ {
317
+ return null ;
318
+ }
266
319
catch ( Exception e )
267
320
{
268
321
Log . Exception ( $ "|JsonRPCPlugin.Query|Exception when query <{ query } >", e ) ;
0 commit comments