Pre-release WinHTTP's version supports client cert authentication over HTTP/2, but the feature must be explicitly opted-in. PR sets WINHTTP_OPTION_ENABLE_HTTP2_PLUS_CLIENT_CERT to TRUE before invoking WinHttpConnect if the request's protocol is HTTP/2 and scheme is HTTPS.
This PR also enables all HTTP 1.1 tests for WinHttpHandler on .Net Core and Framework and the most of HTTP/2 tests on .Net Core.
public const uint WINHTTP_OPTION_ASSURED_NON_BLOCKING_CALLBACKS = 111;
+ public const uint WINHTTP_OPTION_ENABLE_HTTP2_PLUS_CLIENT_CERT = 161;
public const uint WINHTTP_OPTION_ENABLE_HTTP_PROTOCOL = 133;
public const uint WINHTTP_OPTION_HTTP_PROTOCOL_USED = 134;
public const uint WINHTTP_PROTOCOL_FLAG_HTTP2 = 0x1;
+ public const uint WINHTTP_HTTP2_PLUS_CLIENT_CERT_FLAG = 0x1;
public const uint WINHTTP_OPTION_UPGRADE_TO_WEB_SOCKET = 114;
public const uint WINHTTP_OPTION_WEB_SOCKET_CLOSE_TIMEOUT = 115;
// Send a response in the JSON format that the client expects
string username = context.User.Identity.Name;
- await context.Response.OutputStream.WriteAsync(System.Text.Encoding.UTF8.GetBytes($"{{\"authenticated\": \"true\", \"user\": \"{username}\" }}"));
+ byte[] bytes = System.Text.Encoding.UTF8.GetBytes($"{{\"authenticated\": \"true\", \"user\": \"{username}\" }}");
+ await context.Response.OutputStream.WriteAsync(bytes);
context.Response.Close();
}
public IPAddress Address { get; set; } = IPAddress.Loopback;
public bool UseSsl { get; set; } = PlatformDetection.SupportsAlpn && !Capability.Http2ForceUnencryptedLoopback();
public SslProtocols SslProtocols { get; set; } =
-#if !NETSTANDARD2_0
+#if !NETSTANDARD2_0 && !NETFRAMEWORK
SslProtocols.Tls13 |
#endif
SslProtocols.Tls12;
BinaryPrimitives.WriteUInt16BigEndian(buffer, checked((ushort)Origin.Length));
buffer = buffer.Slice(2);
- Encoding.ASCII.GetBytes(Origin, buffer);
+ var tmpBuffer = Encoding.ASCII.GetBytes(Origin);
+ tmpBuffer.CopyTo(buffer);
buffer = buffer.Slice(Origin.Length);
- Encoding.ASCII.GetBytes(AltSvc, buffer);
+ tmpBuffer = Encoding.ASCII.GetBytes(AltSvc);
+ tmpBuffer.CopyTo(buffer);
}
public override string ToString() => $"{base.ToString()}\n{nameof(Origin)}: {Origin}\n{nameof(AltSvc)}: {AltSvc}";
// See the LICENSE file in the project root for more information.
using System.Collections.Generic;
-using System.Diagnostics;
using System.IO;
+using System.Linq;
using System.Net.Http.Functional.Tests;
using System.Net.Security;
using System.Net.Sockets;
private readonly byte[] _prefix;
public string PrefixString => Encoding.UTF8.GetString(_prefix, 0, _prefix.Length);
public bool IsInvalid => _connectionSocket == null;
+ public Stream Stream => _connectionStream;
public Http2LoopbackConnection(Socket socket, Http2Options httpOptions)
{
using (var cert = Configuration.Certificates.GetServerCertificate())
{
+#if !NETFRAMEWORK
SslServerAuthenticationOptions options = new SslServerAuthenticationOptions();
options.EnabledSslProtocols = httpOptions.SslProtocols;
options.ServerCertificate = cert;
- options.ClientCertificateRequired = false;
+ options.ClientCertificateRequired = httpOptions.ClientCertificateRequired;
sslStream.AuthenticateAsServerAsync(options, CancellationToken.None).Wait();
+#else
+ sslStream.AuthenticateAsServerAsync(cert, httpOptions.ClientCertificateRequired, httpOptions.SslProtocols, checkCertificateRevocation: false).Wait();
+#endif
}
_connectionStream = sslStream;
{
throw new Exception("Connection stream closed while attempting to read connection preface.");
}
+ else if (Text.Encoding.ASCII.GetString(_prefix).Contains("HTTP/1.1"))
+ {
+ throw new Exception("HTTP 1.1 request received.");
+ }
}
public async Task SendConnectionPrefaceAsync()
}
else
{
- string value = Encoding.ASCII.GetString(headerBlock.Slice(bytesConsumed, stringLength));
+ string value = Encoding.ASCII.GetString(headerBlock.Slice(bytesConsumed, stringLength).ToArray());
return (bytesConsumed + stringLength, value);
}
}
}
}
- public static async Task CreateClientAndServerAsync(Func<Uri, Task> clientFunc, Func<Http2LoopbackServer, Task> serverFunc, int timeout = 60_000)
+ public static Task CreateClientAndServerAsync(Func<Uri, Task> clientFunc, Func<Http2LoopbackServer, Task> serverFunc, int timeout = 60_000)
{
- using (var server = Http2LoopbackServer.CreateServer())
+ return CreateClientAndServerAsync(clientFunc, serverFunc, null, timeout);
+ }
+
+ public static async Task CreateClientAndServerAsync(Func<Uri, Task> clientFunc, Func<Http2LoopbackServer, Task> serverFunc, Http2Options http2Options, int timeout = 60_000)
+ {
+ using (var server = Http2LoopbackServer.CreateServer(http2Options ?? new Http2Options()))
{
Task clientTask = clientFunc(server.Address);
Task serverTask = serverFunc(server);
{
public int ListenBacklog { get; set; } = 1;
+ public bool ClientCertificateRequired { get; set; }
+
public Http2Options()
{
UseSsl = PlatformDetection.SupportsAlpn && !Capability.Http2ForceUnencryptedLoopback();
}
}
- public override Version Version => HttpVersion.Version20;
+ public override Version Version => HttpVersion20.Value;
}
public enum ProtocolErrors
INADEQUATE_SECURITY = 0xc,
HTTP_1_1_REQUIRED = 0xd
}
+
+ public static class HttpVersion20
+ {
+ public static readonly Version Value = new Version(2, 0);
+ }
}
[InlineData(SslProtocols.Tls, true)]
[InlineData(SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls, false)]
[InlineData(SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls, true)]
+#if !NETFRAMEWORK
[InlineData(SslProtocols.Tls13 | SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls, false)]
[InlineData(SslProtocols.Tls13 | SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls, true)]
+#endif
[InlineData(SslProtocols.None, false)]
[InlineData(SslProtocols.None, true)]
public async Task SetDelegate_ConnectionSucceeds(SslProtocols acceptedProtocol, bool requestOnlyThisProtocol)
// restrictions on minimum TLS/SSL version
// We currently know that some platforms like Debian 10 OpenSSL
// will by default block < TLS 1.2
+#if !NETFRAMEWORK
handler.SslProtocols = SslProtocols.Tls13 | SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls;
+#else
+ handler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls;
+#endif
}
var options = new LoopbackServer.Options { UseSsl = true, SslProtocols = acceptedProtocol };
[InlineData("Negotiate")]
public async Task Credentials_ServerChallengesWithWindowsAuth_ClientSendsWindowsAuthHeader(string authScheme)
{
+#if WINHTTPHANDLER_TEST
+ if (UseVersion > HttpVersion.Version11)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
await LoopbackServerFactory.CreateClientAndServerAsync(
async uri =>
{
{
public HttpClientHandler_Cancellation_Test(ITestOutputHelper output) : base(output) { }
- [Theory]
+ [ConditionalTheory]
[InlineData(false, CancellationMode.Token)]
[InlineData(true, CancellationMode.Token)]
public async Task PostAsync_CancelDuringRequestContentSend_TaskCanceledQuickly(bool chunkedTransfer, CancellationMode mode)
{
- if (LoopbackServerFactory.Version >= HttpVersion.Version20 && chunkedTransfer)
+ if (LoopbackServerFactory.Version >= HttpVersion20.Value && chunkedTransfer)
{
// There is no chunked encoding in HTTP/2 and later
return;
}
+#if WINHTTPHANDLER_TEST
+ if (UseVersion >= HttpVersion20.Value)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
+
var serverRelease = new TaskCompletionSource<bool>();
await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
{
});
}
- [Theory]
+ [ConditionalTheory]
[MemberData(nameof(OneBoolAndCancellationMode))]
public async Task GetAsync_CancelDuringResponseHeadersReceived_TaskCanceledQuickly(bool connectionClose, CancellationMode mode)
{
- if (LoopbackServerFactory.Version >= HttpVersion.Version20 && connectionClose)
+ if (LoopbackServerFactory.Version >= HttpVersion20.Value && connectionClose)
{
// There is no Connection header in HTTP/2 and later
return;
}
+#if WINHTTPHANDLER_TEST
+ if (UseVersion >= HttpVersion20.Value)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
+
using (HttpClient client = CreateHttpClient())
{
client.Timeout = Timeout.InfiniteTimeSpan;
[MemberData(nameof(TwoBoolsAndCancellationMode))]
public async Task GetAsync_CancelDuringResponseBodyReceived_Buffered_TaskCanceledQuickly(bool chunkedTransfer, bool connectionClose, CancellationMode mode)
{
- if (LoopbackServerFactory.Version >= HttpVersion.Version20 && (chunkedTransfer || connectionClose))
+ if (LoopbackServerFactory.Version >= HttpVersion20.Value && (chunkedTransfer || connectionClose))
{
// There is no chunked encoding or connection header in HTTP/2 and later
return;
}
}
- [Theory]
+ [ConditionalTheory]
[MemberData(nameof(ThreeBools))]
public async Task GetAsync_CancelDuringResponseBodyReceived_Unbuffered_TaskCanceledQuickly(bool chunkedTransfer, bool connectionClose, bool readOrCopyToAsync)
{
- if (LoopbackServerFactory.Version >= HttpVersion.Version20 && (chunkedTransfer || connectionClose))
+ if (LoopbackServerFactory.Version >= HttpVersion20.Value && (chunkedTransfer || connectionClose))
{
// There is no chunked encoding or connection header in HTTP/2 and later
return;
}
+#if WINHTTPHANDLER_TEST
+ if (UseVersion >= HttpVersion20.Value)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
+
using (HttpClient client = CreateHttpClient())
{
client.Timeout = Timeout.InfiniteTimeSpan;
});
}
}
-
- [Theory]
+ [ConditionalTheory]
[InlineData(CancellationMode.CancelPendingRequests, false)]
[InlineData(CancellationMode.DisposeHttpClient, false)]
[InlineData(CancellationMode.CancelPendingRequests, true)]
[InlineData(CancellationMode.DisposeHttpClient, true)]
public async Task GetAsync_CancelPendingRequests_DoesntCancelReadAsyncOnResponseStream(CancellationMode mode, bool copyToAsync)
{
+#if WINHTTPHANDLER_TEST
+ if (UseVersion >= HttpVersion20.Value)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
using (HttpClient client = CreateHttpClient())
{
client.Timeout = Timeout.InfiniteTimeSpan;
[ConditionalFact]
public async Task MaxConnectionsPerServer_WaitingConnectionsAreCancelable()
{
- if (LoopbackServerFactory.Version >= HttpVersion.Version20)
+ if (LoopbackServerFactory.Version >= HttpVersion20.Value)
{
// HTTP/2 does not use connection limits.
throw new SkipTestException("Not supported on HTTP/2 and later");
}
}
+#if !NETFRAMEWORK
[OuterLoop("Uses Task.Delay")]
- [Theory]
+ [ConditionalTheory]
[MemberData(nameof(PostAsync_Cancel_CancellationTokenPassedToContent_MemberData))]
public async Task PostAsync_Cancel_CancellationTokenPassedToContent(HttpContent content, CancellationTokenSource cancellationTokenSource)
{
+#if WINHTTPHANDLER_TEST
+ if (UseVersion > HttpVersion.Version11)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
await LoopbackServerFactory.CreateClientAndServerAsync(
async uri =>
{
catch (Exception) { }
});
}
+#endif
private async Task ValidateClientCancellationAsync(Func<Task> clientBodyAsync)
{
{
_output.WriteLine(
"Client cert: {0}",
- ((X509Certificate2)sslStream.RemoteCertificate).GetNameInfo(X509NameType.SimpleName, false));
+ new X509Certificate2(sslStream.RemoteCertificate.Export(X509ContentType.Cert)).GetNameInfo(X509NameType.SimpleName, false));
Assert.Equal(cert, sslStream.RemoteCertificate);
}
else
private string GetCookieValue(HttpRequestData request)
{
+#if !NETFRAMEWORK
if (LoopbackServerFactory.Version < HttpVersion.Version20)
+#else
+ if (LoopbackServerFactory.Version < HttpVersion20.Value)
+#endif
{
// HTTP/1.x must have only one value.
return request.GetSingleHeaderValue("Cookie");
yield return new object[] { "ABC", "123", useCookies };
yield return new object[] { "Hello", "World", useCookies };
yield return new object[] { "foo", "bar", useCookies };
+#if !NETFRAMEWORK
yield return new object[] { "Hello World", "value", useCookies };
+#endif
yield return new object[] { ".AspNetCore.Session", "RAExEmXpoCbueP_QYM", useCookies };
yield return new object[]
public abstract class HttpClientHandler_Decompression_Test : HttpClientHandlerTestBase
{
+#if !NETFRAMEWORK
+ private static readonly DecompressionMethods _all = DecompressionMethods.All;
+#else
+ private static readonly DecompressionMethods _all = DecompressionMethods.Deflate | DecompressionMethods.GZip;
+#endif
public HttpClientHandler_Decompression_Test(ITestOutputHelper output) : base(output) { }
public static IEnumerable<object[]> RemoteServersAndCompressionUris()
{
"deflate",
new Func<Stream, Stream>(s => new DeflateStream(s, CompressionLevel.Optimal, leaveOpen: true)),
- specifyAllMethods ? DecompressionMethods.Deflate : DecompressionMethods.All
+ specifyAllMethods ? DecompressionMethods.Deflate : _all
};
yield return new object[]
{
"gzip",
new Func<Stream, Stream>(s => new GZipStream(s, CompressionLevel.Optimal, leaveOpen: true)),
- specifyAllMethods ? DecompressionMethods.GZip : DecompressionMethods.All
+ specifyAllMethods ? DecompressionMethods.GZip : _all
};
+#if !NETFRAMEWORK
yield return new object[]
{
"br",
new Func<Stream, Stream>(s => new BrotliStream(s, CompressionLevel.Optimal, leaveOpen: true)),
- specifyAllMethods ? DecompressionMethods.Brotli : DecompressionMethods.All
+ specifyAllMethods ? DecompressionMethods.Brotli : _all
};
+#endif
}
}
new Func<Stream, Stream>(s => new DeflateStream(s, CompressionLevel.Optimal, leaveOpen: true)),
DecompressionMethods.None
};
+#if !NETFRAMEWORK
yield return new object[]
{
"gzip",
new Func<Stream, Stream>(s => new BrotliStream(s, CompressionLevel.Optimal, leaveOpen: true)),
DecompressionMethods.Deflate | DecompressionMethods.GZip
};
+#endif
}
[Theory]
}
[Theory]
+#if NETCORE
[InlineData(DecompressionMethods.Brotli, "br", "")]
[InlineData(DecompressionMethods.Brotli, "br", "br")]
[InlineData(DecompressionMethods.Brotli, "br", "gzip")]
[InlineData(DecompressionMethods.Brotli, "br", "gzip, deflate")]
+#endif
[InlineData(DecompressionMethods.GZip, "gzip", "")]
[InlineData(DecompressionMethods.Deflate, "deflate", "")]
[InlineData(DecompressionMethods.GZip | DecompressionMethods.Deflate, "gzip, deflate", "")]
using System.Text;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.DotNet.XUnitExtensions;
using Xunit;
using Xunit.Abstractions;
}
}
- [Fact]
+ [ConditionalFact]
public async Task SetAfterUse_Throws()
{
+#if WINHTTPHANDLER_TEST
+ if (UseVersion >= HttpVersion20.Value)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
{
using HttpClientHandler handler = CreateHttpClientHandler();
{
public HttpClientHandler_Proxy_Test(ITestOutputHelper output) : base(output) { }
- [Fact]
+ [ConditionalFact]
public async Task Dispose_HandlerWithProxy_ProxyNotDisposed()
{
+#if WINHTTPHANDLER_TEST
+ if (UseVersion >= HttpVersion20.Value)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
var proxy = new TrackDisposalProxy();
await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Microsoft.DotNet.RemoteExecutor;
+using Microsoft.DotNet.XUnitExtensions;
using Xunit;
using Xunit.Abstractions;
public HttpClientHandler_ServerCertificates_Test(ITestOutputHelper output) : base(output) { }
- [Fact]
+ [ConditionalFact]
public void Ctor_ExpectedDefaultValues()
{
+#if WINHTTPHANDLER_TEST
+ if (UseVersion > HttpVersion.Version11)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
using (HttpClientHandler handler = CreateHttpClientHandler())
{
Assert.Null(handler.ServerCertificateCustomValidationCallback);
}
}
- [Fact]
+ [ConditionalFact]
public void ServerCertificateCustomValidationCallback_SetGet_Roundtrips()
{
+#if WINHTTPHANDLER_TEST
+ if (UseVersion > HttpVersion.Version11)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
+
using (HttpClientHandler handler = CreateHttpClientHandler())
{
Assert.Null(handler.ServerCertificateCustomValidationCallback);
[InlineData(SslProtocols.Tls11 | SslProtocols.Tls12)]
[InlineData(SslProtocols.Tls | SslProtocols.Tls12)]
[InlineData(SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12)]
+#if !NETFRAMEWORK
[InlineData(SslProtocols.Tls13)]
[InlineData(SslProtocols.Tls11 | SslProtocols.Tls13)]
[InlineData(SslProtocols.Tls12 | SslProtocols.Tls13)]
[InlineData(SslProtocols.Tls | SslProtocols.Tls13)]
[InlineData(SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13)]
+#endif
public void SetGetProtocols_Roundtrips(SslProtocols protocols)
{
using (HttpClientHandler handler = CreateHttpClientHandler())
#pragma warning disable 0618
if (PlatformDetection.SupportsSsl3)
{
+#if !NETFRAMEWORK
yield return new object[] { SslProtocols.Ssl3, true };
+#endif
}
if (PlatformDetection.IsWindows && !PlatformDetection.IsWindows10Version1607OrGreater)
{
// These protocols are new, and might not be enabled everywhere yet
if (PlatformDetection.IsUbuntu1810OrHigher)
{
+#if !NETFRAMEWORK
yield return new object[] { SslProtocols.Tls13, false };
yield return new object[] { SslProtocols.Tls13, true };
+#endif
}
}
// restrictions on minimum TLS/SSL version
// We currently know that some platforms like Debian 10 OpenSSL
// will by default block < TLS 1.2
+#if !NETFRAMEWORK
handler.SslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12 | SslProtocols.Tls13;
+#else
+ handler.SslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
+#endif
}
var options = new LoopbackServer.Options { UseSsl = true, SslProtocols = acceptedProtocol };
[ConditionalFact]
public async Task GetAsync_IPv6LinkLocalAddressUri_Success()
{
+#if WINHTTPHANDLER_TEST
+ if (UseVersion >= HttpVersion20.Value)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
using (HttpClient client = CreateHttpClient())
{
var options = new GenericLoopbackOptions { Address = TestHelper.GetIPv6LinkLocalAddress() };
}
}
- [Theory]
+ [ConditionalTheory]
[MemberData(nameof(GetAsync_IPBasedUri_Success_MemberData))]
public async Task GetAsync_IPBasedUri_Success(IPAddress address)
{
+#if WINHTTPHANDLER_TEST
+ if (UseVersion >= HttpVersion20.Value)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
using (HttpClient client = CreateHttpClient())
{
var options = new GenericLoopbackOptions { Address = address };
[MemberData(nameof(SecureAndNonSecure_IPBasedUri_MemberData))]
public async Task GetAsync_SecureAndNonSecureIPBasedUri_CorrectlyFormatted(IPAddress address, bool useSsl)
{
- if (LoopbackServerFactory.Version >= HttpVersion.Version20)
+ if (LoopbackServerFactory.Version >= HttpVersion20.Value)
{
throw new SkipTestException("Host header is not supported on HTTP/2 and later.");
}
}
}
- [Theory]
+ [ConditionalTheory]
[InlineData("WWW-Authenticate", "CustomAuth")]
[InlineData("", "")] // RFC7235 requires servers to send this header with 401 but some servers don't.
public async Task GetAsync_ServerNeedsNonStandardAuthAndSetCredential_StatusCodeUnauthorized(string authHeadrName, string authHeaderValue)
{
+#if WINHTTPHANDLER_TEST
+ if (UseVersion >= HttpVersion20.Value)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
await LoopbackServerFactory.CreateServerAsync(async (server, url) =>
{
HttpClientHandler handler = CreateHttpClientHandler();
server.AcceptConnectionSendCustomResponseAndCloseAsync("HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nhe"));
}
- [Fact]
+ [ConditionalFact]
public async Task PostAsync_ManyDifferentRequestHeaders_SentCorrectly()
{
+#if WINHTTPHANDLER_TEST
+ if (UseVersion > HttpVersion.Version11)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
const string content = "hello world";
// Using examples from https://en.wikipedia.org/wiki/List_of_HTTP_header_fields#Request_fields
Assert.Equal("X-Underscore_Name", requestData.GetSingleHeaderValue("X-Underscore_Name"));
Assert.Equal("End", requestData.GetSingleHeaderValue("X-End"));
- if (LoopbackServerFactory.Version >= HttpVersion.Version20)
+ if (LoopbackServerFactory.Version >= HttpVersion20.Value)
{
// HTTP/2 and later forbids certain headers or values.
Assert.Equal("trailers", requestData.GetSingleHeaderValue("TE"));
[MemberData(nameof(GetAsync_ManyDifferentResponseHeaders_ParsedCorrectly_MemberData))]
public async Task GetAsync_ManyDifferentResponseHeaders_ParsedCorrectly(string newline, string fold, bool dribble)
{
- if (LoopbackServerFactory.Version >= HttpVersion.Version20)
+ if (LoopbackServerFactory.Version >= HttpVersion20.Value)
{
throw new SkipTestException("Folding is not supported on HTTP/2 and later.");
}
[ConditionalFact]
public async Task GetAsync_NonTraditionalChunkSizes_Accepted()
{
- if (LoopbackServerFactory.Version >= HttpVersion.Version20)
+ if (LoopbackServerFactory.Version >= HttpVersion20.Value)
{
throw new SkipTestException("Chunking is not supported on HTTP/2 and later.");
}
[InlineData(null)]
public async Task ReadAsStreamAsync_HandlerProducesWellBehavedResponseStream(bool? chunked)
{
- if (LoopbackServerFactory.Version >= HttpVersion.Version20 && chunked == true)
+#if WINHTTPHANDLER_TEST
+ if (UseVersion >= HttpVersion20.Value)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
+ if (LoopbackServerFactory.Version >= HttpVersion20.Value && chunked == true)
{
throw new SkipTestException("Chunking is not supported on HTTP/2 and later.");
}
Assert.Throws<NotSupportedException>(() => responseStream.Seek(0, SeekOrigin.Begin));
Assert.Throws<NotSupportedException>(() => responseStream.SetLength(0));
Assert.Throws<NotSupportedException>(() => responseStream.Write(new byte[1], 0, 1));
+#if !NETFRAMEWORK
Assert.Throws<NotSupportedException>(() => responseStream.Write(new Span<byte>(new byte[1])));
Assert.Throws<NotSupportedException>(() => { responseStream.WriteAsync(new Memory<byte>(new byte[1])); });
+#endif
Assert.Throws<NotSupportedException>(() => { responseStream.WriteAsync(new byte[1], 0, 1); });
Assert.Throws<NotSupportedException>(() => responseStream.WriteByte(1));
Assert.Equal(1, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, buffer, 0, 1, null));
Assert.Equal((byte)'e', buffer[0]);
+#if !NETFRAMEWORK
Assert.Equal(1, await responseStream.ReadAsync(new Memory<byte>(buffer)));
+#else
+ Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1));
+#endif
Assert.Equal((byte)'l', buffer[0]);
Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1));
Assert.Equal((byte)'l', buffer[0]);
+#if !NETFRAMEWORK
Assert.Equal(1, responseStream.Read(new Span<byte>(buffer)));
+#else
+ Assert.Equal(1, await responseStream.ReadAsync(buffer, 0, 1));
+#endif
Assert.Equal((byte)'o', buffer[0]);
Assert.Equal(1, responseStream.Read(buffer, 0, 1));
// Doing any of these 0-byte reads causes the connection to fail.
Assert.Equal(0, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, Array.Empty<byte>(), 0, 0, null));
+#if !NETFRAMEWORK
Assert.Equal(0, await responseStream.ReadAsync(Memory<byte>.Empty));
+#endif
Assert.Equal(0, await responseStream.ReadAsync(Array.Empty<byte>(), 0, 0));
+#if !NETFRAMEWORK
Assert.Equal(0, responseStream.Read(Span<byte>.Empty));
+#endif
Assert.Equal(0, responseStream.Read(Array.Empty<byte>(), 0, 0));
// And copying
Assert.Equal(0, ms.Length);
Assert.Equal(-1, responseStream.ReadByte());
Assert.Equal(0, responseStream.Read(buffer, 0, 1));
+#if !NETFRAMEWORK
Assert.Equal(0, responseStream.Read(new Span<byte>(buffer)));
+#endif
Assert.Equal(0, await responseStream.ReadAsync(buffer, 0, 1));
+#if !NETFRAMEWORK
Assert.Equal(0, await responseStream.ReadAsync(new Memory<byte>(buffer)));
+#endif
Assert.Equal(0, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, buffer, 0, 1, null));
}
}
});
}
- [Fact]
+ [ConditionalFact]
public async Task ReadAsStreamAsync_EmptyResponseBody_HandlerProducesWellBehavedResponseStream()
{
+#if WINHTTPHANDLER_TEST
+ if (UseVersion >= HttpVersion20.Value)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
{
using (var client = new HttpMessageInvoker(CreateHttpClientHandler()))
Assert.Throws<NotSupportedException>(() => responseStream.Seek(0, SeekOrigin.Begin));
Assert.Throws<NotSupportedException>(() => responseStream.SetLength(0));
Assert.Throws<NotSupportedException>(() => responseStream.Write(new byte[1], 0, 1));
+#if !NETFRAMEWORK
Assert.Throws<NotSupportedException>(() => responseStream.Write(new Span<byte>(new byte[1])));
await Assert.ThrowsAsync<NotSupportedException>(async () => await responseStream.WriteAsync(new Memory<byte>(new byte[1])));
+#endif
await Assert.ThrowsAsync<NotSupportedException>(async () => await responseStream.WriteAsync(new byte[1], 0, 1));
Assert.Throws<NotSupportedException>(() => responseStream.WriteByte(1));
var buffer = new byte[1];
Assert.Equal(-1, responseStream.ReadByte());
Assert.Equal(0, await Task.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, buffer, 0, 1, null));
+#if !NETFRAMEWORK
Assert.Equal(0, await responseStream.ReadAsync(new Memory<byte>(buffer)));
+#endif
Assert.Equal(0, await responseStream.ReadAsync(buffer, 0, 1));
+#if !NETFRAMEWORK
Assert.Equal(0, responseStream.Read(new Span<byte>(buffer)));
+#endif
Assert.Equal(0, responseStream.Read(buffer, 0, 1));
// Empty copies
},
server => server.AcceptConnectionSendResponseAndCloseAsync());
}
-
- [Fact]
+ [ConditionalFact]
public async Task Dispose_DisposingHandlerCancelsActiveOperationsWithoutResponses()
{
+#if WINHTTPHANDLER_TEST
+ if (UseVersion >= HttpVersion20.Value)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
await LoopbackServerFactory.CreateServerAsync(async (server1, url1) =>
{
await LoopbackServerFactory.CreateServerAsync(async (server2, url2) =>
}
}
- [Fact]
+ [ConditionalFact]
public async Task GetAsync_ExpectContinueTrue_NoContent_StillSendsHeader()
{
+#if WINHTTPHANDLER_TEST
+ if (UseVersion >= HttpVersion20.Value)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
const string ExpectedContent = "Hello, expecting and continuing world.";
var clientCompleted = new TaskCompletionSource<bool>();
await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
yield return new object[] { (HttpStatusCode) 199 };
}
- [Theory]
+ [ConditionalTheory]
[MemberData(nameof(Interim1xxStatusCode))]
public async Task SendAsync_1xxResponsesWithHeaders_InterimResponsesHeadersIgnored(HttpStatusCode responseStatusCode)
{
+#if WINHTTPHANDLER_TEST
+ if (UseVersion >= HttpVersion20.Value)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
var clientFinished = new TaskCompletionSource<bool>();
const string TestString = "test";
const string CookieHeaderExpected = "yummy_cookie=choco";
});
}
- [Theory]
+ [ConditionalTheory]
[MemberData(nameof(Interim1xxStatusCode))]
public async Task SendAsync_Unexpected1xxResponses_DropAllInterimResponses(HttpStatusCode responseStatusCode)
{
+#if WINHTTPHANDLER_TEST
+ if (UseVersion >= HttpVersion20.Value)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
var clientFinished = new TaskCompletionSource<bool>();
const string TestString = "test";
});
}
- [Fact]
+ [ConditionalFact]
public async Task SendAsync_MultipleExpected100Responses_ReceivesCorrectResponse()
{
+#if WINHTTPHANDLER_TEST
+ if (UseVersion >= HttpVersion20.Value)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
var clientFinished = new TaskCompletionSource<bool>();
const string TestString = "test";
});
}
- [Fact]
+ [ConditionalFact]
public async Task SendAsync_No100ContinueReceived_RequestBodySentEventually()
{
+#if WINHTTPHANDLER_TEST
+ if (UseVersion >= HttpVersion20.Value)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
var clientFinished = new TaskCompletionSource<bool>();
const string RequestString = "request";
const string ResponseString = "response";
return;
}
- if (LoopbackServerFactory.Version >= HttpVersion.Version20)
+ if (LoopbackServerFactory.Version >= HttpVersion20.Value)
{
throw new SkipTestException("Upgrade is not supported on HTTP/2 and later");
}
}
}
+#if !NETFRAMEWORK
[OuterLoop("Uses external server")]
[Theory, MemberData(nameof(RemoteServersMemberData))]
public async Task PostAsync_ReuseRequestContent_Success(Configuration.Http.RemoteServer remoteServer)
}
}
}
+#endif
[Theory]
[InlineData(HttpStatusCode.MethodNotAllowed, "Custom description")]
[InlineData(HttpStatusCode.MethodNotAllowed, "")]
public async Task GetAsync_CallMethod_ExpectedStatusLine(HttpStatusCode statusCode, string reasonPhrase)
{
- if (LoopbackServerFactory.Version >= HttpVersion.Version20)
+ if (LoopbackServerFactory.Version >= HttpVersion20.Value)
{
// Custom messages are not supported on HTTP2 and later.
return;
[Fact]
public async Task SendAsync_RequestVersion10_ServerReceivesVersion10Request()
{
+ // Test is not supported for WinHttpHandler and HTTP/2
+ if(IsWinHttpHandler && UseVersion >= HttpVersion20.Value)
+ {
+ return;
+ }
+
Version receivedRequestVersion = await SendRequestAndGetRequestVersionAsync(new Version(1, 0));
Assert.Equal(new Version(1, 0), receivedRequestVersion);
}
}
}
- [Fact]
+ [ConditionalFact]
public async Task SendAsync_RequestVersion20_HttpNotHttps_NoUpgradeRequest()
{
+#if WINHTTPHANDLER_TEST
+ if (UseVersion >= HttpVersion20.Value)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
await LoopbackServerFactory.CreateClientAndServerAsync(async uri =>
{
using (HttpClient client = CreateHttpClient())
return receivedRequestVersion;
}
-#endregion
+ #endregion
-#region Uri wire transmission encoding tests
- [Fact]
+ #region Uri wire transmission encoding tests
+ [ConditionalFact]
public async Task SendRequest_UriPathHasReservedChars_ServerReceivedExpectedPath()
{
+#if WINHTTPHANDLER_TEST
+ if (UseVersion >= HttpVersion20.Value)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
await LoopbackServerFactory.CreateServerAsync(async (server, rootUrl) =>
{
var uri = new Uri($"{rootUrl.Scheme}://{rootUrl.Host}:{rootUrl.Port}/test[]");
protected virtual HttpClient CreateHttpClient() => CreateHttpClient(CreateHttpClientHandler());
protected HttpClient CreateHttpClient(HttpMessageHandler handler) =>
- new HttpClient(handler) { DefaultRequestVersion = UseVersion };
+ new HttpClient(handler) {
+#if !NETFRAMEWORK
+ DefaultRequestVersion = UseVersion
+#endif
+ };
protected static HttpClient CreateHttpClient(string useVersionString) =>
CreateHttpClient(CreateHttpClientHandler(useVersionString), useVersionString);
protected static HttpClient CreateHttpClient(HttpMessageHandler handler, string useVersionString) =>
- new HttpClient(handler) { DefaultRequestVersion = Version.Parse(useVersionString) };
+ new HttpClient(handler) {
+#if !NETFRAMEWORK
+ DefaultRequestVersion = Version.Parse(useVersionString)
+#endif
+ };
protected HttpClientHandler CreateHttpClientHandler() => CreateHttpClientHandler(UseVersion);
{
return useVersion.Major switch
{
-#if NETCOREAPP
+#if NETCOREAPP || WINHTTPHANDLER_TEST
#if HTTP3
3 => Http3LoopbackServerFactory.Singleton,
#endif
wrappedHandler = new VersionCheckerHttpHandler(httpClientHandler, remoteServer.HttpVersion);
}
- return new HttpClient(wrappedHandler) { DefaultRequestVersion = remoteServer.HttpVersion };
+ return new HttpClient(wrappedHandler) {
+#if !NETFRAMEWORK
+ DefaultRequestVersion = remoteServer.HttpVersion
+#endif
+ };
}
private sealed class VersionCheckerHttpHandler : DelegatingHandler
public HttpProtocolTests(ITestOutputHelper output) : base(output) { }
- [Fact]
+ [ConditionalFact]
public async Task GetAsync_RequestVersion10_Success()
{
+#if WINHTTPHANDLER_TEST
+ if (UseVersion > HttpVersion.Version11)
+ {
+ throw new SkipTestException($"Test doesn't support {UseVersion} protocol.");
+ }
+#endif
await LoopbackServer.CreateServerAsync(async (server, url) =>
{
using (HttpClient client = CreateHttpClient())
await LoopbackServer.CreateClientAndServerAsync(async uri =>
{
using (HttpMessageInvoker client = new HttpMessageInvoker(CreateHttpClientHandler()))
- using (HttpResponseMessage resp = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, uri) { Version = UseVersion }, CancellationToken.None))
+ using (HttpResponseMessage resp = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, uri) { Version = base.UseVersion }, CancellationToken.None))
using (Stream respStream = await resp.Content.ReadAsStreamAsync())
{
var actualData = new MemoryStream();
{
byte[] buffer = new byte[4096];
int bytesRead;
+#if !NETFRAMEWORK
while ((bytesRead = await respStream.ReadAsync(buffer)) > 0)
+#else
+ while ((bytesRead = await respStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
+#endif
{
actualData.Write(buffer, 0, bytesRead);
}
{
UseSsl = false;
SslProtocols =
-#if !NETSTANDARD2_0
+#if !NETSTANDARD2_0 && !NETFRAMEWORK
SslProtocols.Tls13 |
#endif
SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
}
return readLength;
+#elif NETFRAMEWORK
+ var tmpBuffer = new byte[buffer.Length];
+ int readBytes = await _stream.ReadAsync(tmpBuffer, offset, size).ConfigureAwait(false);
+ tmpBuffer.CopyTo(buffer);
+ return readBytes;
#else
return await _stream.ReadAsync(buffer.Slice(offset, size)).ConfigureAwait(false);
#endif
public PostScenarioTest(ITestOutputHelper output) : base(output) { }
+#if !NETFRAMEWORK
[OuterLoop("Uses external servers")]
[Theory, MemberData(nameof(RemoteServersMemberData))]
public async Task PostRewindableStreamContentMultipleTimes_StreamContentFullySent(Configuration.Http.RemoteServer remoteServer)
}
}
}
+#endif
[OuterLoop("Uses external servers")]
[Theory, MemberData(nameof(RemoteServersMemberData))]
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System.Linq;
using System.Numerics;
using System.Text;
}
(int varIntLength, int stringLength) = DecodeInteger(buffer, prefixMask);
- string value = Encoding.ASCII.GetString(buffer.Slice(varIntLength, stringLength));
+#if !NETFRAMEWORK
+ ReadOnlySpan<byte> bytes = buffer.Slice(varIntLength, stringLength);
+#else
+ byte[] bytes = buffer.Slice(varIntLength, stringLength).ToArray();
+#endif
+ string value = Encoding.ASCII.GetString(bytes);
return (varIntLength + stringLength, value);
}
new HttpHeaderData("x-frame-options", "deny"),
new HttpHeaderData("x-frame-options", "sameorigin"),
};
- }
+#if NETFRAMEWORK
+ private static class BitOperations
+ {
+ public static int LeadingZeroCount(byte value)
+ {
+ int count = 0;
+ while ((value & 0b1000_0000) != 0)
+ {
+ count++;
+ value <<= 1;
+ }
+ return count;
+ }
+
+ public static int TrailingZeroCount(int value)
+ {
+ int count = 0;
+ while ((value & 1) != 0)
+ {
+ count++;
+ value >>= 1;
+ }
+ return count;
+ }
+ }
+#endif
+ }
}
case 4:
// Individual calls to Read(Span)
+#if !NETFRAMEWORK
while ((bytesRead = stream.Read(new Span<byte>(buffer))) != 0)
+#else
+ while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0)
+#endif
{
ms.Write(buffer, 0, bytesRead);
}
using (Stream stream = await client.GetStreamAsync(remoteServer.EchoUri))
{
Assert.Equal(0, stream.Read(new byte[1], 0, 0));
+#if !NETFRAMEWORK
Assert.Equal(0, stream.Read(new Span<byte>(new byte[1], 0, 0)));
+#endif
Assert.Equal(0, await stream.ReadAsync(new byte[1], 0, 0));
}
}
}
}
}
-
+#if NETCOREAPP
[Theory]
[InlineData(TransferType.ContentLength, TransferError.ContentLengthTooLarge)]
[InlineData(TransferType.Chunked, TransferError.MissingChunkTerminator)]
await ReadAsStreamHelper(uri);
});
}
+#endif
public enum TransferType
{
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.IO;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace System.Net
+{
+ public static class StreamArrayExtensions
+ {
+ public static ValueTask WriteAsync(this Stream stream, ReadOnlyMemory<byte> memory)
+ {
+ bool isArray = MemoryMarshal.TryGetArray(memory, out ArraySegment<byte> segment);
+ Assert.True(isArray);
+
+ return new ValueTask(stream.WriteAsync(segment.Array, segment.Offset, segment.Count));
+ }
+
+ public static ValueTask WriteAsync(this StreamWriter writer, string text)
+ {
+ return new ValueTask(writer.WriteAsync(text.ToCharArray(), 0, text.Length));
+ }
+
+ public static ValueTask<int> ReadAsync(this Stream stream, ReadOnlyMemory<byte> buffer, CancellationToken cancellationToken)
+ {
+ bool isArray = MemoryMarshal.TryGetArray(buffer, out ArraySegment<byte> segment);
+ Assert.True(isArray);
+
+ return new ValueTask<int>(stream.ReadAsync(segment.Array, segment.Offset, segment.Count, cancellationToken));
+ }
+ }
+}
{
EnsureSessionHandleExists(state);
+ SetEnableHttp2PlusClientCertificate(state.RequestMessage.RequestUri, state.RequestMessage.Version);
+
// Specify an HTTP server.
connectHandle = Interop.WinHttp.WinHttpConnect(
_sessionHandle,
SetRequestHandleRedirectionOptions(state.RequestHandle);
SetRequestHandleCookieOptions(state.RequestHandle);
SetRequestHandleTlsOptions(state.RequestHandle);
- SetRequestHandleClientCertificateOptions(state.RequestHandle, state.RequestMessage.RequestUri);
+ SetRequestHandleClientCertificateOptions(state.RequestHandle, state.RequestMessage.RequestUri, state.RequestMessage.Version);
SetRequestHandleCredentialsOptions(state);
SetRequestHandleBufferingOptions(state.RequestHandle);
SetRequestHandleHttp2Options(state.RequestHandle, state.RequestMessage.Version);
}
}
- private void SetRequestHandleClientCertificateOptions(SafeWinHttpHandle requestHandle, Uri requestUri)
+ private void SetRequestHandleClientCertificateOptions(SafeWinHttpHandle requestHandle, Uri requestUri, Version requestVersion)
{
if (requestUri.Scheme != UriScheme.Https)
{
}
}
+ private void SetEnableHttp2PlusClientCertificate(Uri requestUri, Version requestVersion)
+ {
+ if (requestUri.Scheme != UriScheme.Https || requestVersion != HttpVersion20)
+ {
+ return;
+ }
+
+ // Newer versions of WinHTTP fully support HTTP/2 with TLS client certificates.
+ // But the support must be opted in.
+ uint optionData = Interop.WinHttp.WINHTTP_HTTP2_PLUS_CLIENT_CERT_FLAG;
+ if (Interop.WinHttp.WinHttpSetOption(
+ _sessionHandle,
+ Interop.WinHttp.WINHTTP_OPTION_ENABLE_HTTP2_PLUS_CLIENT_CERT,
+ ref optionData))
+ {
+ if (NetEventSource.IsEnabled) NetEventSource.Info(this, "HTTP/2 with TLS client cert supported");
+ }
+ else
+ {
+ if (NetEventSource.IsEnabled) NetEventSource.Info(this, "HTTP/2 with TLS client cert not supported");
+ }
+ }
+
internal static void SetNoClientCertificate(SafeWinHttpHandle requestHandle)
{
SetWinHttpOption(
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Net.Security;
+using System.Security.Cryptography.X509Certificates;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace System.Net.Http.WinHttpHandlerFunctional.Tests
+{
+ public abstract class BaseCertificateTest
+ {
+ private readonly ITestOutputHelper _output;
+
+ protected readonly ValidationCallbackHistory _validationCallbackHistory;
+
+ public BaseCertificateTest(ITestOutputHelper output)
+ {
+ _output = output;
+ _validationCallbackHistory = new ValidationCallbackHistory();
+ }
+
+ public class ValidationCallbackHistory
+ {
+ public bool ThrowException;
+ public bool ReturnFailure;
+ public bool WasCalled;
+ public SslPolicyErrors SslPolicyErrors;
+ public string CertificateSubject;
+ public X509CertificateCollection CertificateChain;
+ public X509ChainStatus[] ChainStatus;
+
+ public ValidationCallbackHistory()
+ {
+ ThrowException = false;
+ ReturnFailure = false;
+ WasCalled = false;
+ SslPolicyErrors = SslPolicyErrors.None;
+ CertificateSubject = null;
+ CertificateChain = new X509CertificateCollection();
+ ChainStatus = null;
+ }
+ }
+
+ protected bool CustomServerCertificateValidationCallback(
+ HttpRequestMessage sender,
+ X509Certificate2 certificate,
+ X509Chain chain,
+ SslPolicyErrors sslPolicyErrors)
+ {
+ _validationCallbackHistory.WasCalled = true;
+ _validationCallbackHistory.CertificateSubject = certificate.Subject;
+ foreach (var element in chain.ChainElements)
+ {
+ _validationCallbackHistory.CertificateChain.Add(element.Certificate);
+ }
+ _validationCallbackHistory.ChainStatus = chain.ChainStatus;
+ _validationCallbackHistory.SslPolicyErrors = sslPolicyErrors;
+
+ if (_validationCallbackHistory.ThrowException)
+ {
+ throw new CustomException();
+ }
+
+ if (_validationCallbackHistory.ReturnFailure)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ protected void ConfirmValidCertificate(string expectedHostName)
+ {
+ Assert.Equal(SslPolicyErrors.None, _validationCallbackHistory.SslPolicyErrors);
+ Assert.True(_validationCallbackHistory.CertificateChain.Count > 0);
+ _output.WriteLine("Certificate.Subject: {0}", _validationCallbackHistory.CertificateSubject);
+ _output.WriteLine("Expected HostName: {0}", expectedHostName);
+ }
+
+ public class CustomException : Exception
+ {
+ public CustomException()
+ {
+ }
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System.Net.Security;
+using System.Net.Test.Common;
+using System.Security.Cryptography.X509Certificates;
+using System.Threading;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace System.Net.Http.WinHttpHandlerFunctional.Tests
+{
+ public class ClientCertificateTest : BaseCertificateTest
+ {
+ public static bool DowngradeToHTTP1IfClientCertSet => PlatformDetection.WindowsVersion < 2004;
+
+ public ClientCertificateTest(ITestOutputHelper output) : base(output)
+ { }
+
+ [ConditionalFact(typeof(ServerCertificateTest), nameof(DowngradeToHTTP1IfClientCertSet))]
+ public async Task UseClientCertOnHttp2_DowngradedToHttp1MutualAuth_Success()
+ {
+ using X509Certificate2 clientCert = Test.Common.Configuration.Certificates.GetClientCertificate();
+ await LoopbackServer.CreateClientAndServerAsync(
+ async address =>
+ {
+ var handler = new WinHttpHandler();
+ handler.ServerCertificateValidationCallback = CustomServerCertificateValidationCallback;
+ handler.ClientCertificates.Add(clientCert);
+ handler.ClientCertificateOption = ClientCertificateOption.Manual;
+ using (var client = new HttpClient(handler))
+ using (HttpResponseMessage response = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, address) { Version = HttpVersion20.Value }))
+ {
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.True(_validationCallbackHistory.WasCalled);
+ Assert.NotEmpty(_validationCallbackHistory.CertificateChain);
+ Assert.Equal(Test.Common.Configuration.Certificates.GetServerCertificate(), _validationCallbackHistory.CertificateChain[0]);
+ }
+ },
+ async s =>
+ {
+ using (LoopbackServer.Connection connection = await s.EstablishConnectionAsync().ConfigureAwait(false))
+ {
+ SslStream sslStream = connection.Stream as SslStream;
+ Assert.NotNull(sslStream);
+ Assert.True(sslStream.IsMutuallyAuthenticated);
+ Assert.Equal(clientCert, sslStream.RemoteCertificate);
+ await connection.ReadRequestHeaderAndSendResponseAsync(HttpStatusCode.OK);
+ }
+ }, new LoopbackServer.Options { UseSsl = true });
+ }
+
+// Disabling it for full .Net Framework due to a missing ALPN API which leads to a protocol downgrade
+#if !NETFRAMEWORK
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows10Version2004OrGreater))]
+ public async Task UseClientCertOnHttp2_OSSupportsIt_Success()
+ {
+ using X509Certificate2 clientCert = Test.Common.Configuration.Certificates.GetClientCertificate();
+ await Http2LoopbackServer.CreateClientAndServerAsync(
+ async address =>
+ {
+ var handler = new WinHttpHandler();
+ handler.ServerCertificateValidationCallback = CustomServerCertificateValidationCallback;
+ handler.ClientCertificates.Add(clientCert);
+ handler.ClientCertificateOption = ClientCertificateOption.Manual;
+ using (var client = new HttpClient(handler))
+ using (HttpResponseMessage response = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, address) { Version = HttpVersion20.Value }))
+ {
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.True(_validationCallbackHistory.WasCalled);
+ Assert.NotEmpty(_validationCallbackHistory.CertificateChain);
+ Assert.Equal(Test.Common.Configuration.Certificates.GetServerCertificate(), _validationCallbackHistory.CertificateChain[0]);
+ }
+ },
+ async s =>
+ {
+ using (Http2LoopbackConnection connection = await s.EstablishConnectionAsync().ConfigureAwait(false))
+ {
+ SslStream sslStream = connection.Stream as SslStream;
+ Assert.NotNull(sslStream);
+ Assert.True(sslStream.IsMutuallyAuthenticated);
+ Assert.Equal(clientCert, sslStream.RemoteCertificate);
+
+ int streamId = await connection.ReadRequestHeaderAsync();
+ await connection.SendDefaultResponseAsync(streamId);
+ }
+ }, new Http2Options { ClientCertificateRequired = true });
+ }
+#endif
+ [OuterLoop]
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows10Version1607OrGreater))]
+ public async Task UseClientCertOnHttp2_OSSupportsItButCertNotSet_SuccessWithOneWayAuth()
+ {
+ WinHttpHandler handler = new WinHttpHandler();
+ handler.ServerCertificateValidationCallback = CustomServerCertificateValidationCallback;
+ string payload = "Mutual Authentication Test";
+ HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, Test.Common.Configuration.Http.Http2RemoteEchoServer) { Version = HttpVersion20.Value };
+ request.Content = new StringContent(payload);
+ using (var client = new HttpClient(handler))
+ using (HttpResponseMessage response = await client.SendAsync(request))
+ {
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal(HttpVersion20.Value, response.Version);
+ string responsePayload = await response.Content.ReadAsStringAsync();
+ var responseContent = JsonConvert.DeserializeAnonymousType(responsePayload, new { Method = "_", BodyContent = "_", ClientCertificatePresent = "_", ClientCertificate = "_" });
+ Assert.Equal("POST", responseContent.Method);
+ Assert.Equal(payload, responseContent.BodyContent);
+ Assert.Equal("false", responseContent.ClientCertificatePresent);
+ Assert.Null(responseContent.ClientCertificate);
+ Assert.True(_validationCallbackHistory.WasCalled);
+ Assert.NotEmpty(_validationCallbackHistory.CertificateChain);
+ ConfirmValidCertificate("*.azurewebsites.net");
+ };
+ }
+ }
+}
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System.Net.Test.Common;
using System.IO;
namespace System.Net.Http.Functional.Tests
{
protected static bool IsWinHttpHandler => true;
+ protected static bool AllowAllCertificates { get; set; } = true;
+
protected static WinHttpClientHandler CreateHttpClientHandler(Version useVersion = null)
{
useVersion ??= HttpVersion.Version11;
- WinHttpClientHandler handler = new WinHttpClientHandler();
+ WinHttpClientHandler handler = new WinHttpClientHandler(useVersion);
- if (useVersion >= HttpVersion.Version20)
+ if (useVersion >= HttpVersion20.Value && AllowAllCertificates)
{
handler.ServerCertificateCustomValidationCallback = TestHelper.AllowAllCertificates;
}
public PlatformHandler_HttpClientHandler_Authentication_Test(ITestOutputHelper output) : base(output) { }
}
- // Enable this to run HTTP2 tests on platform handler
-#if PLATFORM_HANDLER_HTTP2_TESTS
- public sealed class PlatformHandlerTest_Http2 : HttpClientHandlerTest_Http2
+#if NETCOREAPP
+ [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows10Version1607OrGreater))]
+ public sealed class PlatformHandlerTest_Cookies_Http2 : HttpClientHandlerTest_Cookies
{
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandlerTest_Cookies_Http2(ITestOutputHelper output) : base(output) { }
}
-
- [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.SupportsAlpn))]
- public sealed class PlatformHandlerTest_Cookies_Http2 : HttpClientHandlerTest_Cookies
+
+ public sealed class PlatformHandler_HttpClientHandler_Asynchrony_Http2_Test : HttpClientHandler_Asynchrony_Test
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_HttpClientHandler_Asynchrony_Http2_Test(ITestOutputHelper output) : base(output) { }
+ }
+
+ public sealed class PlatformHandler_HttpProtocol_Http2_Tests : HttpProtocolTests
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_HttpProtocol_Http2_Tests(ITestOutputHelper output) : base(output) { }
+ }
+
+ public sealed class PlatformHandler_HttpProtocolTests_Http2_Dribble : HttpProtocolTests_Dribble
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_HttpProtocolTests_Http2_Dribble(ITestOutputHelper output) : base(output) { }
+ }
+
+ public sealed class PlatformHandler_HttpClient_SelectedSites_Http2_Test : HttpClient_SelectedSites_Test
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_HttpClient_SelectedSites_Http2_Test(ITestOutputHelper output) : base(output) { }
+ }
+
+ public sealed class PlatformHandler_HttpClientEKU_Http2_Test : HttpClientEKUTest
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_HttpClientEKU_Http2_Test(ITestOutputHelper output) : base(output) { }
+ }
+
+ public sealed class PlatformHandler_HttpClientHandler_Decompression_Http2_Tests : HttpClientHandler_Decompression_Test
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_HttpClientHandler_Decompression_Http2_Tests(ITestOutputHelper output) : base(output) { }
+ }
+
+ public sealed class PlatformHandler_HttpClientHandler_DangerousAcceptAllCertificatesValidator_Http2_Test : HttpClientHandler_DangerousAcceptAllCertificatesValidator_Test
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_HttpClientHandler_DangerousAcceptAllCertificatesValidator_Http2_Test(ITestOutputHelper output) : base(output) { }
+ }
+
+ public sealed class PlatformHandler_HttpClientHandler_ClientCertificates_Http2_Test : HttpClientHandler_ClientCertificates_Test
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_HttpClientHandler_ClientCertificates_Http2_Test(ITestOutputHelper output) : base(output) { }
+ }
+
+ public sealed class PlatformHandler_HttpClientHandler_DefaultProxyCredentials_Http2_Test : HttpClientHandler_DefaultProxyCredentials_Test
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_HttpClientHandler_DefaultProxyCredentials_Http2_Test(ITestOutputHelper output) : base(output) { }
+ }
+
+ public sealed class PlatformHandler_HttpClientHandler_MaxConnectionsPerServer_Http2_Test : HttpClientHandler_MaxConnectionsPerServer_Test
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_HttpClientHandler_MaxConnectionsPerServer_Http2_Test(ITestOutputHelper output) : base(output) { }
+ }
+
+ public sealed class PlatformHandler_HttpClientHandler_ServerCertificates_Http2_Test : HttpClientHandler_ServerCertificates_Test
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_HttpClientHandler_ServerCertificates_Http2_Test(ITestOutputHelper output) : base(output) {
+ AllowAllCertificates = false;
+ }
+ }
+
+ public sealed class PlatformHandler_PostScenario_Http2_Test : PostScenarioTest
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_PostScenario_Http2_Test(ITestOutputHelper output) : base(output) { }
+ }
+
+ public sealed class PlatformHandler_HttpClientHandler_SslProtocols_Http2_Test : HttpClientHandler_SslProtocols_Test
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_HttpClientHandler_SslProtocols_Http2_Test(ITestOutputHelper output) : base(output) { }
+ }
+
+ public sealed class PlatformHandler_HttpClientHandler_Proxy_Http2_Test : HttpClientHandler_Proxy_Test
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_HttpClientHandler_Proxy_Http2_Test(ITestOutputHelper output) : base(output) { }
+ }
+
+ public sealed class PlatformHandler_SchSendAuxRecordHttp_Http2_Test : SchSendAuxRecordHttpTest
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_SchSendAuxRecordHttp_Http2_Test(ITestOutputHelper output) : base(output) { }
+ }
+
+ [ConditionalClass(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows10Version1607OrGreater))]
+ public sealed class PlatformHandler_HttpClientHandler_Http2_Test : HttpClientHandlerTest
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_HttpClientHandler_Http2_Test(ITestOutputHelper output) : base(output) { }
+ }
+
+ public sealed class PlatformHandlerTest_AutoRedirect_Http2 : HttpClientHandlerTest_AutoRedirect
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandlerTest_AutoRedirect_Http2(ITestOutputHelper output) : base(output) { }
+ }
+
+ public sealed class PlatformHandler_DefaultCredentials_Http2_Test : DefaultCredentialsTest
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_DefaultCredentials_Http2_Test(ITestOutputHelper output) : base(output) { }
+ }
+
+ public sealed class PlatformHandler_IdnaProtocol_Http2_Tests : IdnaProtocolTests
{
- protected override bool UseHttp2LoopbackServer => true;
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_IdnaProtocol_Http2_Tests(ITestOutputHelper output) : base(output) { }
+ // WinHttp on Win7 does not support IDNA
+ protected override bool SupportsIdna => !PlatformDetection.IsWindows7;
+ }
+
+ public sealed class PlatformHandler_HttpRetryProtocol_Http2_Tests : HttpRetryProtocolTests
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_HttpRetryProtocol_Http2_Tests(ITestOutputHelper output) : base(output) { }
+ }
+
+ public sealed class PlatformHandlerTest_Cookies_Http11_Http2 : HttpClientHandlerTest_Cookies_Http11
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandlerTest_Cookies_Http11_Http2(ITestOutputHelper output) : base(output) { }
+ }
+
+ public sealed class PlatformHandler_HttpClientHandler_MaxResponseHeadersLength_Http2_Test : HttpClientHandler_MaxResponseHeadersLength_Test
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_HttpClientHandler_MaxResponseHeadersLength_Http2_Test(ITestOutputHelper output) : base(output) { }
+ }
+
+ public sealed class PlatformHandler_HttpClientHandler_Cancellation_Http2_Test : HttpClientHandler_Cancellation_Test
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_HttpClientHandler_Cancellation_Http2_Test(ITestOutputHelper output) : base(output) { }
+ }
+
+ public sealed class PlatformHandler_HttpClientHandler_Authentication_Http2_Test : HttpClientHandler_Authentication_Test
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_HttpClientHandler_Authentication_Http2_Test(ITestOutputHelper output) : base(output) { }
}
#endif
+ public sealed class PlatformHandler_ResponseStream_Http2_Test : ResponseStreamTest
+ {
+ protected override Version UseVersion => HttpVersion20.Value;
+
+ public PlatformHandler_ResponseStream_Http2_Test(ITestOutputHelper output) : base(output) { }
+ }
}
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-using System;
using System.ComponentModel;
-using System.Net;
-using System.Net.Http;
using System.Net.Security;
-using System.Net.Test.Common;
-using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
-
using Xunit;
using Xunit.Abstractions;
namespace System.Net.Http.WinHttpHandlerFunctional.Tests
{
- public class ServerCertificateTest
+ public class ServerCertificateTest : BaseCertificateTest
{
- private readonly ITestOutputHelper _output;
- private readonly ValidationCallbackHistory _validationCallbackHistory;
+ public ServerCertificateTest(ITestOutputHelper output) : base(output)
+ { }
- public ServerCertificateTest(ITestOutputHelper output)
- {
- _output = output;
- _validationCallbackHistory = new ValidationCallbackHistory();
- }
+ public static bool DowngradeToHTTP1IfClientCertSet => PlatformDetection.WindowsVersion < 2004;
[OuterLoop]
[Fact]
{
var handler = new WinHttpHandler();
using (var client = new HttpClient(handler))
- using (HttpResponseMessage response = await client.GetAsync(System.Net.Test.Common.Configuration.Http.SecureRemoteEchoServer))
+ using (HttpResponseMessage response = await client.GetAsync(Test.Common.Configuration.Http.SecureRemoteEchoServer))
{
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.False(_validationCallbackHistory.WasCalled);
var handler = new WinHttpHandler();
handler.ServerCertificateValidationCallback = CustomServerCertificateValidationCallback;
using (var client = new HttpClient(handler))
- using (HttpResponseMessage response = await client.GetAsync(System.Net.Test.Common.Configuration.Http.RemoteEchoServer))
+ using (HttpResponseMessage response = await client.GetAsync(Test.Common.Configuration.Http.RemoteEchoServer))
{
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.False(_validationCallbackHistory.WasCalled);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.True(_validationCallbackHistory.WasCalled);
- ConfirmValidCertificate(System.Net.Test.Common.Configuration.Http.Host);
+ ConfirmValidCertificate(Test.Common.Configuration.Http.Host);
}
}
[Fact]
public async Task UseCallback_RedirectandValidCertificate_ExpectedValuesDuringCallback()
{
- Uri uri = System.Net.Test.Common.Configuration.Http.RemoteSecureHttp11Server.RedirectUriForDestinationUri(302, System.Net.Test.Common.Configuration.Http.SecureRemoteEchoServer, 1);
+ Uri uri = Test.Common.Configuration.Http.RemoteSecureHttp11Server.RedirectUriForDestinationUri(302, System.Net.Test.Common.Configuration.Http.SecureRemoteEchoServer, 1);
var handler = new WinHttpHandler();
handler.ServerCertificateValidationCallback = CustomServerCertificateValidationCallback;
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.True(_validationCallbackHistory.WasCalled);
- ConfirmValidCertificate(System.Net.Test.Common.Configuration.Http.Host);
+ ConfirmValidCertificate(Test.Common.Configuration.Http.Host);
}
}
handler.ServerCertificateValidationCallback = CustomServerCertificateValidationCallback;
using (var client = new HttpClient(handler))
{
- var request = new HttpRequestMessage(HttpMethod.Get, System.Net.Test.Common.Configuration.Http.SecureRemoteEchoServer);
+ var request = new HttpRequestMessage(HttpMethod.Get, Test.Common.Configuration.Http.SecureRemoteEchoServer);
_validationCallbackHistory.ReturnFailure = true;
HttpRequestException ex = await Assert.ThrowsAsync<HttpRequestException>(() =>
client.GetAsync(System.Net.Test.Common.Configuration.Http.SecureRemoteEchoServer));
Assert.True(ex.GetBaseException() is CustomException);
}
}
-
- private void ConfirmValidCertificate(string expectedHostName)
- {
- Assert.Equal(SslPolicyErrors.None, _validationCallbackHistory.SslPolicyErrors);
- Assert.True(_validationCallbackHistory.CertificateChain.Count > 0);
- _output.WriteLine("Certificate.Subject: {0}", _validationCallbackHistory.CertificateSubject);
- _output.WriteLine("Expected HostName: {0}", expectedHostName);
- }
-
- private bool CustomServerCertificateValidationCallback(
- HttpRequestMessage sender,
- X509Certificate2 certificate,
- X509Chain chain,
- SslPolicyErrors sslPolicyErrors)
- {
- _validationCallbackHistory.WasCalled = true;
- _validationCallbackHistory.CertificateSubject = certificate.Subject;
- foreach (var element in chain.ChainElements)
- {
- _validationCallbackHistory.CertificateChain.Add(element.Certificate);
- }
- _validationCallbackHistory.ChainStatus = chain.ChainStatus;
- _validationCallbackHistory.SslPolicyErrors = sslPolicyErrors;
-
- if (_validationCallbackHistory.ThrowException)
- {
- throw new CustomException();
- }
-
- if (_validationCallbackHistory.ReturnFailure)
- {
- return false;
- }
-
- return true;
- }
-
- public class CustomException : Exception
- {
- public CustomException()
- {
- }
- }
-
- public class ValidationCallbackHistory
- {
- public bool ThrowException;
- public bool ReturnFailure;
- public bool WasCalled;
- public SslPolicyErrors SslPolicyErrors;
- public string CertificateSubject;
- public X509CertificateCollection CertificateChain;
- public X509ChainStatus[] ChainStatus;
-
- public ValidationCallbackHistory()
- {
- ThrowException = false;
- ReturnFailure = false;
- WasCalled = false;
- SslPolicyErrors = SslPolicyErrors.None;
- CertificateSubject = null;
- CertificateChain = new X509CertificateCollection();
- ChainStatus = null;
- }
- }
}
}
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>$(NetCoreAppCurrent)-Windows_NT;$(NetFrameworkCurrent)-Windows_NT</TargetFrameworks>
<IncludeRemoteExecutor>true</IncludeRemoteExecutor>
<DefineConstants>$(DefineConstants);WINHTTPHANDLER_TEST</DefineConstants>
+ <LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetsWindows)' == 'true' ">
<Compile Include="$(CommonTestPath)System\Net\Configuration.cs">
<Compile Include="$(CommonTestPath)System\Net\Configuration.Http.cs">
<Link>Common\System\Net\Configuration.Http.cs</Link>
</Compile>
+ <Compile Include="BaseCertificateTest.cs" />
<Compile Include="ServerCertificateTest.cs" />
<Compile Include="WinHttpHandlerTest.cs" />
- <Compile Include="XunitTestAssemblyAtrributes.cs" />
- </ItemGroup>
- <ItemGroup Condition=" '$(TargetsWindows)' == 'true' And '$(TargetsNetFx)' != 'true' ">
+ <Compile Include="XunitTestAssemblyAtrributes.cs" />
<Compile Include="$(CommonPath)\System\Net\Http\HttpHandlerDefaults.cs">
<Link>Common\System\Net\Http\HttpHandlerDefaults.cs</Link>
</Compile>
<Compile Include="$(CommonTestPath)System\Net\TestWebProxies.cs">
<Link>Common\System\Net\TestWebProxies.cs</Link>
</Compile>
+ <Compile Include="$(CommonTestPath)System\Net\StreamArrayExtensions.cs">
+ <Link>Common\System\Net\StreamArrayExtensions.cs</Link>
+ </Compile>
<Compile Include="$(CommonTestPath)System\Net\Http\ByteAtATimeContent.cs">
<Link>Common\System\Net\Http\ByteAtATimeContent.cs</Link>
</Compile>
<Compile Include="HttpClientHandlerTestBase.WinHttpHandler.cs" />
<Compile Include="WinHttpClientHandler.cs" />
<Compile Include="PlatformHandlerTest.cs" />
+ <Compile Include="ClientCertificateTest.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Net.TestData" Version="$(SystemNetTestDataVersion)" />
using System.Collections.Generic;
using System.Net.Security;
+using System.Net.Test.Common;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
public class WinHttpClientHandler : WinHttpHandler
{
private bool _useProxy;
+ private readonly Version _requestVersion;
- public WinHttpClientHandler()
+ public WinHttpClientHandler(Version requestVersion)
{
// Adjust defaults to match current .NET Desktop HttpClientHandler (based on HWR stack).
AllowAutoRedirect = true;
ReceiveHeadersTimeout = Timeout.InfiniteTimeSpan;
ReceiveDataTimeout = Timeout.InfiniteTimeSpan;
SendTimeout = Timeout.InfiniteTimeSpan;
+
+ _requestVersion = requestVersion;
}
public virtual bool SupportsAutomaticDecompression => true;
}
}
+ if(_requestVersion >= HttpVersion20.Value)
+ {
+ request.Version = _requestVersion;
+ }
+
return base.SendAsync(request, cancellationToken);
}
}
// See the LICENSE file in the project root for more information.
using System;
-using System.Net;
-using System.Net.Http;
+using System.Collections.Generic;
+using System.Linq;
using System.Net.Test.Common;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
}
}
+ [OuterLoop]
+ [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows10Version1607OrGreater))]
+ public async Task GetAsync_SetCookieContainerMultipleCookies_CookiesSent()
+ {
+ var cookies = new Cookie[]
+ {
+ new Cookie("hello", "world"),
+ new Cookie("foo", "bar"),
+ new Cookie("ABC", "123")
+ };
+
+ WinHttpHandler handler = new WinHttpHandler();
+ var cookieContainer = new CookieContainer();
+
+ foreach (Cookie c in cookies)
+ {
+ cookieContainer.Add(Configuration.Http.Http2RemoteEchoServer, c);
+ }
+
+ handler.CookieContainer = cookieContainer;
+ handler.CookieUsePolicy = CookieUsePolicy.UseSpecifiedCookieContainer;
+ handler.ServerCertificateValidationCallback = (m, cert, chain, err) => true;
+ string payload = "Cookie Test";
+ HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, Configuration.Http.Http2RemoteEchoServer) { Version = HttpVersion20.Value };
+ request.Content = new StringContent(payload);
+ using (var client = new HttpClient(handler))
+ using (HttpResponseMessage response = await client.SendAsync(request))
+ {
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal(HttpVersion20.Value, response.Version);
+ string responsePayload = await response.Content.ReadAsStringAsync();
+ var responseContent = Newtonsoft.Json.JsonConvert
+ .DeserializeAnonymousType(responsePayload, new { Method = "_", BodyContent = "_", Cookies = new Dictionary<string, string>() });
+ Assert.Equal("POST", responseContent.Method);
+ Assert.Equal(payload, responseContent.BodyContent);
+ Assert.Equal(cookies.ToDictionary(c => c.Name, c => c.Value), responseContent.Cookies);
+
+ };
+ }
+
public static bool JsonMessageContainsKeyValue(string message, string key, string value)
{
string pattern = string.Format(@"""{0}"": ""{1}""", key, value);