Dispose Hubs from ActivatorUtilities

This commit is contained in:
BrennanConroy 2016-11-21 14:02:56 -08:00
parent 7c8eeb11f4
commit 28e3c8331b
6 changed files with 215 additions and 18 deletions

View File

@ -43,6 +43,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "SocialWeather", "samples\So
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.SignalR.Test.Server", "test\Microsoft.AspNetCore.SignalR.Test.Server\Microsoft.AspNetCore.SignalR.Test.Server.xproj", "{A0BF246B-FE7D-4E12-99BF-FFDC131B85D8}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.SignalR.Tests", "test\Microsoft.AspNetCore.SignalR.Tests\Microsoft.AspNetCore.SignalR.Tests.xproj", "{1CE2B3BE-056C-41E3-A5F5-6A1EF1D288BA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -105,6 +107,10 @@ Global
{A0BF246B-FE7D-4E12-99BF-FFDC131B85D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A0BF246B-FE7D-4E12-99BF-FFDC131B85D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A0BF246B-FE7D-4E12-99BF-FFDC131B85D8}.Release|Any CPU.Build.0 = Release|Any CPU
{1CE2B3BE-056C-41E3-A5F5-6A1EF1D288BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1CE2B3BE-056C-41E3-A5F5-6A1EF1D288BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1CE2B3BE-056C-41E3-A5F5-6A1EF1D288BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1CE2B3BE-056C-41E3-A5F5-6A1EF1D288BA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -124,5 +130,6 @@ Global
{300979F6-A02E-407A-B8DF-F6200806C18D} = {C4BC9889-B49F-41B6-806B-F84941B2549B}
{8D789F94-CB74-45FD-ACE7-92AF6E55042E} = {C4BC9889-B49F-41B6-806B-F84941B2549B}
{A0BF246B-FE7D-4E12-99BF-FFDC131B85D8} = {6A35B453-52EC-48AF-89CA-D4A69800F131}
{1CE2B3BE-056C-41E3-A5F5-6A1EF1D288BA} = {6A35B453-52EC-48AF-89CA-D4A69800F131}
EndGlobalSection
EndGlobal

View File

@ -63,9 +63,15 @@ namespace Microsoft.AspNetCore.SignalR
using (var scope = _serviceScopeFactory.CreateScope())
{
var hub = scope.ServiceProvider.GetService<THub>() ?? Activator.CreateInstance<THub>();
InitializeHub(connection, hub);
bool created;
var hub = CreateHub(scope.ServiceProvider, connection, out created);
await hub.OnConnectedAsync();
if (created)
{
hub.Dispose();
}
}
await DispatchMessagesAsync(connection);
@ -74,9 +80,15 @@ namespace Microsoft.AspNetCore.SignalR
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var hub = scope.ServiceProvider.GetService<THub>() ?? Activator.CreateInstance<THub>();
InitializeHub(connection, hub);
bool created;
var hub = CreateHub(scope.ServiceProvider, connection, out created);
await hub.OnDisconnectedAsync();
if (created)
{
hub.Dispose();
}
}
await _lifetimeManager.OnDisconnectedAsync(connection);
@ -132,11 +144,22 @@ namespace Microsoft.AspNetCore.SignalR
}
}
private void InitializeHub(Connection connection, THub hub)
private THub CreateHub(IServiceProvider provider, Connection connection, out bool created)
{
var hub = provider.GetService<THub>();
created = false;
if (hub == null)
{
hub = ActivatorUtilities.CreateInstance<THub>(provider);
created = true;
}
hub.Clients = _hubContext.Clients;
hub.Context = new HubCallerContext(connection);
hub.Groups = new GroupManager<THub>(connection, _lifetimeManager);
return hub;
}
private void DiscoverHubMethods()
@ -169,17 +192,8 @@ namespace Microsoft.AspNetCore.SignalR
using (var scope = _serviceScopeFactory.CreateScope())
{
var hub = scope.ServiceProvider.GetService<THub>();
bool created = false;
if (hub == null)
{
hub = Activator.CreateInstance<THub>();
created = true;
}
InitializeHub(connection, hub);
bool created;
var hub = CreateHub(scope.ServiceProvider, connection, out created);
try
{
@ -212,8 +226,7 @@ namespace Microsoft.AspNetCore.SignalR
if (created)
{
// Dispose the object if it's disposable and we created it
(hub as IDisposable)?.Dispose();
hub.Dispose();
}
}

View File

@ -0,0 +1,107 @@
// 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 System.IO.Pipelines;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Internal;
using Microsoft.AspNetCore.Sockets;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Xunit;
namespace Microsoft.AspNetCore.SignalR.Tests
{
public class HubEndpointTests
{
[Fact]
public async Task HubsAreDisposed()
{
var trackDispose = new TrackDispose();
var serviceProvider = CreateServiceProvider(s => s.AddSingleton(trackDispose));
var endPoint = serviceProvider.GetService<HubEndPoint<TestHub>>();
using (var connectionWrapper = new ConnectionWrapper())
{
var endPointTask = endPoint.OnConnectedAsync(connectionWrapper.Connection);
await connectionWrapper.HttpConnection.Input.ReadingStarted;
// kill the connection
connectionWrapper.Connection.Channel.Dispose();
await endPointTask;
Assert.Equal(2, trackDispose.DisposeCount);
}
}
private class TestHub : Hub
{
private TrackDispose _trackDispose;
public TestHub(TrackDispose trackDispose)
{
_trackDispose = trackDispose;
}
protected override void Dispose(bool dispose)
{
if (dispose)
{
_trackDispose.DisposeCount++;
}
}
}
private class TrackDispose
{
public int DisposeCount = 0;
}
private IServiceProvider CreateServiceProvider(Action<ServiceCollection> addServices = null)
{
var services = new ServiceCollection();
services.AddOptions()
.AddLogging()
.AddSignalR();
addServices?.Invoke(services);
return services.BuildServiceProvider();
}
private class ConnectionWrapper : IDisposable
{
private PipelineFactory _factory;
private HttpConnection _httpConnection;
public Connection Connection;
public HttpConnection HttpConnection => (HttpConnection)Connection.Channel;
public ConnectionWrapper(string format = "json")
{
_factory = new PipelineFactory();
_httpConnection = new HttpConnection(_factory);
var lifetime = new ApplicationLifetime(new Logger<ApplicationLifetime>(new LoggerFactory()), Enumerable.Empty<IApplicationLifetimeEvents>());
var connectionManager = new ConnectionManager(lifetime);
Connection = connectionManager.AddNewConnection(_httpConnection).Connection;
Connection.Metadata["formatType"] = format;
Connection.User = new ClaimsPrincipal(new ClaimsIdentity());
}
public void Dispose()
{
Connection.Channel.Dispose();
_httpConnection.Dispose();
_factory.Dispose();
}
}
}
}

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>1ce2b3be-056c-41e3-a5f5-6a1ef1d288ba</ProjectGuid>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,19 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Microsoft.AspNetCore.SignalR.Tests")]
[assembly: AssemblyTrademark("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("1ce2b3be-056c-41e3-a5f5-6a1ef1d288ba")]

View File

@ -0,0 +1,31 @@
{
"buildOptions": {
"warningsAsErrors": true
},
"dependencies": {
"dotnet-test-xunit": "2.2.0-*",
"Microsoft.AspNetCore.Hosting": "1.2.0-*",
"Microsoft.AspNetCore.Sockets": {
"target": "project"
},
"Microsoft.AspNetCore.SignalR": {
"target": "project"
},
"Microsoft.Extensions.DependencyInjection": "1.2.0-*",
"Microsoft.Extensions.Logging": "1.2.0-*",
"xunit": "2.2.0-*"
},
"frameworks": {
"netcoreapp1.1": {
"dependencies": {
"Microsoft.NETCore.App": {
"version": "1.1.0-*",
"type": "platform"
}
}
},
"net451": { }
},
"testRunner": "xunit"
}