Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

add support for 100-continue for H/2#36884

Merged
wtgodbe merged 31 commits intodotnet:masterfrom
wfurt:100expect_31312
May 31, 2019
Merged

add support for 100-continue for H/2#36884
wtgodbe merged 31 commits intodotnet:masterfrom
wfurt:100expect_31312

Conversation

@wfurt
Copy link
Copy Markdown
Member

@wfurt wfurt commented Apr 15, 2019

fixes #31312
This changes adds logic to process Expect: 100-continue request header and 10x responses.

@wfurt wfurt requested a review from a team April 15, 2019 16:16
@wfurt wfurt self-assigned this Apr 15, 2019
@davidsh davidsh added this to the 3.0 milestone Apr 15, 2019
@davidsh davidsh requested a review from stephentoub April 15, 2019 16:38
Comment thread src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.cs
Comment thread src/System.Net.Http/tests/FunctionalTests/HttpClientHandlerTest.cs
Comment thread src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs Outdated
Comment thread src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs Outdated
Comment thread src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs Outdated
Comment thread src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs Outdated
@wfurt
Copy link
Copy Markdown
Member Author

wfurt commented Apr 23, 2019

all tests are finally passing.

Comment thread src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs Outdated
Comment thread src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs Outdated
Comment thread src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs Outdated
Comment thread src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs Outdated
Comment thread src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs Outdated
Comment thread src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs Outdated
// Sending request body finished before getting headers.
Task t = bodyTask;
bodyTask = null;
await t.ConfigureAwait(false);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If this throws, what happens to potential failures in responseHeadersTask?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We will call http2Stream.Dispose() bellow and that should make it fail. I'm not sure if you want to propagate the error or if you think we need to so something about it. Right now, I think we will simply throw first failure to caller.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not sure if you want to propagate the error or if you think we need to so something about it.

If the body task fails, we don't need to also somehow propagate the failure in the responseHeadersTask, but that Task is almost certainly going to also fail with an exception, and if we don't do anything with it, that'll cause TaskScheduler.UnobservedTaskException to be raised. It'd be better to observe the exception in a continuation and log it... that continuation can be hooked up only in the case in a catch around the await, such that we only pay for it if we're failing anyway.

Comment thread src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs Outdated
Comment thread src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs Outdated
}
else
{
if (bodyTask.IsCompleted)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If we get here, the responseHeadersTask completed, right? We're not awaiting it to propagate any exceptions that may have occurred.

Seems like this logic could be restructured a little, e.g.

if (bodyTask == await Task.WhenAny(bodyTask, responseHeadersTask).ConfigureAwait(false) ||
    bodyTask.IsCompleted)
{
    // The sending of the request body completed before receiving all of the request headers.
    Task t = bodyTask;
    bodyTask = null;
    try
    {
        await t.ConfigureAwait(false);
    }
    catch
    {
        LogExceptionAsync(this, responseHeadersTask, "Response headers"); // ContinueWith logic factored into a helper
        throw;
    }

    await responseHeadersTask.ConfigureAwait(false);
}
else
{
    // We received the response headers but the request body hasn't yet finished.
    bodyTask.LogExceptionAsync(this, responseHeadersTask, "Request body");
}

That said, I'm not clear on the else case above where the request headers have completed but the response body hasn't. If the response body subsequently fails and we're still handling the response body, wouldn't we want to propagate any failure there out via the response body handling? If that's already completed as well, we're out of luck and logging is the best we can do, but if it hasn't yet completed, seems we'd want to take the opportunity to forward the error out?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

we don't have problem with ProcessIncomingFramesAsync() because we catch all exceptions, right? If so, can we do something similar and use _abortException?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

BTW we have few places where we have "ignored" task. Do we have same issue there?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

BTW we have few places where we have "ignored" task. Do we have same issue there?

If exceptions can go unhandled in their implementations, then yes.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

we don't have problem with ProcessIncomingFramesAsync() because we catch all exceptions, right?

Right.

If so, can we do something similar and use _abortException?

Quite possibly.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I updated the code. I did look at what is happening with SendRequestBodyAsync(). If wee get IO exception or if server sends something we do not like (include RST and GOAWAY) we will store that in stream._abortException and we will throw if we still processing response.
I could explore that more as followup (so as ignored tasks) and we could possibly save all exceptions from SendRequestBodyAsync(). We could also log everything in there to simplify the ContinueWith()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I could explore that more as followup (so as ignored tasks) and we could possibly save all exceptions from SendRequestBodyAsync()

Yes, let's please follow-up. There are two concerns:

  1. That if an error occurs from sending we don't lose the exception if at all possible. That means best case propagating it to the response and worst case logging it.
  2. That we don't end up with Faulted Tasks that never have their Exceptions observed, which will cause noise for anyone listening to TaskScheduler.UnobservedTaskExceptions.

_ = _connection.SendRstStreamAsync(_streamId, Http2ProtocolErrorCode.Cancel);

// if we decided abandon sending request and we get ObjectDisposed as result of it, just eat exception.
if (_shouldSendRequestBody || (!(e is ObjectDisposedException) && !(e.InnerException is ObjectDisposedException)))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

How do we know that the ObjectDisposedExceptions we're eating here are being of abandoning sending a request?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

_shouldSendRequestBody be set to false when we get deny (300+) response code. Originally that was done only for 100Continue but it got changed. Assuming that sending more body will not change server's decision

@wfurt
Copy link
Copy Markdown
Member Author

wfurt commented May 31, 2019

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 2 pipeline(s), but failed to run 2 pipeline(s).

@safern safern closed this May 31, 2019
@safern safern reopened this May 31, 2019
@safern safern closed this May 31, 2019
@safern safern reopened this May 31, 2019
@safern safern closed this May 31, 2019
@safern safern reopened this May 31, 2019
@wfurt
Copy link
Copy Markdown
Member Author

wfurt commented May 31, 2019

I executed streaming gRPC tests (and few others as well) and it looks OK.

@wtgodbe
Copy link
Copy Markdown
Member

wtgodbe commented May 31, 2019

The test timeout was bogus, the tests completed but never reported back to AzDO. Second or third time I've seen this today (#38099 (comment)). We should be good to merge this - @wfurt any last concerns?

@stephentoub
Copy link
Copy Markdown
Member

Let's go ahead and get this in and we can address additional clean-up / fix-up subsequently.

@wtgodbe wtgodbe merged commit b4fa200 into dotnet:master May 31, 2019
wtgodbe pushed a commit to dotnet-maestro-bot/corefx that referenced this pull request May 31, 2019
* expect100

* updates

* remove dead code

* fix tests

* use configured value for allowExpect100ToContinue timer

* feedback from review

* dispose expect100Timer

* feedback from review

* feedback from review

* feedback from review

* small updates to sync up with master changes

* add concurent send/recive and more tests

* fix netfx

* feedback from review

* feedback from review

* feedback from review

* feedback from review

* feedback from review

* kick ci
@geoffkizer
Copy link
Copy Markdown

@stephentoub @wfurt is there an issue for the "additional cleanup" needed here?

@wfurt
Copy link
Copy Markdown
Member Author

wfurt commented Jun 4, 2019

#36855 is still open @geoffkizer and I plan to use it to close raised issues. I just posed #38226 to do what I discussed with @stephentoub

picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
* expect100

* updates

* remove dead code

* fix tests

* use configured value for allowExpect100ToContinue timer

* feedback from review

* dispose expect100Timer

* feedback from review

* feedback from review

* feedback from review

* small updates to sync up with master changes

* add concurent send/recive and more tests

* fix netfx

* feedback from review

* feedback from review

* feedback from review

* feedback from review

* feedback from review

* kick ci


Commit migrated from dotnet/corefx@b4fa200
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HTTP2: Expect: 100-Continue support and early response handling

8 participants