Skip to content

Commit 8da7eb7

Browse files
committed
Wire up the default handler
1 parent f073950 commit 8da7eb7

File tree

5 files changed

+65
-21
lines changed

5 files changed

+65
-21
lines changed

src/Components/Web.JS/src/Boot.Server.Common.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,20 @@ async function startServerCore(components: RootComponentManager<ServerComponentD
6767
return true;
6868
};
6969

70+
Blazor.resume = async () => {
71+
if (circuit.didRenderingFail()) {
72+
// We can't resume after a failure, so exit early.
73+
return false;
74+
}
75+
76+
if (!(await circuit.resume())) {
77+
logger.log(LogLevel.Information, 'Resume attempt to the circuit was rejected by the server. This may indicate that the associated state is no longer available on the server.');
78+
return false;
79+
}
80+
81+
return true;
82+
};
83+
7084
Blazor.defaultReconnectionHandler = new DefaultReconnectionHandler(logger);
7185
options.reconnectionHandler = options.reconnectionHandler || Blazor.defaultReconnectionHandler;
7286

src/Components/Web.JS/src/GlobalExports.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export interface IBlazor {
3838
removeEventListener?: typeof JSEventRegistry.prototype.removeEventListener;
3939
disconnect?: () => void;
4040
reconnect?: (existingConnection?: HubConnection) => Promise<boolean>;
41+
resume?: (existingConnection?: HubConnection) => Promise<boolean>;
4142
defaultReconnectionHandler?: DefaultReconnectionHandler;
4243
start?: ((userOptions?: Partial<CircuitStartOptions>) => Promise<void>) | ((options?: Partial<WebAssemblyStartOptions>) => Promise<void>) | ((options?: Partial<WebStartOptions>) => Promise<void>);
4344
platform?: Platform;

src/Components/Web.JS/src/Platform/Circuits/CircuitManager.ts

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { attachWebRendererInterop, detachWebRendererInterop } from '../../Render
2020
import { sendJSDataStream } from './CircuitStreamingInterop';
2121

2222
export class CircuitManager implements DotNet.DotNetCallDispatcher {
23+
2324
private readonly _componentManager: RootComponentManager<ServerComponentDescriptor>;
2425

2526
private _applicationState: string;
@@ -224,28 +225,36 @@ export class CircuitManager implements DotNet.DotNetCallDispatcher {
224225
}
225226

226227
if (!await this._connection!.invoke<boolean>('ConnectCircuit', this._circuitId)) {
227-
const resume = await this._connection!.invoke<string>(
228-
'ResumeCircuit',
229-
this._circuitId,
230-
navigationManagerFunctions.getBaseURI(),
231-
navigationManagerFunctions.getLocationHref(),
232-
'[]',
233-
''
234-
);
235-
if (!resume) {
236-
detachWebRendererInterop(WebRendererId.Server);
237-
this._interopMethodsForReconnection = undefined;
238-
return false;
239-
}
228+
return false;
229+
}
240230

241-
this._circuitId = resume;
242-
this._renderQueue = new RenderQueue(this._logger);
231+
this._options.reconnectionHandler!.onConnectionUp();
243232

244-
this._componentManager.onComponentReload?.();
233+
return true;
234+
}
235+
236+
public async resume(): Promise<boolean> {
237+
if (!this._circuitId) {
238+
throw new Error('Method not implemented.');
245239
}
246240

247-
this._options.reconnectionHandler!.onConnectionUp();
241+
const resume = await this._connection!.invoke<string>(
242+
'ResumeCircuit',
243+
this._circuitId,
244+
navigationManagerFunctions.getBaseURI(),
245+
navigationManagerFunctions.getLocationHref(),
246+
'[]',
247+
''
248+
);
249+
if (!resume) {
250+
return false;
251+
}
248252

253+
this._circuitId = resume;
254+
this._renderQueue = new RenderQueue(this._logger);
255+
this._options.reconnectionHandler!.onCircuitResumed();
256+
this._options.reconnectionHandler!.onConnectionUp();
257+
this._componentManager.onComponentReload?.();
249258
return true;
250259
}
251260

src/Components/Web.JS/src/Platform/Circuits/CircuitStartOptions.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export interface CircuitHandler {
4747
export interface ReconnectionHandler {
4848
onConnectionDown(options: ReconnectionOptions, error?: Error): void;
4949
onConnectionUp(): void;
50+
onCircuitResumed(): void;
5051
}
5152

5253
function computeDefaultRetryInterval(previousAttempts: number, maxRetries?: number): number | null {

src/Components/Web.JS/src/Platform/Circuits/DefaultReconnectionHandler.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,17 @@ export class DefaultReconnectionHandler implements ReconnectionHandler {
1313

1414
private readonly _reconnectCallback: () => Promise<boolean>;
1515

16+
private readonly _resumeCallback: () => Promise<boolean>;
17+
1618
private _currentReconnectionProcess: ReconnectionProcess | null = null;
1719

1820
private _reconnectionDisplay?: ReconnectDisplay;
1921

20-
constructor(logger: Logger, overrideDisplay?: ReconnectDisplay, reconnectCallback?: () => Promise<boolean>) {
22+
constructor(logger: Logger, overrideDisplay?: ReconnectDisplay, reconnectCallback?: () => Promise<boolean>, resumeCallback?: () => Promise<boolean>) {
2123
this._logger = logger;
2224
this._reconnectionDisplay = overrideDisplay;
2325
this._reconnectCallback = reconnectCallback || Blazor.reconnect!;
26+
this._resumeCallback = resumeCallback || Blazor.resume!;
2427
}
2528

2629
onConnectionDown(options: ReconnectionOptions, _error?: Error): void {
@@ -32,7 +35,13 @@ export class DefaultReconnectionHandler implements ReconnectionHandler {
3235
}
3336

3437
if (!this._currentReconnectionProcess) {
35-
this._currentReconnectionProcess = new ReconnectionProcess(options, this._logger, this._reconnectCallback, this._reconnectionDisplay);
38+
this._currentReconnectionProcess = new ReconnectionProcess(
39+
options,
40+
this._logger,
41+
this._reconnectCallback,
42+
this._resumeCallback,
43+
this._reconnectionDisplay
44+
);
3645
}
3746
}
3847

@@ -42,6 +51,10 @@ export class DefaultReconnectionHandler implements ReconnectionHandler {
4251
this._currentReconnectionProcess = null;
4352
}
4453
}
54+
55+
onCircuitResumed(): void {
56+
return;
57+
}
4558
}
4659

4760
class ReconnectionProcess {
@@ -51,7 +64,7 @@ class ReconnectionProcess {
5164

5265
isDisposed = false;
5366

54-
constructor(options: ReconnectionOptions, private logger: Logger, private reconnectCallback: () => Promise<boolean>, display: ReconnectDisplay) {
67+
constructor(options: ReconnectionOptions, private logger: Logger, private reconnectCallback: () => Promise<boolean>, private resumeCallback: () => Promise<boolean>, display: ReconnectDisplay) {
5568
this.reconnectDisplay = display;
5669
this.reconnectDisplay.show();
5770
this.attemptPeriodicReconnection(options);
@@ -65,7 +78,7 @@ class ReconnectionProcess {
6578
async attemptPeriodicReconnection(options: ReconnectionOptions) {
6679
for (let i = 0; options.maxRetries === undefined || i < options.maxRetries; i++) {
6780
let retryInterval: number;
68-
if (typeof(options.retryIntervalMilliseconds) === 'function') {
81+
if (typeof (options.retryIntervalMilliseconds) === 'function') {
6982
const computedRetryInterval = options.retryIntervalMilliseconds(i);
7083
if (computedRetryInterval === null || computedRetryInterval === undefined) {
7184
break;
@@ -92,6 +105,12 @@ class ReconnectionProcess {
92105
// - exception to mean we didn't reach the server (this can be sync or async)
93106
const result = await this.reconnectCallback();
94107
if (!result) {
108+
// Try to resume the circuit if the reconnect failed
109+
const resumeResult = await this.resumeCallback();
110+
if (resumeResult) {
111+
return;
112+
}
113+
95114
// If the server responded and refused to reconnect, stop auto-retrying.
96115
this.reconnectDisplay.rejected();
97116
return;

0 commit comments

Comments
 (0)