diff --git a/samples/AppSettings/AppSettings.csproj b/samples/AppSettings/AppSettings.csproj index bf6be606f3..621da3c9ca 100644 --- a/samples/AppSettings/AppSettings.csproj +++ b/samples/AppSettings/AppSettings.csproj @@ -16,10 +16,8 @@ - diff --git a/samples/AppSettings/appsettings.json b/samples/AppSettings/appsettings.json index fac810ec12..ab315a0242 100644 --- a/samples/AppSettings/appsettings.json +++ b/samples/AppSettings/appsettings.json @@ -27,8 +27,6 @@ "Certificate": { "Source": "File", "Path": "testCert.pfx", - // TODO: remove when dotnet user-secrets is working again - "Password": "testPassword" } }, // Add testCert.pfx to the current user's certificate store to enable this scenario. @@ -48,8 +46,6 @@ "TestCert": { "Source": "File", "Path": "testCert.pfx", - // TODO: remove when dotnet user-secrets is working again - "Password": "testPassword" }, // Add testCert.pfx to the current user's certificate store to enable this scenario. //"TestCertInStore": { diff --git a/test/Microsoft.AspNetCore.FunctionalTests/HttpClientSlim.cs b/test/Microsoft.AspNetCore.FunctionalTests/HttpClientSlim.cs new file mode 100644 index 0000000000..a85b03aa40 --- /dev/null +++ b/test/Microsoft.AspNetCore.FunctionalTests/HttpClientSlim.cs @@ -0,0 +1,130 @@ +// 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.IO; +using System.Net; +using System.Net.Http; +using System.Net.Security; +using System.Net.Sockets; +using System.Security.Authentication; +using System.Text; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Testing +{ + // Lightweight version of HttpClient implemented using Socket and SslStream + public static class HttpClientSlim + { + public static async Task GetStringAsync(string requestUri, bool validateCertificate = true) + => await GetStringAsync(new Uri(requestUri), validateCertificate).ConfigureAwait(false); + + public static async Task GetStringAsync(Uri requestUri, bool validateCertificate = true) + { + using (var stream = await GetStream(requestUri, validateCertificate).ConfigureAwait(false)) + { + using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize: 1024, leaveOpen: true)) + { + await writer.WriteAsync($"GET {requestUri.PathAndQuery} HTTP/1.0\r\n").ConfigureAwait(false); + await writer.WriteAsync($"Host: {requestUri.Authority}\r\n").ConfigureAwait(false); + await writer.WriteAsync("\r\n").ConfigureAwait(false); + } + + return await ReadResponse(stream).ConfigureAwait(false); + } + } + + public static async Task PostAsync(string requestUri, HttpContent content, bool validateCertificate = true) + => await PostAsync(new Uri(requestUri), content, validateCertificate).ConfigureAwait(false); + + public static async Task PostAsync(Uri requestUri, HttpContent content, bool validateCertificate = true) + { + using (var stream = await GetStream(requestUri, validateCertificate)) + { + using (var writer = new StreamWriter(stream, Encoding.ASCII, bufferSize: 1024, leaveOpen: true)) + { + await writer.WriteAsync($"POST {requestUri.PathAndQuery} HTTP/1.0\r\n").ConfigureAwait(false); + await writer.WriteAsync($"Host: {requestUri.Authority}\r\n").ConfigureAwait(false); + await writer.WriteAsync($"Content-Type: {content.Headers.ContentType}\r\n").ConfigureAwait(false); + await writer.WriteAsync($"Content-Length: {content.Headers.ContentLength}\r\n").ConfigureAwait(false); + await writer.WriteAsync("\r\n").ConfigureAwait(false); + } + + await content.CopyToAsync(stream).ConfigureAwait(false); + + return await ReadResponse(stream).ConfigureAwait(false); + } + } + + private static async Task ReadResponse(Stream stream) + { + using (var reader = new StreamReader(stream, Encoding.ASCII, detectEncodingFromByteOrderMarks: true, + bufferSize: 1024, leaveOpen: true)) + { + var response = await reader.ReadToEndAsync().ConfigureAwait(false); + + var status = GetStatus(response); + new HttpResponseMessage(status).EnsureSuccessStatusCode(); + + var body = response.Substring(response.IndexOf("\r\n\r\n") + 4); + return body; + } + + } + + private static HttpStatusCode GetStatus(string response) + { + var statusStart = response.IndexOf(' ') + 1; + var statusEnd = response.IndexOf(' ', statusStart) - 1; + var statusLength = statusEnd - statusStart + 1; + return (HttpStatusCode)int.Parse(response.Substring(statusStart, statusLength)); + } + + private static async Task GetStream(Uri requestUri, bool validateCertificate) + { + var socket = await GetSocket(requestUri); + var stream = new NetworkStream(socket, ownsSocket: true); + + if (requestUri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase)) + { + var sslStream = new SslStream(stream, leaveInnerStreamOpen: false, userCertificateValidationCallback: + validateCertificate ? null : (RemoteCertificateValidationCallback)((a, b, c, d) => true)); + + await sslStream.AuthenticateAsClientAsync(requestUri.Host, clientCertificates: null, + enabledSslProtocols: SslProtocols.Tls11 | SslProtocols.Tls12, + checkCertificateRevocation: validateCertificate).ConfigureAwait(false); + return sslStream; + } + else + { + return stream; + } + } + + public static async Task GetSocket(Uri requestUri) + { + var tcs = new TaskCompletionSource(); + + var socketArgs = new SocketAsyncEventArgs(); + socketArgs.RemoteEndPoint = new DnsEndPoint(requestUri.DnsSafeHost, requestUri.Port); + socketArgs.Completed += (s, e) => tcs.TrySetResult(e.ConnectSocket); + + // Must use static ConnectAsync(), since instance Connect() does not support DNS names on OSX/Linux. + if (Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, socketArgs)) + { + await tcs.Task.ConfigureAwait(false); + } + + var socket = socketArgs.ConnectSocket; + + if (socket == null) + { + throw new SocketException((int)socketArgs.SocketError); + } + else + { + return socket; + } + } + } +} diff --git a/test/Microsoft.AspNetCore.FunctionalTests/Microsoft.AspNetCore.FunctionalTests.csproj b/test/Microsoft.AspNetCore.FunctionalTests/Microsoft.AspNetCore.FunctionalTests.csproj index 13d57e6616..e2596080ea 100644 --- a/test/Microsoft.AspNetCore.FunctionalTests/Microsoft.AspNetCore.FunctionalTests.csproj +++ b/test/Microsoft.AspNetCore.FunctionalTests/Microsoft.AspNetCore.FunctionalTests.csproj @@ -7,7 +7,7 @@ - + diff --git a/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate.pfx b/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate.pfx deleted file mode 100644 index c792c52248..0000000000 Binary files a/test/Microsoft.AspNetCore.FunctionalTests/TestArtifacts/Certificate.pfx and /dev/null differ diff --git a/test/Microsoft.AspNetCore.FunctionalTests/WebHostFunctionalTests.cs b/test/Microsoft.AspNetCore.FunctionalTests/WebHostFunctionalTests.cs index 00e1fbfba6..fa24b34684 100644 --- a/test/Microsoft.AspNetCore.FunctionalTests/WebHostFunctionalTests.cs +++ b/test/Microsoft.AspNetCore.FunctionalTests/WebHostFunctionalTests.cs @@ -11,6 +11,7 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting.Server.Features; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Server.IntegrationTesting; +using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Testing; using Xunit; @@ -134,7 +135,8 @@ namespace Microsoft.AspNetCore.Tests ""Certificates"": { ""TestCert"": { ""Source"": ""File"", - ""Path"": ""TestArtifacts/Certificate.pfx"" + ""Path"": ""testCert.pfx"", + ""Password"": ""testPassword"" } } } @@ -143,13 +145,8 @@ namespace Microsoft.AspNetCore.Tests { var port = GetWebHostPort(webHost); - Assert.NotEqual(0, port); - - using (var client = new HttpClient(new HttpClientHandler { ServerCertificateCustomValidationCallback = (msg, cert, chain, errors) => true })) - { - var response = await client.GetAsync($"https://127.0.0.1:{port}"); - response.EnsureSuccessStatusCode(); - } + var response = await HttpClientSlim.GetStringAsync($"https://127.0.0.1:{port}", validateCertificate: false); + Assert.Equal("Hello, World!", response); } } finally @@ -172,7 +169,8 @@ namespace Microsoft.AspNetCore.Tests ""Port"": 0, ""Certificate"": { ""Source"": ""File"", - ""Path"": ""TestArtifacts/Certificate.pfx"", + ""Path"": ""testCert.pfx"", + ""Password"": ""testPassword"" } } } @@ -183,13 +181,8 @@ namespace Microsoft.AspNetCore.Tests { var port = GetWebHostPort(webHost); - Assert.NotEqual(0, port); - - using (var client = new HttpClient(new HttpClientHandler { ServerCertificateCustomValidationCallback = (msg, cert, chain, errors) => true })) - { - var response = await client.GetAsync($"https://127.0.0.1:{port}"); - response.EnsureSuccessStatusCode(); - } + var response = await HttpClientSlim.GetStringAsync($"https://127.0.0.1:{port}", validateCertificate: false); + Assert.Equal("Hello, World!", response); } } finally diff --git a/test/Microsoft.AspNetCore.FunctionalTests/testCert.pfx b/test/Microsoft.AspNetCore.FunctionalTests/testCert.pfx new file mode 100644 index 0000000000..7118908c2d Binary files /dev/null and b/test/Microsoft.AspNetCore.FunctionalTests/testCert.pfx differ