diff --git a/src/SignalR/common/Http.Connections/src/ConnectionOptions.cs b/src/SignalR/common/Http.Connections/src/ConnectionOptions.cs
new file mode 100644
index 0000000000..657e51938c
--- /dev/null
+++ b/src/SignalR/common/Http.Connections/src/ConnectionOptions.cs
@@ -0,0 +1,15 @@
+// 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.
+
+using System;
+
+namespace Microsoft.AspNetCore.Http.Connections
+{
+ public class ConnectionOptions
+ {
+ ///
+ /// Gets or sets the interval used by the server to timeout idle connections.
+ ///
+ public TimeSpan? DisconnectTimeout { get; set; }
+ }
+}
diff --git a/src/SignalR/common/Http.Connections/src/ConnectionOptionsSetup.cs b/src/SignalR/common/Http.Connections/src/ConnectionOptionsSetup.cs
new file mode 100644
index 0000000000..13437aea93
--- /dev/null
+++ b/src/SignalR/common/Http.Connections/src/ConnectionOptionsSetup.cs
@@ -0,0 +1,21 @@
+// 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.
+
+using System;
+using Microsoft.Extensions.Options;
+
+namespace Microsoft.AspNetCore.Http.Connections
+{
+ public class ConnectionOptionsSetup : IConfigureOptions
+ {
+ public static TimeSpan DefaultDisconectTimeout = TimeSpan.FromSeconds(15);
+
+ public void Configure(ConnectionOptions options)
+ {
+ if (options.DisconnectTimeout == null)
+ {
+ options.DisconnectTimeout = DefaultDisconectTimeout;
+ }
+ }
+ }
+}
diff --git a/src/SignalR/common/Http.Connections/src/ConnectionsDependencyInjectionExtensions.cs b/src/SignalR/common/Http.Connections/src/ConnectionsDependencyInjectionExtensions.cs
index c77bcda43f..feb78771ca 100644
--- a/src/SignalR/common/Http.Connections/src/ConnectionsDependencyInjectionExtensions.cs
+++ b/src/SignalR/common/Http.Connections/src/ConnectionsDependencyInjectionExtensions.cs
@@ -1,8 +1,11 @@
// 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.
+using System;
+using Microsoft.AspNetCore.Http.Connections;
using Microsoft.AspNetCore.Http.Connections.Internal;
using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.DependencyInjection
{
@@ -20,9 +23,22 @@ namespace Microsoft.Extensions.DependencyInjection
{
services.AddRouting();
services.AddAuthorizationPolicyEvaluator();
+ services.TryAddEnumerable(ServiceDescriptor.Singleton, ConnectionOptionsSetup>());
services.TryAddSingleton();
services.TryAddSingleton();
return services;
}
+
+ ///
+ /// Adds required services for ASP.NET Core Connection Handlers to the specified .
+ ///
+ /// The to add services to.
+ /// A callback to configure
+ /// The same instance of the for chaining.
+ public static IServiceCollection AddConnections(this IServiceCollection services, Action options)
+ {
+ return services.Configure(options)
+ .AddConnections();
+ }
}
}
diff --git a/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionManager.cs b/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionManager.cs
index b604efb0eb..231eb8fd87 100644
--- a/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionManager.cs
+++ b/src/SignalR/common/Http.Connections/src/Internal/HttpConnectionManager.cs
@@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
-using System.Buffers.Text;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
@@ -15,6 +14,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Internal;
using Microsoft.Extensions.Internal;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Http.Connections.Internal
{
@@ -30,13 +30,19 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
private readonly TimerAwaitable _nextHeartbeat;
private readonly ILogger _logger;
private readonly ILogger _connectionLogger;
+ private readonly TimeSpan _disconnectTimeout;
public HttpConnectionManager(ILoggerFactory loggerFactory, IApplicationLifetime appLifetime)
+ : this(loggerFactory, appLifetime, Options.Create(new ConnectionOptions() { DisconnectTimeout = ConnectionOptionsSetup.DefaultDisconectTimeout }))
+ {
+ }
+
+ public HttpConnectionManager(ILoggerFactory loggerFactory, IApplicationLifetime appLifetime, IOptions connectionOptions)
{
_logger = loggerFactory.CreateLogger();
_connectionLogger = loggerFactory.CreateLogger();
_nextHeartbeat = new TimerAwaitable(_heartbeatTickRate, _heartbeatTickRate);
-
+ _disconnectTimeout = connectionOptions.Value.DisconnectTimeout ?? ConnectionOptionsSetup.DefaultDisconectTimeout;
// Register these last as the callbacks could run immediately
appLifetime.ApplicationStarted.Register(() => Start());
appLifetime.ApplicationStopping.Register(() => CloseConnections());
@@ -155,7 +161,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Internal
// Once the decision has been made to dispose we don't check the status again
// But don't clean up connections while the debugger is attached.
- if (!Debugger.IsAttached && status == HttpConnectionStatus.Inactive && (DateTimeOffset.UtcNow - lastSeenUtc).TotalSeconds > 5)
+ if (!Debugger.IsAttached && status == HttpConnectionStatus.Inactive && (DateTimeOffset.UtcNow - lastSeenUtc).TotalSeconds > _disconnectTimeout.TotalSeconds)
{
Log.ConnectionTimedOut(_logger, connection.ConnectionId);
HttpConnectionsEventSource.Log.ConnectionTimedOut(connection.ConnectionId);
diff --git a/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs b/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs
index 59617e965b..ba2f3c24d8 100644
--- a/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs
+++ b/src/SignalR/common/Http.Connections/test/HttpConnectionDispatcherTests.cs
@@ -25,6 +25,7 @@ using Microsoft.AspNetCore.SignalR.Tests;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Testing;
+using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using Moq;
using Newtonsoft.Json;
@@ -395,7 +396,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
{
using (StartVerifiableLog())
{
- var manager = CreateConnectionManager(LoggerFactory);
+ var manager = CreateConnectionManager(LoggerFactory, TimeSpan.FromSeconds(5));
var dispatcher = new HttpConnectionDispatcher(manager, LoggerFactory);
var connection = manager.CreateConnection();
connection.TransportType = HttpTransportType.LongPolling;
@@ -2178,6 +2179,13 @@ namespace Microsoft.AspNetCore.Http.Connections.Tests
return new HttpConnectionManager(loggerFactory ?? new LoggerFactory(), new EmptyApplicationLifetime());
}
+ private static HttpConnectionManager CreateConnectionManager(ILoggerFactory loggerFactory, TimeSpan disconnectTimeout)
+ {
+ var connectionOptions = new ConnectionOptions();
+ connectionOptions.DisconnectTimeout = disconnectTimeout;
+ return new HttpConnectionManager(loggerFactory ?? new LoggerFactory(), new EmptyApplicationLifetime(), Options.Create(connectionOptions));
+ }
+
private string GetContentAsString(Stream body)
{
Assert.True(body.CanSeek, "Can't get content of a non-seekable stream");
diff --git a/src/SignalR/common/Http.Connections/test/HttpConnectionManagerTests.cs b/src/SignalR/common/Http.Connections/test/HttpConnectionManagerTests.cs
index 13f569dac7..bd31f0c1b8 100644
--- a/src/SignalR/common/Http.Connections/test/HttpConnectionManagerTests.cs
+++ b/src/SignalR/common/Http.Connections/test/HttpConnectionManagerTests.cs
@@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http.Connections.Internal;
using Microsoft.AspNetCore.SignalR.Tests;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
using Xunit;
namespace Microsoft.AspNetCore.Http.Connections.Tests