Skip to content

Move BadHttpRequestException to Http.Abstractions #20339

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

Merged
merged 14 commits into from
Apr 6, 2020

Conversation

cado1982
Copy link
Contributor

Summary of the changes (Less than 80 chars)

  • Replace Kestrel and IIS versions of BadHttpRequestException with shared version in Http Abstractions

Addresses #11208

This is my first contribution so I'm open to any feedback you may have.

@dnfclas
Copy link

dnfclas commented Mar 30, 2020

CLA assistant check
All CLA requirements met.

@Tratcher
Copy link
Member

@halter73 @anurse #15038 proposed making the old BadHttpRequestException derive from the new one to limit the breaking change impact. The servers would continue throwing the old ones for now.

#17804 also proposed making the Reason enum public for more granular error management.

These had both been resolved as duplicates of #11208 but the context didn't get pulled over.

@analogrelay
Copy link
Contributor

I reopened the Reason one, we can do that in a separate PR.

@cado1982
Copy link
Contributor Author

@halter73 Let me recap what I think I need to do. Correct me if any points are wrong.

  • Keep the newly created Microsoft.AspNetCore.Http.BadHttpRequestException but it won't be sealed now
  • Make IIS and Kestrel's BadHttpRequestException derive from Microsoft.AspNetCore.Http.BadHttpRequestException
  • Remove the Obsolete attributes from IIS and Kestrel's old BadHttpRequestException, they'll still be used as they are now
  • Move all of the internal members from Kestrel's old BadHttpRequestException and have them in KestrelBadHttpRequestException instead which will derive from Kestrel's old BadHttpRequestException

I also have a question about AllowedHeader. That is currently only used in the following two places

BadHttpRequestException.Throw(RequestRejectionReason.ConnectMethodRequired);

BadHttpRequestException.Throw(RequestRejectionReason.OptionsMethodRequired);

The property is then used to set the http header here

HttpResponseHeaders.HeaderAllow = ex.AllowedHeader;

Can the response header be set in the place the exceptions are thrown in Http1Connection or is it too early in the request pipeline?

@halter73
Copy link
Member

@cado1982 Looks correct except one thing:

Remove the Obsolete attributes from IIS and Kestrel's old BadHttpRequestException, they'll still be used as they are now

Keep obsoleting IIS's and Kestrel's BadHttpRequestException. You'll probably need add some pragmas to silence warnings about our own internal usage, but we want to encourage customers to catch the base type. Eventually we might remove the obsoleted derived types or make them internal.

cado1982 added 4 commits April 1, 2020 10:35
…s version

- Mark existing BadHttpRequestException in IIS and Kestrel Obsolete
- Add pragmas to ignore internal Obsolete usage
@cado1982
Copy link
Contributor Author

cado1982 commented Apr 1, 2020

@halter73 @anurse @Tratcher I'm not sure about this failing build. Did I break something?

@Tratcher
Copy link
Member

Tratcher commented Apr 1, 2020

@halter73 @anurse @Tratcher I'm not sure about this failing build. Did I break something?

No, looks like a networking issue. I've re-run it.

Copy link
Member

@Tratcher Tratcher left a comment

Choose a reason for hiding this comment

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

Looks pretty good. Most my comments are around reducing the need for pragmas.

@@ -122,7 +122,9 @@ private async Task ReadBody()

if (_consumedBytes > MaxRequestBodySize)
{
#pragma warning disable CS0618 // Type or member is obsolete
BadHttpRequestException.Throw(RequestRejectionReason.RequestBodyTooLarge);
Copy link
Member

Choose a reason for hiding this comment

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

Lets move the static Throw API to a non obsolete internal type so we don't have to use pragma everywhere. I know that's what you were trying to do with the KestrelBadHttpRequestException, but I think we only need to move the static API.

@@ -38,7 +38,9 @@ public static void UnexpectedError(ILogger logger, string className, Exception e
_unexpectedError(logger, className, methodName, ex);
}

#pragma warning disable CS0618 // Type or member is obsolete
public static void ConnectionBadRequest(ILogger logger, string connectionId, BadHttpRequestException ex)
Copy link
Member

Choose a reason for hiding this comment

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

Most of these usages can switch to referencing the base type either inline or with a using statement in the file. That will avoid excess pragmas. The two places we need to directly reference the old types are when it's first created & thrown, as well as tests that check which type was thrown. All of the other catch blocks and fields can use the new base type.

@@ -150,7 +150,9 @@ public void StopProcessingNextRequest()

public void HandleRequestHeadersTimeout()
{
#pragma warning disable CS0618 // Type or member is obsolete
Log.ConnectionBadRequest(ConnectionId, BadHttpRequestException.GetException(RequestRejectionReason.RequestHeadersTimeout));
Copy link
Member

Choose a reason for hiding this comment

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

Ah, GetException should move to a new static type as well.

@@ -90,11 +90,15 @@ public void ParseRequestLineThrowsOnInvalidRequestLine(string requestLine)
var buffer = new ReadOnlySequence<byte>(Encoding.ASCII.GetBytes(requestLine));
var requestHandler = new RequestHandler();

#pragma warning disable CS0618 // Type or member is obsolete
var exception = Assert.Throws<BadHttpRequestException>(() =>
Copy link
Member

Choose a reason for hiding this comment

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

This is a good example of where we want to keep the pragmas so we can test that we're throwing the old exception type.

cado1982 added 2 commits April 2, 2020 11:24
- StatusCode now simply returns the base.StatusCode
- Static API from IIS and Kestrel BadHttpRequestException moved to respective IISBadHttpRequestException and KestrelBadHttpRequestException types
- Most usages of deprecated IIS and Kestrel BadHttpRequestException moved to base type
- Overload added to Microsoft.AspNetCore.Http.BadHttpRequestException to accept inner exception
Copy link
Member

@Tratcher Tratcher left a comment

Choose a reason for hiding this comment

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

Much better.

@@ -52,7 +52,7 @@ protected override Task OnConsumeAsync()
}
}
}
catch (BadHttpRequestException ex)
catch (Microsoft.AspNetCore.Http.BadHttpRequestException ex)
Copy link
Member

Choose a reason for hiding this comment

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

Using statement?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can use a using directive here but it must go inside the namespace again.

private BadHttpRequestException GetInvalidRequestTargetException(Span<byte> target)
=> BadHttpRequestException.GetException(
#pragma warning disable CS0618 // Type or member is obsolete
private Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException GetInvalidRequestTargetException(Span<byte> target)
Copy link
Member

Choose a reason for hiding this comment

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

Base type/using?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Again callers of this method throw the returned exception so I think it needs to be the old obsoleted version?

@Tratcher
Copy link
Member

Tratcher commented Apr 3, 2020

Looks like a test issue in IIS.Tests.MaxRequestBodySizeTests.EveryReadFailsWhenContentLengthHeaderExceedsGlobalLimit:
https://dev.azure.com/dnceng/public/_build/results?buildId=587168&view=ms.vss-test-web.build-test-results-tab&runId=18503624&resultId=119834&paneView=debug

Assert.Equal() Failure

↓ (pos 9)

Expected: HTTP/1.1 413 Payload Too Large

Actual: HTTP/1.1 500 Internal Server E

↑ (pos 9)

I think it's this part:
https://github.com/dotnet/aspnetcore/pull/20339/files#diff-f7fe79bd73501e55cf962d965d1dce28R320-R323

                    requestRejectedEx1 = await Assert.ThrowsAsync<BadHttpRequestException>(
                        async () => await ctx.Request.Body.ReadAsync(buffer, 0, 1));
                    requestRejectedEx2 = await Assert.ThrowsAsync<BadHttpRequestException>(
                        async () => await ctx.Request.Body.ReadAsync(buffer, 0, 1));

ThrowsAsync checks for the exact exception type, not derived types. You can use ThrowsAnyAsync or check for the derived type here.

@cado1982
Copy link
Contributor Author

cado1982 commented Apr 6, 2020

I applied the change you mentioned but I'm struggling to run the IIS tests locally. They run continuously and don't complete (for at least 2 hours). Was that the only one that was broken?

@Tratcher
Copy link
Member

Tratcher commented Apr 6, 2020

Looks like you fixed it. The remaining issue with Node seems to be unrelated, I'll see to that.

@Tratcher Tratcher self-assigned this Apr 6, 2020
var exception = Assert.Throws<BadHttpRequestException>(() => _http1Connection.TakeMessageHeaders(readableBuffer, trailers: false, out _consumed, out _examined));
#pragma warning restore CS0618 // Type or member is obsolete
Copy link
Member

Choose a reason for hiding this comment

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

Nit: we could get rid of a lot of these pragmas in the tests by using Assert.ThrowsAny.

@@ -87,7 +89,7 @@ protected async Task OnConsumeAsyncAwaited()
AdvanceTo(result.Buffer.End);
} while (!result.IsCompleted);
}
catch (BadHttpRequestException ex)
catch (Microsoft.AspNetCore.Http.BadHttpRequestException ex)
Copy link
Member

Choose a reason for hiding this comment

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

Nit: There's already a using that take's care of this, right?

Copy link
Member

Choose a reason for hiding this comment

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

I asked them to keep many of the test pragmas to make sure the right old exception type was being thrown.

Copy link
Member

@halter73 halter73 left a comment

Choose a reason for hiding this comment

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

Some of the test #pragma's could be removed, but nbd.

@Tratcher Tratcher merged commit b463e04 into dotnet:master Apr 6, 2020
@Tratcher
Copy link
Member

Tratcher commented Apr 6, 2020

Thanks @cado1982

@cado1982
Copy link
Contributor Author

cado1982 commented Apr 6, 2020

@halter73 @Tratcher Thanks. I learnt a lot from this.

@cado1982 cado1982 deleted the temp-11208 branch April 6, 2020 22:15
@amcasey amcasey added area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions and removed area-runtime labels Jun 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-networking Includes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants