diff --git a/Hosting.sln b/Hosting.sln new file mode 100644 index 0000000000..3394417c72 --- /dev/null +++ b/Hosting.sln @@ -0,0 +1,43 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.21005.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E0497F39-AFFB-4819-A116-E39E361915AB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{FEB39027-9158-4DE2-997F-7ADAEF8188D0}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.Hosting.net45", "src\Microsoft.AspNet.Hosting\Microsoft.AspNet.Hosting.net45.csproj", "{D546290B-E280-4D99-BA9C-0D364A0AFB54}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.Hosting.k10", "src\Microsoft.AspNet.Hosting\Microsoft.AspNet.Hosting.k10.csproj", "{DBB72F0F-755D-41CF-8FE2-F4B6AE214E91}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.Hosting.Tests.net45", "test\Microsoft.AspNet.Hosting.Tests\Microsoft.AspNet.Hosting.Tests.net45.csproj", "{80588AF3-6B14-4D11-9DC4-1EF6453B54C9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D546290B-E280-4D99-BA9C-0D364A0AFB54}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D546290B-E280-4D99-BA9C-0D364A0AFB54}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D546290B-E280-4D99-BA9C-0D364A0AFB54}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D546290B-E280-4D99-BA9C-0D364A0AFB54}.Release|Any CPU.Build.0 = Release|Any CPU + {DBB72F0F-755D-41CF-8FE2-F4B6AE214E91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DBB72F0F-755D-41CF-8FE2-F4B6AE214E91}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DBB72F0F-755D-41CF-8FE2-F4B6AE214E91}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DBB72F0F-755D-41CF-8FE2-F4B6AE214E91}.Release|Any CPU.Build.0 = Release|Any CPU + {80588AF3-6B14-4D11-9DC4-1EF6453B54C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {80588AF3-6B14-4D11-9DC4-1EF6453B54C9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {80588AF3-6B14-4D11-9DC4-1EF6453B54C9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {80588AF3-6B14-4D11-9DC4-1EF6453B54C9}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {D546290B-E280-4D99-BA9C-0D364A0AFB54} = {E0497F39-AFFB-4819-A116-E39E361915AB} + {DBB72F0F-755D-41CF-8FE2-F4B6AE214E91} = {E0497F39-AFFB-4819-A116-E39E361915AB} + {80588AF3-6B14-4D11-9DC4-1EF6453B54C9} = {FEB39027-9158-4DE2-997F-7ADAEF8188D0} + EndGlobalSection +EndGlobal diff --git a/src/Microsoft.AspNet.Hosting/Builder/BuilderFactory.cs b/src/Microsoft.AspNet.Hosting/Builder/BuilderFactory.cs new file mode 100644 index 0000000000..bee42edcd3 --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/Builder/BuilderFactory.cs @@ -0,0 +1,20 @@ +using System; +using Microsoft.AspNet.Abstractions; + +namespace Microsoft.AspNet.Hosting.Builder +{ + public class BuilderFactory : IBuilderFactory + { + private readonly IServiceProvider _serviceProvider; + + public BuilderFactory(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public IBuilder CreateBuilder() + { + return new PipelineCore.Builder(_serviceProvider); + } + } +} diff --git a/src/Microsoft.AspNet.Hosting/Builder/HttpContextFactory.cs b/src/Microsoft.AspNet.Hosting/Builder/HttpContextFactory.cs new file mode 100644 index 0000000000..18dc5a4341 --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/Builder/HttpContextFactory.cs @@ -0,0 +1,18 @@ +using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.FeatureModel; +using Microsoft.AspNet.Hosting.Server; +using Microsoft.AspNet.PipelineCore; + +namespace Microsoft.AspNet.Hosting.Builder +{ + public class HttpContextFactory : IHttpContextFactory + { + public HttpContext CreateHttpContext(object serverContext) + { + var featureObject = serverContext as IFeatureCollection ?? new FeatureObject(serverContext); + var featureCollection = new FeatureCollection(featureObject); + var httpContext = new DefaultHttpContext(featureCollection); + return httpContext; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Hosting/Builder/IBuilderFactory.cs b/src/Microsoft.AspNet.Hosting/Builder/IBuilderFactory.cs new file mode 100644 index 0000000000..f69589727a --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/Builder/IBuilderFactory.cs @@ -0,0 +1,9 @@ +using Microsoft.AspNet.Abstractions; + +namespace Microsoft.AspNet.Hosting.Builder +{ + public interface IBuilderFactory + { + IBuilder CreateBuilder(); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Hosting/Builder/IHttpContextFactory.cs b/src/Microsoft.AspNet.Hosting/Builder/IHttpContextFactory.cs new file mode 100644 index 0000000000..3db4473167 --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/Builder/IHttpContextFactory.cs @@ -0,0 +1,9 @@ +using Microsoft.AspNet.Abstractions; + +namespace Microsoft.AspNet.Hosting.Builder +{ + public interface IHttpContextFactory + { + HttpContext CreateHttpContext(object serverContext); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Hosting/HostingContext.cs b/src/Microsoft.AspNet.Hosting/HostingContext.cs new file mode 100644 index 0000000000..303e9e7770 --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/HostingContext.cs @@ -0,0 +1,20 @@ +using System; +using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.Hosting.Server; + +namespace Microsoft.AspNet.Hosting +{ + public class HostingContext + { + public IServiceProvider Services { get; set; } + + public IBuilder Builder { get; set; } + + public string ApplicationName { get; set; } + public Action ApplicationStartup { get; set; } + public RequestDelegate ApplicationDelegate { get; set; } + + public string ServerName { get; set; } + public IServerFactory ServerFactory { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Hosting/HostingEngine.cs b/src/Microsoft.AspNet.Hosting/HostingEngine.cs new file mode 100644 index 0000000000..6a33dbde7d --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/HostingEngine.cs @@ -0,0 +1,104 @@ +using System; +using System.Threading; +using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.Hosting.Builder; +using Microsoft.AspNet.Hosting.Startup; +using Microsoft.AspNet.Hosting.Server; + +namespace Microsoft.AspNet.Hosting +{ + public class HostingEngine : IHostingEngine + { + private readonly IServerManager _serverManager; + private readonly IStartupManager _startupManager; + private readonly IBuilderFactory _builderFactory; + private readonly IHttpContextFactory _httpContextFactory; + + public HostingEngine( + IServerManager serverManager, + IStartupManager startupManager, + IBuilderFactory builderFactory, + IHttpContextFactory httpContextFactory) + { + _serverManager = serverManager; + _startupManager = startupManager; + _builderFactory = builderFactory; + _httpContextFactory = httpContextFactory; + } + + public IDisposable Start(HostingContext context) + { + EnsureBuilder(context); + EnsureServerFactory(context); + EnsureApplicationDelegate(context); + + var pipeline = new PipelineInstance(_httpContextFactory, context.ApplicationDelegate); + var server = context.ServerFactory.Start(pipeline.Invoke); + + return new Disposable(() => + { + server.Dispose(); + pipeline.Dispose(); + }); + } + + private void EnsureBuilder(HostingContext context) + { + if (context.Builder != null) + { + return; + } + + context.Builder = _builderFactory.CreateBuilder(); + } + + private void EnsureServerFactory(HostingContext context) + { + if (context.ServerFactory != null) + { + return; + } + + context.ServerFactory = _serverManager.GetServer(context.ServerName); + } + + private void EnsureApplicationDelegate(HostingContext context) + { + if (context.ApplicationDelegate != null) + { + return; + } + + EnsureApplicationStartup(context); + EnsureBuilder(context); + + context.ApplicationStartup.Invoke(context.Builder); + context.ApplicationDelegate = context.Builder.Build(); + } + + private void EnsureApplicationStartup(HostingContext context) + { + if (context.ApplicationStartup != null) + { + return; + } + + context.ApplicationStartup = _startupManager.LoadStartup(context.ApplicationName); + } + + private class Disposable : IDisposable + { + private Action _dispose; + + public Disposable(Action dispose) + { + _dispose = dispose; + } + + public void Dispose() + { + Interlocked.Exchange(ref _dispose, () => { }).Invoke(); + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Hosting/HostingServices.cs b/src/Microsoft.AspNet.Hosting/HostingServices.cs new file mode 100644 index 0000000000..5af7a8c077 --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/HostingServices.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using Microsoft.AspNet.Configuration; +using Microsoft.AspNet.DependencyInjection; +using Microsoft.AspNet.Hosting.Builder; +using Microsoft.AspNet.Hosting.Startup; +using Microsoft.AspNet.Hosting.Server; + +namespace Microsoft.AspNet.Hosting +{ + public static class HostingServices + { + public static IEnumerable DefaultServices() + { + return DefaultServices(new EmptyConfiguration()); + } + + public static IEnumerable DefaultServices(IConfiguration configuration) + { + yield return DescribeService(configuration); + yield return DescribeService(configuration); + + yield return DescribeService(configuration); + yield return DescribeService(configuration); + + yield return DescribeService(configuration); + yield return DescribeService(configuration); + } + + public static IServiceDescriptor DescribeService(IConfiguration configuration, + LifecycleKind lifecycle = LifecycleKind.Transient) + { + return DescribeService(typeof(TService), typeof(TImplementation), configuration, lifecycle); + } + + public static IServiceDescriptor DescribeService( + Type serviceType, + Type implementationType, + IConfiguration configuration, + LifecycleKind lifecycle) + { + var serviceTypeName = serviceType.FullName; + var implemenationTypeName = configuration.Get(serviceTypeName); + if (!String.IsNullOrEmpty(implemenationTypeName)) + { + try + { + implementationType = Type.GetType(implemenationTypeName); + } + catch (Exception ex) + { + throw new Exception(string.Format("TODO: unable to locate implementation {0} for service {1}", implemenationTypeName, serviceTypeName), ex); + } + } + return new ServiceTypeDescriptor(serviceType, implementationType, lifecycle); + } + + public class EmptyConfiguration : IConfiguration + { + public string Get(string key) + { + return null; + } + } + + public class ServiceTypeDescriptor : IServiceDescriptor + { + public ServiceTypeDescriptor(Type serviceType, Type implementationType, LifecycleKind lifecycle) + { + ServiceType = serviceType; + ImplementationType = implementationType; + Lifecycle = lifecycle; + } + + public LifecycleKind Lifecycle { get; private set; } + public Type ServiceType { get; private set; } + public Type ImplementationType { get; private set; } + public object ImplementationInstance { get; private set; } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Hosting/IHostingEngine.cs b/src/Microsoft.AspNet.Hosting/IHostingEngine.cs new file mode 100644 index 0000000000..89195e69f4 --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/IHostingEngine.cs @@ -0,0 +1,9 @@ +using System; + +namespace Microsoft.AspNet.Hosting +{ + public interface IHostingEngine + { + IDisposable Start(HostingContext context); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Hosting/PipelineInstance.cs b/src/Microsoft.AspNet.Hosting/PipelineInstance.cs new file mode 100644 index 0000000000..cc7580b713 --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/PipelineInstance.cs @@ -0,0 +1,31 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.Hosting.Builder; +using Microsoft.AspNet.Hosting.Server; + +namespace Microsoft.AspNet.Hosting +{ + public class PipelineInstance : IDisposable + { + private readonly IHttpContextFactory _httpContextFactory; + private readonly RequestDelegate _requestDelegate; + + public PipelineInstance(IHttpContextFactory httpContextFactory, RequestDelegate requestDelegate) + { + _httpContextFactory = httpContextFactory; + _requestDelegate = requestDelegate; + } + + public Task Invoke(object serverEnvironment) + { + var httpContext = _httpContextFactory.CreateHttpContext(serverEnvironment); + return _requestDelegate(httpContext); + } + + public void Dispose() + { + // TODO: application notification of disposal + } + } +} diff --git a/src/Microsoft.AspNet.Hosting/Server/IServerFactory.cs b/src/Microsoft.AspNet.Hosting/Server/IServerFactory.cs new file mode 100644 index 0000000000..2d8350d53e --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/Server/IServerFactory.cs @@ -0,0 +1,11 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNet.Abstractions; + +namespace Microsoft.AspNet.Hosting.Server +{ + public interface IServerFactory + { + IDisposable Start(Func application); + } +} diff --git a/src/Microsoft.AspNet.Hosting/Server/IServerFactoryProvider.cs b/src/Microsoft.AspNet.Hosting/Server/IServerFactoryProvider.cs new file mode 100644 index 0000000000..714a51ba8b --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/Server/IServerFactoryProvider.cs @@ -0,0 +1,7 @@ +namespace Microsoft.AspNet.Hosting.Server +{ + public interface IServerFactoryProvider + { + IServerFactory GetServerFactory(string serverName); + } +} diff --git a/src/Microsoft.AspNet.Hosting/Server/IServerManager.cs b/src/Microsoft.AspNet.Hosting/Server/IServerManager.cs new file mode 100644 index 0000000000..471f9445bd --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/Server/IServerManager.cs @@ -0,0 +1,7 @@ +namespace Microsoft.AspNet.Hosting.Server +{ + public interface IServerManager + { + IServerFactory GetServer(string serverName); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Hosting/Server/ServerManager.cs b/src/Microsoft.AspNet.Hosting/Server/ServerManager.cs new file mode 100644 index 0000000000..b8bf9cf6b5 --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/Server/ServerManager.cs @@ -0,0 +1,10 @@ +namespace Microsoft.AspNet.Hosting.Server +{ + public class ServerManager : IServerManager + { + public IServerFactory GetServer(string serverName) + { + throw new System.NotImplementedException(); + } + } +} diff --git a/src/Microsoft.AspNet.Hosting/ServerFactoryProvider.cs b/src/Microsoft.AspNet.Hosting/ServerFactoryProvider.cs new file mode 100644 index 0000000000..eb5743a86a --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/ServerFactoryProvider.cs @@ -0,0 +1,13 @@ +using System; +using Microsoft.AspNet.Hosting.Server; + +namespace Microsoft.AspNet.Hosting +{ + public class ServerFactoryProvider : IServerFactoryProvider + { + public IServerFactory GetServerFactory(string serverFactoryIdentifier) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Hosting/Startup/IStartupLoader.cs b/src/Microsoft.AspNet.Hosting/Startup/IStartupLoader.cs new file mode 100644 index 0000000000..067e5a4ebe --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/Startup/IStartupLoader.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using Microsoft.AspNet.Abstractions; + +namespace Microsoft.AspNet.Hosting.Startup +{ + public interface IStartupLoader + { + Action LoadStartup(string applicationName, IList diagnosticMessages); + } +} diff --git a/src/Microsoft.AspNet.Hosting/Startup/IStartupLoaderProvider.cs b/src/Microsoft.AspNet.Hosting/Startup/IStartupLoaderProvider.cs new file mode 100644 index 0000000000..ad96af9793 --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/Startup/IStartupLoaderProvider.cs @@ -0,0 +1,9 @@ +namespace Microsoft.AspNet.Hosting.Startup +{ + public interface IStartupLoaderProvider + { + int Order { get; } + + IStartupLoader CreateStartupLoader(IStartupLoader next); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Hosting/Startup/IStartupManager.cs b/src/Microsoft.AspNet.Hosting/Startup/IStartupManager.cs new file mode 100644 index 0000000000..0d92e09a89 --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/Startup/IStartupManager.cs @@ -0,0 +1,10 @@ +using System; +using Microsoft.AspNet.Abstractions; + +namespace Microsoft.AspNet.Hosting.Startup +{ + public interface IStartupManager + { + Action LoadStartup(string applicationName); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Hosting/Startup/NullStartupLoader.cs b/src/Microsoft.AspNet.Hosting/Startup/NullStartupLoader.cs new file mode 100644 index 0000000000..e5e63c01f7 --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/Startup/NullStartupLoader.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using Microsoft.AspNet.Abstractions; + +namespace Microsoft.AspNet.Hosting.Startup +{ + public class NullStartupLoader : IStartupLoader + { + static NullStartupLoader() + { + Instance = new NullStartupLoader(); + } + + public static IStartupLoader Instance { get; private set; } + + public Action LoadStartup(string applicationName, IList diagnosticMessages) + { + return null; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Hosting/Startup/StartupLoader.cs b/src/Microsoft.AspNet.Hosting/Startup/StartupLoader.cs new file mode 100644 index 0000000000..c0949b178f --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/Startup/StartupLoader.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.DependencyInjection; + +namespace Microsoft.AspNet.Hosting.Startup +{ + public class StartupLoader : IStartupLoader + { + private readonly IServiceProvider _services; + private readonly IStartupLoader _next; + + public StartupLoader(IServiceProvider services, IStartupLoader next) + { + _services = services; + _next = next; + } + + public Action LoadStartup(string applicationName, IList diagnosticMessages) + { + if (String.IsNullOrWhiteSpace(applicationName)) + { + return _next.LoadStartup(applicationName, diagnosticMessages); + } + + var parts = applicationName.Split(new[] { ',' }, 2); + if (parts.Length == 2) + { + var typeName = parts[0]; + var assemblyName = parts[1]; + + var assembly = Assembly.Load(new AssemblyName(assemblyName)); + if (assembly == null) + { + throw new Exception(String.Format("TODO: assembly {0} failed to load message", assemblyName)); + } + + var type = assembly.GetType(typeName); + if (type == null) + { + throw new Exception(String.Format("TODO: type {0} failed to load message", typeName)); + } + + var methodInfo = type.GetTypeInfo().GetDeclaredMethod("Configuration"); + if (methodInfo == null) + { + throw new Exception("TODO: Configuration method not found"); + } + + object instance = null; + if (!methodInfo.IsStatic) + { + instance = ActivatorUtilities.GetServiceOrCreateInstance(_services, type); + } + return builder => methodInfo.Invoke(instance, new object[] { builder }); + } + throw new Exception("TODO: Unrecognized format"); + } + } +} diff --git a/src/Microsoft.AspNet.Hosting/Startup/StartupLoaderProvider.cs b/src/Microsoft.AspNet.Hosting/Startup/StartupLoaderProvider.cs new file mode 100644 index 0000000000..a30cc50488 --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/Startup/StartupLoaderProvider.cs @@ -0,0 +1,21 @@ +using System; + +namespace Microsoft.AspNet.Hosting.Startup +{ + public class StartupLoaderProvider : IStartupLoaderProvider + { + private readonly IServiceProvider _services; + + public StartupLoaderProvider(IServiceProvider services) + { + _services = services; + } + + public int Order { get { return -100; } } + + public IStartupLoader CreateStartupLoader(IStartupLoader next) + { + return new StartupLoader(_services, next); + } + } +} diff --git a/src/Microsoft.AspNet.Hosting/Startup/StartupManager.cs b/src/Microsoft.AspNet.Hosting/Startup/StartupManager.cs new file mode 100644 index 0000000000..41d68b03da --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/Startup/StartupManager.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.AspNet.Abstractions; + +namespace Microsoft.AspNet.Hosting.Startup +{ + public class StartupManager : IStartupManager + { + private readonly IEnumerable _providers; + + public StartupManager(IEnumerable providers) + { + _providers = providers; + } + + public Action LoadStartup(string applicationName) + { + // build ordered chain of application loaders + var chain = _providers + .OrderBy(provider => provider.Order) + .Aggregate(NullStartupLoader.Instance, (next, provider) => provider.CreateStartupLoader(next)); + + // invoke chain to acquire application entrypoint and diagnostic messages + var diagnosticMessages = new List(); + var application = chain.LoadStartup(applicationName, diagnosticMessages); + + if (application == null) + { + throw new Exception(diagnosticMessages.Aggregate("TODO: web application entrypoint not found message", (a, b) => a + "\r\n" + b)); + } + + return application; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Hosting/WebApplication.cs b/src/Microsoft.AspNet.Hosting/WebApplication.cs new file mode 100644 index 0000000000..773ae80d26 --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/WebApplication.cs @@ -0,0 +1,23 @@ +using System; +using Microsoft.AspNet.DependencyInjection; + +namespace Microsoft.AspNet.Hosting +{ + public static class WebApplication + { + public static IDisposable Start() + { + var context = new HostingContext + { + Services = new ServiceProvider().Add(HostingServices.DefaultServices()) + }; + + var engine = context.Services.GetService(); + if (engine == null) + { + throw new Exception("TODO: IHostingEngine service not available exception"); + } + return engine.Start(context); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Hosting/project.json b/src/Microsoft.AspNet.Hosting/project.json new file mode 100644 index 0000000000..57e379cfb1 --- /dev/null +++ b/src/Microsoft.AspNet.Hosting/project.json @@ -0,0 +1,14 @@ +{ + "version": "0.1-alpha-*", + "dependencies":{ + "Microsoft.AspNet.DependencyInjection":"0.1-alpha-*", + "Microsoft.AspNet.Configuration":"0.1-alpha-*", + "Microsoft.AspNet.PipelineCore":"0.1-alpha-*", + "Microsoft.AspNet.Abstractions":"0.1-alpha-*", + "Microsoft.AspNet.FeatureModel":"0.1-alpha-*" + }, + "configurations": { + "net45" : {}, + "k10" : {} + } +} diff --git a/test/Microsoft.AspNet.Hosting.Tests/Fakes/FakeStartup.cs b/test/Microsoft.AspNet.Hosting.Tests/Fakes/FakeStartup.cs new file mode 100644 index 0000000000..f8aae20612 --- /dev/null +++ b/test/Microsoft.AspNet.Hosting.Tests/Fakes/FakeStartup.cs @@ -0,0 +1,11 @@ +using Microsoft.AspNet.Abstractions; + +namespace Microsoft.AspNet.Hosting.Tests.Fakes +{ + public class FakeStartup + { + public void Configuration(IBuilder builder) + { + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Hosting.Tests/Fakes/FakeStartupWithServices.cs b/test/Microsoft.AspNet.Hosting.Tests/Fakes/FakeStartupWithServices.cs new file mode 100644 index 0000000000..1557c568ab --- /dev/null +++ b/test/Microsoft.AspNet.Hosting.Tests/Fakes/FakeStartupWithServices.cs @@ -0,0 +1,19 @@ +using Microsoft.AspNet.Abstractions; + +namespace Microsoft.AspNet.Hosting.Tests.Fakes +{ + public class FakeStartupWithServices + { + private readonly IFakeStartupCallback _fakeStartupCallback; + + public FakeStartupWithServices(IFakeStartupCallback fakeStartupCallback) + { + _fakeStartupCallback = fakeStartupCallback; + } + + public void Configuration(IBuilder builder) + { + _fakeStartupCallback.ConfigurationMethodCalled(this); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Hosting.Tests/Fakes/IFakeStartupCallback.cs b/test/Microsoft.AspNet.Hosting.Tests/Fakes/IFakeStartupCallback.cs new file mode 100644 index 0000000000..cfe02c2b46 --- /dev/null +++ b/test/Microsoft.AspNet.Hosting.Tests/Fakes/IFakeStartupCallback.cs @@ -0,0 +1,7 @@ +namespace Microsoft.AspNet.Hosting.Tests.Fakes +{ + public interface IFakeStartupCallback + { + void ConfigurationMethodCalled(object instance); + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.Hosting.Tests/HostingEngineTests.cs b/test/Microsoft.AspNet.Hosting.Tests/HostingEngineTests.cs new file mode 100644 index 0000000000..ab29f28bb5 --- /dev/null +++ b/test/Microsoft.AspNet.Hosting.Tests/HostingEngineTests.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.DependencyInjection; +using Microsoft.AspNet.Hosting.Server; +using Xunit; + +namespace Microsoft.AspNet.Hosting.Tests +{ + public class HostingEngineTests : IServerManager, IServerFactory + { + private readonly IList _startInstances = new List(); + + [Fact] + public void HostingEngineCanBeResolvedWithDefaultServices() + { + var services = new ServiceProvider() + .Add(HostingServices.DefaultServices()); + + var engine = services.GetService(); + + Assert.NotNull(engine); + } + + [Fact] + public void HostingEngineCanBeStarted() + { + var services = new ServiceProvider() + .Add(HostingServices.DefaultServices() + .Where(descriptor => descriptor.ServiceType != typeof(IServerManager))) + .AddInstance(this); + + var engine = services.GetService(); + + var context = new HostingContext + { + ApplicationName = "Microsoft.AspNet.Hosting.Tests.Fakes.FakeStartup, Microsoft.AspNet.Hosting.Tests" + }; + + var engineStart = engine.Start(context); + + Assert.NotNull(engineStart); + Assert.Equal(1, _startInstances.Count); + Assert.Equal(0, _startInstances[0].DisposeCalls); + + engineStart.Dispose(); + + Assert.Equal(1, _startInstances[0].DisposeCalls); + } + + public IServerFactory GetServer(string serverName) + { + return this; + } + + public void Initialize(IBuilder builder) + { + + } + + public IDisposable Start(Func application) + { + var startInstance = new StartInstance(application); + _startInstances.Add(startInstance); + return startInstance; + } + + public class StartInstance : IDisposable + { + private readonly Func _application; + + public StartInstance(Func application) + { + _application = application; + } + + public int DisposeCalls { get; set; } + + public void Dispose() + { + DisposeCalls += 1; + } + } + } +} diff --git a/test/Microsoft.AspNet.Hosting.Tests/StartupManagerTests.cs b/test/Microsoft.AspNet.Hosting.Tests/StartupManagerTests.cs new file mode 100644 index 0000000000..4a848b13fb --- /dev/null +++ b/test/Microsoft.AspNet.Hosting.Tests/StartupManagerTests.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.DependencyInjection; +using Microsoft.AspNet.Hosting.Startup; +using Microsoft.AspNet.Hosting.Tests.Fakes; +using Xunit; + +namespace Microsoft.AspNet.Hosting.Tests +{ + + public class StartupManagerTests : IFakeStartupCallback + { + private readonly IList _configurationMethodCalledList = new List(); + + [Fact] + public void DefaultServicesLocateStartupByNameAndNamespace() + { + IServiceProvider services = new ServiceProvider().Add(HostingServices.DefaultServices()); + + var manager = services.GetService(); + + var startup = manager.LoadStartup("Microsoft.AspNet.Hosting.Tests.Fakes.FakeStartup, Microsoft.AspNet.Hosting.Tests"); + + Assert.IsType(manager); + Assert.NotNull(startup); + } + + [Fact] + public void StartupClassMayHaveHostingServicesInjected() + { + IServiceProvider services = new ServiceProvider() + .Add(HostingServices.DefaultServices()) + .AddInstance(this); + + var manager = services.GetService(); + + var startup = manager.LoadStartup("Microsoft.AspNet.Hosting.Tests.Fakes.FakeStartupWithServices, Microsoft.AspNet.Hosting.Tests"); + + startup.Invoke(null); + + Assert.Equal(1, _configurationMethodCalledList.Count); + } + + public void ConfigurationMethodCalled(object instance) + { + _configurationMethodCalledList.Add(instance); + } + } +} diff --git a/test/Microsoft.AspNet.Hosting.Tests/project.json b/test/Microsoft.AspNet.Hosting.Tests/project.json new file mode 100644 index 0000000000..486e0ac6b1 --- /dev/null +++ b/test/Microsoft.AspNet.Hosting.Tests/project.json @@ -0,0 +1,12 @@ +{ + "version": "0.1-alpha-*", + "dependencies": { + "xunit": "1.9.2", + "Microsoft.AspNet.Hosting":"", + "Microsoft.AspNet.Abstractions":"0.1-alpha-*", + "Microsoft.AspNet.DependencyInjection":"0.1-alpha-*" + }, + "configurations": { + "net45": {} + } +} \ No newline at end of file