Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Commit

Permalink
Add HttpClient test with IPv6 Uris
Browse files Browse the repository at this point in the history
Adds support to the loopback server for binding to IPv6 local addresses, then adds a test that uses both ::1 and a link-local address for the current machine.
  • Loading branch information
stephentoub committed May 11, 2016
1 parent bdcf11d commit f431442
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public async Task SetProtcols_AfterRequest_ThrowsException()
{
await LoopbackServer.CreateServerAsync(async (server, url) =>
{
await WhenAllCompletedOrAnyFailed(
await TestHelper.WhenAllCompletedOrAnyFailed(
LoopbackServer.ReadRequestAndSendResponseAsync(server),
client.GetAsync(url));
});
Expand Down Expand Up @@ -87,7 +87,7 @@ public async Task GetAsync_AllowedSSLVersion_Succeeds(SslProtocols acceptedProto
var options = new LoopbackServer.Options { UseSsl = true, SslProtocols = acceptedProtocol };
await LoopbackServer.CreateServerAsync(async (server, url) =>
{
await WhenAllCompletedOrAnyFailed(
await TestHelper.WhenAllCompletedOrAnyFailed(
LoopbackServer.ReadRequestAndSendResponseAsync(server, options: options),
client.GetAsync(url));
}, options);
Expand All @@ -107,7 +107,7 @@ public async Task GetAsync_AllowedSSLVersion_Succeeds(SslProtocols acceptedProto
var options = new LoopbackServer.Options { UseSsl = true, SslProtocols = acceptedProtocol };
await LoopbackServer.CreateServerAsync(async (server, url) =>
{
await WhenAllCompletedOrAnyFailed(
await TestHelper.WhenAllCompletedOrAnyFailed(
Assert.ThrowsAsync(exceptedServerException, () => LoopbackServer.ReadRequestAndSendResponseAsync(server, options: options)),
Assert.ThrowsAsync<HttpRequestException>(() => client.GetAsync(url)));
}, options);
Expand All @@ -127,7 +127,7 @@ public async Task GetAsync_DisallowTls10_AllowTls11_AllowTls12()
options.SslProtocols = SslProtocols.Tls;
await LoopbackServer.CreateServerAsync(async (server, url) =>
{
await WhenAllCompletedOrAnyFailed(
await TestHelper.WhenAllCompletedOrAnyFailed(
Assert.ThrowsAsync<IOException>(() => LoopbackServer.ReadRequestAndSendResponseAsync(server, options: options)),
Assert.ThrowsAsync<HttpRequestException>(() => client.GetAsync(url)));
}, options);
Expand All @@ -137,7 +137,7 @@ public async Task GetAsync_DisallowTls10_AllowTls11_AllowTls12()
options.SslProtocols = prot;
await LoopbackServer.CreateServerAsync(async (server, url) =>
{
await WhenAllCompletedOrAnyFailed(
await TestHelper.WhenAllCompletedOrAnyFailed(
LoopbackServer.ReadRequestAndSendResponseAsync(server, options: options),
client.GetAsync(url));
}, options);
Expand All @@ -150,24 +150,6 @@ public async Task GetAsync_DisallowTls10_AllowTls11_AllowTls12()
}
}

private static Task WhenAllCompletedOrAnyFailed(params Task[] tasks)
{
var tcs = new TaskCompletionSource<bool>();

int remaining = tasks.Length;
foreach (var task in tasks)
{
task.ContinueWith(t =>
{
if (t.IsFaulted) tcs.SetException(t.Exception.InnerExceptions);
else if (t.IsCanceled) tcs.SetCanceled();
else if (Interlocked.Decrement(ref remaining) == 0) tcs.SetResult(true);
}, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}

return tcs.Task;
}

private static bool BackendSupportsSslConfiguration =>
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ||
(CurlSslVersionDescription()?.StartsWith("OpenSSL") ?? false);
Expand Down
27 changes: 27 additions & 0 deletions src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,33 @@ public async Task SendAsync_SimpleGet_Success(Uri remoteServer)
}
}

[Theory]
[MemberData(nameof(GetAsync_IPv6Uri_Success_MemberData))]
public async Task GetAsync_IPv6Uri_Success(IPAddress address)
{
using (var client = new HttpClient())
{
var options = new LoopbackServer.Options { Address = address };
await LoopbackServer.CreateServerAsync(async (server, url) =>
{
await TestHelper.WhenAllCompletedOrAnyFailed(
LoopbackServer.ReadRequestAndSendResponseAsync(server, options: options),
client.GetAsync(url));
}, options);
}
}

public static IEnumerable<object[]> GetAsync_IPv6Uri_Success_MemberData()
{
yield return new object[] { IPAddress.IPv6Loopback };

IPAddress addr = LoopbackServer.GetIPv6LinkLocalAddress();
if (addr != null)
{
yield return new object[] { addr };
}
}

[Fact]
public async Task SendAsync_MultipleRequestsReusingSameClient_Success()
{
Expand Down
20 changes: 17 additions & 3 deletions src/System.Net.Http/tests/FunctionalTests/LoopbackServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// See the LICENSE file in the project root for more information.

using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using System.Net.Security;
using System.Net.Sockets;
using System.Net.Tests;
Expand All @@ -20,6 +22,7 @@ public class LoopbackServer

public class Options
{
public IPAddress Address { get; set; } = IPAddress.Loopback;
public int ListenBacklog { get; set; } = 1;
public bool UseSsl { get; set; } = false;
public SslProtocols SslProtocols { get; set; } = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
Expand All @@ -36,13 +39,16 @@ public static Task CreateServerAsync(Func<Socket, Uri, Task> funcAsync, out IPEn
options = options ?? new Options();
try
{
var server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
var server = new Socket(options.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

server.Bind(new IPEndPoint(IPAddress.Loopback, 0));
server.Bind(new IPEndPoint(options.Address, 0));
server.Listen(options.ListenBacklog);

localEndPoint = (IPEndPoint)server.LocalEndPoint;
var url = new Uri($"{(options.UseSsl ? "https" : "http")}://{localEndPoint.Address}:{localEndPoint.Port}/");
string host = options.Address.AddressFamily == AddressFamily.InterNetworkV6 ?
$"[{localEndPoint.Address}]" :
localEndPoint.ToString();
var url = new Uri($"{(options.UseSsl ? "https" : "http")}://{host}:{localEndPoint.Port}/");

return funcAsync(server, url).ContinueWith(t =>
{
Expand All @@ -59,6 +65,14 @@ public static Task CreateServerAsync(Func<Socket, Uri, Task> funcAsync, out IPEn

public static string DefaultHttpResponse => $"HTTP/1.1 200 OK\r\nDate: {DateTimeOffset.UtcNow:R}\r\nContent-Length: 0\r\n\r\n";

public static IPAddress GetIPv6LinkLocalAddress() =>
NetworkInterface
.GetAllNetworkInterfaces()
.SelectMany(i => i.GetIPProperties().UnicastAddresses)
.Select(a => a.Address)
.Where(a => a.IsIPv6LinkLocal)
.FirstOrDefault();

public static Task ReadRequestAndSendResponseAsync(Socket server, string response = null, Options options = null)
{
return AcceptSocketAsync(server, async (s, stream, reader, writer) =>
Expand Down
21 changes: 20 additions & 1 deletion src/System.Net.Http/tests/FunctionalTests/TestHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;

using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace System.Net.Http.Functional.Tests
Expand Down Expand Up @@ -79,5 +80,23 @@ public static byte[] ComputeMD5Hash(byte[] data)
return md5.ComputeHash(data);
}
}

public static Task WhenAllCompletedOrAnyFailed(params Task[] tasks)
{
var tcs = new TaskCompletionSource<bool>();

int remaining = tasks.Length;
foreach (var task in tasks)
{
task.ContinueWith(t =>
{
if (t.IsFaulted) tcs.SetException(t.Exception.InnerExceptions);
else if (t.IsCanceled) tcs.SetCanceled();
else if (Interlocked.Decrement(ref remaining) == 0) tcs.SetResult(true);
}, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default);
}

return tcs.Task;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"System.IO": "4.1.0-rc3-24111-00",
"System.IO.FileSystem": "4.0.1-rc3-24111-00",
"System.Linq.Expressions": "4.1.0-rc3-24111-00",
"System.Net.NetworkInformation": "4.1.0-rc3-24111-00",
"System.Net.Primitives": "4.0.11-rc3-24111-00",
"System.Net.Security": "4.0.0-rc3-24111-00",
"System.Net.Sockets": "4.1.0-rc3-24111-00",
Expand Down
1 change: 1 addition & 0 deletions src/System.Net.Http/tests/FunctionalTests/win/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"System.IO": "4.1.0-rc3-24111-00",
"System.IO.FileSystem": "4.0.1-rc3-24111-00",
"System.Linq.Expressions": "4.1.0-rc3-24111-00",
"System.Net.NetworkInformation": "4.1.0-rc3-24111-00",
"System.Net.Primitives": "4.0.11-rc3-24111-00",
"System.Net.Security": "4.0.0-rc3-24111-00",
"System.Net.Sockets": "4.1.0-rc3-24111-00",
Expand Down

0 comments on commit f431442

Please sign in to comment.