Skip to content

Commit c560ede

Browse files
zlatanovTratcher
authored andcommitted
Fixed the bug in the windows service host where if application stop is requested, the service wouldn't know about it. (#4553)
* Fixed the bug in the windows service host where if application stop is requested, the service wouldn't know about it. * Fixed a typo. * Update src/Hosting/WindowsServices/test/Microsoft.AspNetCore.Hosting.WindowsServices.Tests.csproj * Removed unneeded EditorBrowsable for internal API. * Fixed tests to ignore Linux and MacOS. * Added new line to end of file. * Using [ConditionalFact] instead of [Fact].
1 parent 3f4622f commit c560ede

File tree

6 files changed

+116
-6
lines changed

6 files changed

+116
-6
lines changed

src/Hosting/Hosting.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.TestHo
2525
EndProject
2626
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Hosting.WindowsServices", "WindowsServices\src\Microsoft.AspNetCore.Hosting.WindowsServices.csproj", "{FA29445B-1BA7-448D-8ADF-56BF6D6633BB}"
2727
EndProject
28+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Hosting.WindowsServices.Tests", "WindowsServices\test\Microsoft.AspNetCore.Hosting.WindowsServices.Tests.csproj", "{CCD5D010-7E06-4209-ADD5-3B010A41DCF1}"
29+
EndProject
2830
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "testassets", "testassets", "{31587D24-F6B5-4A47-A962-47CA7FEA79D0}"
2931
EndProject
3032
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BuildWebHostInvalidSignature", "test\testassets\BuildWebHostInvalidSignature\BuildWebHostInvalidSignature.csproj", "{BF146375-AA6C-43F3-BF0A-DCA551892DF8}"
@@ -89,6 +91,10 @@ Global
8991
{FA29445B-1BA7-448D-8ADF-56BF6D6633BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
9092
{FA29445B-1BA7-448D-8ADF-56BF6D6633BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
9193
{FA29445B-1BA7-448D-8ADF-56BF6D6633BB}.Release|Any CPU.Build.0 = Release|Any CPU
94+
{CCD5D010-7E06-4209-ADD5-3B010A41DCF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
95+
{CCD5D010-7E06-4209-ADD5-3B010A41DCF1}.Debug|Any CPU.Build.0 = Debug|Any CPU
96+
{CCD5D010-7E06-4209-ADD5-3B010A41DCF1}.Release|Any CPU.ActiveCfg = Release|Any CPU
97+
{CCD5D010-7E06-4209-ADD5-3B010A41DCF1}.Release|Any CPU.Build.0 = Release|Any CPU
9298
{BF146375-AA6C-43F3-BF0A-DCA551892DF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
9399
{BF146375-AA6C-43F3-BF0A-DCA551892DF8}.Debug|Any CPU.Build.0 = Debug|Any CPU
94100
{BF146375-AA6C-43F3-BF0A-DCA551892DF8}.Release|Any CPU.ActiveCfg = Release|Any CPU
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System.Runtime.CompilerServices;
5+
6+
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Hosting.WindowsServices.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]

src/Hosting/WindowsServices/src/WebHostService.cs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using System.ComponentModel;
56
using System.ServiceProcess;
67
using Microsoft.Extensions.DependencyInjection;
78

@@ -12,7 +13,7 @@ namespace Microsoft.AspNetCore.Hosting.WindowsServices
1213
/// </summary>
1314
public class WebHostService : ServiceBase
1415
{
15-
private IWebHost _host;
16+
private readonly IWebHost _host;
1617
private bool _stopRequestedByWindows;
1718

1819
/// <summary>
@@ -24,25 +25,34 @@ public WebHostService(IWebHost host)
2425
_host = host ?? throw new ArgumentNullException(nameof(host));
2526
}
2627

28+
/// <summary>
29+
/// This method is not intended for direct use. Its sole purpose is to allow
30+
/// the service to be started by the tests.
31+
/// </summary>
32+
internal void Start() => OnStart(Array.Empty<string>());
33+
2734
protected sealed override void OnStart(string[] args)
2835
{
2936
OnStarting(args);
3037

38+
_host.Start();
39+
40+
OnStarted();
41+
42+
// Register callback for application stopping after we've
43+
// started the service, because otherwise we might introduce unwanted
44+
// race conditions.
3145
_host
3246
.Services
3347
.GetRequiredService<IApplicationLifetime>()
34-
.ApplicationStopped
48+
.ApplicationStopping
3549
.Register(() =>
3650
{
3751
if (!_stopRequestedByWindows)
3852
{
3953
Stop();
4054
}
4155
});
42-
43-
_host.Start();
44-
45-
OnStarted();
4656
}
4757

4858
protected sealed override void OnStop()
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFrameworks>netcoreapp3.0</TargetFrameworks>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<Reference Include="Microsoft.AspNetCore.Hosting" />
9+
<Reference Include="Microsoft.AspNetCore.Hosting.WindowsServices" />
10+
</ItemGroup>
11+
12+
</Project>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using Microsoft.AspNetCore.Testing.xunit;
5+
using Xunit;
6+
7+
[assembly: OSSkipCondition(OperatingSystems.MacOSX)]
8+
[assembly: OSSkipCondition(OperatingSystems.Linux)]
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
using System;
5+
using System.ServiceProcess;
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.AspNetCore.Hosting.Server;
9+
using Microsoft.AspNetCore.Hosting.WindowsServices;
10+
using Microsoft.AspNetCore.Http;
11+
using Microsoft.AspNetCore.Http.Features;
12+
using Microsoft.Extensions.DependencyInjection;
13+
using Microsoft.AspNetCore.Testing.xunit;
14+
using Xunit;
15+
16+
namespace Microsoft.AspNetCore.Hosting
17+
{
18+
public class WebHostServiceTests
19+
{
20+
// Reasonable timeout for our test operations to complete.
21+
private static readonly TimeSpan OperationTimeout = TimeSpan.FromSeconds( 5 );
22+
23+
[ConditionalFact]
24+
public async Task StopBeforeServiceStarted()
25+
{
26+
var host = new WebHostBuilder().UseServer(new FakeServer()).Configure(x => { }).Build();
27+
var webHostService = new WebHostService(host);
28+
var applicationLifetime = host.Services.GetRequiredService<IApplicationLifetime>();
29+
30+
applicationLifetime.StopApplication();
31+
webHostService.Start();
32+
33+
await Assert.ThrowsAsync<TaskCanceledException>(
34+
() => Task.Delay(OperationTimeout, applicationLifetime.ApplicationStopped));
35+
}
36+
37+
[ConditionalFact]
38+
public async Task StopAfterServiceStarted()
39+
{
40+
var host = new WebHostBuilder().UseServer( new FakeServer() ).Configure( x => { } ).Build();
41+
var webHostService = new WebHostService(host);
42+
var applicationLifetime = host.Services.GetRequiredService<IApplicationLifetime>();
43+
44+
webHostService.Start();
45+
applicationLifetime.StopApplication();
46+
47+
await Assert.ThrowsAsync<TaskCanceledException>(
48+
() => Task.Delay(OperationTimeout, applicationLifetime.ApplicationStopped));
49+
}
50+
51+
private sealed class FakeServer : IServer
52+
{
53+
IFeatureCollection IServer.Features { get; }
54+
public RequestDelegate RequestDelegate { get; private set; }
55+
56+
public void Dispose() { }
57+
58+
public Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)
59+
{
60+
RequestDelegate = ctx => throw new NotSupportedException();
61+
62+
return Task.CompletedTask;
63+
}
64+
65+
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)