@@ -209,33 +209,42 @@ public async Task UpdateRootComponents(string serializedComponentOperations, str
209
209
_ = circuitHost . UpdateRootComponents ( operations , store , Context . ConnectionAborted ) ;
210
210
}
211
211
212
- public async ValueTask < bool > ConnectCircuit ( string circuitIdSecret )
212
+ public void ConnectCircuit ( string circuitIdSecret )
213
213
{
214
214
// TryParseCircuitId will not throw.
215
215
if ( ! _circuitIdFactory . TryParseCircuitId ( circuitIdSecret , out var circuitId ) )
216
216
{
217
217
// Invalid id.
218
218
Log . InvalidCircuitId ( _logger , circuitIdSecret ) ;
219
- return false ;
219
+ _ = Clients . Caller . SendAsync ( "JS.EndConnectCircuit" , false ) ;
220
+ return ;
220
221
}
221
222
222
- // ConnectAsync will not throw.
223
- var circuitHost = await _circuitRegistry . ConnectAsync (
224
- circuitId ,
225
- Clients . Caller ,
226
- Context . ConnectionId ,
227
- Context . ConnectionAborted ) ;
228
- if ( circuitHost != null )
223
+ _ = ConnectCircuitCore ( circuitId ) ;
224
+
225
+ return ;
226
+
227
+ async Task ConnectCircuitCore ( CircuitId circuitId )
229
228
{
230
- _circuitHandleRegistry . SetCircuit ( Context . Items , CircuitKey , circuitHost ) ;
231
- circuitHost . SetCircuitUser ( Context . User ) ;
232
- circuitHost . SendPendingBatches ( ) ;
233
- return true ;
234
- }
229
+ // ConnectAsync will not throw.
230
+ var circuitHost = await _circuitRegistry . ConnectAsync (
231
+ circuitId ,
232
+ Clients . Caller ,
233
+ Context . ConnectionId ,
234
+ Context . ConnectionAborted ) ;
235
+ if ( circuitHost != null )
236
+ {
237
+ _circuitHandleRegistry . SetCircuit ( Context . Items , CircuitKey , circuitHost ) ;
238
+ circuitHost . SetCircuitUser ( Context . User ) ;
239
+ circuitHost . SendPendingBatches ( ) ;
240
+ _ = Clients . Caller . SendAsync ( "JS.EndConnectCircuit" , true ) ;
241
+ return ;
242
+ }
235
243
236
- // If we get here the circuit does not exist anymore. This is something that's valid for a client to
237
- // recover from, and the client is not holding any resources right now other than the connection.
238
- return false ;
244
+ // If we get here the circuit does not exist anymore. This is something that's valid for a client to
245
+ // recover from, and the client is not holding any resources right now other than the connection.
246
+ _ = Clients . Caller . SendAsync ( "JS.EndConnectCircuit" , false ) ;
247
+ }
239
248
}
240
249
241
250
// This method drives the resumption of a circuit that has been previously paused and ejected out of memory.
@@ -266,10 +275,10 @@ public async ValueTask<bool> ConnectCircuit(string circuitIdSecret)
266
275
// * B might complete the handshake and then the circuit will resume on B.
267
276
// * A deletes the state before B is able to read it. Then "resumption" fails, as the circuit state is gone.
268
277
269
- // On the server we are going to have a public method on Circuit.cs to trigger pausing a circuit from the server
270
- // that returns the root components and application state as strings data-protected by the data protection provider.
271
- // Those can be then passed to this method for resuming the circuit.
272
- public async ValueTask < string > ResumeCircuit (
278
+ // On the server we are going to have a public method on Circuit.cs to trigger pausing a circuit from the server
279
+ // that returns the root components and application state as strings data-protected by the data protection provider.
280
+ // Those can be then passed to this method for resuming the circuit.
281
+ public async void ResumeCircuit (
273
282
string circuitIdSecret ,
274
283
string baseUri ,
275
284
string uri ,
@@ -281,7 +290,8 @@ public async ValueTask<string> ResumeCircuit(
281
290
{
282
291
// Invalid id.
283
292
Log . ResumeInvalidCircuitId ( _logger , circuitIdSecret ) ;
284
- return null ;
293
+ _ = Clients . Caller . SendAsync ( "JS.EndResumeCircuit" , ( string ) null , Context . ConnectionAborted ) ;
294
+ return ;
285
295
}
286
296
287
297
var circuitHost = _circuitHandleRegistry . GetCircuit ( Context . Items , CircuitKey ) ;
@@ -291,8 +301,9 @@ public async ValueTask<string> ResumeCircuit(
291
301
// We can reject this and terminate the connection.
292
302
Log . CircuitAlreadyInitialized ( _logger , circuitHost . CircuitId ) ;
293
303
await NotifyClientError ( Clients . Caller , $ "The circuit host '{ circuitHost . CircuitId } ' has already been initialized.") ;
304
+ _ = Clients . Caller . SendAsync ( "JS.EndResumeCircuit" , ( string ) null , Context . ConnectionAborted ) ;
294
305
Context . Abort ( ) ;
295
- return null ;
306
+ return ;
296
307
}
297
308
298
309
if ( baseUri == null ||
@@ -307,92 +318,100 @@ public async ValueTask<string> ResumeCircuit(
307
318
// We can reject this and terminate the connection.
308
319
Log . InvalidInputData ( _logger ) ;
309
320
await NotifyClientError ( Clients . Caller , "The uris provided are invalid." ) ;
321
+ _ = Clients . Caller . SendAsync ( "JS.EndResumeCircuit" , ( string ) null , Context . ConnectionAborted ) ;
310
322
Context . Abort ( ) ;
311
- return null ;
323
+ return ;
312
324
}
313
325
314
- PersistedCircuitState ? persistedCircuitState ;
315
- if ( RootComponentIsEmpty ( rootComponents ) && string . IsNullOrEmpty ( applicationState ) )
326
+ _ = ResumeCircuitCore ( baseUri , uri , rootComponents , applicationState , circuitId ) ;
327
+ return ;
328
+
329
+ static bool RootComponentIsEmpty ( string rootComponents ) =>
330
+ string . IsNullOrEmpty ( rootComponents ) || rootComponents == "[]" ;
331
+
332
+ async Task ResumeCircuitCore ( string baseUri , string uri , string rootComponents , string applicationState , CircuitId circuitId )
316
333
{
317
- persistedCircuitState = await _circuitPersistenceManager . ResumeCircuitAsync ( circuitId , Context . ConnectionAborted ) ;
318
- if ( persistedCircuitState == null )
334
+ PersistedCircuitState ? persistedCircuitState ;
335
+ if ( RootComponentIsEmpty ( rootComponents ) && string . IsNullOrEmpty ( applicationState ) )
319
336
{
320
- Log . InvalidInputData ( _logger ) ;
321
- await NotifyClientError ( Clients . Caller , "The circuit state could not be retrieved. It may have been deleted or expired." ) ;
322
- Context . Abort ( ) ;
323
- return null ;
337
+ var resumeTask = _circuitPersistenceManager . ResumeCircuitAsync ( circuitId , Context . ConnectionAborted ) ;
338
+ persistedCircuitState = await resumeTask ;
339
+ if ( persistedCircuitState == null )
340
+ {
341
+ Log . InvalidInputData ( _logger ) ;
342
+ await NotifyClientError ( Clients . Caller , "The circuit state could not be retrieved. It may have been deleted or expired." ) ;
343
+ Context . Abort ( ) ;
344
+ return ;
345
+ }
324
346
}
325
- }
326
- else if ( ! RootComponentIsEmpty ( rootComponents ) && ! string . IsNullOrEmpty ( applicationState ) )
327
- {
328
- persistedCircuitState = _circuitPersistenceManager . FromProtectedState ( rootComponents , applicationState ) ;
329
- if ( persistedCircuitState == null )
347
+ else if ( ! RootComponentIsEmpty ( rootComponents ) && ! string . IsNullOrEmpty ( applicationState ) )
348
+ {
349
+ persistedCircuitState = _circuitPersistenceManager . FromProtectedState ( rootComponents , applicationState ) ;
350
+ if ( persistedCircuitState == null )
351
+ {
352
+ // If we couldn't deserialize the persisted state, signal that.
353
+ Log . InvalidInputData ( _logger ) ;
354
+ await NotifyClientError ( Clients . Caller , "The root components or application state provided are invalid." ) ;
355
+ Context . Abort ( ) ;
356
+ return ;
357
+ }
358
+ }
359
+ else
330
360
{
331
- // If we couldn't deserialize the persisted state, signal that.
332
361
Log . InvalidInputData ( _logger ) ;
333
- await NotifyClientError ( Clients . Caller , "The root components or application state provided are invalid." ) ;
362
+ await NotifyClientError (
363
+ Clients . Caller ,
364
+ RootComponentIsEmpty ( rootComponents ) ?
365
+ "The root components provided are invalid." :
366
+ "The application state provided is invalid."
367
+ ) ;
334
368
Context . Abort ( ) ;
335
- return null ;
369
+ return ;
336
370
}
337
- }
338
- else
339
- {
340
- Log . InvalidInputData ( _logger ) ;
341
- await NotifyClientError (
342
- Clients . Caller ,
343
- RootComponentIsEmpty ( rootComponents ) ?
344
- "The root components provided are invalid." :
345
- "The application state provided is invalid."
346
- ) ;
347
- Context . Abort ( ) ;
348
- return null ;
349
- }
350
371
351
- try
352
- {
353
- var circuitClient = new CircuitClientProxy ( Clients . Caller , Context . ConnectionId ) ;
354
- var resourceCollection = Context . GetHttpContext ( ) . GetEndpoint ( ) ? . Metadata . GetMetadata < ResourceAssetCollection > ( ) ;
355
- circuitHost = await _circuitFactory . CreateCircuitHostAsync (
356
- [ ] ,
357
- circuitClient ,
358
- baseUri ,
359
- uri ,
360
- Context . User ,
361
- store : null ,
362
- resourceCollection ) ;
363
-
364
- // Fire-and-forget the initialization process, because we can't block the
365
- // SignalR message loop (we'd get a deadlock if any of the initialization
366
- // logic relied on receiving a subsequent message from SignalR), and it will
367
- // take care of its own errors anyway.
368
- _ = circuitHost . InitializeAsync ( store : null , _httpContext , Context . ConnectionAborted ) ;
369
-
370
- circuitHost . AttachPersistedState ( persistedCircuitState ) ;
371
-
372
- // It's safe to *publish* the circuit now because nothing will be able
373
- // to run inside it until after InitializeAsync completes.
374
- _circuitRegistry . Register ( circuitHost ) ;
375
- _circuitHandleRegistry . SetCircuit ( Context . Items , CircuitKey , circuitHost ) ;
376
-
377
- // Returning the secret here so the client can reconnect.
378
- //
379
- // Logging the secret and circuit ID here so we can associate them with just logs (if TRACE level is on).
380
- Log . CreatedCircuit ( _logger , circuitHost . CircuitId , circuitHost . CircuitId . Secret , Context . ConnectionId ) ;
381
-
382
- return circuitHost . CircuitId . Secret ;
383
- }
384
- catch ( Exception ex )
385
- {
386
- // If the circuit fails to initialize synchronously we can notify the client immediately
387
- // and shut down the connection.
388
- Log . CircuitInitializationFailed ( _logger , ex ) ;
389
- await NotifyClientError ( Clients . Caller , "The circuit failed to initialize." ) ;
390
- Context . Abort ( ) ;
391
- return null ;
372
+ try
373
+ {
374
+ var circuitClient = new CircuitClientProxy ( Clients . Caller , Context . ConnectionId ) ;
375
+ var resourceCollection = Context . GetHttpContext ( ) . GetEndpoint ( ) ? . Metadata . GetMetadata < ResourceAssetCollection > ( ) ;
376
+ var circuitHost = await _circuitFactory . CreateCircuitHostAsync (
377
+ [ ] ,
378
+ circuitClient ,
379
+ baseUri ,
380
+ uri ,
381
+ Context . User ,
382
+ store : null ,
383
+ resourceCollection ) ;
384
+
385
+ // Fire-and-forget the initialization process, because we can't block the
386
+ // SignalR message loop (we'd get a deadlock if any of the initialization
387
+ // logic relied on receiving a subsequent message from SignalR), and it will
388
+ // take care of its own errors anyway.
389
+ await circuitHost . InitializeAsync ( store : null , _httpContext , Context . ConnectionAborted ) ;
390
+
391
+ circuitHost . AttachPersistedState ( persistedCircuitState ) ;
392
+
393
+ // It's safe to *publish* the circuit now because nothing will be able
394
+ // to run inside it until after InitializeAsync completes.
395
+ _circuitRegistry . Register ( circuitHost ) ;
396
+ _circuitHandleRegistry . SetCircuit ( Context . Items , CircuitKey , circuitHost ) ;
397
+
398
+ // Returning the secret here so the client can reconnect.
399
+ //
400
+ // Logging the secret and circuit ID here so we can associate them with just logs (if TRACE level is on).
401
+ Log . CreatedCircuit ( _logger , circuitHost . CircuitId , circuitHost . CircuitId . Secret , Context . ConnectionId ) ;
402
+
403
+ await Clients . Caller . SendAsync ( "JS.EndResumeCircuit" , circuitHost . CircuitId . Secret , Context . ConnectionAborted ) ;
404
+ }
405
+ catch ( Exception ex )
406
+ {
407
+ // If the circuit fails to initialize synchronously we can notify the client immediately
408
+ // and shut down the connection.
409
+ Log . CircuitInitializationFailed ( _logger , ex ) ;
410
+ await NotifyClientError ( Clients . Caller , "The circuit failed to initialize." ) ;
411
+ Context . Abort ( ) ;
412
+ return ;
413
+ }
392
414
}
393
-
394
- static bool RootComponentIsEmpty ( string rootComponents ) =>
395
- string . IsNullOrEmpty ( rootComponents ) || rootComponents == "[]" ;
396
415
}
397
416
398
417
// Client initiated pauses work as follows:
0 commit comments