Skip to content

Conversation

@rzikm
Copy link
Member

@rzikm rzikm commented May 11, 2022

Fixes #67442, #68958.

This PR builds latest MsQuic inside the http stress docker image.

It also adds special case for listening on IPv6Any to accept connections from all IP addresses (i.e. including IPv4), which does not happen by default (see microsoft/msquic#2704).

@ghost ghost added the area-System.Net.Http label May 11, 2022
@ghost ghost assigned rzikm May 11, 2022
@ghost
Copy link

ghost commented May 11, 2022

Tagging subscribers to this area: @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

Issue Details

Contributes to #67442.

This PR builds latest MsQuic inside the http stress docker image.

It also adds a (temporary) workaround for microsoft/msquic#2704 to unblock running H3 stress.

Author: rzikm
Assignees: -
Labels:

area-System.Net.Http

Milestone: -

@rzikm
Copy link
Member Author

rzikm commented May 11, 2022

cc: @ManickaP

@rzikm rzikm requested a review from ManickaP May 11, 2022 12:02
@ManickaP
Copy link
Member

/azp run runtime-libraries stress-http

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@rzikm rzikm force-pushed the 67442-H3-stress-not-running branch from a34a91e to 44ca4ad Compare May 12, 2022 13:08
@rzikm
Copy link
Member Author

rzikm commented May 12, 2022

/azp run runtime-libraries stress-http

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@rzikm
Copy link
Member Author

rzikm commented May 12, 2022

The stress CI leg is failing because we run out of space when building the docker image, I filed dotnet/arcade#9349

@rzikm
Copy link
Member Author

rzikm commented May 13, 2022

/azp run runtime-libraries stress-http

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@rzikm
Copy link
Member Author

rzikm commented May 13, 2022

/azp run runtime-libraries stress-http

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

Copy link
Member

@ManickaP ManickaP left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM in general.
Some more comments that are not necessarily prereqs for this PR:

return MsQuicParameterHelpers.GetIPEndPointParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LISTENER_LOCAL_ADDRESS);
QuicAddr listenAddr = MsQuicParameterHelpers.GetQuicAddrParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LISTENER_LOCAL_ADDRESS);
int port = BinaryPrimitives.ReadUInt16BigEndian(MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref listenAddr.Ipv4.sin_port, 1)));
return new IPEndPoint(listenEndPoint.Address, port);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you sure that we always gonna get back the exact same listening address as we passed in?

Copy link
Member Author

@rzikm rzikm May 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have checked MsQuic source code, and they only change the port (if it was 0)

}

// return the actual bound address, including a port. Since the address family may be Unspecified,
// we cannot use GetIPEndPointParam. We have to manually read the port from the raw QuicAddr structure.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could pass in optional param ipEndPoint.AddressFamily and in case msquic returns QUIC_ADDRESS_FAMILY_UNSPEC we would used it, wdyt?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mean add optional parameter to GetIPEndPointParam? that seems to work. Thanks.

pool:
name: NetCore1ESPool-Public
demands: ImageOverride -equals Build.Ubuntu.1804.Amd64.Open
demands: ImageOverride -equals 1es-ubuntu-1804-open
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this solving the issue with not enough space? Or why this change?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it was suggested to me in dotnet/arcade#9349

@rzikm
Copy link
Member Author

rzikm commented May 18, 2022

I ran this test:
https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicListenerTests.cs#L31
But I changed IPv6Loopback -> IPv6Any and it got stuck somewhere in the middle of a handshake. I didn't investigate further.

The test tries to connect to listener.ListenEndPoint, so if you didn't change that, then you tried to connect to IPv6Any, which dos not work.

what if you use IPv4.Any and client tries IPv6 address? How do sockets behave?

Even if you use DualMode sockets, then you can't connect via IPv6 if server listens on IPv4Any

@ManickaP
Copy link
Member

I ran this test:
https://github.com/dotnet/runtime/blob/main/src/libraries/System.Net.Quic/tests/FunctionalTests/QuicListenerTests.cs#L31
But I changed IPv6Loopback -> IPv6Any and it got stuck somewhere in the middle of a handshake. I didn't investigate further.

The test tries to connect to listener.ListenEndPoint, so if you didn't change that, then you tried to connect to IPv6Any, which dos not work.

Works with sockets though, I just tested it. It'll connect to IPv6 loopback if you pass IPv6Any to client connect, the same for IPv4Any.

@rzikm
Copy link
Member Author

rzikm commented May 18, 2022

It does not seem to work for me

using System.Net.Sockets;
using System.Net;

int port = 5004;

Socket listener = new Socket(SocketType.Stream, ProtocolType.Tcp);
listener.DualMode = true;
listener.Bind(new IPEndPoint(IPAddress.IPv6Any, port));
listener.Listen();
Console.WriteLine($"Listening on {listener.LocalEndPoint}");

Console.WriteLine($"IsDualMode: {listener.DualMode}");
Console.WriteLine($"IPV6_V6Only: {listener.GetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only)}");

using Socket clientIpv6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
Console.WriteLine($"clientIpv6.DualMode == {clientIpv6.DualMode}");
await clientIpv6.ConnectAsync(new IPEndPoint(IPAddress.IPv6Any, port));

Console.WriteLine($"IsDualMode: {listener.DualMode}");
Console.WriteLine($"IPV6_V6Only: {listener.GetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only)}");
Listening on [::]:5004
IsDualMode: True
IPV6_V6Only: 0
clientIpv6.DualMode == False
Unhandled exception. System.Net.Sockets.SocketException (10049): The requested address is not valid in its context.
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken)
   at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.System.Threading.Tasks.Sources.IValueTaskSource.GetResult(Int16 token)
   at System.Threading.Tasks.ValueTask.ValueTaskSourceAsTask.<>c.<.cctor>b__4_0(Object state)
--- End of stack trace from previous location ---
   at Program.<Main>$(String[] args) in C:\source\DotnetSandbox\Program.cs:line 22
   at Program.<Main>(String[] args)

@ManickaP
Copy link
Member

Works for me:

class Program
{
    static async Task Main(string[] args)
    {
        await Task.WhenAll(RunServer(), RunClient());
    }

    static readonly TaskCompletionSource<IPEndPoint> serverEndpoint = new TaskCompletionSource<IPEndPoint>();

    static async Task RunClient()
    {
        var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
        socket.NoDelay = true;
        var endpoint = await serverEndpoint.Task;
        Console.WriteLine("Client connecting to: " + endpoint);
        socket.Connect(endpoint);
        Console.WriteLine("Client connected to: " + socket.RemoteEndPoint);
        var stream = new NetworkStream(socket, ownsSocket: true);
        await stream.WriteAsync(UTF8Encoding.UTF8.GetBytes("Ahoj"));
        var buffer = new byte[100];
        int readBytes;
        while ((readBytes = await stream.ReadAsync(buffer)) > 0)
        {
            Console.WriteLine("Client:" + UTF8Encoding.UTF8.GetString(buffer, 0, readBytes));
        }
        Console.WriteLine("Client:" + readBytes);
    }
    static async Task RunServer()
    {
        var listenSocket = new Socket(SocketType.Stream, ProtocolType.Tcp);
        listenSocket.Bind(new IPEndPoint(IPAddress.IPv6Any, 0));
        listenSocket.Listen();
        serverEndpoint.SetResult(listenSocket.LocalEndPoint as IPEndPoint);
        Console.WriteLine("Server listening on: " + listenSocket.LocalEndPoint);
        var socket = await listenSocket.AcceptAsync().ConfigureAwait(false);
        var stream = new NetworkStream(socket, ownsSocket: true);
        var buffer = new byte[100];
        int readBytes = await stream.ReadAsync(buffer);
        Console.WriteLine("Server:" + UTF8Encoding.UTF8.GetString(buffer, 0, readBytes));
        stream.Dispose();
    }
}
Server listening on: [::]:38987
Client connecting to: [::]:38987
Client connected to: [::1]:38987
Server:Ahoj
Client:0

@ManickaP
Copy link
Member

ManickaP commented May 18, 2022

Your code runs for me as well:

Listening on [::]:5004
IsDualMode: True
IPV6_V6Only: 0
clientIpv6.DualMode == False
IsDualMode: True
IPV6_V6Only: 0

This smells like platform inconsistency with sockets. And the question is what behavior is correct.
cc @antonfirsov

@rzikm
Copy link
Member Author

rzikm commented May 18, 2022

That would be probably a difference between Windows and Linux networking stack. It runs for me in WSL

Copy link
Member

@ManickaP ManickaP left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, we can finish the discussion about sockets offline 😄 Thanks!

@rzikm rzikm merged commit 8d4a724 into dotnet:main May 20, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Jun 19, 2022
@karelz karelz added this to the 7.0.0 milestone Jul 19, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[H/3] stress not running

4 participants