Unify C# and TS SignalR client auto-reconnect APIs (#10678)
This commit is contained in:
parent
915cc74df8
commit
81f2d46660
|
|
@ -1,20 +1,20 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
import { IReconnectPolicy } from "./IReconnectPolicy";
|
||||
import { IRetryPolicy, RetryContext } from "./IRetryPolicy";
|
||||
|
||||
// 0, 2, 10, 30 second delays before reconnect attempts.
|
||||
const DEFAULT_RETRY_DELAYS_IN_MILLISECONDS = [0, 2000, 10000, 30000, null];
|
||||
|
||||
/** @private */
|
||||
export class DefaultReconnectPolicy implements IReconnectPolicy {
|
||||
export class DefaultReconnectPolicy implements IRetryPolicy {
|
||||
private readonly retryDelays: Array<number | null>;
|
||||
|
||||
constructor(retryDelays?: number[]) {
|
||||
this.retryDelays = retryDelays !== undefined ? [...retryDelays, null] : DEFAULT_RETRY_DELAYS_IN_MILLISECONDS;
|
||||
}
|
||||
|
||||
public nextRetryDelayInMilliseconds(previousRetryCount: number): number | null {
|
||||
return this.retryDelays[previousRetryCount];
|
||||
public nextRetryDelayInMilliseconds(retryContext: RetryContext): number | null {
|
||||
return this.retryDelays[retryContext.previousRetryCount];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { HandshakeProtocol, HandshakeRequestMessage, HandshakeResponseMessage }
|
|||
import { IConnection } from "./IConnection";
|
||||
import { CancelInvocationMessage, CompletionMessage, IHubProtocol, InvocationMessage, MessageType, StreamInvocationMessage, StreamItemMessage } from "./IHubProtocol";
|
||||
import { ILogger, LogLevel } from "./ILogger";
|
||||
import { IReconnectPolicy } from "./IReconnectPolicy";
|
||||
import { IRetryPolicy } from "./IRetryPolicy";
|
||||
import { IStreamResult } from "./Stream";
|
||||
import { Subject } from "./Subject";
|
||||
import { Arg } from "./Utils";
|
||||
|
|
@ -32,7 +32,7 @@ export class HubConnection {
|
|||
private readonly cachedPingMessage: string | ArrayBuffer;
|
||||
private readonly connection: IConnection;
|
||||
private readonly logger: ILogger;
|
||||
private readonly reconnectPolicy?: IReconnectPolicy;
|
||||
private readonly reconnectPolicy?: IRetryPolicy;
|
||||
private protocol: IHubProtocol;
|
||||
private handshakeProtocol: HandshakeProtocol;
|
||||
private callbacks: { [invocationId: string]: (invocationEvent: StreamItemMessage | CompletionMessage | null, error?: Error) => void };
|
||||
|
|
@ -81,11 +81,11 @@ export class HubConnection {
|
|||
// create method that can be used by HubConnectionBuilder. An "internal" constructor would just
|
||||
// be stripped away and the '.d.ts' file would have no constructor, which is interpreted as a
|
||||
// public parameter-less constructor.
|
||||
public static create(connection: IConnection, logger: ILogger, protocol: IHubProtocol, reconnectPolicy?: IReconnectPolicy): HubConnection {
|
||||
public static create(connection: IConnection, logger: ILogger, protocol: IHubProtocol, reconnectPolicy?: IRetryPolicy): HubConnection {
|
||||
return new HubConnection(connection, logger, protocol, reconnectPolicy);
|
||||
}
|
||||
|
||||
private constructor(connection: IConnection, logger: ILogger, protocol: IHubProtocol, reconnectPolicy?: IReconnectPolicy) {
|
||||
private constructor(connection: IConnection, logger: ILogger, protocol: IHubProtocol, reconnectPolicy?: IRetryPolicy) {
|
||||
Arg.isRequired(connection, "connection");
|
||||
Arg.isRequired(logger, "logger");
|
||||
Arg.isRequired(protocol, "protocol");
|
||||
|
|
@ -666,11 +666,12 @@ export class HubConnection {
|
|||
private async reconnect(error?: Error) {
|
||||
const reconnectStartTime = Date.now();
|
||||
let previousReconnectAttempts = 0;
|
||||
let retryError = error !== undefined ? error : new Error("Attempting to reconnect due to a unknown error.");
|
||||
|
||||
let nextRetryDelay = this.getNextRetryDelay(previousReconnectAttempts++, 0);
|
||||
let nextRetryDelay = this.getNextRetryDelay(previousReconnectAttempts++, 0, retryError);
|
||||
|
||||
if (nextRetryDelay === null) {
|
||||
this.logger.log(LogLevel.Debug, "Connection not reconnecting because the IReconnectPolicy returned null on the first reconnect attempt.");
|
||||
this.logger.log(LogLevel.Debug, "Connection not reconnecting because the IRetryPolicy returned null on the first reconnect attempt.");
|
||||
this.completeClose(error);
|
||||
return;
|
||||
}
|
||||
|
|
@ -732,9 +733,10 @@ export class HubConnection {
|
|||
this.logger.log(LogLevel.Debug, "Connection left the reconnecting state during reconnect attempt. Done reconnecting.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nextRetryDelay = this.getNextRetryDelay(previousReconnectAttempts++, Date.now() - reconnectStartTime);
|
||||
retryError = e instanceof Error ? e : new Error(e.toString());
|
||||
nextRetryDelay = this.getNextRetryDelay(previousReconnectAttempts++, Date.now() - reconnectStartTime, retryError);
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.log(LogLevel.Information, `Reconnect retries have been exhausted after ${Date.now() - reconnectStartTime} ms and ${previousReconnectAttempts} failed attempts. Connection disconnecting.`);
|
||||
|
|
@ -742,11 +744,15 @@ export class HubConnection {
|
|||
this.completeClose();
|
||||
}
|
||||
|
||||
private getNextRetryDelay(previousRetryCount: number, elapsedMilliseconds: number) {
|
||||
private getNextRetryDelay(previousRetryCount: number, elapsedMilliseconds: number, retryReason: Error) {
|
||||
try {
|
||||
return this.reconnectPolicy!.nextRetryDelayInMilliseconds(previousRetryCount, elapsedMilliseconds);
|
||||
return this.reconnectPolicy!.nextRetryDelayInMilliseconds({
|
||||
elapsedMilliseconds,
|
||||
previousRetryCount,
|
||||
retryReason,
|
||||
});
|
||||
} catch (e) {
|
||||
this.logger.log(LogLevel.Error, `IReconnectPolicy.nextRetryDelayInMilliseconds(${previousRetryCount}, ${elapsedMilliseconds}) threw error '${e}'.`);
|
||||
this.logger.log(LogLevel.Error, `IRetryPolicy.nextRetryDelayInMilliseconds(${previousRetryCount}, ${elapsedMilliseconds}) threw error '${e}'.`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { HubConnection } from "./HubConnection";
|
|||
import { IHttpConnectionOptions } from "./IHttpConnectionOptions";
|
||||
import { IHubProtocol } from "./IHubProtocol";
|
||||
import { ILogger, LogLevel } from "./ILogger";
|
||||
import { IReconnectPolicy } from "./IReconnectPolicy";
|
||||
import { IRetryPolicy } from "./IRetryPolicy";
|
||||
import { HttpTransportType } from "./ITransport";
|
||||
import { JsonHubProtocol } from "./JsonHubProtocol";
|
||||
import { NullLogger } from "./Loggers";
|
||||
|
|
@ -51,7 +51,7 @@ export class HubConnectionBuilder {
|
|||
|
||||
/** If defined, this indicates the client should automatically attempt to reconnect if the connection is lost. */
|
||||
/** @internal */
|
||||
public reconnectPolicy?: IReconnectPolicy;
|
||||
public reconnectPolicy?: IRetryPolicy;
|
||||
|
||||
/** Configures console logging for the {@link @aspnet/signalr.HubConnection}.
|
||||
*
|
||||
|
|
@ -164,10 +164,10 @@ export class HubConnectionBuilder {
|
|||
|
||||
/** Configures the {@link @aspnet/signalr.HubConnection} to automatically attempt to reconnect if the connection is lost.
|
||||
*
|
||||
* @param {IReconnectPolicy} reconnectPolicy An {@link @aspnet/signalR.IReconnectPolicy} that controls the timing and number of reconnect attempts.
|
||||
* @param {IRetryPolicy} reconnectPolicy An {@link @aspnet/signalR.IRetryPolicy} that controls the timing and number of reconnect attempts.
|
||||
*/
|
||||
public withAutomaticReconnect(reconnectPolicy: IReconnectPolicy): HubConnectionBuilder;
|
||||
public withAutomaticReconnect(retryDelaysOrReconnectPolicy?: number[] | IReconnectPolicy): HubConnectionBuilder {
|
||||
public withAutomaticReconnect(reconnectPolicy: IRetryPolicy): HubConnectionBuilder;
|
||||
public withAutomaticReconnect(retryDelaysOrReconnectPolicy?: number[] | IRetryPolicy): HubConnectionBuilder {
|
||||
if (this.reconnectPolicy) {
|
||||
throw new Error("A reconnectPolicy has already been set.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
/** An abstraction that controls when the client attempts to reconnect and how many times it does so. */
|
||||
export interface IReconnectPolicy {
|
||||
/** Called after the transport loses the connection.
|
||||
*
|
||||
* @param {number} previousRetryCount The number of consecutive failed reconnect attempts so far.
|
||||
*
|
||||
* @param {number} elapsedMilliseconds The amount of time in milliseconds spent reconnecting so far.
|
||||
*
|
||||
* @returns {number | null} The amount of time in milliseconds to wait before the next reconnect attempt. `null` tells the client to stop retrying and close.
|
||||
*/
|
||||
nextRetryDelayInMilliseconds(previousRetryCount: number, elapsedMilliseconds: number): number | null;
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
/** An abstraction that controls when the client attempts to reconnect and how many times it does so. */
|
||||
export interface IRetryPolicy {
|
||||
/** Called after the transport loses the connection.
|
||||
*
|
||||
* @param {RetryContext} retryContext Details related to the retry event to help determine how long to wait for the next retry.
|
||||
*
|
||||
* @returns {number | null} The amount of time in milliseconds to wait before the next retry. `null` tells the client to stop retrying.
|
||||
*/
|
||||
nextRetryDelayInMilliseconds(retryContext: RetryContext): number | null;
|
||||
}
|
||||
|
||||
export interface RetryContext {
|
||||
/**
|
||||
* The number of consecutive failed tries so far.
|
||||
*/
|
||||
readonly previousRetryCount: number;
|
||||
|
||||
/**
|
||||
* The amount of time in milliseconds spent retrying so far.
|
||||
*/
|
||||
readonly elapsedMilliseconds: number;
|
||||
|
||||
/**
|
||||
* The error that forced the upcoming retry.
|
||||
*/
|
||||
readonly retryReason: Error;
|
||||
}
|
||||
|
|
@ -21,4 +21,4 @@ export { IStreamSubscriber, IStreamResult, ISubscription } from "./Stream";
|
|||
export { NullLogger } from "./Loggers";
|
||||
export { JsonHubProtocol } from "./JsonHubProtocol";
|
||||
export { Subject } from "./Subject";
|
||||
export { IReconnectPolicy } from "./IReconnectPolicy";
|
||||
export { IRetryPolicy, RetryContext } from "./IRetryPolicy";
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
import { DefaultReconnectPolicy } from "../src/DefaultReconnectPolicy";
|
||||
import { HubConnection, HubConnectionState } from "../src/HubConnection";
|
||||
import { RetryContext } from "../src/IRetryPolicy";
|
||||
import { JsonHubProtocol } from "../src/JsonHubProtocol";
|
||||
|
||||
import { VerifyLogger } from "./Common";
|
||||
|
|
@ -46,15 +47,17 @@ describe("auto reconnect", () => {
|
|||
|
||||
let lastRetryCount = -1;
|
||||
let lastElapsedMs = -1;
|
||||
let retryReason = null;
|
||||
let onreconnectingCount = 0;
|
||||
let onreconnectedCount = 0;
|
||||
let closeCount = 0;
|
||||
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = HubConnection.create(connection, logger, new JsonHubProtocol(), {
|
||||
nextRetryDelayInMilliseconds(previousRetryCount: number, elapsedMilliseconds: number) {
|
||||
lastRetryCount = previousRetryCount;
|
||||
lastElapsedMs = elapsedMilliseconds;
|
||||
nextRetryDelayInMilliseconds(retryContext: RetryContext) {
|
||||
lastRetryCount = retryContext.previousRetryCount;
|
||||
lastElapsedMs = retryContext.elapsedMilliseconds;
|
||||
retryReason = retryContext.retryReason;
|
||||
nextRetryDelayCalledPromise.resolve();
|
||||
return 0;
|
||||
},
|
||||
|
|
@ -81,8 +84,11 @@ describe("auto reconnect", () => {
|
|||
return promise;
|
||||
};
|
||||
|
||||
const oncloseError = new Error("Connection lost");
|
||||
const continueRetryingError = new Error("Reconnect attempt failed");
|
||||
|
||||
// Typically this would be called by the transport
|
||||
connection.onclose!(new Error("Connection lost"));
|
||||
connection.onclose!(oncloseError);
|
||||
|
||||
await nextRetryDelayCalledPromise;
|
||||
nextRetryDelayCalledPromise = new PromiseSource();
|
||||
|
|
@ -90,17 +96,19 @@ describe("auto reconnect", () => {
|
|||
expect(hubConnection.state).toBe(HubConnectionState.Reconnecting);
|
||||
expect(lastRetryCount).toBe(0);
|
||||
expect(lastElapsedMs).toBe(0);
|
||||
expect(retryReason).toBe(oncloseError);
|
||||
expect(onreconnectingCount).toBe(1);
|
||||
expect(onreconnectedCount).toBe(0);
|
||||
expect(closeCount).toBe(0);
|
||||
|
||||
// Make sure the the Promise is "handled" immediately upon rejection or else this test fails.
|
||||
continueRetryingPromise.catch(() => { });
|
||||
continueRetryingPromise.reject(new Error("Reconnect attempt failed"));
|
||||
continueRetryingPromise.reject(continueRetryingError);
|
||||
await nextRetryDelayCalledPromise;
|
||||
|
||||
expect(lastRetryCount).toBe(1);
|
||||
expect(lastElapsedMs).toBeGreaterThanOrEqual(0);
|
||||
expect(retryReason).toBe(continueRetryingError);
|
||||
expect(onreconnectingCount).toBe(1);
|
||||
expect(onreconnectedCount).toBe(0);
|
||||
expect(closeCount).toBe(0);
|
||||
|
|
@ -133,18 +141,20 @@ describe("auto reconnect", () => {
|
|||
|
||||
let lastRetryCount = -1;
|
||||
let lastElapsedMs = -1;
|
||||
let retryReason = null;
|
||||
let onreconnectingCount = 0;
|
||||
let onreconnectedCount = 0;
|
||||
let closeCount = 0;
|
||||
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = HubConnection.create(connection, logger, new JsonHubProtocol(), {
|
||||
nextRetryDelayInMilliseconds(previousRetryCount: number, elapsedMilliseconds: number) {
|
||||
lastRetryCount = previousRetryCount;
|
||||
lastElapsedMs = elapsedMilliseconds;
|
||||
nextRetryDelayInMilliseconds(retryContext: RetryContext) {
|
||||
lastRetryCount = retryContext.previousRetryCount;
|
||||
lastElapsedMs = retryContext.elapsedMilliseconds;
|
||||
retryReason = retryContext.retryReason;
|
||||
nextRetryDelayCalledPromise.resolve();
|
||||
|
||||
return previousRetryCount === 0 ? 0 : null;
|
||||
return retryContext.previousRetryCount === 0 ? 0 : null;
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -163,12 +173,15 @@ describe("auto reconnect", () => {
|
|||
|
||||
await hubConnection.start();
|
||||
|
||||
const oncloseError = new Error("Connection lost");
|
||||
const startError = new Error("Reconnect attempt failed");
|
||||
|
||||
connection.start = () => {
|
||||
return Promise.reject("Reconnect attempt failed");
|
||||
throw startError;
|
||||
};
|
||||
|
||||
// Typically this would be called by the transport
|
||||
connection.onclose!(new Error("Connection lost"));
|
||||
connection.onclose!(oncloseError);
|
||||
|
||||
await nextRetryDelayCalledPromise;
|
||||
nextRetryDelayCalledPromise = new PromiseSource();
|
||||
|
|
@ -176,6 +189,7 @@ describe("auto reconnect", () => {
|
|||
expect(hubConnection.state).toBe(HubConnectionState.Reconnecting);
|
||||
expect(lastRetryCount).toBe(0);
|
||||
expect(lastElapsedMs).toBe(0);
|
||||
expect(retryReason).toBe(oncloseError);
|
||||
expect(onreconnectingCount).toBe(1);
|
||||
expect(onreconnectedCount).toBe(0);
|
||||
expect(closeCount).toBe(0);
|
||||
|
|
@ -185,6 +199,7 @@ describe("auto reconnect", () => {
|
|||
expect(hubConnection.state).toBe(HubConnectionState.Disconnected);
|
||||
expect(lastRetryCount).toBe(1);
|
||||
expect(lastElapsedMs).toBeGreaterThanOrEqual(0);
|
||||
expect(retryReason).toBe(startError);
|
||||
expect(onreconnectingCount).toBe(1);
|
||||
expect(onreconnectedCount).toBe(0);
|
||||
expect(closeCount).toBe(1);
|
||||
|
|
@ -198,15 +213,17 @@ describe("auto reconnect", () => {
|
|||
|
||||
let lastRetryCount = -1;
|
||||
let lastElapsedMs = -1;
|
||||
let retryReason = null;
|
||||
let onreconnectingCount = 0;
|
||||
let onreconnectedCount = 0;
|
||||
let closeCount = 0;
|
||||
|
||||
const connection = new TestConnection();
|
||||
const hubConnection = HubConnection.create(connection, logger, new JsonHubProtocol(), {
|
||||
nextRetryDelayInMilliseconds(previousRetryCount: number, elapsedMilliseconds: number) {
|
||||
lastRetryCount = previousRetryCount;
|
||||
lastElapsedMs = elapsedMilliseconds;
|
||||
nextRetryDelayInMilliseconds(retryContext: RetryContext) {
|
||||
lastRetryCount = retryContext.previousRetryCount;
|
||||
lastElapsedMs = retryContext.elapsedMilliseconds;
|
||||
retryReason = retryContext.retryReason;
|
||||
nextRetryDelayCalledPromise.resolve();
|
||||
return 0;
|
||||
},
|
||||
|
|
@ -227,8 +244,11 @@ describe("auto reconnect", () => {
|
|||
|
||||
await hubConnection.start();
|
||||
|
||||
const oncloseError = new Error("Connection lost 1");
|
||||
const oncloseError2 = new Error("Connection lost 2");
|
||||
|
||||
// Typically this would be called by the transport
|
||||
connection.onclose!(new Error("Connection lost"));
|
||||
connection.onclose!(oncloseError);
|
||||
|
||||
await nextRetryDelayCalledPromise;
|
||||
nextRetryDelayCalledPromise = new PromiseSource();
|
||||
|
|
@ -236,6 +256,7 @@ describe("auto reconnect", () => {
|
|||
expect(hubConnection.state).toBe(HubConnectionState.Reconnecting);
|
||||
expect(lastRetryCount).toBe(0);
|
||||
expect(lastElapsedMs).toBe(0);
|
||||
expect(retryReason).toBe(oncloseError);
|
||||
expect(onreconnectingCount).toBe(1);
|
||||
expect(onreconnectedCount).toBe(0);
|
||||
expect(closeCount).toBe(0);
|
||||
|
|
@ -250,13 +271,14 @@ describe("auto reconnect", () => {
|
|||
expect(onreconnectedCount).toBe(1);
|
||||
expect(closeCount).toBe(0);
|
||||
|
||||
connection.onclose!(new Error("Connection lost"));
|
||||
connection.onclose!(oncloseError2);
|
||||
|
||||
await nextRetryDelayCalledPromise;
|
||||
|
||||
expect(hubConnection.state).toBe(HubConnectionState.Reconnecting);
|
||||
expect(lastRetryCount).toBe(0);
|
||||
expect(lastElapsedMs).toBe(0);
|
||||
expect(retryReason).toBe(oncloseError2);
|
||||
expect(onreconnectingCount).toBe(2);
|
||||
expect(onreconnectedCount).toBe(1);
|
||||
expect(closeCount).toBe(0);
|
||||
|
|
@ -362,6 +384,7 @@ describe("auto reconnect", () => {
|
|||
let nextRetryDelayCalledPromise = new PromiseSource();
|
||||
|
||||
let lastRetryCount = 0;
|
||||
let retryReason = null;
|
||||
let onreconnectingCount = 0;
|
||||
let onreconnectedCount = 0;
|
||||
let closeCount = 0;
|
||||
|
|
@ -369,8 +392,9 @@ describe("auto reconnect", () => {
|
|||
// Disable autoHandshake in TestConnection
|
||||
const connection = new TestConnection(false);
|
||||
const hubConnection = HubConnection.create(connection, logger, new JsonHubProtocol(), {
|
||||
nextRetryDelayInMilliseconds(previousRetryCount: number) {
|
||||
lastRetryCount = previousRetryCount;
|
||||
nextRetryDelayInMilliseconds(retryContext: RetryContext) {
|
||||
lastRetryCount = retryContext.previousRetryCount;
|
||||
retryReason = retryContext.retryReason;
|
||||
nextRetryDelayCalledPromise.resolve();
|
||||
return 0;
|
||||
},
|
||||
|
|
@ -400,14 +424,18 @@ describe("auto reconnect", () => {
|
|||
return Promise.resolve();
|
||||
};
|
||||
|
||||
const oncloseError = new Error("Connection lost 1");
|
||||
const oncloseError2 = new Error("Connection lost 2");
|
||||
|
||||
// Typically this would be called by the transport
|
||||
connection.onclose!(new Error("Connection lost"));
|
||||
connection.onclose!(oncloseError);
|
||||
|
||||
await nextRetryDelayCalledPromise;
|
||||
nextRetryDelayCalledPromise = new PromiseSource();
|
||||
|
||||
expect(hubConnection.state).toBe(HubConnectionState.Reconnecting);
|
||||
expect(lastRetryCount).toBe(0);
|
||||
expect(retryReason).toBe(oncloseError);
|
||||
expect(onreconnectingCount).toBe(1);
|
||||
expect(onreconnectedCount).toBe(0);
|
||||
expect(closeCount).toBe(0);
|
||||
|
|
@ -416,12 +444,13 @@ describe("auto reconnect", () => {
|
|||
replacedStartCalledPromise = new PromiseSource();
|
||||
|
||||
// Fail underlying connection during reconnect during handshake
|
||||
connection.onclose!(new Error("Connection lost"));
|
||||
connection.onclose!(oncloseError2);
|
||||
|
||||
await nextRetryDelayCalledPromise;
|
||||
|
||||
expect(hubConnection.state).toBe(HubConnectionState.Reconnecting);
|
||||
expect(lastRetryCount).toBe(1);
|
||||
expect(retryReason).toBe(oncloseError2);
|
||||
expect(onreconnectingCount).toBe(1);
|
||||
expect(onreconnectedCount).toBe(0);
|
||||
expect(closeCount).toBe(0);
|
||||
|
|
@ -461,8 +490,8 @@ describe("auto reconnect", () => {
|
|||
// Disable autoHandshake in TestConnection
|
||||
const connection = new TestConnection(false);
|
||||
const hubConnection = HubConnection.create(connection, logger, new JsonHubProtocol(), {
|
||||
nextRetryDelayInMilliseconds(previousRetryCount: number) {
|
||||
lastRetryCount = previousRetryCount;
|
||||
nextRetryDelayInMilliseconds(retryContext: RetryContext) {
|
||||
lastRetryCount = retryContext.previousRetryCount;
|
||||
nextRetryDelayCalledPromise.resolve();
|
||||
return 0;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -322,7 +322,13 @@ describe("HubConnectionBuilder", () => {
|
|||
|
||||
let retryCount = 0;
|
||||
for (const delay of DEFAULT_RETRY_DELAYS_IN_MILLISECONDS) {
|
||||
expect(builder.reconnectPolicy!.nextRetryDelayInMilliseconds(retryCount++, 0)).toBe(delay);
|
||||
const retryContext = {
|
||||
previousRetryCount: retryCount++,
|
||||
elapsedMilliseconds: 0,
|
||||
retryReason: new Error(),
|
||||
};
|
||||
|
||||
expect(builder.reconnectPolicy!.nextRetryDelayInMilliseconds(retryContext)).toBe(delay);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -333,23 +339,47 @@ describe("HubConnectionBuilder", () => {
|
|||
|
||||
let retryCount = 0;
|
||||
for (const delay of customRetryDelays) {
|
||||
expect(builder.reconnectPolicy!.nextRetryDelayInMilliseconds(retryCount++, 0)).toBe(delay);
|
||||
const retryContext = {
|
||||
previousRetryCount: retryCount++,
|
||||
elapsedMilliseconds: 0,
|
||||
retryReason: new Error(),
|
||||
};
|
||||
|
||||
expect(builder.reconnectPolicy!.nextRetryDelayInMilliseconds(retryContext)).toBe(delay);
|
||||
}
|
||||
|
||||
expect(builder.reconnectPolicy!.nextRetryDelayInMilliseconds(retryCount, 0)).toBe(null);
|
||||
const retryContextFinal = {
|
||||
previousRetryCount: retryCount++,
|
||||
elapsedMilliseconds: 0,
|
||||
retryReason: new Error(),
|
||||
};
|
||||
|
||||
expect(builder.reconnectPolicy!.nextRetryDelayInMilliseconds(retryContextFinal)).toBe(null);
|
||||
});
|
||||
|
||||
it("withAutomaticReconnect uses a custom IReconnectPolicy when provided", () => {
|
||||
it("withAutomaticReconnect uses a custom IRetryPolicy when provided", () => {
|
||||
const customRetryDelays = [127, 0, 0, 1];
|
||||
const builder = new HubConnectionBuilder()
|
||||
.withAutomaticReconnect(new DefaultReconnectPolicy(customRetryDelays));
|
||||
|
||||
let retryCount = 0;
|
||||
for (const delay of customRetryDelays) {
|
||||
expect(builder.reconnectPolicy!.nextRetryDelayInMilliseconds(retryCount++, 0)).toBe(delay);
|
||||
const retryContext = {
|
||||
previousRetryCount: retryCount++,
|
||||
elapsedMilliseconds: 0,
|
||||
retryReason: new Error(),
|
||||
};
|
||||
|
||||
expect(builder.reconnectPolicy!.nextRetryDelayInMilliseconds(retryContext)).toBe(delay);
|
||||
}
|
||||
|
||||
expect(builder.reconnectPolicy!.nextRetryDelayInMilliseconds(retryCount, 0)).toBe(null);
|
||||
const retryContextFinal = {
|
||||
previousRetryCount: retryCount++,
|
||||
elapsedMilliseconds: 0,
|
||||
retryReason: new Error(),
|
||||
};
|
||||
|
||||
expect(builder.reconnectPolicy!.nextRetryDelayInMilliseconds(retryContextFinal)).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue