예제 #1
0
        private void Start()
        {
            LoadLibrary(HostableWebCoreLocation);
            _appHostConfigPath = Path.GetTempFileName();

            set_main_handler(_hostfxrMainFn);

            Retry(() =>
            {
                _currentPort = TestPortHelper.GetNextPort();

                InitializeConfig(_currentPort);

                var startResult = WebCoreActivate(_appHostConfigPath, null, "Instance");
                if (startResult != 0)
                {
                    throw new InvalidOperationException($"Error while running WebCoreActivate: {startResult} on port {_currentPort}");
                }
            }, PortRetryCount);

            HttpClient = new HttpClient(new LoggingHandler(new SocketsHttpHandler(), _loggerFactory.CreateLogger <TestServer>()))
            {
                BaseAddress = BaseUri
            };
        }
예제 #2
0
        public async Task HttpsHelloWorld(TestVariant variant)
        {
            var port = TestPortHelper.GetNextSSLPort();
            var deploymentParameters = _fixture.GetBaseDeploymentParameters(variant);

            deploymentParameters.ApplicationBaseUriHint = $"https://localhost:{port}/";
            deploymentParameters.AddHttpsToServerConfig();

            var deploymentResult = await DeployAsync(deploymentParameters);

            var handler = new HttpClientHandler
            {
                ServerCertificateCustomValidationCallback = (a, b, c, d) => true
            };
            var client   = deploymentResult.CreateClient(handler);
            var response = await client.GetAsync("HttpsHelloWorld");

            var responseText = await response.Content.ReadAsStringAsync();

            if (variant.HostingModel == HostingModel.OutOfProcess)
            {
                Assert.Equal("Scheme:https; Original:http", responseText);
            }
            else
            {
                Assert.Equal("Scheme:https; Original:", responseText);
            }
        }
예제 #3
0
        public Http2TrailerResetTests(IISTestSiteFixture fixture)
        {
            var port = TestPortHelper.GetNextSSLPort();

            fixture.DeploymentParameters.ApplicationBaseUriHint = $"https://localhost:{port}/";
            fixture.DeploymentParameters.AddHttpsToServerConfig();
            fixture.DeploymentParameters.SetWindowsAuth(false);
            Fixture = fixture;
        }
예제 #4
0
        private IISDeploymentParameters GetHttpsDeploymentParameters()
        {
            var port = TestPortHelper.GetNextSSLPort();
            var deploymentParameters = Fixture.GetBaseDeploymentParameters();

            deploymentParameters.ApplicationBaseUriHint = $"https://localhost:{port}/";
            deploymentParameters.AddHttpsToServerConfig();
            return(deploymentParameters);
        }
예제 #5
0
        private async Task ClientCertTest(TestVariant variant, bool sendClientCert)
        {
            var port = TestPortHelper.GetNextSSLPort();
            var deploymentParameters = Fixture.GetBaseDeploymentParameters(variant);

            deploymentParameters.ApplicationBaseUriHint = $"https://localhost:{port}/";
            deploymentParameters.AddHttpsToServerConfig();

            var handler = new HttpClientHandler
            {
                ServerCertificateCustomValidationCallback = (a, b, c, d) => true,
                ClientCertificateOptions = ClientCertificateOption.Manual,
            };

            X509Certificate2 cert = null;

            if (sendClientCert)
            {
                cert = _certFixture.GetOrCreateCertificate();
                handler.ClientCertificates.Add(cert);
            }

            var deploymentResult = await DeployAsync(deploymentParameters);

            var client   = deploymentResult.CreateClient(handler);
            var response = await client.GetAsync("GetClientCert");

            var responseText = await response.Content.ReadAsStringAsync();

            try
            {
                if (sendClientCert)
                {
                    Assert.Equal($"Enabled;{cert.GetCertHashString()}", responseText);
                }
                else
                {
                    Assert.Equal("Disabled", responseText);
                }
            }
            catch (Exception ex)
            {
                Logger.LogError($"Certificate is invalid. Issuer name: {cert.Issuer}");
                using (var store = new X509Store(StoreName.Root, StoreLocation.LocalMachine))
                {
                    Logger.LogError($"List of current certificates in root store:");
                    store.Open(OpenFlags.ReadWrite);
                    foreach (var otherCert in store.Certificates)
                    {
                        Logger.LogError(otherCert.Issuer);
                    }
                    store.Close();
                }
                throw ex;
            }
        }
예제 #6
0
 internal static Uri BuildTestUri(ServerType serverType, string scheme, string hint, bool statusMessagesEnabled)
 {
     if (string.IsNullOrEmpty(hint))
     {
         if (serverType == ServerType.Kestrel && statusMessagesEnabled)
         {
             // Most functional tests use this codepath and should directly bind to dynamic port "0" and scrape
             // the assigned port from the status message, which should be 100% reliable since the port is bound
             // once and never released.  Binding to dynamic port "0" on "localhost" (both IPv4 and IPv6) is not
             // supported, so the port is only bound on "127.0.0.1" (IPv4).  If a test explicitly requires IPv6,
             // it should provide a hint URL with "localhost" (IPv4 and IPv6) or "[::1]" (IPv6-only).
             return(new UriBuilder(scheme, "127.0.0.1", 0).Uri);
         }
         else if (serverType == ServerType.HttpSys)
         {
             Debug.Assert(scheme == "http", "Https not supported");
             return(new UriBuilder(scheme, "localhost", 0).Uri);
         }
         else
         {
             // If the server type is not Kestrel, or status messages are disabled, there is no status message
             // from which to scrape the assigned port, so the less reliable GetNextPort() must be used.  The
             // port is bound on "localhost" (both IPv4 and IPv6), since this is supported when using a specific
             // (non-zero) port.
             return(new UriBuilder(scheme, "localhost", TestPortHelper.GetNextPort()).Uri);
         }
     }
     else
     {
         var uriHint = new Uri(hint);
         if (uriHint.Port == 0)
         {
             // Only a few tests use this codepath, so it's fine to use the less reliable GetNextPort() for simplicity.
             // The tests using this codepath will be reviewed to see if they can be changed to directly bind to dynamic
             // port "0" on "127.0.0.1" and scrape the assigned port from the status message (the default codepath).
             return(new UriBuilder(uriHint)
             {
                 Port = TestPortHelper.GetNextPort()
             }.Uri);
         }
         else
         {
             // If the hint contains a specific port, return it unchanged.
             return(uriHint);
         }
     }
 }
        public async Task CheckProtocolIsHttp2()
        {
            var port = TestPortHelper.GetNextSSLPort();
            var deploymentParameters = Fixture.GetBaseDeploymentParameters(HostingModel.InProcess);

            deploymentParameters.ApplicationBaseUriHint = $"https://localhost:{port}/";
            deploymentParameters.AddHttpsToServerConfig();
            deploymentParameters.SetWindowsAuth(false);

            var deploymentResult = await DeployAsync(deploymentParameters);

            var client = CreateNonValidatingClient(deploymentResult);

            client.DefaultRequestVersion = HttpVersion.Version20;

            Assert.Equal("HTTP/2", await client.GetStringAsync($"/CheckProtocol"));
        }
예제 #8
0
        public async Task HttpsRedirectionWorksIn30AndNot22()
        {
            var port = TestPortHelper.GetNextSSLPort();
            var deploymentParameters = Fixture.GetBaseDeploymentParameters(HostingModel.OutOfProcess);

            deploymentParameters.WebConfigBasedEnvironmentVariables["ENABLE_HTTPS_REDIRECTION"] = "true";
            deploymentParameters.ApplicationBaseUriHint = $"http://localhost:{TestPortHelper.GetNextPort()}/";

            deploymentParameters.AddServerConfigAction(
                element => {
                element.Descendants("bindings")
                .Single()
                .AddAndGetInnerElement("binding", "protocol", "https")
                .SetAttributeValue("bindingInformation", $":{port}:localhost");

                element.Descendants("access")
                .Single()
                .SetAttributeValue("sslFlags", "None");
            });

            var deploymentResult = await DeployAsync(deploymentParameters);

            var handler = new HttpClientHandler
            {
                ServerCertificateCustomValidationCallback = (a, b, c, d) => true,
                AllowAutoRedirect = false
            };
            var client = new HttpClient(handler)
            {
                BaseAddress = new Uri(deploymentParameters.ApplicationBaseUriHint)
            };

            if (DeployerSelector.HasNewHandler)
            {
                var response = await client.GetAsync("/ANCM_HTTPS_PORT");

                Assert.Equal(307, (int)response.StatusCode);
            }
            else
            {
                var response = await client.GetAsync("/ANCM_HTTPS_PORT");

                Assert.Equal(200, (int)response.StatusCode);
            }
        }
예제 #9
0
        public async Task HttpsPortCanBeOverriden()
        {
            var deploymentParameters = _fixture.GetBaseDeploymentParameters(HostingModel.OutOfProcess, publish: true);

            deploymentParameters.AddServerConfigAction(
                element => {
                element.Descendants("bindings")
                .Single()
                .GetOrAdd("binding", "protocol", "https")
                .SetAttributeValue("bindingInformation", $":{TestPortHelper.GetNextSSLPort()}:localhost");
            });

            deploymentParameters.WebConfigBasedEnvironmentVariables["ASPNETCORE_HTTPS_PORT"] = "123";

            var deploymentResult = await DeployAsync(deploymentParameters);

            Assert.Equal("123", await deploymentResult.HttpClient.GetStringAsync("/HTTPS_PORT"));
        }
예제 #10
0
        public async Task ServerAddressesIncludesBaseAddress()
        {
            var appName = "\u041C\u043E\u0451\u041F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435";

            var port = TestPortHelper.GetNextSSLPort();
            var deploymentParameters = _fixture.GetBaseDeploymentParameters(HostingModel.InProcess, publish: true);

            deploymentParameters.ApplicationBaseUriHint = $"https://localhost:{port}/";
            deploymentParameters.AddHttpsToServerConfig();
            deploymentParameters.AddServerConfigAction(
                (element, root) => {
                element.Descendants("site").Single().Element("application").SetAttributeValue("path", "/" + appName);
                Helpers.CreateEmptyApplication(element, root);
            });

            var deploymentResult = await DeployAsync(deploymentParameters);

            Assert.Equal(deploymentParameters.ApplicationBaseUriHint + appName, await deploymentResult.HttpClient.GetStringAsync($"/{appName}/ServerAddresses"));
        }
예제 #11
0
        public async Task HttpsHelloWorld(TestVariant variant)
        {
            var port = TestPortHelper.GetNextSSLPort();
            var deploymentParameters = _fixture.GetBaseDeploymentParameters(variant);

            deploymentParameters.ApplicationBaseUriHint = $"https://localhost:{port}/";
            deploymentParameters.AddHttpsToServerConfig();

            var deploymentResult = await DeployAsync(deploymentParameters);

            var handler = new HttpClientHandler
            {
                ServerCertificateCustomValidationCallback = (a, b, c, d) => true
            };
            var client   = deploymentResult.CreateClient(handler);
            var response = await client.GetAsync("HttpsHelloWorld");

            var responseText = await response.Content.ReadAsStringAsync();

            if (variant.HostingModel == HostingModel.OutOfProcess)
            {
                Assert.Equal("Scheme:https; Original:http", responseText);
            }
            else
            {
                Assert.Equal("Scheme:https; Original:", responseText);
            }

            if (variant.AncmVersion == AncmVersion.AspNetCoreModuleV2 &&
                DeployerSelector.HasNewHandler &&
                DeployerSelector.HasNewShim)
            {
                // We expect ServerAddress to be set for InProcess and HTTPS_PORT for OutOfProcess
                if (variant.HostingModel == HostingModel.InProcess)
                {
                    Assert.Equal(deploymentParameters.ApplicationBaseUriHint, await client.GetStringAsync("/ServerAddresses"));
                }
                else
                {
                    Assert.Equal(port.ToString(), await client.GetStringAsync("/HTTPS_PORT"));
                }
            }
        }
예제 #12
0
        private async Task HttpsHelloWorldCerts(TestVariant variant, bool sendClientCert)
        {
            var port = TestPortHelper.GetNextSSLPort();
            var deploymentParameters = new IISDeploymentParameters(variant)
            {
                ApplicationPath        = Helpers.GetOutOfProcessTestSitesPath(),
                ApplicationBaseUriHint = $"https://localhost:{port}/",
            };

            deploymentParameters.AddHttpsToServerConfig();

            var deploymentResult = await DeployAsync(deploymentParameters);

            var handler = new HttpClientHandler
            {
                ServerCertificateCustomValidationCallback = (a, b, c, d) => true,
                ClientCertificateOptions = ClientCertificateOption.Manual
            };

            if (sendClientCert)
            {
                X509Certificate2 clientCert = FindClientCert();
                Assert.NotNull(clientCert);
                handler.ClientCertificates.Add(clientCert);
            }

            var client = deploymentResult.CreateRetryClient(handler);

            // Request to base address and check if various parts of the body are rendered & measure the cold startup time.
            var response = await client.GetAsync("checkclientcert");

            var responseText = await response.Content.ReadAsStringAsync();

            if (sendClientCert)
            {
                Assert.Equal("Scheme:https; Original:http; has cert? True", responseText);
            }
            else
            {
                Assert.Equal("Scheme:https; Original:http; has cert? False", responseText);
            }
        }
예제 #13
0
        public async Task HttpsHelloWorld(TestVariant variant)
        {
            var port = TestPortHelper.GetNextSSLPort();
            var deploymentParameters = Fixture.GetBaseDeploymentParameters(variant);

            deploymentParameters.ApplicationBaseUriHint = $"https://localhost:{port}/";
            deploymentParameters.AddHttpsToServerConfig();

            var deploymentResult = await DeployAsync(deploymentParameters);

            var client   = CreateNonValidatingClient(deploymentResult);
            var response = await client.GetAsync("HttpsHelloWorld");

            var responseText = await response.Content.ReadAsStringAsync();

            if (variant.HostingModel == HostingModel.OutOfProcess)
            {
                Assert.Equal("Scheme:https; Original:http", responseText);
            }
            else
            {
                Assert.Equal("Scheme:https; Original:", responseText);
            }

            if (DeployerSelector.HasNewHandler &&
                DeployerSelector.HasNewShim)
            {
                // We expect ServerAddress to be set for InProcess and ANCM_HTTPS_PORT for OutOfProcess
                if (variant.HostingModel == HostingModel.InProcess)
                {
                    Assert.Equal(deploymentParameters.ApplicationBaseUriHint, await client.GetStringAsync("/ServerAddresses"));
                }
                else
                {
                    Assert.Equal(port.ToString(), await client.GetStringAsync("/ANCM_HTTPS_PORT"));
                }
            }
        }
예제 #14
0
        public async Task HttpsHelloWorld(TestVariant variant)
        {
            var port = TestPortHelper.GetNextSSLPort();
            var deploymentParameters = new DeploymentParameters(variant)
            {
                ApplicationPath             = Helpers.GetOutOfProcessTestSitesPath(),
                ApplicationBaseUriHint      = $"https://localhost:{port}/",
                ServerConfigTemplateContent = GetHttpsServerConfig()
            };

            var deploymentResult = await DeployAsync(deploymentParameters);

            var handler = new HttpClientHandler
            {
                ServerCertificateCustomValidationCallback = (a, b, c, d) => true
            };
            var client   = deploymentResult.CreateRetryClient(handler);
            var response = await client.GetAsync("HttpsHelloWorld");

            var responseText = await response.Content.ReadAsStringAsync();

            Assert.Equal("Scheme:https; Original:http", responseText);
        }
예제 #15
0
        private async Task <(Uri url, CancellationToken hostExitToken)> StartIISExpressAsync(Uri uri, string contentRoot)
        {
            using (Logger.BeginScope("StartIISExpress"))
            {
                var port = uri.Port;
                if (port == 0)
                {
                    port = (uri.Scheme == "https") ? TestPortHelper.GetNextSSLPort() : TestPortHelper.GetNextPort();
                }

                Logger.LogInformation("Attempting to start IIS Express on port: {port}", port);
                PrepareConfig(contentRoot, port);

                var parameters = string.IsNullOrEmpty(DeploymentParameters.ServerConfigLocation) ?
                                 string.Format("/port:{0} /path:\"{1}\" /trace:error /systray:false", uri.Port, contentRoot) :
                                 string.Format("/site:{0} /config:{1} /trace:error /systray:false", DeploymentParameters.SiteName, DeploymentParameters.ServerConfigLocation);

                var iisExpressPath = GetIISExpressPath();

                for (var attempt = 0; attempt < MaximumAttempts; attempt++)
                {
                    Logger.LogInformation("Executing command : {iisExpress} {parameters}", iisExpressPath, parameters);

                    var startInfo = new ProcessStartInfo
                    {
                        FileName               = iisExpressPath,
                        Arguments              = parameters,
                        UseShellExecute        = false,
                        CreateNoWindow         = true,
                        RedirectStandardError  = true,
                        RedirectStandardOutput = true,
                        // VS sets current directory to C:\Program Files\IIS Express
                        WorkingDirectory = Path.GetDirectoryName(iisExpressPath)
                    };

                    AddEnvironmentVariablesToProcess(startInfo, DeploymentParameters.EnvironmentVariables);

                    Uri url     = null;
                    var started = new TaskCompletionSource <bool>();

                    var process = new Process()
                    {
                        StartInfo = startInfo
                    };
                    process.OutputDataReceived += (sender, dataArgs) =>
                    {
                        if (string.Equals(dataArgs.Data, UnableToStartIISExpressMessage))
                        {
                            // We completely failed to start and we don't really know why
                            started.TrySetException(new InvalidOperationException("Failed to start IIS Express"));
                        }
                        else if (string.Equals(dataArgs.Data, FailedToInitializeBindingsMessage))
                        {
                            started.TrySetResult(false);
                        }
                        else if (string.Equals(dataArgs.Data, IISExpressRunningMessage))
                        {
                            started.TrySetResult(true);
                        }
                        else if (!string.IsNullOrEmpty(dataArgs.Data))
                        {
                            var m = UrlDetectorRegex.Match(dataArgs.Data);
                            if (m.Success)
                            {
                                url = new Uri(m.Groups["url"].Value);
                            }
                        }
                    };

                    process.EnableRaisingEvents = true;
                    var hostExitTokenSource = new CancellationTokenSource();
                    process.Exited += (sender, e) =>
                    {
                        Logger.LogInformation("iisexpress Process {pid} shut down", process.Id);

                        // If TrySetResult was called above, this will just silently fail to set the new state, which is what we want
                        started.TrySetException(new Exception($"Command exited unexpectedly with exit code: {process.ExitCode}"));

                        TriggerHostShutdown(hostExitTokenSource);
                    };
                    process.StartAndCaptureOutAndErrToLogger("iisexpress", Logger);
                    Logger.LogInformation("iisexpress Process {pid} started", process.Id);

                    if (process.HasExited)
                    {
                        Logger.LogError("Host process {processName} {pid} exited with code {exitCode} or failed to start.", startInfo.FileName, process.Id, process.ExitCode);
                        throw new Exception("Failed to start host");
                    }

                    // Wait for the app to start
                    // The timeout here is large, because we don't know how long the test could need
                    // We cover a lot of error cases above, but I want to make sure we eventually give up and don't hang the build
                    // just in case we missed one -anurse
                    if (!await started.Task.TimeoutAfter(TimeSpan.FromMinutes(10)))
                    {
                        Logger.LogInformation("iisexpress Process {pid} failed to bind to port {port}, trying again", process.Id, port);

                        // Wait for the process to exit and try again
                        process.WaitForExit(30 * 1000);
                        await Task.Delay(1000); // Wait a second to make sure the socket is completely cleaned up
                    }
                    else
                    {
                        _hostProcess = process;

                        // Ensure iisexpress.exe is killed if test process termination is non-graceful.
                        // Prevents locked files when stop debugging unit test.
                        ProcessTracker.Add(_hostProcess);

                        // cache the process start time for verifying log file name.
                        var _ = _hostProcess.StartTime;

                        Logger.LogInformation("Started iisexpress successfully. Process Id : {processId}, Port: {port}", _hostProcess.Id, port);
                        return(url : url, hostExitToken : hostExitTokenSource.Token);
                    }
                }

                var message = $"Failed to initialize IIS Express after {MaximumAttempts} attempts to select a port";
                Logger.LogError(message);
                throw new TimeoutException(message);
            }
        }
예제 #16
0
        private async Task <(Uri url, CancellationToken hostExitToken)> StartIISExpressAsync(Uri uri, string contentRoot)
        {
            using (Logger.BeginScope("StartIISExpress"))
            {
                var port = uri.Port;
                if (port == 0)
                {
                    port = (uri.Scheme == "https") ? TestPortHelper.GetNextSSLPort() : TestPortHelper.GetNextPort();
                }

                for (var attempt = 0; attempt < MaximumAttempts; attempt++)
                {
                    Logger.LogInformation("Attempting to start IIS Express on port: {port}", port);

                    if (!string.IsNullOrWhiteSpace(DeploymentParameters.ServerConfigTemplateContent))
                    {
                        var serverConfig = DeploymentParameters.ServerConfigTemplateContent;

                        // Pass on the applicationhost.config to iis express. With this don't need to pass in the /path /port switches as they are in the applicationHost.config
                        // We take a copy of the original specified applicationHost.Config to prevent modifying the one in the repo.
                        serverConfig = ModifyANCMPathInConfig(replaceFlag: "[ANCMPath]", dllName: "aspnetcore.dll", serverConfig, contentRoot);

                        serverConfig = ModifyANCMPathInConfig(replaceFlag: "[ANCMV2Path]", dllName: "aspnetcorev2.dll", serverConfig, contentRoot);

                        Logger.LogDebug("Writing ApplicationPhysicalPath '{applicationPhysicalPath}' to config", contentRoot);
                        Logger.LogDebug("Writing Port '{port}' to config", port);
                        serverConfig =
                            serverConfig
                            .Replace("[ApplicationPhysicalPath]", contentRoot)
                            .Replace("[PORT]", port.ToString());

                        DeploymentParameters.ServerConfigLocation = Path.GetTempFileName();

                        if (serverConfig.Contains("[HostingModel]"))
                        {
                            var hostingModel = DeploymentParameters.HostingModel.ToString();
                            serverConfig.Replace("[HostingModel]", hostingModel);
                            Logger.LogDebug("Writing HostingModel '{hostingModel}' to config", hostingModel);
                        }

                        Logger.LogDebug("Saving Config to {configPath}", DeploymentParameters.ServerConfigLocation);

                        if (Logger.IsEnabled(LogLevel.Trace))
                        {
                            Logger.LogTrace($"Config File Content:{Environment.NewLine}===START CONFIG==={Environment.NewLine}{{configContent}}{Environment.NewLine}===END CONFIG===", serverConfig);
                        }

                        File.WriteAllText(DeploymentParameters.ServerConfigLocation, serverConfig);
                    }

                    if (DeploymentParameters.HostingModel == HostingModel.InProcess)
                    {
                        ModifyAspNetCoreSectionInWebConfig(key: "hostingModel", value: "inprocess");
                    }

                    ModifyHandlerSectionInWebConfig(key: "modules", value: DeploymentParameters.AncmVersion.ToString());
                    ModifyDotNetExePathInWebConfig();

                    var parameters = string.IsNullOrWhiteSpace(DeploymentParameters.ServerConfigLocation) ?
                                     string.Format("/port:{0} /path:\"{1}\" /trace:error", uri.Port, contentRoot) :
                                     string.Format("/site:{0} /config:{1} /trace:error", DeploymentParameters.SiteName, DeploymentParameters.ServerConfigLocation);

                    var iisExpressPath = GetIISExpressPath();

                    Logger.LogInformation("Executing command : {iisExpress} {parameters}", iisExpressPath, parameters);

                    var startInfo = new ProcessStartInfo
                    {
                        FileName               = iisExpressPath,
                        Arguments              = parameters,
                        UseShellExecute        = false,
                        CreateNoWindow         = true,
                        RedirectStandardError  = true,
                        RedirectStandardOutput = true
                    };

                    AddEnvironmentVariablesToProcess(startInfo, DeploymentParameters.EnvironmentVariables);

                    Uri url     = null;
                    var started = new TaskCompletionSource <bool>();

                    var process = new Process()
                    {
                        StartInfo = startInfo
                    };
                    process.OutputDataReceived += (sender, dataArgs) =>
                    {
                        if (string.Equals(dataArgs.Data, UnableToStartIISExpressMessage))
                        {
                            // We completely failed to start and we don't really know why
                            started.TrySetException(new InvalidOperationException("Failed to start IIS Express"));
                        }
                        else if (string.Equals(dataArgs.Data, FailedToInitializeBindingsMessage))
                        {
                            started.TrySetResult(false);
                        }
                        else if (string.Equals(dataArgs.Data, IISExpressRunningMessage))
                        {
                            started.TrySetResult(true);
                        }
                        else if (!string.IsNullOrEmpty(dataArgs.Data))
                        {
                            var m = UrlDetectorRegex.Match(dataArgs.Data);
                            if (m.Success)
                            {
                                url = new Uri(m.Groups["url"].Value);
                            }
                        }
                    };

                    process.EnableRaisingEvents = true;
                    var hostExitTokenSource = new CancellationTokenSource();
                    process.Exited += (sender, e) =>
                    {
                        Logger.LogInformation("iisexpress Process {pid} shut down", process.Id);

                        // If TrySetResult was called above, this will just silently fail to set the new state, which is what we want
                        started.TrySetException(new Exception($"Command exited unexpectedly with exit code: {process.ExitCode}"));

                        TriggerHostShutdown(hostExitTokenSource);
                    };
                    process.StartAndCaptureOutAndErrToLogger("iisexpress", Logger);
                    Logger.LogInformation("iisexpress Process {pid} started", process.Id);

                    if (process.HasExited)
                    {
                        Logger.LogError("Host process {processName} {pid} exited with code {exitCode} or failed to start.", startInfo.FileName, process.Id, process.ExitCode);
                        throw new Exception("Failed to start host");
                    }

                    // Wait for the app to start
                    // The timeout here is large, because we don't know how long the test could need
                    // We cover a lot of error cases above, but I want to make sure we eventually give up and don't hang the build
                    // just in case we missed one -anurse
                    if (!await started.Task.TimeoutAfter(TimeSpan.FromMinutes(10)))
                    {
                        Logger.LogInformation("iisexpress Process {pid} failed to bind to port {port}, trying again", _hostProcess.Id, port);

                        // Wait for the process to exit and try again
                        process.WaitForExit(30 * 1000);
                        await Task.Delay(1000); // Wait a second to make sure the socket is completely cleaned up
                    }
                    else
                    {
                        _hostProcess = process;
                        Logger.LogInformation("Started iisexpress successfully. Process Id : {processId}, Port: {port}", _hostProcess.Id, port);
                        return(url : url, hostExitToken : hostExitTokenSource.Token);
                    }
                }

                var message = $"Failed to initialize IIS Express after {MaximumAttempts} attempts to select a port";
                Logger.LogError(message);
                throw new TimeoutException(message);
            }
        }
예제 #17
0
 public WebSocketMiddlewareTests()
 {
     ClientPort    = TestPortHelper.GetNextPort();
     ClientAddress = $"ws://localhost:{ClientPort}/";
 }
예제 #18
0
    public override async Task <DeploymentResult> DeployAsync()
    {
        using (Logger.BeginScope("Deploy"))
        {
            _configFile = Path.GetTempFileName();

            var uri = string.IsNullOrEmpty(DeploymentParameters.ApplicationBaseUriHint) ?
                      new Uri("http://localhost:0") :
                      new Uri(DeploymentParameters.ApplicationBaseUriHint);

            if (uri.Port == 0)
            {
                var builder = new UriBuilder(uri);
                if (OperatingSystem.IsLinux())
                {
                    // This works with nginx 1.9.1 and later using the reuseport flag, available on Ubuntu 16.04.
                    // Keep it open so nobody else claims the port
                    _portSelector = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    _portSelector.Bind(new IPEndPoint(IPAddress.Loopback, 0));
                    builder.Port = ((IPEndPoint)_portSelector.LocalEndPoint).Port;
                }
                else
                {
                    builder.Port = TestPortHelper.GetNextPort();
                }
                uri = builder.Uri;
            }

            var redirectUri = TestUriHelper.BuildTestUri(ServerType.Nginx);

            if (DeploymentParameters.RuntimeFlavor == RuntimeFlavor.CoreClr &&
                DeploymentParameters.ApplicationType == ApplicationType.Standalone)
            {
                // Publish is required to get the correct files in the output directory
                DeploymentParameters.PublishApplicationBeforeDeployment = true;
            }

            if (DeploymentParameters.PublishApplicationBeforeDeployment)
            {
                DotnetPublish();
            }

            var(appUri, exitToken) = await StartSelfHostAsync(redirectUri);

            SetupNginx(appUri.ToString(), uri);

            Logger.LogInformation("Application ready at URL: {appUrl}", uri);

            // Wait for App to be loaded since Nginx returns 502 instead of 503 when App isn't loaded
            // Target actual address to avoid going through Nginx proxy
            using (var httpClient = new HttpClient())
            {
                var response = await RetryHelper.RetryRequest(() =>
                {
                    return(httpClient.GetAsync(redirectUri));
                }, Logger, exitToken);

                if (!response.IsSuccessStatusCode)
                {
                    throw new InvalidOperationException("Deploy failed");
                }
            }

            return(new DeploymentResult(
                       LoggerFactory,
                       DeploymentParameters,
                       applicationBaseUri: uri.ToString(),
                       contentRoot: DeploymentParameters.ApplicationPath,
                       hostShutdownToken: exitToken));
        }
    }