// 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.Diagnostics; using System.IO; using System.Threading; using Microsoft.Extensions.Logging; using Microsoft.AspNet.Server.Testing.Common; namespace Microsoft.AspNet.Server.Testing { /// /// Deployer for WebListener and Kestrel. /// public class SelfHostDeployer : ApplicationDeployer { private Process _hostProcess; public SelfHostDeployer(DeploymentParameters deploymentParameters, ILogger logger) : base(deploymentParameters, logger) { } public override DeploymentResult Deploy() { // Start timer StartTimer(); PickRuntime(); if (DeploymentParameters.PublishApplicationBeforeDeployment) { DnuPublish(); } var uri = TestUriHelper.BuildTestUri(DeploymentParameters.ApplicationBaseUriHint); // Launch the host process. var hostExitToken = StartSelfHost(uri); return new DeploymentResult { WebRootLocation = DeploymentParameters.ApplicationPath, DeploymentParameters = DeploymentParameters, ApplicationBaseUri = uri.ToString(), HostShutdownToken = hostExitToken }; } private CancellationToken StartSelfHost(Uri uri) { var commandName = DeploymentParameters.Command; if (string.IsNullOrEmpty(commandName)) { commandName = "run"; } var dnxPath = Path.Combine(TargetRuntimeBinPath, DnxCommandName); var dnxArgs = $"-p \"{DeploymentParameters.ApplicationPath}\" {commandName} " + $"--server.urls {uri} " + $"--server {(DeploymentParameters.ServerType == ServerType.WebListener ? "Microsoft.AspNet.Server.WebListener" : "Microsoft.AspNet.Server.Kestrel")}"; Logger.LogInformation($"Executing {dnxPath} {dnxArgs}"); var startInfo = new ProcessStartInfo { FileName = dnxPath, Arguments = dnxArgs, UseShellExecute = false, CreateNoWindow = true, RedirectStandardError = true, RedirectStandardOutput = true, // Trying a work around for https://github.com/aspnet/Hosting/issues/140. RedirectStandardInput = true }; AddEnvironmentVariablesToProcess(startInfo); _hostProcess = new Process() { StartInfo = startInfo }; _hostProcess.ErrorDataReceived += (sender, dataArgs) => { Logger.LogError(dataArgs.Data ?? string.Empty); }; _hostProcess.OutputDataReceived += (sender, dataArgs) => { Logger.LogInformation(dataArgs.Data ?? string.Empty); }; _hostProcess.EnableRaisingEvents = true; var hostExitTokenSource = new CancellationTokenSource(); _hostProcess.Exited += (sender, e) => { TriggerHostShutdown(hostExitTokenSource); }; _hostProcess.Start(); _hostProcess.BeginErrorReadLine(); _hostProcess.BeginOutputReadLine(); if (_hostProcess.HasExited) { Logger.LogError("Host process {processName} exited with code {exitCode} or failed to start.", startInfo.FileName, _hostProcess.ExitCode); throw new Exception("Failed to start host"); } Logger.LogInformation("Started {fileName}. Process Id : {processId}", startInfo.FileName, _hostProcess.Id); return hostExitTokenSource.Token; } public override void Dispose() { ShutDownIfAnyHostProcess(_hostProcess); if (DeploymentParameters.PublishApplicationBeforeDeployment) { CleanPublishedOutput(); } InvokeUserApplicationCleanup(); StopTimer(); } } }