Skip to content

Conversation

@rzikm
Copy link
Member

@rzikm rzikm commented Jul 13, 2022

Follow-up on #71432.

Closes #70684.

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

ghost commented Jul 13, 2022

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

Issue Details

Follow-up on #71432.

Closes #70684.

Author: rzikm
Assignees: -
Labels:

area-System.Net.Http

Milestone: -

@rzikm rzikm requested a review from antonfirsov July 13, 2022 13:33
@antonfirsov
Copy link
Contributor

We have plenty of tests for HTTP/2 exercising error scenarios, for example:

public async Task Http2_StreamResetByServerBeforeHeadersSent_RequestFails()
{
using (Http2LoopbackServer server = Http2LoopbackServer.CreateServer())
using (HttpClient client = CreateHttpClient())
{
Task<HttpResponseMessage> sendTask = client.GetAsync(server.Address);
Http2LoopbackConnection connection = await server.EstablishConnectionAsync();
int streamId = await connection.ReadRequestHeaderAsync();
// Send a reset stream frame so that the stream moves to a terminal state.
RstStreamFrame resetStream = new RstStreamFrame(FrameFlags.None, (int)ProtocolErrors.INTERNAL_ERROR, streamId);
await connection.WriteFrameAsync(resetStream);
await AssertProtocolErrorAsync(sendTask, ProtocolErrors.INTERNAL_ERROR);
}
}

Is it possible to cover at least a few HTTP/3 cases to verify that we are throwing this exception (including at least one response stream case)?

Priority = 0b00100000,

ValidBits = 0b00101101
ValidBits = 0b00101101
Copy link
Member

Choose a reason for hiding this comment

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

Was this formatting alignment intentionally removed?

@ManickaP
Copy link
Member

ManickaP commented Jul 13, 2022

Please don't merge this before #71969 unless you're 100% sure it will not create conflicts. I really need to get that PR in main.

@rzikm rzikm requested a review from a team July 14, 2022 11:37
// Our stream was reset.
Exception? abortException = _connection.AbortException;
throw new HttpRequestException(SR.net_http_client_execution_error, abortException ?? ex);
throw new HttpRequestException(SR.net_http_client_execution_error, HttpProtocolException.CreateHttp3StreamException(code));
Copy link
Member

Choose a reason for hiding this comment

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

Is it ok that we are ignoring both _connection.AbortException and ex now?

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 don't think there is any value of including ex in the HttpProtocolException (it will always be the same and the error code is already in HttpProtocolException.

To tell the truth, I am not 100% sure what the right behavior here is. I didn't notice anything in the RFC which would let server close the stream with other error codes than the two handled above. I would consider it protocol violation and tear down the connection, but the RFC does not AFAIK prohibit using any other code, so we should just pass it by.

As for the _connection.AbortException, there is no harm in checking first and prioritizing it if there is some already. This branch should be pretty rare anyway as explained above.

Exception abortException = _connection.Abort(ex);
throw new IOException(SR.net_http_client_execution_error, new HttpRequestException(SR.net_http_client_execution_error, abortException));
case Http3ConnectionException:
case HttpProtocolException:
Copy link
Member

Choose a reason for hiding this comment

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

Judging by CreateHttp3StreamException in HttpProtocolException it seems to me like HttpProtocolException may not always be connection-level. Is that right? This line does not seem as equivalent change then. Or am I missing something?

Copy link
Member Author

Choose a reason for hiding this comment

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

The HttpProtocolException thrown from stream is not on a code path which would affect this. In other words, only connection-level exceptions can be encountered here

Comment on lines 37 to 38
_output.WriteLine(outerEx.InnerException.Message);
HttpProtocolException protocolEx = Assert.IsType<HttpProtocolException>(outerEx.InnerException);
Copy link
Member

Choose a reason for hiding this comment

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

Should it be this instead?

Suggested change
_output.WriteLine(outerEx.InnerException.Message);
HttpProtocolException protocolEx = Assert.IsType<HttpProtocolException>(outerEx.InnerException);
HttpProtocolException protocolEx = Assert.IsType<HttpProtocolException>(outerEx.InnerException);
_output.WriteLine(protocolEx.Message);

Also, if I'm wrong and it's for diagnostics purposes in case it was not a HttpProtocolException, would it be better to include all the info about exceptions, even the top one? Meaning, should we log ToString instead of just Message

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 copied it from HttpClientHandlerTest.Http2 implementation, I can change it

Copy link
Contributor

Choose a reason for hiding this comment

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

This was somewhat accidental in my PR, we don't have to print anything, though it's informative for manual validation of HttpProtocolException's message.

@rzikm rzikm force-pushed the use-protocol-exception branch from d467805 to 9e4324b Compare July 14, 2022 13:50
@rzikm rzikm removed request for lewing and radical July 14, 2022 13:50
@rzikm rzikm requested review from CarnaViire and removed request for MichalStrehovsky, SamMonoRT, lambdageek, marek-safar, thaystg and vargaz July 14, 2022 13:50
await new[] { clientTask, serverTask }.WhenAllOrAnyFailed(20_000);
}


Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change

case QuicException e when (e.QuicError == QuicError.ConnectionAborted):
// Our connection was reset. Start aborting the connection.
Exception abortException = _connection.Abort(ex);
throw new IOException(SR.net_http_client_execution_error, new HttpRequestException(SR.net_http_client_execution_error, abortException));
Copy link
Contributor

@antonfirsov antonfirsov Jul 14, 2022

Choose a reason for hiding this comment

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

I'm wondering why are we throwing IOException here? Aren't these errors supposed to surface as HttpProtocolException from HttpContent's Http3ReadStream (which seems to delegate to this method)?

HttpProtocolException protocolEx = Assert.IsType<HttpProtocolException>(outerEx.InnerException);
_output.WriteLine(protocolEx.Message);
Assert.Equal(errorCode, protocolEx.ErrorCode);
}
Copy link
Contributor

@antonfirsov antonfirsov Jul 14, 2022

Choose a reason for hiding this comment

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

I would add yet another test (or a parameteric variant of existing tests), that validates the exception thrown from the response content stream. See AssertProtocolErrorForIOExceptionAsync in http2 tests (I should have renamed that method ...).

@rzikm rzikm requested a review from antonfirsov July 14, 2022 16:00
Copy link
Contributor

@antonfirsov antonfirsov left a comment

Choose a reason for hiding this comment

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

LGTM, thanks!

@rzikm rzikm merged commit e2e9df7 into dotnet:main Jul 14, 2022
@rzikm
Copy link
Member Author

rzikm commented Jul 14, 2022

/backport to release/7.0-preview7

@github-actions
Copy link
Contributor

Started backporting to release/7.0-preview7: https://github.com/dotnet/runtime/actions/runs/2672573752

@karelz karelz added this to the 7.0.0 milestone Jul 19, 2022
@ghost ghost locked as resolved and limited conversation to collaborators Aug 18, 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.

[API Proposal]: Exposing HTTP/2 and HTTP/3 protocol error codes

6 participants