Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Throw exception with correct QuicError for ConnectionTimeout #73227

Conversation

rzikm
Copy link
Member

@rzikm rzikm commented Aug 2, 2022

Fixes #73141.

Looks like I missed one QuicError member when mapping MsQuic statuses to exceptions. Now they are all correctly mapped.

@ghost
Copy link

ghost commented Aug 2, 2022

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

Issue Details

Fixes #73141.

Looks like I missed one QuicError member when mapping MsQuic statuses to exceptions. Now they are all correctly mapped.

Author: rzikm
Assignees: -
Labels:

area-System.Net.Quic

Milestone: -

@rzikm rzikm requested a review from a team August 2, 2022 11:47
@@ -68,6 +68,7 @@ static Exception GetExceptionInternal(int status, string? message)
if (status == QUIC_STATUS_ADDRESS_IN_USE) return new QuicException(QuicError.AddressInUse, null, SR.net_quic_address_in_use);
if (status == QUIC_STATUS_UNREACHABLE) return new QuicException(QuicError.HostUnreachable, null, SR.net_quic_host_unreachable);
if (status == QUIC_STATUS_CONNECTION_REFUSED) return new QuicException(QuicError.ConnectionRefused, null, SR.net_quic_connection_refused);
if (status == QUIC_STATUS_CONNECTION_TIMEOUT) return new QuicException(QuicError.ConnectionTimeout, null, SR.net_quic_timeout);
Copy link
Member

Choose a reason for hiding this comment

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

This change looks fine, but subsequently I think this method would be cleaner if implemented as a switch.

Copy link
Member Author

@rzikm rzikm Aug 2, 2022

Choose a reason for hiding this comment

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

the QUIC_STATUS_* members are not compile-time constants, unfortunately, they are get-only properties with if-else per platform (they come from autogenerated .cs files from MsQuic)

Copy link
Member

@stephentoub stephentoub Aug 2, 2022

Choose a reason for hiding this comment

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

they are get-only properties with if-else per platform (they come from autogenerated .cs files from MsQuic)

😮

Copy link
Member

Choose a reason for hiding this comment

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

Do we know why msquic defines different values for these constants per platform? That sounds like a very leaky abstraction.

Copy link
Member Author

@rzikm rzikm Aug 2, 2022

Choose a reason for hiding this comment

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

It tries to conform to the usual platform error codes: HRESULTs on Windows, ERRNOs on Unixes

Copy link
Member

Choose a reason for hiding this comment

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

That's misguided from my perspective, but ok.

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

I think it's a design flaw that the library is surfacing platform-specific details like this... it doesn't need to in a case like this, and is just pushing the pain upwards to consumers of the library.

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it's a design flaw that the library is surfacing platform-specific details like this... it doesn't need to in a case like this, and is just pushing the pain upwards to consumers of the library.

I am not thrilled about it either, but at least now they give us autogeneated stubs which contain the right numbers so we can be reasonably sure that we did not typo it.

Copy link
Member

@stephentoub stephentoub left a comment

Choose a reason for hiding this comment

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

Are we missing a test?

@rzikm
Copy link
Member Author

rzikm commented Aug 2, 2022

Are we missing a test?

I am not sure how to test this, this gets thrown when MsQuic fails to receive an ACK for some time and decides that the connection is dead.

@wfurt
Copy link
Member

wfurt commented Aug 2, 2022

Are we missing a test?

I am not sure how to test this, this gets thrown when MsQuic fails to receive an ACK for some time and decides that the connection is dead.

Is that only on packet loss? I would expect that one can send idle timeout and it would surface if there is no activity.
We could probably create OuterLoop test that waits long enough....

@rzikm
Copy link
Member Author

rzikm commented Aug 3, 2022

Is that only on packet loss?

As far as I can tell from the MsQuic source, yes, it is when we expect an ACK from a peer and it does not arrive within some time window (16s by default).

It could also apply when attempting a new connection and the receiver is not replying with anything (not even ICMP refusal).

I would expect that one can send idle timeout and it would surface if there is no activity.
We could probably create OuterLoop test that waits long enough....

We already have tests for IdleTimeout. Testing for ConnectionTimeout would require something like an additional pair of UDP sockets and us manually relaying datagrams between them (and stopping at some point to trick MsQuic into thinking that the network link was terminated).

@rzikm
Copy link
Member Author

rzikm commented Aug 4, 2022

I tried to create a test for connection timeout when establishing a new connection:

        [Fact]
        // [OuterLoop("Can take a long time")]
        public async Task ConnectAsync_ServerNotResponding_ThrowsConnectTimeoutException()
        {
            // Open a socket to make the target host reachable
            using Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            s.Bind(new IPEndPoint(IPAddress.Loopback, 0));


            await AssertThrowsQuicExceptionAsync(QuicError.ConnectionTimeout, async () =>
                await CreateQuicConnection((IPEndPoint)s.LocalEndPoint));
        }

But this fails with ConnectionIdle because during handshake, there is a smaller timeout (10s) for connection going idle. This value is configurable, but we don't expose that API yet (the API is proposed in #72984). So we don't have a way to test for this at the moment without significant investment.

@rzikm
Copy link
Member Author

rzikm commented Aug 4, 2022

The test failure is unrelated

@rzikm rzikm merged commit d76428b into dotnet:main Aug 4, 2022
@karelz karelz added this to the 7.0.0 milestone Aug 7, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Sep 6, 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.

QUIC: Connection timeout has QuicError.InternalError
5 participants