Skip to content

Commit 2edaf9e

Browse files
committed
tmp
1 parent 3ccb603 commit 2edaf9e

File tree

2 files changed

+180
-126
lines changed

2 files changed

+180
-126
lines changed

src/Components/Server/src/ComponentHub.cs

Lines changed: 116 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -209,33 +209,42 @@ public async Task UpdateRootComponents(string serializedComponentOperations, str
209209
_ = circuitHost.UpdateRootComponents(operations, store, Context.ConnectionAborted);
210210
}
211211

212-
public async ValueTask<bool> ConnectCircuit(string circuitIdSecret)
212+
public void ConnectCircuit(string circuitIdSecret)
213213
{
214214
// TryParseCircuitId will not throw.
215215
if (!_circuitIdFactory.TryParseCircuitId(circuitIdSecret, out var circuitId))
216216
{
217217
// Invalid id.
218218
Log.InvalidCircuitId(_logger, circuitIdSecret);
219-
return false;
219+
_ = Clients.Caller.SendAsync("JS.EndConnectCircuit", false);
220+
return;
220221
}
221222

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)
229228
{
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+
}
235243

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+
}
239248
}
240249

241250
// 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)
266275
// * B might complete the handshake and then the circuit will resume on B.
267276
// * A deletes the state before B is able to read it. Then "resumption" fails, as the circuit state is gone.
268277

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(
273282
string circuitIdSecret,
274283
string baseUri,
275284
string uri,
@@ -281,7 +290,8 @@ public async ValueTask<string> ResumeCircuit(
281290
{
282291
// Invalid id.
283292
Log.ResumeInvalidCircuitId(_logger, circuitIdSecret);
284-
return null;
293+
_ = Clients.Caller.SendAsync("JS.EndResumeCircuit", (string)null, Context.ConnectionAborted);
294+
return;
285295
}
286296

287297
var circuitHost = _circuitHandleRegistry.GetCircuit(Context.Items, CircuitKey);
@@ -291,8 +301,9 @@ public async ValueTask<string> ResumeCircuit(
291301
// We can reject this and terminate the connection.
292302
Log.CircuitAlreadyInitialized(_logger, circuitHost.CircuitId);
293303
await NotifyClientError(Clients.Caller, $"The circuit host '{circuitHost.CircuitId}' has already been initialized.");
304+
_ = Clients.Caller.SendAsync("JS.EndResumeCircuit", (string)null, Context.ConnectionAborted);
294305
Context.Abort();
295-
return null;
306+
return;
296307
}
297308

298309
if (baseUri == null ||
@@ -307,92 +318,100 @@ public async ValueTask<string> ResumeCircuit(
307318
// We can reject this and terminate the connection.
308319
Log.InvalidInputData(_logger);
309320
await NotifyClientError(Clients.Caller, "The uris provided are invalid.");
321+
_ = Clients.Caller.SendAsync("JS.EndResumeCircuit", (string)null, Context.ConnectionAborted);
310322
Context.Abort();
311-
return null;
323+
return;
312324
}
313325

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)
316333
{
317-
persistedCircuitState = await _circuitPersistenceManager.ResumeCircuitAsync(circuitId, Context.ConnectionAborted);
318-
if (persistedCircuitState == null)
334+
PersistedCircuitState? persistedCircuitState;
335+
if (RootComponentIsEmpty(rootComponents) && string.IsNullOrEmpty(applicationState))
319336
{
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+
}
324346
}
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
330360
{
331-
// If we couldn't deserialize the persisted state, signal that.
332361
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+
);
334368
Context.Abort();
335-
return null;
369+
return;
336370
}
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-
}
350371

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+
}
392414
}
393-
394-
static bool RootComponentIsEmpty(string rootComponents) =>
395-
string.IsNullOrEmpty(rootComponents) || rootComponents == "[]";
396415
}
397416

398417
// Client initiated pauses work as follows:

0 commit comments

Comments
 (0)