Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable setting ServerTimeout and KeepAliveInterval in HubConnectionBuilder #46065

Merged
merged 15 commits into from
Jan 19, 2023
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
using Microsoft.AspNetCore.SignalR.Client.Internal;
using Microsoft.AspNetCore.SignalR.Internal;
using Microsoft.AspNetCore.SignalR.Protocol;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;

namespace Microsoft.AspNetCore.SignalR.Client;

Expand Down Expand Up @@ -152,15 +154,15 @@ public partial class HubConnection : IAsyncDisposable
/// <remarks>
/// The client times out if it hasn't heard from the server for `this` long.
/// </remarks>
public TimeSpan ServerTimeout { get; set; } = DefaultServerTimeout;
public TimeSpan ServerTimeout { get; set; }

/// <summary>
/// Gets or sets the interval at which the client sends ping messages.
/// </summary>
/// <remarks>
/// Sending any message resets the timer to the start of the interval.
/// </remarks>
public TimeSpan KeepAliveInterval { get; set; } = DefaultKeepAliveInterval;
public TimeSpan KeepAliveInterval { get; set; }

/// <summary>
/// Gets or sets the timeout for the initial handshake.
Expand Down Expand Up @@ -227,6 +229,12 @@ public HubConnection(IConnectionFactory connectionFactory,
_state = new ReconnectingConnectionState(_logger);

_logScope = new ConnectionLogScope();

var options = serviceProvider.GetService<IOptions<HubConnectionOptions>>();

ServerTimeout = options?.Value.ServerTimeout ?? DefaultServerTimeout;

KeepAliveInterval = options?.Value.KeepAliveInterval ?? DefaultKeepAliveInterval;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,28 @@ public static IHubConnectionBuilder WithAutomaticReconnect(this IHubConnectionBu
hubConnectionBuilder.Services.AddSingleton(retryPolicy);
return hubConnectionBuilder;
}

/// <summary>
/// Configures ServerTimeout for the <see cref="HubConnection" />.
/// </summary>
/// <param name="hubConnectionBuilder">The <see cref="IHubConnectionBuilder" /> to configure.</param>
/// <param name="timeout">ServerTimeout for the <see cref="HubConnection"/>.</param>
/// <returns>The same instance of the <see cref="IHubConnectionBuilder"/> for chaining.</returns>
public static IHubConnectionBuilder WithServerTimeout(this IHubConnectionBuilder hubConnectionBuilder, TimeSpan timeout)
{
hubConnectionBuilder.Services.Configure<HubConnectionOptions>(o => o.ServerTimeout = timeout);
return hubConnectionBuilder;
}

/// <summary>
/// Configures KeepAliveInterval for the <see cref="HubConnection" />.
/// </summary>
/// <param name="hubConnectionBuilder">The <see cref="IHubConnectionBuilder" /> to configure.</param>
/// <param name="interval">KeepAliveInterval for the <see cref="HubConnection"/>.</param>
/// <returns>The same instance of the <see cref="IHubConnectionBuilder"/> for chaining.</returns>
public static IHubConnectionBuilder WithKeepAliveInterval(this IHubConnectionBuilder hubConnectionBuilder, TimeSpan interval)
{
hubConnectionBuilder.Services.Configure<HubConnectionOptions>(o => o.KeepAliveInterval = interval);
return hubConnectionBuilder;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Microsoft.AspNetCore.SignalR.Client;

/// <summary>
/// Configures timeouts for the <see cref="HubConnection" />.
/// </summary>
internal sealed class HubConnectionOptions
{
/// <summary>
/// Configures ServerTimeout for the <see cref="HubConnection" />.
/// </summary>
public TimeSpan? ServerTimeout { get; set; }

/// <summary>
/// Configures KeepAliveInterval for the <see cref="HubConnection" />.
/// </summary>
public TimeSpan? KeepAliveInterval { get; set; }
}
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
#nullable enable
static Microsoft.AspNetCore.SignalR.Client.HubConnectionBuilderExtensions.WithKeepAliveInterval(this Microsoft.AspNetCore.SignalR.Client.IHubConnectionBuilder! hubConnectionBuilder, System.TimeSpan interval) -> Microsoft.AspNetCore.SignalR.Client.IHubConnectionBuilder!
static Microsoft.AspNetCore.SignalR.Client.HubConnectionBuilderExtensions.WithServerTimeout(this Microsoft.AspNetCore.SignalR.Client.IHubConnectionBuilder! hubConnectionBuilder, System.TimeSpan timeout) -> Microsoft.AspNetCore.SignalR.Client.IHubConnectionBuilder!
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,46 @@ public void AddMessagePackProtocolSetsHubProtocolToMsgPack()

Assert.IsType<MessagePackHubProtocol>(serviceProvider.GetService<IHubProtocol>());
}

[Fact]
surayya-MS marked this conversation as resolved.
Show resolved Hide resolved
public void CanConfigureServerTimeout()
{
var serverTimeout = TimeSpan.FromMinutes(1);
var builder = new HubConnectionBuilder();
builder.WithUrl("http://example.com")
.WithServerTimeout(serverTimeout);

var connection = builder.Build();

Assert.Equal(serverTimeout, connection.ServerTimeout);
}

[Fact]
public void CanConfigureKeepAliveInterval()
{
var keepAliveInterval = TimeSpan.FromMinutes(1);
var builder = new HubConnectionBuilder();
builder.WithUrl("http://example.com")
.WithKeepAliveInterval(keepAliveInterval);

var connection = builder.Build();

Assert.Equal(keepAliveInterval, connection.KeepAliveInterval);
}

[Fact]
public void CanConfigureServerTimeoutAndKeepAliveInterval()
{
var serverTimeout = TimeSpan.FromMinutes(2);
var keepAliveInterval = TimeSpan.FromMinutes(3);
var builder = new HubConnectionBuilder();
builder.WithUrl("http://example.com")
.WithServerTimeout(serverTimeout)
.WithKeepAliveInterval(keepAliveInterval);

var connection = builder.Build();

Assert.Equal(serverTimeout, connection.ServerTimeout);
Assert.Equal(keepAliveInterval, connection.KeepAliveInterval);
}
}
31 changes: 26 additions & 5 deletions src/SignalR/clients/ts/signalr/src/HubConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,17 +92,38 @@ 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?: IRetryPolicy): HubConnection {
return new HubConnection(connection, logger, protocol, reconnectPolicy);
public static create(
connection: IConnection,
logger: ILogger,
protocol: IHubProtocol,
reconnectPolicy?: IRetryPolicy,
serverTimeoutInMilliseconds?: number,
keepAliveIntervalInMilliseconds?: number): HubConnection {
return new HubConnection(connection, logger, protocol, reconnectPolicy, serverTimeoutInMilliseconds, keepAliveIntervalInMilliseconds);
}

private constructor(connection: IConnection, logger: ILogger, protocol: IHubProtocol, reconnectPolicy?: IRetryPolicy) {
private constructor(
connection: IConnection,
logger: ILogger,
protocol: IHubProtocol,
reconnectPolicy?: IRetryPolicy,
serverTimeoutInMilliseconds?: number,
keepAliveIntervalInMilliseconds?: number) {
Arg.isRequired(connection, "connection");
Arg.isRequired(logger, "logger");
Arg.isRequired(protocol, "protocol");

this.serverTimeoutInMilliseconds = DEFAULT_TIMEOUT_IN_MS;
this.keepAliveIntervalInMilliseconds = DEFAULT_PING_INTERVAL_IN_MS;
if (serverTimeoutInMilliseconds !== undefined) {
this.serverTimeoutInMilliseconds = serverTimeoutInMilliseconds;
} else {
this.serverTimeoutInMilliseconds = DEFAULT_TIMEOUT_IN_MS;
}
surayya-MS marked this conversation as resolved.
Show resolved Hide resolved

if (keepAliveIntervalInMilliseconds !== undefined) {
this.keepAliveIntervalInMilliseconds = keepAliveIntervalInMilliseconds;
} else {
this.keepAliveIntervalInMilliseconds = DEFAULT_PING_INTERVAL_IN_MS;
}

this._logger = logger;
this._protocol = protocol;
Expand Down
31 changes: 30 additions & 1 deletion src/SignalR/clients/ts/signalr/src/HubConnectionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ function parseLogLevel(name: string): LogLevel {

/** A builder for configuring {@link @microsoft/signalr.HubConnection} instances. */
export class HubConnectionBuilder {
private _serverTimeoutInMilliseconds?: number;
private _keepAliveIntervalInMilliseconds ?: number;

/** @internal */
public protocol?: IHubProtocol;
/** @internal */
Expand Down Expand Up @@ -183,6 +186,30 @@ export class HubConnectionBuilder {
return this;
}

/** Configures {@link @microsoft/signalr.HubConnection.serverTimeoutInMilliseconds} for the {@link @microsoft/signalr.HubConnection}.
*
* @returns The {@link @microsoft/signalr.HubConnectionBuilder} instance, for chaining.
*/
public withServerTimeout(milliseconds: number): HubConnectionBuilder {
Arg.isRequired(milliseconds, "milliseconds");

this._serverTimeoutInMilliseconds = milliseconds;

return this;
}

/** Configures {@link @microsoft/signalr.HubConnection.keepAliveIntervalInMilliseconds} for the {@link @microsoft/signalr.HubConnection}.
*
* @returns The {@link @microsoft/signalr.HubConnectionBuilder} instance, for chaining.
*/
public withKeepAliveInterval(milliseconds: number): HubConnectionBuilder {
Arg.isRequired(milliseconds, "milliseconds");

this._keepAliveIntervalInMilliseconds = milliseconds;

return this;
}

/** Creates a {@link @microsoft/signalr.HubConnection} from the configuration options specified in this builder.
*
* @returns {HubConnection} The configured {@link @microsoft/signalr.HubConnection}.
Expand All @@ -208,7 +235,9 @@ export class HubConnectionBuilder {
connection,
this.logger || NullLogger.instance,
this.protocol || new JsonHubProtocol(),
this.reconnectPolicy);
this.reconnectPolicy,
this._serverTimeoutInMilliseconds,
this._keepAliveIntervalInMilliseconds);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,28 @@ describe("HubConnectionBuilder", () => {

expect(builder.reconnectPolicy!.nextRetryDelayInMilliseconds(retryContextFinal)).toBe(null);
});

it("can configure serverTimeoutInMilliseconds for HubConnection", async () => {
const milliseconds = 60000;

const connection = createConnectionBuilder()
.withUrl("http://example.com")
.withServerTimeout(milliseconds)
.build();

expect(connection.serverTimeoutInMilliseconds).toBe(milliseconds);
});

it("can configure keepAliveIntervalInMilliseconds for HubConnection", async () => {
const milliseconds = 60000;

const connection = createConnectionBuilder()
.withUrl("http://example.com")
.withKeepAliveInterval(milliseconds)
.build();

expect(connection.keepAliveIntervalInMilliseconds).toBe(milliseconds);
});
});

class CaptureLogger implements ILogger {
Expand Down