Merge the master branch of aspnet/Hosting
This commit is contained in:
commit
425c196cba
|
|
@ -19,25 +19,33 @@
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.ClosedGenericMatcher.Sources" Version="$(MicrosoftExtensionsClosedGenericMatcherSourcesPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.CopyOnWriteDictionary.Sources" Version="$(MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.CopyOnWriteDictionary.Sources" Version="$(MicrosoftExtensionsCopyOnWriteDictionarySourcesPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.CommandLineUtils.Sources" Version="$(MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.CommandLineUtils.Sources" Version="$(MicrosoftExtensionsCommandLineUtilsSourcesPackageVersion)" />
|
||||||
|
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="$(MicrosoftExtensionsConfigurationAbstractionsPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="$(MicrosoftExtensionsConfigurationBinderPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="$(MicrosoftExtensionsConfigurationBinderPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="$(MicrosoftExtensionsConfigurationCommandLinePackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="$(MicrosoftExtensionsConfigurationCommandLinePackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="$(MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="$(MicrosoftExtensionsConfigurationEnvironmentVariablesPackageVersion)" />
|
||||||
|
<LatestPackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="$(MicrosoftExtensionsConfigurationFileExtensionsPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftExtensionsConfigurationJsonPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Configuration.Json" Version="$(MicrosoftExtensionsConfigurationJsonPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="$(MicrosoftExtensionsConfigurationUserSecretsPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="$(MicrosoftExtensionsConfigurationUserSecretsPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftExtensionsConfigurationPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Configuration" Version="$(MicrosoftExtensionsConfigurationPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="$(MicrosoftExtensionsDependencyInjectionAbstractionsPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.DependencyInjection" Version="$(MicrosoftExtensionsDependencyInjectionPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="$(MicrosoftExtensionsDiagnosticAdapterPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.DiagnosticAdapter" Version="$(MicrosoftExtensionsDiagnosticAdapterPackageVersion)" />
|
||||||
|
<LatestPackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="$(MicrosoftExtensionsHostingAbstractionsPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Hosting" Version="$(MicrosoftExtensionsHostingPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Hosting" Version="$(MicrosoftExtensionsHostingPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="$(MicrosoftExtensionsLoggingAbstractionsPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Logging.Console" Version="$(MicrosoftExtensionsLoggingConsolePackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Logging.Testing" Version="$(MicrosoftExtensionsLoggingTestingPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Logging" Version="$(MicrosoftExtensionsLoggingPackageVersion)" />
|
||||||
|
<LatestPackageReference Include="Microsoft.Extensions.FileProviders.Abstractions" Version="$(MicrosoftExtensionsFileProvidersAbstractionsPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(MicrosoftExtensionsFileProvidersEmbeddedPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.FileProviders.Embedded" Version="$(MicrosoftExtensionsFileProvidersEmbeddedPackageVersion)" />
|
||||||
|
<LatestPackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="$(MicrosoftExtensionsFileProvidersPhysicalPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Options" Version="$(MicrosoftExtensionsOptionsPackageVersion)" />
|
||||||
|
<LatestPackageReference Include="Microsoft.Extensions.ObjectPool" Version="$(MicrosoftExtensionsObjectPoolPackageVersion)" />
|
||||||
|
<LatestPackageReference Include="Microsoft.Extensions.Primitives" Version="$(MicrosoftExtensionsPrimitivesPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.Process.Sources" Version="$(MicrosoftExtensionsProcessSourcesPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.Process.Sources" Version="$(MicrosoftExtensionsProcessSourcesPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.RazorViews.Sources" Version="$(MicrosoftExtensionsRazorViewsSourcesPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.RazorViews.Sources" Version="$(MicrosoftExtensionsRazorViewsSourcesPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.StackTrace.Sources" Version="$(MicrosoftExtensionsStackTraceSourcesPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.StackTrace.Sources" Version="$(MicrosoftExtensionsStackTraceSourcesPackageVersion)" />
|
||||||
|
<LatestPackageReference Include="Microsoft.Extensions.PropertyHelper.Sources" Version="$(MicrosoftExtensionsPropertyHelperSourcesPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.TypeNameHelper.Sources" Version="$(MicrosoftExtensionsTypeNameHelperSourcesPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.TypeNameHelper.Sources" Version="$(MicrosoftExtensionsTypeNameHelperSourcesPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.WebEncoders.Sources" Version="$(MicrosoftExtensionsWebEncodersSourcesPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.WebEncoders.Sources" Version="$(MicrosoftExtensionsWebEncodersSourcesPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Extensions.WebEncoders" Version="$(MicrosoftExtensionsWebEncodersPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Extensions.WebEncoders" Version="$(MicrosoftExtensionsWebEncodersPackageVersion)" />
|
||||||
|
|
@ -45,16 +53,20 @@
|
||||||
<LatestPackageReference Include="Microsoft.Internal.AspNetCore.H2Spec.All" Version="$(MicrosoftInternalAspNetCoreH2SpecAllPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Internal.AspNetCore.H2Spec.All" Version="$(MicrosoftInternalAspNetCoreH2SpecAllPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.Win32.Registry" Version="$(MicrosoftWin32RegistryPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.Win32.Registry" Version="$(MicrosoftWin32RegistryPackageVersion)" />
|
||||||
<LatestPackageReference Include="Microsoft.NETCore.Windows.ApiSets" Version="$(MicrosoftNETCoreWindowsApiSetsPackageVersion)" />
|
<LatestPackageReference Include="Microsoft.NETCore.Windows.ApiSets" Version="$(MicrosoftNETCoreWindowsApiSetsPackageVersion)" />
|
||||||
|
<LatestPackageReference Include="System.Buffers" Version="$(SystemBuffersPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Data.SqlClient" Version="$(SystemDataSqlClientPackageVersion)" />
|
<LatestPackageReference Include="System.Data.SqlClient" Version="$(SystemDataSqlClientPackageVersion)" />
|
||||||
|
<LatestPackageReference Include="System.Diagnostics.DiagnosticSource" Version="$(SystemDiagnosticsDiagnosticSourcePackageVersion)" />
|
||||||
<LatestPackageReference Include="System.IO.Pipelines" Version="$(SystemIOPipelinesPackageVersion)" />
|
<LatestPackageReference Include="System.IO.Pipelines" Version="$(SystemIOPipelinesPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Memory" Version="$(SystemMemoryPackageVersion)" />
|
<LatestPackageReference Include="System.Memory" Version="$(SystemMemoryPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Net.Http.WinHttpHandler" Version="$(SystemNetHttpWinHttpHandlerPackageVersion)" />
|
<LatestPackageReference Include="System.Net.Http.WinHttpHandler" Version="$(SystemNetHttpWinHttpHandlerPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Net.WebSockets.WebSocketProtocol" Version="$(SystemNetWebSocketsWebSocketProtocolPackageVersion)" />
|
<LatestPackageReference Include="System.Net.WebSockets.WebSocketProtocol" Version="$(SystemNetWebSocketsWebSocketProtocolPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Numerics.Vectors" Version="$(SystemNumericsVectorsPackageVersion)" />
|
<LatestPackageReference Include="System.Numerics.Vectors" Version="$(SystemNumericsVectorsPackageVersion)" />
|
||||||
|
<LatestPackageReference Include="System.Reflection.Metadata" Version="$(SystemReflectionMetadataPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="$(SystemRuntimeCompilerServicesUnsafePackageVersion)" />
|
<LatestPackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="$(SystemRuntimeCompilerServicesUnsafePackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Security.Cryptography.Cng" Version="$(SystemSecurityCryptographyCngPackageVersion)" />
|
<LatestPackageReference Include="System.Security.Cryptography.Cng" Version="$(SystemSecurityCryptographyCngPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Security.Cryptography.Xml" Version="$(SystemSecurityCryptographyXmlPackageVersion)" />
|
<LatestPackageReference Include="System.Security.Cryptography.Xml" Version="$(SystemSecurityCryptographyXmlPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Security.Principal.Windows" Version="$(SystemSecurityPrincipalWindowsPackageVersion)" />
|
<LatestPackageReference Include="System.Security.Principal.Windows" Version="$(SystemSecurityPrincipalWindowsPackageVersion)" />
|
||||||
|
<LatestPackageReference Include="System.ServiceProcess.ServiceController" Version="$(SystemServiceProcessServiceControllerPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Text.Encodings.Web" Version="$(SystemTextEncodingsWebPackageVersion)" />
|
<LatestPackageReference Include="System.Text.Encodings.Web" Version="$(SystemTextEncodingsWebPackageVersion)" />
|
||||||
<LatestPackageReference Include="System.Threading.Tasks.Extensions" Version="$(SystemThreadingTasksExtensionsPackageVersion)" />
|
<LatestPackageReference Include="System.Threading.Tasks.Extensions" Version="$(SystemThreadingTasksExtensionsPackageVersion)" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
// 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.Threading;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Hosting.Internal
|
||||||
|
{
|
||||||
|
internal class GenericWebHostApplicationLifetime : IApplicationLifetime
|
||||||
|
{
|
||||||
|
private readonly Microsoft.Extensions.Hosting.IApplicationLifetime _applicationLifetime;
|
||||||
|
public GenericWebHostApplicationLifetime(Microsoft.Extensions.Hosting.IApplicationLifetime applicationLifetime)
|
||||||
|
{
|
||||||
|
_applicationLifetime = applicationLifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CancellationToken ApplicationStarted => _applicationLifetime.ApplicationStarted;
|
||||||
|
|
||||||
|
public CancellationToken ApplicationStopping => _applicationLifetime.ApplicationStopping;
|
||||||
|
|
||||||
|
public CancellationToken ApplicationStopped => _applicationLifetime.ApplicationStopped;
|
||||||
|
|
||||||
|
public void StopApplication() => _applicationLifetime.StopApplication();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,376 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.ExceptionServices;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting.Builder;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.ObjectPool;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Hosting.Internal
|
||||||
|
{
|
||||||
|
internal class GenericWebHostBuilder : IWebHostBuilder, ISupportsStartup, ISupportsUseDefaultServiceProvider
|
||||||
|
{
|
||||||
|
private readonly IHostBuilder _builder;
|
||||||
|
private readonly IConfiguration _config;
|
||||||
|
private readonly object _startupKey = new object();
|
||||||
|
|
||||||
|
private AggregateException _hostingStartupErrors;
|
||||||
|
private HostingStartupWebHostBuilder _hostingStartupWebHostBuilder;
|
||||||
|
|
||||||
|
public GenericWebHostBuilder(IHostBuilder builder)
|
||||||
|
{
|
||||||
|
_builder = builder;
|
||||||
|
|
||||||
|
_config = new ConfigurationBuilder()
|
||||||
|
.AddEnvironmentVariables(prefix: "ASPNETCORE_")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
_builder.ConfigureHostConfiguration(config =>
|
||||||
|
{
|
||||||
|
config.AddConfiguration(_config);
|
||||||
|
|
||||||
|
// We do this super early but still late enough that we can process the configuration
|
||||||
|
// wired up by calls to UseSetting
|
||||||
|
ExecuteHostingStartups();
|
||||||
|
});
|
||||||
|
|
||||||
|
// IHostingStartup needs to be executed before any direct methods on the builder
|
||||||
|
// so register these callbacks first
|
||||||
|
_builder.ConfigureAppConfiguration((context, configurationBuilder) =>
|
||||||
|
{
|
||||||
|
if (_hostingStartupWebHostBuilder != null)
|
||||||
|
{
|
||||||
|
var webhostContext = GetWebHostBuilderContext(context);
|
||||||
|
_hostingStartupWebHostBuilder.ConfigureAppConfiguration(webhostContext, configurationBuilder);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_builder.ConfigureServices((context, services) =>
|
||||||
|
{
|
||||||
|
if (_hostingStartupWebHostBuilder != null)
|
||||||
|
{
|
||||||
|
var webhostContext = GetWebHostBuilderContext(context);
|
||||||
|
_hostingStartupWebHostBuilder.ConfigureServices(webhostContext, services);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_builder.ConfigureServices((context, services) =>
|
||||||
|
{
|
||||||
|
var webhostContext = GetWebHostBuilderContext(context);
|
||||||
|
var webHostOptions = (WebHostOptions)context.Properties[typeof(WebHostOptions)];
|
||||||
|
|
||||||
|
// Add the IHostingEnvironment and IApplicationLifetime from Microsoft.AspNetCore.Hosting
|
||||||
|
services.AddSingleton(webhostContext.HostingEnvironment);
|
||||||
|
services.AddSingleton<IApplicationLifetime, GenericWebHostApplicationLifetime>();
|
||||||
|
|
||||||
|
services.Configure<GenericWebHostServiceOptions>(options =>
|
||||||
|
{
|
||||||
|
// Set the options
|
||||||
|
options.WebHostOptions = webHostOptions;
|
||||||
|
// Store and forward any startup errors
|
||||||
|
options.HostingStartupExceptions = _hostingStartupErrors;
|
||||||
|
});
|
||||||
|
|
||||||
|
services.AddHostedService<GenericWebHostService>();
|
||||||
|
|
||||||
|
// REVIEW: This is bad since we don't own this type. Anybody could add one of these and it would mess things up
|
||||||
|
// We need to flow this differently
|
||||||
|
var listener = new DiagnosticListener("Microsoft.AspNetCore");
|
||||||
|
services.TryAddSingleton<DiagnosticListener>(listener);
|
||||||
|
services.TryAddSingleton<DiagnosticSource>(listener);
|
||||||
|
|
||||||
|
services.TryAddSingleton<IHttpContextFactory, HttpContextFactory>();
|
||||||
|
services.TryAddScoped<IMiddlewareFactory, MiddlewareFactory>();
|
||||||
|
services.TryAddSingleton<IApplicationBuilderFactory, ApplicationBuilderFactory>();
|
||||||
|
|
||||||
|
// Conjure up a RequestServices
|
||||||
|
services.TryAddTransient<IStartupFilter, AutoRequestServicesStartupFilter>();
|
||||||
|
|
||||||
|
// Ensure object pooling is available everywhere.
|
||||||
|
services.TryAddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
|
||||||
|
|
||||||
|
// Support UseStartup(assemblyName)
|
||||||
|
if (!string.IsNullOrEmpty(webHostOptions.StartupAssembly))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var startupType = StartupLoader.FindStartupType(webHostOptions.StartupAssembly, webhostContext.HostingEnvironment.EnvironmentName);
|
||||||
|
UseStartup(startupType, context, services);
|
||||||
|
}
|
||||||
|
catch (Exception ex) when (webHostOptions.CaptureStartupErrors)
|
||||||
|
{
|
||||||
|
var capture = ExceptionDispatchInfo.Capture(ex);
|
||||||
|
|
||||||
|
services.Configure<GenericWebHostServiceOptions>(options =>
|
||||||
|
{
|
||||||
|
options.ConfigureApplication = app =>
|
||||||
|
{
|
||||||
|
// Throw if there was any errors initializing startup
|
||||||
|
capture.Throw();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteHostingStartups()
|
||||||
|
{
|
||||||
|
var webHostOptions = new WebHostOptions(_config, Assembly.GetEntryAssembly()?.GetName().Name);
|
||||||
|
|
||||||
|
if (webHostOptions.PreventHostingStartup)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var exceptions = new List<Exception>();
|
||||||
|
_hostingStartupWebHostBuilder = new HostingStartupWebHostBuilder(this);
|
||||||
|
|
||||||
|
// Execute the hosting startup assemblies
|
||||||
|
foreach (var assemblyName in webHostOptions.GetFinalHostingStartupAssemblies().Distinct(StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var assembly = Assembly.Load(new AssemblyName(assemblyName));
|
||||||
|
|
||||||
|
foreach (var attribute in assembly.GetCustomAttributes<HostingStartupAttribute>())
|
||||||
|
{
|
||||||
|
var hostingStartup = (IHostingStartup)Activator.CreateInstance(attribute.HostingStartupType);
|
||||||
|
hostingStartup.Configure(_hostingStartupWebHostBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Capture any errors that happen during startup
|
||||||
|
exceptions.Add(new InvalidOperationException($"Startup assembly {assemblyName} failed to execute. See the inner exception for more details.", ex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (exceptions.Count > 0)
|
||||||
|
{
|
||||||
|
_hostingStartupErrors = new AggregateException(exceptions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWebHost Build()
|
||||||
|
{
|
||||||
|
throw new NotSupportedException($"Building this implementation of {nameof(IWebHostBuilder)} is not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWebHostBuilder ConfigureAppConfiguration(Action<WebHostBuilderContext, IConfigurationBuilder> configureDelegate)
|
||||||
|
{
|
||||||
|
_builder.ConfigureAppConfiguration((context, builder) =>
|
||||||
|
{
|
||||||
|
var webhostBuilderContext = GetWebHostBuilderContext(context);
|
||||||
|
configureDelegate(webhostBuilderContext, builder);
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWebHostBuilder ConfigureServices(Action<IServiceCollection> configureServices)
|
||||||
|
{
|
||||||
|
return ConfigureServices((context, services) => configureServices(services));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWebHostBuilder ConfigureServices(Action<WebHostBuilderContext, IServiceCollection> configureServices)
|
||||||
|
{
|
||||||
|
_builder.ConfigureServices((context, builder) =>
|
||||||
|
{
|
||||||
|
var webhostBuilderContext = GetWebHostBuilderContext(context);
|
||||||
|
configureServices(webhostBuilderContext, builder);
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWebHostBuilder UseDefaultServiceProvider(Action<WebHostBuilderContext, ServiceProviderOptions> configure)
|
||||||
|
{
|
||||||
|
// REVIEW: This is a hack to change the builder with the HostBuilderContext in scope,
|
||||||
|
// we're not actually using configuration here
|
||||||
|
_builder.ConfigureAppConfiguration((context, _) =>
|
||||||
|
{
|
||||||
|
var webHostBuilderContext = GetWebHostBuilderContext(context);
|
||||||
|
var options = new ServiceProviderOptions();
|
||||||
|
configure(webHostBuilderContext, options);
|
||||||
|
|
||||||
|
// This is only fine because this runs last
|
||||||
|
_builder.UseServiceProviderFactory(new DefaultServiceProviderFactory(options));
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWebHostBuilder UseStartup(Type startupType)
|
||||||
|
{
|
||||||
|
_builder.ConfigureServices((context, services) =>
|
||||||
|
{
|
||||||
|
UseStartup(startupType, context, services);
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UseStartup(Type startupType, HostBuilderContext context, IServiceCollection services)
|
||||||
|
{
|
||||||
|
var webHostBuilderContext = GetWebHostBuilderContext(context);
|
||||||
|
var webHostOptions = (WebHostOptions)context.Properties[typeof(WebHostOptions)];
|
||||||
|
|
||||||
|
ExceptionDispatchInfo startupError = null;
|
||||||
|
object instance = null;
|
||||||
|
ConfigureBuilder configureBuilder = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// We cannot support methods that return IServiceProvider as that is terminal and we need ConfigureServices to compose
|
||||||
|
if (typeof(IStartup).IsAssignableFrom(startupType))
|
||||||
|
{
|
||||||
|
throw new NotSupportedException($"{typeof(IStartup)} isn't supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
instance = ActivatorUtilities.CreateInstance(new HostServiceProvider(webHostBuilderContext), startupType);
|
||||||
|
context.Properties[_startupKey] = instance;
|
||||||
|
|
||||||
|
// Startup.ConfigureServices
|
||||||
|
var configureServicesBuilder = StartupLoader.FindConfigureServicesDelegate(startupType, context.HostingEnvironment.EnvironmentName);
|
||||||
|
var configureServices = configureServicesBuilder.Build(instance);
|
||||||
|
|
||||||
|
configureServices(services);
|
||||||
|
|
||||||
|
// REVIEW: We're doing this in the callback so that we have access to the hosting environment
|
||||||
|
// Startup.ConfigureContainer
|
||||||
|
var configureContainerBuilder = StartupLoader.FindConfigureContainerDelegate(startupType, context.HostingEnvironment.EnvironmentName);
|
||||||
|
if (configureContainerBuilder.MethodInfo != null)
|
||||||
|
{
|
||||||
|
var containerType = configureContainerBuilder.GetContainerType();
|
||||||
|
// Store the builder in the property bag
|
||||||
|
_builder.Properties[typeof(ConfigureContainerBuilder)] = configureContainerBuilder;
|
||||||
|
|
||||||
|
var actionType = typeof(Action<,>).MakeGenericType(typeof(HostBuilderContext), containerType);
|
||||||
|
|
||||||
|
// Get the private ConfigureContainer method on this type then close over the container type
|
||||||
|
var configureCallback = GetType().GetMethod(nameof(ConfigureContainer), BindingFlags.NonPublic | BindingFlags.Instance)
|
||||||
|
.MakeGenericMethod(containerType)
|
||||||
|
.CreateDelegate(actionType, this);
|
||||||
|
|
||||||
|
// _builder.ConfigureContainer<T>(ConfigureContainer);
|
||||||
|
typeof(IHostBuilder).GetMethods().First(m => m.Name == nameof(IHostBuilder.ConfigureContainer))
|
||||||
|
.MakeGenericMethod(containerType)
|
||||||
|
.Invoke(_builder, new object[] { configureCallback });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve Configure after calling ConfigureServices and ConfigureContainer
|
||||||
|
configureBuilder = StartupLoader.FindConfigureDelegate(startupType, context.HostingEnvironment.EnvironmentName);
|
||||||
|
}
|
||||||
|
catch (Exception ex) when (webHostOptions.CaptureStartupErrors)
|
||||||
|
{
|
||||||
|
startupError = ExceptionDispatchInfo.Capture(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Startup.Configure
|
||||||
|
services.Configure<GenericWebHostServiceOptions>(options =>
|
||||||
|
{
|
||||||
|
options.ConfigureApplication = app =>
|
||||||
|
{
|
||||||
|
// Throw if there was any errors initializing startup
|
||||||
|
startupError?.Throw();
|
||||||
|
|
||||||
|
// Execute Startup.Configure
|
||||||
|
if (instance != null && configureBuilder != null)
|
||||||
|
{
|
||||||
|
configureBuilder.Build(instance)(app);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ConfigureContainer<TContainer>(HostBuilderContext context, TContainer container)
|
||||||
|
{
|
||||||
|
var instance = context.Properties[_startupKey];
|
||||||
|
var builder = (ConfigureContainerBuilder)context.Properties[typeof(ConfigureContainerBuilder)];
|
||||||
|
builder.Build(instance)(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWebHostBuilder Configure(Action<IApplicationBuilder> configure)
|
||||||
|
{
|
||||||
|
_builder.ConfigureServices((context, services) =>
|
||||||
|
{
|
||||||
|
services.Configure<GenericWebHostServiceOptions>(options =>
|
||||||
|
{
|
||||||
|
options.ConfigureApplication = configure;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private WebHostBuilderContext GetWebHostBuilderContext(HostBuilderContext context)
|
||||||
|
{
|
||||||
|
if (!context.Properties.TryGetValue(typeof(WebHostBuilderContext), out var contextVal))
|
||||||
|
{
|
||||||
|
var options = new WebHostOptions(context.Configuration, Assembly.GetEntryAssembly()?.GetName().Name);
|
||||||
|
var hostingEnvironment = new HostingEnvironment();
|
||||||
|
hostingEnvironment.Initialize(context.HostingEnvironment.ContentRootPath, options);
|
||||||
|
|
||||||
|
var webHostBuilderContext = new WebHostBuilderContext
|
||||||
|
{
|
||||||
|
Configuration = context.Configuration,
|
||||||
|
HostingEnvironment = hostingEnvironment
|
||||||
|
};
|
||||||
|
context.Properties[typeof(WebHostBuilderContext)] = webHostBuilderContext;
|
||||||
|
context.Properties[typeof(WebHostOptions)] = options;
|
||||||
|
return webHostBuilderContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (WebHostBuilderContext)contextVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetSetting(string key)
|
||||||
|
{
|
||||||
|
return _config[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWebHostBuilder UseSetting(string key, string value)
|
||||||
|
{
|
||||||
|
_config[key] = value;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This exists just so that we can use ActivatorUtilities.CreateInstance on the Startup class
|
||||||
|
private class HostServiceProvider : IServiceProvider
|
||||||
|
{
|
||||||
|
private readonly WebHostBuilderContext _context;
|
||||||
|
|
||||||
|
public HostServiceProvider(WebHostBuilderContext context)
|
||||||
|
{
|
||||||
|
_context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object GetService(Type serviceType)
|
||||||
|
{
|
||||||
|
// The implementation of the HostingEnvironment supports both interfaces
|
||||||
|
if (serviceType == typeof(Microsoft.AspNetCore.Hosting.IHostingEnvironment) || serviceType == typeof(IHostingEnvironment))
|
||||||
|
{
|
||||||
|
return _context.HostingEnvironment;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (serviceType == typeof(IConfiguration))
|
||||||
|
{
|
||||||
|
return _context.Configuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// 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.Builder;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Hosting.Internal
|
||||||
|
{
|
||||||
|
internal class GenericWebHostServiceOptions
|
||||||
|
{
|
||||||
|
public Action<IApplicationBuilder> ConfigureApplication { get; set; }
|
||||||
|
|
||||||
|
public WebHostOptions WebHostOptions { get; set; }
|
||||||
|
|
||||||
|
public AggregateException HostingStartupExceptions { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,201 @@
|
||||||
|
// 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.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting.Server;
|
||||||
|
using Microsoft.AspNetCore.Hosting.Server.Features;
|
||||||
|
using Microsoft.AspNetCore.Hosting.Views;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Microsoft.Extensions.StackTrace.Sources;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Hosting.Internal
|
||||||
|
{
|
||||||
|
internal class GenericWebHostService : IHostedService
|
||||||
|
{
|
||||||
|
public GenericWebHostService(IOptions<GenericWebHostServiceOptions> options,
|
||||||
|
IServer server,
|
||||||
|
ILogger<GenericWebHostService> logger,
|
||||||
|
DiagnosticListener diagnosticListener,
|
||||||
|
IHttpContextFactory httpContextFactory,
|
||||||
|
IApplicationBuilderFactory applicationBuilderFactory,
|
||||||
|
IEnumerable<IStartupFilter> startupFilters,
|
||||||
|
IConfiguration configuration,
|
||||||
|
IHostingEnvironment hostingEnvironment)
|
||||||
|
{
|
||||||
|
Options = options.Value;
|
||||||
|
Server = server;
|
||||||
|
Logger = logger;
|
||||||
|
DiagnosticListener = diagnosticListener;
|
||||||
|
HttpContextFactory = httpContextFactory;
|
||||||
|
ApplicationBuilderFactory = applicationBuilderFactory;
|
||||||
|
StartupFilters = startupFilters;
|
||||||
|
Configuration = configuration;
|
||||||
|
HostingEnvironment = hostingEnvironment;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GenericWebHostServiceOptions Options { get; }
|
||||||
|
public IServer Server { get; }
|
||||||
|
public ILogger<GenericWebHostService> Logger { get; }
|
||||||
|
public DiagnosticListener DiagnosticListener { get; }
|
||||||
|
public IHttpContextFactory HttpContextFactory { get; }
|
||||||
|
public IApplicationBuilderFactory ApplicationBuilderFactory { get; }
|
||||||
|
public IEnumerable<IStartupFilter> StartupFilters { get; }
|
||||||
|
public IConfiguration Configuration { get; }
|
||||||
|
public IHostingEnvironment HostingEnvironment { get; }
|
||||||
|
|
||||||
|
public async Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
HostingEventSource.Log.HostStart();
|
||||||
|
|
||||||
|
var serverAddressesFeature = Server.Features?.Get<IServerAddressesFeature>();
|
||||||
|
var addresses = serverAddressesFeature?.Addresses;
|
||||||
|
if (addresses != null && !addresses.IsReadOnly && addresses.Count == 0)
|
||||||
|
{
|
||||||
|
var urls = Configuration[WebHostDefaults.ServerUrlsKey];
|
||||||
|
if (!string.IsNullOrEmpty(urls))
|
||||||
|
{
|
||||||
|
serverAddressesFeature.PreferHostingUrls = WebHostUtilities.ParseBool(Configuration, WebHostDefaults.PreferHostingUrlsKey);
|
||||||
|
|
||||||
|
foreach (var value in urls.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries))
|
||||||
|
{
|
||||||
|
addresses.Add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestDelegate application = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Action<IApplicationBuilder> configure = Options.ConfigureApplication;
|
||||||
|
|
||||||
|
if (configure == null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"No application configured. Please specify an application via IWebHostBuilder.UseStartup, IWebHostBuilder.Configure, or specifying the startup assembly via {nameof(WebHostDefaults.StartupAssemblyKey)} in the web host configuration.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var builder = ApplicationBuilderFactory.CreateBuilder(Server.Features);
|
||||||
|
|
||||||
|
foreach (var filter in StartupFilters.Reverse())
|
||||||
|
{
|
||||||
|
configure = filter.Configure(configure);
|
||||||
|
}
|
||||||
|
|
||||||
|
configure(builder);
|
||||||
|
|
||||||
|
// Build the request pipeline
|
||||||
|
application = builder.Build();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.ApplicationError(ex);
|
||||||
|
|
||||||
|
if (!Options.WebHostOptions.CaptureStartupErrors)
|
||||||
|
{
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
application = BuildErrorPageApplication(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
var httpApplication = new HostingApplication(application, Logger, DiagnosticListener, HttpContextFactory);
|
||||||
|
|
||||||
|
await Server.StartAsync(httpApplication, cancellationToken);
|
||||||
|
|
||||||
|
if (addresses != null)
|
||||||
|
{
|
||||||
|
foreach (var address in addresses)
|
||||||
|
{
|
||||||
|
Logger.LogInformation("Now listening on: {address}", address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Logger.IsEnabled(LogLevel.Debug))
|
||||||
|
{
|
||||||
|
foreach (var assembly in Options.WebHostOptions.GetFinalHostingStartupAssemblies())
|
||||||
|
{
|
||||||
|
Logger.LogDebug("Loaded hosting startup assembly {assemblyName}", assembly);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Options.HostingStartupExceptions != null)
|
||||||
|
{
|
||||||
|
foreach (var exception in Options.HostingStartupExceptions.InnerExceptions)
|
||||||
|
{
|
||||||
|
Logger.HostingStartupAssemblyError(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private RequestDelegate BuildErrorPageApplication(Exception exception)
|
||||||
|
{
|
||||||
|
if (exception is TargetInvocationException tae)
|
||||||
|
{
|
||||||
|
exception = tae.InnerException;
|
||||||
|
}
|
||||||
|
|
||||||
|
var showDetailedErrors = HostingEnvironment.IsDevelopment() || Options.WebHostOptions.DetailedErrors;
|
||||||
|
|
||||||
|
var model = new ErrorPageModel
|
||||||
|
{
|
||||||
|
RuntimeDisplayName = RuntimeInformation.FrameworkDescription
|
||||||
|
};
|
||||||
|
var systemRuntimeAssembly = typeof(System.ComponentModel.DefaultValueAttribute).GetTypeInfo().Assembly;
|
||||||
|
var assemblyVersion = new AssemblyName(systemRuntimeAssembly.FullName).Version.ToString();
|
||||||
|
var clrVersion = assemblyVersion;
|
||||||
|
model.RuntimeArchitecture = RuntimeInformation.ProcessArchitecture.ToString();
|
||||||
|
var currentAssembly = typeof(ErrorPage).GetTypeInfo().Assembly;
|
||||||
|
model.CurrentAssemblyVesion = currentAssembly
|
||||||
|
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
|
||||||
|
.InformationalVersion;
|
||||||
|
model.ClrVersion = clrVersion;
|
||||||
|
model.OperatingSystemDescription = RuntimeInformation.OSDescription;
|
||||||
|
|
||||||
|
if (showDetailedErrors)
|
||||||
|
{
|
||||||
|
var exceptionDetailProvider = new ExceptionDetailsProvider(
|
||||||
|
HostingEnvironment.ContentRootFileProvider,
|
||||||
|
sourceCodeLineCount: 6);
|
||||||
|
|
||||||
|
model.ErrorDetails = exceptionDetailProvider.GetDetails(exception);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
model.ErrorDetails = new ExceptionDetails[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
var errorPage = new ErrorPage(model);
|
||||||
|
return context =>
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 500;
|
||||||
|
context.Response.Headers["Cache-Control"] = "no-cache";
|
||||||
|
return errorPage.ExecuteAsync(context);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StopAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Server.StopAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
HostingEventSource.Log.HostStop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
// 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.Builder;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Hosting.Internal
|
||||||
|
{
|
||||||
|
// We use this type to capture calls to the IWebHostBuilder so the we can properly order calls to
|
||||||
|
// to GenericHostWebHostBuilder.
|
||||||
|
internal class HostingStartupWebHostBuilder : IWebHostBuilder, ISupportsStartup, ISupportsUseDefaultServiceProvider
|
||||||
|
{
|
||||||
|
private readonly GenericWebHostBuilder _builder;
|
||||||
|
private Action<WebHostBuilderContext, IConfigurationBuilder> _configureConfiguration;
|
||||||
|
private Action<WebHostBuilderContext, IServiceCollection> _configureServices;
|
||||||
|
|
||||||
|
public HostingStartupWebHostBuilder(GenericWebHostBuilder builder)
|
||||||
|
{
|
||||||
|
_builder = builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWebHost Build()
|
||||||
|
{
|
||||||
|
throw new NotSupportedException($"Building this implementation of {nameof(IWebHostBuilder)} is not supported.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWebHostBuilder ConfigureAppConfiguration(Action<WebHostBuilderContext, IConfigurationBuilder> configureDelegate)
|
||||||
|
{
|
||||||
|
_configureConfiguration += configureDelegate;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWebHostBuilder ConfigureServices(Action<IServiceCollection> configureServices)
|
||||||
|
{
|
||||||
|
return ConfigureServices((context, services) => configureServices(services));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWebHostBuilder ConfigureServices(Action<WebHostBuilderContext, IServiceCollection> configureServices)
|
||||||
|
{
|
||||||
|
_configureServices += configureServices;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetSetting(string key) => _builder.GetSetting(key);
|
||||||
|
|
||||||
|
public IWebHostBuilder UseSetting(string key, string value)
|
||||||
|
{
|
||||||
|
_builder.UseSetting(key, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ConfigureServices(WebHostBuilderContext context, IServiceCollection services)
|
||||||
|
{
|
||||||
|
_configureServices?.Invoke(context, services);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ConfigureAppConfiguration(WebHostBuilderContext context, IConfigurationBuilder builder)
|
||||||
|
{
|
||||||
|
_configureConfiguration?.Invoke(context, builder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWebHostBuilder UseDefaultServiceProvider(Action<WebHostBuilderContext, ServiceProviderOptions> configure)
|
||||||
|
{
|
||||||
|
return _builder.UseDefaultServiceProvider(configure);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWebHostBuilder Configure(Action<IApplicationBuilder> configure)
|
||||||
|
{
|
||||||
|
return _builder.Configure(configure);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWebHostBuilder UseStartup(Type startupType)
|
||||||
|
{
|
||||||
|
return _builder.UseStartup(startupType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
// 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.Builder;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Hosting.Internal
|
||||||
|
{
|
||||||
|
internal interface ISupportsStartup
|
||||||
|
{
|
||||||
|
IWebHostBuilder Configure(Action<IApplicationBuilder> configure);
|
||||||
|
IWebHostBuilder UseStartup(Type startupType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
// 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.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Hosting.Internal
|
||||||
|
{
|
||||||
|
internal interface ISupportsUseDefaultServiceProvider
|
||||||
|
{
|
||||||
|
IWebHostBuilder UseDefaultServiceProvider(Action<WebHostBuilderContext, ServiceProviderOptions> configure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
using System;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Hosting.Internal;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Hosting
|
||||||
|
{
|
||||||
|
public static class GenericHostWebHostBuilderExtensions
|
||||||
|
{
|
||||||
|
public static IHostBuilder ConfigureWebHost(this IHostBuilder builder, Action<IWebHostBuilder> configure)
|
||||||
|
{
|
||||||
|
var webhostBuilder = new GenericWebHostBuilder(builder);
|
||||||
|
configure(webhostBuilder);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Hosting.Internal
|
||||||
|
|
||||||
public MethodInfo MethodInfo { get; }
|
public MethodInfo MethodInfo { get; }
|
||||||
|
|
||||||
public Func<Action<object>, Action<object>> ConfigureContainerFilters { get; set; }
|
public Func<Action<object>, Action<object>> ConfigureContainerFilters { get; set; } = f => f;
|
||||||
|
|
||||||
public Action<object> Build(object instance) => container => Invoke(instance, container);
|
public Action<object> Build(object instance) => container => Invoke(instance, container);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.Hosting.Internal
|
||||||
|
|
||||||
public MethodInfo MethodInfo { get; }
|
public MethodInfo MethodInfo { get; }
|
||||||
|
|
||||||
public Func<Func<IServiceCollection, IServiceProvider>, Func<IServiceCollection, IServiceProvider>> StartupServiceFilters { get; set; }
|
public Func<Func<IServiceCollection, IServiceProvider>, Func<IServiceCollection, IServiceProvider>> StartupServiceFilters { get; set; } = f => f;
|
||||||
|
|
||||||
public Func<IServiceCollection, IServiceProvider> Build(object instance) => services => Invoke(instance, services);
|
public Func<IServiceCollection, IServiceProvider> Build(object instance) => services => Invoke(instance, services);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -267,19 +267,19 @@ namespace Microsoft.AspNetCore.Hosting.Internal
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ConfigureBuilder FindConfigureDelegate(Type startupType, string environmentName)
|
internal static ConfigureBuilder FindConfigureDelegate(Type startupType, string environmentName)
|
||||||
{
|
{
|
||||||
var configureMethod = FindMethod(startupType, "Configure{0}", environmentName, typeof(void), required: true);
|
var configureMethod = FindMethod(startupType, "Configure{0}", environmentName, typeof(void), required: true);
|
||||||
return new ConfigureBuilder(configureMethod);
|
return new ConfigureBuilder(configureMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ConfigureContainerBuilder FindConfigureContainerDelegate(Type startupType, string environmentName)
|
internal static ConfigureContainerBuilder FindConfigureContainerDelegate(Type startupType, string environmentName)
|
||||||
{
|
{
|
||||||
var configureMethod = FindMethod(startupType, "Configure{0}Container", environmentName, typeof(void), required: false);
|
var configureMethod = FindMethod(startupType, "Configure{0}Container", environmentName, typeof(void), required: false);
|
||||||
return new ConfigureContainerBuilder(configureMethod);
|
return new ConfigureContainerBuilder(configureMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ConfigureServicesBuilder FindConfigureServicesDelegate(Type startupType, string environmentName)
|
internal static ConfigureServicesBuilder FindConfigureServicesDelegate(Type startupType, string environmentName)
|
||||||
{
|
{
|
||||||
var servicesMethod = FindMethod(startupType, "Configure{0}Services", environmentName, typeof(IServiceProvider), required: false)
|
var servicesMethod = FindMethod(startupType, "Configure{0}Services", environmentName, typeof(IServiceProvider), required: false)
|
||||||
?? FindMethod(startupType, "Configure{0}Services", environmentName, typeof(void), required: false);
|
?? FindMethod(startupType, "Configure{0}Services", environmentName, typeof(void), required: false);
|
||||||
|
|
|
||||||
|
|
@ -192,7 +192,7 @@ namespace Microsoft.AspNetCore.Hosting.Internal
|
||||||
|
|
||||||
if (_startup == null)
|
if (_startup == null)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException($"No startup configured. Please specify startup via WebHostBuilder.UseStartup, WebHostBuilder.Configure, injecting {nameof(IStartup)} or specifying the startup assembly via {nameof(WebHostDefaults.StartupAssemblyKey)} in the web host configuration.");
|
throw new InvalidOperationException($"No application configured. Please specify startup via IWebHostBuilder.UseStartup, IWebHostBuilder.Configure, injecting {nameof(IStartup)} or specifying the startup assembly via {nameof(WebHostDefaults.StartupAssemblyKey)} in the web host configuration.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,13 +25,13 @@ namespace Microsoft.AspNetCore.Hosting
|
||||||
public class WebHostBuilder : IWebHostBuilder
|
public class WebHostBuilder : IWebHostBuilder
|
||||||
{
|
{
|
||||||
private readonly HostingEnvironment _hostingEnvironment;
|
private readonly HostingEnvironment _hostingEnvironment;
|
||||||
private readonly List<Action<WebHostBuilderContext, IServiceCollection>> _configureServicesDelegates;
|
private Action<WebHostBuilderContext, IServiceCollection> _configureServices;
|
||||||
|
|
||||||
private IConfiguration _config;
|
private IConfiguration _config;
|
||||||
private WebHostOptions _options;
|
private WebHostOptions _options;
|
||||||
private WebHostBuilderContext _context;
|
private WebHostBuilderContext _context;
|
||||||
private bool _webHostBuilt;
|
private bool _webHostBuilt;
|
||||||
private List<Action<WebHostBuilderContext, IConfigurationBuilder>> _configureAppConfigurationBuilderDelegates;
|
private Action<WebHostBuilderContext, IConfigurationBuilder> _configureAppConfigurationBuilder;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="WebHostBuilder"/> class.
|
/// Initializes a new instance of the <see cref="WebHostBuilder"/> class.
|
||||||
|
|
@ -39,8 +39,6 @@ namespace Microsoft.AspNetCore.Hosting
|
||||||
public WebHostBuilder()
|
public WebHostBuilder()
|
||||||
{
|
{
|
||||||
_hostingEnvironment = new HostingEnvironment();
|
_hostingEnvironment = new HostingEnvironment();
|
||||||
_configureServicesDelegates = new List<Action<WebHostBuilderContext, IServiceCollection>>();
|
|
||||||
_configureAppConfigurationBuilderDelegates = new List<Action<WebHostBuilderContext, IConfigurationBuilder>>();
|
|
||||||
|
|
||||||
_config = new ConfigurationBuilder()
|
_config = new ConfigurationBuilder()
|
||||||
.AddEnvironmentVariables(prefix: "ASPNETCORE_")
|
.AddEnvironmentVariables(prefix: "ASPNETCORE_")
|
||||||
|
|
@ -111,12 +109,7 @@ namespace Microsoft.AspNetCore.Hosting
|
||||||
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
|
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
|
||||||
public IWebHostBuilder ConfigureServices(Action<WebHostBuilderContext, IServiceCollection> configureServices)
|
public IWebHostBuilder ConfigureServices(Action<WebHostBuilderContext, IServiceCollection> configureServices)
|
||||||
{
|
{
|
||||||
if (configureServices == null)
|
_configureServices += configureServices;
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(configureServices));
|
|
||||||
}
|
|
||||||
|
|
||||||
_configureServicesDelegates.Add(configureServices);
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -131,12 +124,7 @@ namespace Microsoft.AspNetCore.Hosting
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public IWebHostBuilder ConfigureAppConfiguration(Action<WebHostBuilderContext, IConfigurationBuilder> configureDelegate)
|
public IWebHostBuilder ConfigureAppConfiguration(Action<WebHostBuilderContext, IConfigurationBuilder> configureDelegate)
|
||||||
{
|
{
|
||||||
if (configureDelegate == null)
|
_configureAppConfigurationBuilder += configureDelegate;
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(configureDelegate));
|
|
||||||
}
|
|
||||||
|
|
||||||
_configureAppConfigurationBuilderDelegates.Add(configureDelegate);
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -273,10 +261,7 @@ namespace Microsoft.AspNetCore.Hosting
|
||||||
.SetBasePath(_hostingEnvironment.ContentRootPath)
|
.SetBasePath(_hostingEnvironment.ContentRootPath)
|
||||||
.AddConfiguration(_config);
|
.AddConfiguration(_config);
|
||||||
|
|
||||||
foreach (var configureAppConfiguration in _configureAppConfigurationBuilderDelegates)
|
_configureAppConfigurationBuilder?.Invoke(_context, builder);
|
||||||
{
|
|
||||||
configureAppConfiguration(_context, builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
var configuration = builder.Build();
|
var configuration = builder.Build();
|
||||||
services.AddSingleton<IConfiguration>(configuration);
|
services.AddSingleton<IConfiguration>(configuration);
|
||||||
|
|
@ -330,10 +315,7 @@ namespace Microsoft.AspNetCore.Hosting
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var configureServices in _configureServicesDelegates)
|
_configureServices?.Invoke(_context, services);
|
||||||
{
|
|
||||||
configureServices(_context, services);
|
|
||||||
}
|
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,15 +29,21 @@ namespace Microsoft.AspNetCore.Hosting
|
||||||
|
|
||||||
var startupAssemblyName = configureApp.GetMethodInfo().DeclaringType.GetTypeInfo().Assembly.GetName().Name;
|
var startupAssemblyName = configureApp.GetMethodInfo().DeclaringType.GetTypeInfo().Assembly.GetName().Name;
|
||||||
|
|
||||||
return hostBuilder
|
hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName);
|
||||||
.UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName)
|
|
||||||
.ConfigureServices(services =>
|
// Light up the ISupportsStartup implementation
|
||||||
|
if (hostBuilder is ISupportsStartup supportsStartup)
|
||||||
|
{
|
||||||
|
return supportsStartup.Configure(configureApp);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hostBuilder.ConfigureServices(services =>
|
||||||
|
{
|
||||||
|
services.AddSingleton<IStartup>(sp =>
|
||||||
{
|
{
|
||||||
services.AddSingleton<IStartup>(sp =>
|
return new DelegateStartup(sp.GetRequiredService<IServiceProviderFactory<IServiceCollection>>(), configureApp);
|
||||||
{
|
|
||||||
return new DelegateStartup(sp.GetRequiredService<IServiceProviderFactory<IServiceCollection>>(), configureApp);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -51,8 +57,15 @@ namespace Microsoft.AspNetCore.Hosting
|
||||||
{
|
{
|
||||||
var startupAssemblyName = startupType.GetTypeInfo().Assembly.GetName().Name;
|
var startupAssemblyName = startupType.GetTypeInfo().Assembly.GetName().Name;
|
||||||
|
|
||||||
|
hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName);
|
||||||
|
|
||||||
|
// Light up the GenericWebHostBuilder implementation
|
||||||
|
if (hostBuilder is ISupportsStartup supportsStartup)
|
||||||
|
{
|
||||||
|
return supportsStartup.UseStartup(startupType);
|
||||||
|
}
|
||||||
|
|
||||||
return hostBuilder
|
return hostBuilder
|
||||||
.UseSetting(WebHostDefaults.ApplicationKey, startupAssemblyName)
|
|
||||||
.ConfigureServices(services =>
|
.ConfigureServices(services =>
|
||||||
{
|
{
|
||||||
if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo()))
|
if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo()))
|
||||||
|
|
@ -100,6 +113,12 @@ namespace Microsoft.AspNetCore.Hosting
|
||||||
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
|
/// <returns>The <see cref="IWebHostBuilder"/>.</returns>
|
||||||
public static IWebHostBuilder UseDefaultServiceProvider(this IWebHostBuilder hostBuilder, Action<WebHostBuilderContext, ServiceProviderOptions> configure)
|
public static IWebHostBuilder UseDefaultServiceProvider(this IWebHostBuilder hostBuilder, Action<WebHostBuilderContext, ServiceProviderOptions> configure)
|
||||||
{
|
{
|
||||||
|
// Light up the GenericWebHostBuilder implementation
|
||||||
|
if (hostBuilder is ISupportsUseDefaultServiceProvider supportsDefaultServiceProvider)
|
||||||
|
{
|
||||||
|
return supportsDefaultServiceProvider.UseDefaultServiceProvider(configure);
|
||||||
|
}
|
||||||
|
|
||||||
return hostBuilder.ConfigureServices((context, services) =>
|
return hostBuilder.ConfigureServices((context, services) =>
|
||||||
{
|
{
|
||||||
var options = new ServiceProviderOptions();
|
var options = new ServiceProviderOptions();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
// 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.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Hosting.Server;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Hosting.Tests.Fakes
|
||||||
|
{
|
||||||
|
internal class GenericWebHost : IWebHost
|
||||||
|
{
|
||||||
|
private readonly IHost _host;
|
||||||
|
|
||||||
|
public GenericWebHost(IHost host)
|
||||||
|
{
|
||||||
|
_host = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IFeatureCollection ServerFeatures => Services.GetRequiredService<IServer>().Features;
|
||||||
|
|
||||||
|
public IServiceProvider Services => _host.Services;
|
||||||
|
|
||||||
|
public void Dispose() => _host.Dispose();
|
||||||
|
|
||||||
|
public void Start() => _host.Start();
|
||||||
|
|
||||||
|
public Task StartAsync(CancellationToken cancellationToken = default) => _host.StartAsync(cancellationToken);
|
||||||
|
|
||||||
|
public Task StopAsync(CancellationToken cancellationToken = default) => _host.StopAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
// 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.Builder;
|
||||||
|
using Microsoft.AspNetCore.Hosting.Internal;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Hosting.Tests.Fakes
|
||||||
|
{
|
||||||
|
public class GenericWebHostBuilderWrapper : IWebHostBuilder, ISupportsStartup, ISupportsUseDefaultServiceProvider
|
||||||
|
{
|
||||||
|
private readonly GenericWebHostBuilder _builder;
|
||||||
|
private readonly HostBuilder _hostBuilder;
|
||||||
|
|
||||||
|
internal GenericWebHostBuilderWrapper(HostBuilder hostBuilder)
|
||||||
|
{
|
||||||
|
_builder = new GenericWebHostBuilder(hostBuilder);
|
||||||
|
_hostBuilder = hostBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the only one that doesn't pass through
|
||||||
|
public IWebHost Build()
|
||||||
|
{
|
||||||
|
return new GenericWebHost(_hostBuilder.Build());
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWebHostBuilder Configure(Action<IApplicationBuilder> configure)
|
||||||
|
{
|
||||||
|
_builder.Configure(configure);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWebHostBuilder ConfigureAppConfiguration(Action<WebHostBuilderContext, IConfigurationBuilder> configureDelegate)
|
||||||
|
{
|
||||||
|
_builder.ConfigureAppConfiguration(configureDelegate);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWebHostBuilder ConfigureServices(Action<IServiceCollection> configureServices)
|
||||||
|
{
|
||||||
|
_builder.ConfigureServices(configureServices);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWebHostBuilder ConfigureServices(Action<WebHostBuilderContext, IServiceCollection> configureServices)
|
||||||
|
{
|
||||||
|
_builder.ConfigureServices(configureServices);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetSetting(string key)
|
||||||
|
{
|
||||||
|
return _builder.GetSetting(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWebHostBuilder UseDefaultServiceProvider(Action<WebHostBuilderContext, ServiceProviderOptions> configure)
|
||||||
|
{
|
||||||
|
_builder.UseDefaultServiceProvider(configure);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWebHostBuilder UseSetting(string key, string value)
|
||||||
|
{
|
||||||
|
_builder.UseSetting(key, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IWebHostBuilder UseStartup(Type startupType)
|
||||||
|
{
|
||||||
|
_builder.UseStartup(startupType);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.Hosting.Fakes
|
||||||
|
{
|
||||||
|
public class StartupNoServicesNoInterface
|
||||||
|
{
|
||||||
|
public void ConfigureServices(IServiceCollection services)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Configure(IApplicationBuilder app)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.AspNetCore.Owin" />
|
<Reference Include="Microsoft.AspNetCore.Owin" />
|
||||||
<Reference Include="Microsoft.AspNetCore.Hosting" />
|
<Reference Include="Microsoft.AspNetCore.Hosting" />
|
||||||
|
<Reference Include="Microsoft.Extensions.Hosting" />
|
||||||
<Reference Include="Microsoft.Extensions.Logging.Testing" />
|
<Reference Include="Microsoft.Extensions.Logging.Testing" />
|
||||||
<Reference Include="Microsoft.Extensions.Options" />
|
<Reference Include="Microsoft.Extensions.Options" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -11,6 +11,7 @@ namespace Microsoft.AspNetCore.Server.IntegrationTesting
|
||||||
public const string NetCoreApp20 = "netcoreapp2.0";
|
public const string NetCoreApp20 = "netcoreapp2.0";
|
||||||
public const string NetCoreApp21 = "netcoreapp2.1";
|
public const string NetCoreApp21 = "netcoreapp2.1";
|
||||||
public const string NetCoreApp22 = "netcoreapp2.2";
|
public const string NetCoreApp22 = "netcoreapp2.2";
|
||||||
|
public const string NetCoreApp30 = "netcoreapp3.0";
|
||||||
|
|
||||||
public static bool Matches(string tfm1, string tfm2)
|
public static bool Matches(string tfm1, string tfm2)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -72,10 +72,20 @@ namespace Microsoft.AspNetCore.TestHost
|
||||||
req.Method = request.Method.ToString();
|
req.Method = request.Method.ToString();
|
||||||
|
|
||||||
req.Scheme = request.RequestUri.Scheme;
|
req.Scheme = request.RequestUri.Scheme;
|
||||||
req.Host = HostString.FromUriComponent(request.RequestUri);
|
|
||||||
if (request.RequestUri.IsDefaultPort)
|
foreach (var header in request.Headers)
|
||||||
{
|
{
|
||||||
req.Host = new HostString(req.Host.Host);
|
req.Headers.Append(header.Key, header.Value.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.Host == null || !req.Host.HasValue)
|
||||||
|
{
|
||||||
|
// If Host wasn't explicitly set as a header, let's infer it from the Uri
|
||||||
|
req.Host = HostString.FromUriComponent(request.RequestUri);
|
||||||
|
if (request.RequestUri.IsDefaultPort)
|
||||||
|
{
|
||||||
|
req.Host = new HostString(req.Host.Host);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Path = PathString.FromUriComponent(request.RequestUri);
|
req.Path = PathString.FromUriComponent(request.RequestUri);
|
||||||
|
|
@ -87,10 +97,6 @@ namespace Microsoft.AspNetCore.TestHost
|
||||||
}
|
}
|
||||||
req.QueryString = QueryString.FromUriComponent(request.RequestUri);
|
req.QueryString = QueryString.FromUriComponent(request.RequestUri);
|
||||||
|
|
||||||
foreach (var header in request.Headers)
|
|
||||||
{
|
|
||||||
req.Headers.Append(header.Key, header.Value.ToArray());
|
|
||||||
}
|
|
||||||
if (requestContent != null)
|
if (requestContent != null)
|
||||||
{
|
{
|
||||||
foreach (var header in requestContent.Headers)
|
foreach (var header in requestContent.Headers)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
// 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.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.AspNetCore.Hosting.Server;
|
||||||
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
namespace Microsoft.AspNetCore.TestHost
|
||||||
|
{
|
||||||
|
public static class HostBuilderTestServerExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the TestServer from the host services.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="host"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static TestServer GetTestServer(this IHost host)
|
||||||
|
{
|
||||||
|
return (TestServer)host.Services.GetRequiredService<IServer>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the test client from the TestServer in the host services.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="host"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static HttpClient GetTestClient(this IHost host)
|
||||||
|
{
|
||||||
|
return host.GetTestServer().CreateClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -109,7 +109,16 @@ namespace Microsoft.AspNetCore.TestHost
|
||||||
if (!_responseFeature.HasStarted)
|
if (!_responseFeature.HasStarted)
|
||||||
{
|
{
|
||||||
// Sets HasStarted
|
// Sets HasStarted
|
||||||
await _responseFeature.FireOnSendingHeadersAsync();
|
try
|
||||||
|
{
|
||||||
|
await _responseFeature.FireOnSendingHeadersAsync();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Abort(ex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Copy the feature collection so we're not multi-threading on the same collection.
|
// Copy the feature collection so we're not multi-threading on the same collection.
|
||||||
var newFeatures = new FeatureCollection();
|
var newFeatures = new FeatureCollection();
|
||||||
foreach (var pair in _httpContext.Features)
|
foreach (var pair in _httpContext.Features)
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Hosting.Internal;
|
||||||
using Microsoft.AspNetCore.Hosting.Server;
|
using Microsoft.AspNetCore.Hosting.Server;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Features;
|
using Microsoft.AspNetCore.Http.Features;
|
||||||
|
|
@ -15,28 +16,48 @@ namespace Microsoft.AspNetCore.TestHost
|
||||||
{
|
{
|
||||||
public class TestServer : IServer
|
public class TestServer : IServer
|
||||||
{
|
{
|
||||||
private const string ServerName = nameof(TestServer);
|
|
||||||
private IWebHost _hostInstance;
|
private IWebHost _hostInstance;
|
||||||
private bool _disposed = false;
|
private bool _disposed = false;
|
||||||
private IHttpApplication<Context> _application;
|
private IHttpApplication<Context> _application;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For use with IHostBuilder or IWebHostBuilder.
|
||||||
|
/// </summary>
|
||||||
|
public TestServer()
|
||||||
|
: this(new FeatureCollection())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For use with IHostBuilder or IWebHostBuilder.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="featureCollection"></param>
|
||||||
|
public TestServer(IFeatureCollection featureCollection)
|
||||||
|
{
|
||||||
|
Features = featureCollection ?? throw new ArgumentNullException(nameof(featureCollection));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For use with IWebHostBuilder.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder"></param>
|
||||||
public TestServer(IWebHostBuilder builder)
|
public TestServer(IWebHostBuilder builder)
|
||||||
: this(builder, new FeatureCollection())
|
: this(builder, new FeatureCollection())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For use with IWebHostBuilder.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder"></param>
|
||||||
|
/// <param name="featureCollection"></param>
|
||||||
public TestServer(IWebHostBuilder builder, IFeatureCollection featureCollection)
|
public TestServer(IWebHostBuilder builder, IFeatureCollection featureCollection)
|
||||||
|
: this(featureCollection)
|
||||||
{
|
{
|
||||||
if (builder == null)
|
if (builder == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(builder));
|
throw new ArgumentNullException(nameof(builder));
|
||||||
}
|
}
|
||||||
if (featureCollection == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(featureCollection));
|
|
||||||
}
|
|
||||||
|
|
||||||
Features = featureCollection;
|
|
||||||
|
|
||||||
var host = builder.UseServer(this).Build();
|
var host = builder.UseServer(this).Build();
|
||||||
host.StartAsync().GetAwaiter().GetResult();
|
host.StartAsync().GetAwaiter().GetResult();
|
||||||
|
|
@ -49,16 +70,22 @@ namespace Microsoft.AspNetCore.TestHost
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return _hostInstance;
|
return _hostInstance
|
||||||
|
?? throw new InvalidOperationException("The TestServer constructor was not called with a IWebHostBuilder so IWebHost is not available.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IFeatureCollection Features { get; }
|
public IFeatureCollection Features { get; }
|
||||||
|
|
||||||
|
private IHttpApplication<Context> Application
|
||||||
|
{
|
||||||
|
get => _application ?? throw new InvalidOperationException("The server has not been started or no web application was configured.");
|
||||||
|
}
|
||||||
|
|
||||||
public HttpMessageHandler CreateHandler()
|
public HttpMessageHandler CreateHandler()
|
||||||
{
|
{
|
||||||
var pathBase = BaseAddress == null ? PathString.Empty : PathString.FromUriComponent(BaseAddress);
|
var pathBase = BaseAddress == null ? PathString.Empty : PathString.FromUriComponent(BaseAddress);
|
||||||
return new ClientHandler(pathBase, _application);
|
return new ClientHandler(pathBase, Application);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpClient CreateClient()
|
public HttpClient CreateClient()
|
||||||
|
|
@ -69,7 +96,7 @@ namespace Microsoft.AspNetCore.TestHost
|
||||||
public WebSocketClient CreateWebSocketClient()
|
public WebSocketClient CreateWebSocketClient()
|
||||||
{
|
{
|
||||||
var pathBase = BaseAddress == null ? PathString.Empty : PathString.FromUriComponent(BaseAddress);
|
var pathBase = BaseAddress == null ? PathString.Empty : PathString.FromUriComponent(BaseAddress);
|
||||||
return new WebSocketClient(pathBase, _application);
|
return new WebSocketClient(pathBase, Application);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -93,7 +120,7 @@ namespace Microsoft.AspNetCore.TestHost
|
||||||
throw new ArgumentNullException(nameof(configureContext));
|
throw new ArgumentNullException(nameof(configureContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
var builder = new HttpContextBuilder(_application);
|
var builder = new HttpContextBuilder(Application);
|
||||||
builder.Configure(context =>
|
builder.Configure(context =>
|
||||||
{
|
{
|
||||||
var request = context.Request;
|
var request = context.Request;
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,21 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
using Microsoft.AspNetCore.Hosting.Internal;
|
using Microsoft.AspNetCore.Hosting.Internal;
|
||||||
|
using Microsoft.AspNetCore.Hosting.Server;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.TestHost
|
namespace Microsoft.AspNetCore.TestHost
|
||||||
{
|
{
|
||||||
public static class WebHostBuilderExtensions
|
public static class WebHostBuilderExtensions
|
||||||
{
|
{
|
||||||
|
public static IWebHostBuilder UseTestServer(this IWebHostBuilder builder)
|
||||||
|
{
|
||||||
|
return builder.ConfigureServices(services =>
|
||||||
|
{
|
||||||
|
services.AddSingleton<IServer, TestServer>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public static IWebHostBuilder ConfigureTestServices(this IWebHostBuilder webHostBuilder, Action<IServiceCollection> servicesConfiguration)
|
public static IWebHostBuilder ConfigureTestServices(this IWebHostBuilder webHostBuilder, Action<IServiceCollection> servicesConfiguration)
|
||||||
{
|
{
|
||||||
if (webHostBuilder == null)
|
if (webHostBuilder == null)
|
||||||
|
|
|
||||||
|
|
@ -272,6 +272,63 @@ namespace Microsoft.AspNetCore.TestHost
|
||||||
Assert.IsType<InvalidOperationException>(ex.GetBaseException());
|
Assert.IsType<InvalidOperationException>(ex.GetBaseException());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public Task ExceptionFromOnStartingFirstWriteIsReported()
|
||||||
|
{
|
||||||
|
var handler = new ClientHandler(PathString.Empty, new DummyApplication(context =>
|
||||||
|
{
|
||||||
|
context.Response.OnStarting(() =>
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(new string('a', 1024 * 32));
|
||||||
|
});
|
||||||
|
return context.Response.WriteAsync("Hello World");
|
||||||
|
}));
|
||||||
|
var httpClient = new HttpClient(handler);
|
||||||
|
return Assert.ThrowsAsync<InvalidOperationException>(() => httpClient.GetAsync("https://example.com/",
|
||||||
|
HttpCompletionOption.ResponseHeadersRead));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public Task ExceptionFromOnStartingWithNoWriteIsReported()
|
||||||
|
{
|
||||||
|
var handler = new ClientHandler(PathString.Empty, new DummyApplication(context =>
|
||||||
|
{
|
||||||
|
context.Response.OnStarting(() =>
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(new string('a', 1024 * 32));
|
||||||
|
});
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}));
|
||||||
|
var httpClient = new HttpClient(handler);
|
||||||
|
return Assert.ThrowsAsync<InvalidOperationException>(() => httpClient.GetAsync("https://example.com/",
|
||||||
|
HttpCompletionOption.ResponseHeadersRead));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public Task ExceptionFromOnStartingWithErrorHandlerIsReported()
|
||||||
|
{
|
||||||
|
var handler = new ClientHandler(PathString.Empty, new DummyApplication(async context =>
|
||||||
|
{
|
||||||
|
context.Response.OnStarting(() =>
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException(new string('a', 1024 * 32));
|
||||||
|
});
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await context.Response.WriteAsync("Hello World");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// This is no longer the first write, so it doesn't trigger OnStarting again.
|
||||||
|
// The exception is large enough that it fills the pipe and stalls.
|
||||||
|
await context.Response.WriteAsync(ex.ToString());
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
var httpClient = new HttpClient(handler);
|
||||||
|
return Assert.ThrowsAsync<InvalidOperationException>(() => httpClient.GetAsync("https://example.com/",
|
||||||
|
HttpCompletionOption.ResponseHeadersRead));
|
||||||
|
}
|
||||||
|
|
||||||
private class DummyApplication : IHttpApplication<Context>
|
private class DummyApplication : IHttpApplication<Context>
|
||||||
{
|
{
|
||||||
RequestDelegate _application;
|
RequestDelegate _application;
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="Microsoft.AspNetCore.TestHost" />
|
<Reference Include="Microsoft.AspNetCore.TestHost" />
|
||||||
<Reference Include="Microsoft.Extensions.DiagnosticAdapter" />
|
<Reference Include="Microsoft.Extensions.DiagnosticAdapter" />
|
||||||
|
<Reference Include="Microsoft.Extensions.Hosting" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,65 @@ using Microsoft.AspNetCore.Http.Features;
|
||||||
using Microsoft.AspNetCore.Testing.xunit;
|
using Microsoft.AspNetCore.Testing.xunit;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.DiagnosticAdapter;
|
using Microsoft.Extensions.DiagnosticAdapter;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Net.Http.Headers;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.TestHost
|
namespace Microsoft.AspNetCore.TestHost
|
||||||
{
|
{
|
||||||
public class TestServerTests
|
public class TestServerTests
|
||||||
{
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task GenericRawCreate()
|
||||||
|
{
|
||||||
|
var server = new TestServer();
|
||||||
|
var host = new HostBuilder()
|
||||||
|
.ConfigureWebHost(webBuilder =>
|
||||||
|
{
|
||||||
|
webBuilder
|
||||||
|
.UseServer(server)
|
||||||
|
.Configure(app => { });
|
||||||
|
})
|
||||||
|
.Build();
|
||||||
|
await host.StartAsync();
|
||||||
|
|
||||||
|
var response = await server.CreateClient().GetAsync("/");
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GenericCreateAndStartHost_GetTestServer()
|
||||||
|
{
|
||||||
|
var host = await new HostBuilder()
|
||||||
|
.ConfigureWebHost(webBuilder =>
|
||||||
|
{
|
||||||
|
webBuilder
|
||||||
|
.UseTestServer()
|
||||||
|
.Configure(app => { });
|
||||||
|
})
|
||||||
|
.StartAsync();
|
||||||
|
|
||||||
|
var response = await host.GetTestServer().CreateClient().GetAsync("/");
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GenericCreateAndStartHost_GetTestClient()
|
||||||
|
{
|
||||||
|
var host = await new HostBuilder()
|
||||||
|
.ConfigureWebHost(webBuilder =>
|
||||||
|
{
|
||||||
|
webBuilder
|
||||||
|
.UseTestServer()
|
||||||
|
.Configure(app => { });
|
||||||
|
})
|
||||||
|
.StartAsync();
|
||||||
|
|
||||||
|
var response = await host.GetTestClient().GetAsync("/");
|
||||||
|
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void CreateWithDelegate()
|
public void CreateWithDelegate()
|
||||||
{
|
{
|
||||||
|
|
@ -30,6 +82,17 @@ namespace Microsoft.AspNetCore.TestHost
|
||||||
new TestServer(new WebHostBuilder().Configure(app => { }));
|
new TestServer(new WebHostBuilder().Configure(app => { }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void CreateWithDelegate_DI()
|
||||||
|
{
|
||||||
|
var builder = new WebHostBuilder()
|
||||||
|
.Configure(app => { })
|
||||||
|
.UseTestServer();
|
||||||
|
|
||||||
|
var host = builder.Build();
|
||||||
|
host.Start();
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void DoesNotCaptureStartupErrorsByDefault()
|
public void DoesNotCaptureStartupErrorsByDefault()
|
||||||
{
|
{
|
||||||
|
|
@ -576,6 +639,29 @@ namespace Microsoft.AspNetCore.TestHost
|
||||||
Assert.NotNull(listener.UnhandledException?.Exception);
|
Assert.NotNull(listener.UnhandledException?.Exception);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("http://localhost:12345")]
|
||||||
|
[InlineData("http://localhost:12345/")]
|
||||||
|
[InlineData("http://localhost:12345/hellohellohello")]
|
||||||
|
[InlineData("/isthereanybodyinthere?")]
|
||||||
|
public async Task ManuallySetHostWinsOverInferredHostFromRequestUri(string uri)
|
||||||
|
{
|
||||||
|
RequestDelegate appDelegate = ctx =>
|
||||||
|
ctx.Response.WriteAsync(ctx.Request.Headers[HeaderNames.Host]);
|
||||||
|
|
||||||
|
var builder = new WebHostBuilder().Configure(app => app.Run(appDelegate));
|
||||||
|
var server = new TestServer(builder);
|
||||||
|
var client = server.CreateClient();
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Get, uri);
|
||||||
|
request.Headers.Host = "otherhost:5678";
|
||||||
|
|
||||||
|
var response = await client.SendAsync(request);
|
||||||
|
var responseBody = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
Assert.Equal("otherhost:5678", responseBody);
|
||||||
|
}
|
||||||
|
|
||||||
public class TestDiagnosticListener
|
public class TestDiagnosticListener
|
||||||
{
|
{
|
||||||
public class OnBeginRequestEventData
|
public class OnBeginRequestEventData
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
using System;
|
using System.Threading.Tasks;
|
||||||
using System.Net;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
|
||||||
namespace GenericWebHost
|
namespace GenericWebHost
|
||||||
{
|
{
|
||||||
|
|
@ -19,22 +18,20 @@ namespace GenericWebHost
|
||||||
config.AddJsonFile("appsettings.json", optional: true);
|
config.AddJsonFile("appsettings.json", optional: true);
|
||||||
config.AddCommandLine(args);
|
config.AddCommandLine(args);
|
||||||
})
|
})
|
||||||
.ConfigureServices((hostContext, services) =>
|
|
||||||
{
|
|
||||||
})
|
|
||||||
.UseFakeServer()
|
.UseFakeServer()
|
||||||
.ConfigureWebHost((hostContext, app) =>
|
.ConfigureWebHost(builder =>
|
||||||
{
|
{
|
||||||
app.Run(async (context) =>
|
builder.Configure(app =>
|
||||||
{
|
{
|
||||||
await context.Response.WriteAsync("Hello World!");
|
app.Run(async (context) =>
|
||||||
|
{
|
||||||
|
await context.Response.WriteAsync("Hello World!");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.UseConsoleLifetime()
|
.UseConsoleLifetime()
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
var s = host.Services;
|
|
||||||
|
|
||||||
await host.RunAsync();
|
await host.RunAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue