@@ -209,42 +209,33 @@ public async Task UpdateRootComponents(string serializedComponentOperations, str
209
209
_ = circuitHost . UpdateRootComponents ( operations , store , Context . ConnectionAborted ) ;
210
210
}
211
211
212
- public void ConnectCircuit ( string circuitIdSecret )
212
+ public async ValueTask < bool > 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
- _ = Clients . Caller . SendAsync ( "JS.EndConnectCircuit" , false ) ;
220
- return ;
219
+ return false ;
221
220
}
222
221
223
- _ = ConnectCircuitCore ( circuitId ) ;
224
-
225
- return ;
226
-
227
- async Task ConnectCircuitCore ( CircuitId circuitId )
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 )
228
229
{
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
- }
243
-
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 ) ;
230
+ _circuitHandleRegistry . SetCircuit ( Context . Items , CircuitKey , circuitHost ) ;
231
+ circuitHost . SetCircuitUser ( Context . User ) ;
232
+ circuitHost . SendPendingBatches ( ) ;
233
+ return true ;
247
234
}
235
+
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 ;
248
239
}
249
240
250
241
// This method drives the resumption of a circuit that has been previously paused and ejected out of memory.
@@ -275,10 +266,10 @@ async Task ConnectCircuitCore(CircuitId circuitId)
275
266
// * B might complete the handshake and then the circuit will resume on B.
276
267
// * A deletes the state before B is able to read it. Then "resumption" fails, as the circuit state is gone.
277
268
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 (
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 (
282
273
string circuitIdSecret ,
283
274
string baseUri ,
284
275
string uri ,
@@ -290,8 +281,7 @@ public async void ResumeCircuit(
290
281
{
291
282
// Invalid id.
292
283
Log . ResumeInvalidCircuitId ( _logger , circuitIdSecret ) ;
293
- _ = Clients . Caller . SendAsync ( "JS.EndResumeCircuit" , ( string ) null , Context . ConnectionAborted ) ;
294
- return ;
284
+ return null ;
295
285
}
296
286
297
287
var circuitHost = _circuitHandleRegistry . GetCircuit ( Context . Items , CircuitKey ) ;
@@ -301,9 +291,8 @@ public async void ResumeCircuit(
301
291
// We can reject this and terminate the connection.
302
292
Log . CircuitAlreadyInitialized ( _logger , circuitHost . CircuitId ) ;
303
293
await NotifyClientError ( Clients . Caller , $ "The circuit host '{ circuitHost . CircuitId } ' has already been initialized.") ;
304
- _ = Clients . Caller . SendAsync ( "JS.EndResumeCircuit" , ( string ) null , Context . ConnectionAborted ) ;
305
294
Context . Abort ( ) ;
306
- return ;
295
+ return null ;
307
296
}
308
297
309
298
if ( baseUri == null ||
@@ -318,100 +307,92 @@ public async void ResumeCircuit(
318
307
// We can reject this and terminate the connection.
319
308
Log . InvalidInputData ( _logger ) ;
320
309
await NotifyClientError ( Clients . Caller , "The uris provided are invalid." ) ;
321
- _ = Clients . Caller . SendAsync ( "JS.EndResumeCircuit" , ( string ) null , Context . ConnectionAborted ) ;
322
310
Context . Abort ( ) ;
323
- return ;
311
+ return null ;
324
312
}
325
313
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 )
314
+ PersistedCircuitState ? persistedCircuitState ;
315
+ if ( RootComponentIsEmpty ( rootComponents ) && string . IsNullOrEmpty ( applicationState ) )
333
316
{
334
- PersistedCircuitState ? persistedCircuitState ;
335
- if ( RootComponentIsEmpty ( rootComponents ) && string . IsNullOrEmpty ( applicationState ) )
336
- {
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
- }
346
- }
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
317
+ persistedCircuitState = await _circuitPersistenceManager . ResumeCircuitAsync ( circuitId , Context . ConnectionAborted ) ;
318
+ if ( persistedCircuitState == null )
360
319
{
361
320
Log . InvalidInputData ( _logger ) ;
362
- await NotifyClientError (
363
- Clients . Caller ,
364
- RootComponentIsEmpty ( rootComponents ) ?
365
- "The root components provided are invalid." :
366
- "The application state provided is invalid."
367
- ) ;
321
+ await NotifyClientError ( Clients . Caller , "The circuit state could not be retrieved. It may have been deleted or expired." ) ;
368
322
Context . Abort ( ) ;
369
- return ;
323
+ return null ;
370
324
}
371
-
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 )
325
+ }
326
+ else if ( ! RootComponentIsEmpty ( rootComponents ) && ! string . IsNullOrEmpty ( applicationState ) )
327
+ {
328
+ persistedCircuitState = _circuitPersistenceManager . FromProtectedState ( rootComponents , applicationState ) ;
329
+ if ( persistedCircuitState == null )
406
330
{
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." ) ;
331
+ // If we couldn't deserialize the persisted state, signal that.
332
+ Log . InvalidInputData ( _logger ) ;
333
+ await NotifyClientError ( Clients . Caller , "The root components or application state provided are invalid." ) ;
411
334
Context . Abort ( ) ;
412
- return ;
335
+ return null ;
413
336
}
414
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
+
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 ;
392
+ }
393
+
394
+ static bool RootComponentIsEmpty ( string rootComponents ) =>
395
+ string . IsNullOrEmpty ( rootComponents ) || rootComponents == "[]" ;
415
396
}
416
397
417
398
// Client initiated pauses work as follows:
0 commit comments