Skip to content

Remove connection-specific headeres for HTTP/2 and HTTP/3 #24543

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
7 commits merged into from
Nov 25, 2020

Conversation

Kahbazi
Copy link
Member

@Kahbazi Kahbazi commented Aug 3, 2020

Fixes #24502

@ghost ghost added the area-servers label Aug 3, 2020
@Kahbazi Kahbazi marked this pull request as ready for review August 3, 2020 21:06
@Pilchie
Copy link
Member

Pilchie commented Aug 13, 2020

I think this conflicts with the changes @JamesNK just made.

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.

Also, please rebase.

@Pilchie Pilchie added the community-contribution Indicates that the PR has been added by a community member label Aug 24, 2020
@BrennanConroy
Copy link
Member

@Kahbazi Are you still interested in proceeding with this PR?

@Kahbazi Kahbazi force-pushed the kahbazi/HTTP2Headers branch from af5bff1 to d6752d6 Compare October 8, 2020 10:22
@Kahbazi
Copy link
Member Author

Kahbazi commented Oct 8, 2020

@BrennanConroy Sorry for the delay. I rebased and updated the PR.

@JamesNK
Copy link
Member

JamesNK commented Oct 13, 2020

Spec - https://quicwg.org/base-drafts/draft-ietf-quic-http.html#name-field-formatting-and-compre

This means that an intermediary transforming an HTTP/1.x message to HTTP/3 will need to remove any fields nominated by the Connection field, along with the Connection field itself. Such intermediaries SHOULD also remove other connection-specific fields, such as Keep-Alive, Proxy-Connection, Transfer-Encoding, and Upgrade, even if they are not nominated by the Connection field.

Kestrel isn't an intermediary and it doesn't transform request protocols. It could be used as part of an intermediary, like YARP, but in that case it is YARPs job to remove these headers when transforming a HTTP/1.1 call to 2 or 3.

I don't see why this code should be part of Kestrel.

@Tratcher
Copy link
Member

I don't see why this code should be part of Kestrel.

A) It's kestrel's job to enforce protocol correctness, it shouldn't allow outgoing or incoming protocol violations. We have similar checks for incoming requests:

if (IsConnectionSpecificHeaderField(name, value))
{
throw new Http2ConnectionErrorException(CoreStrings.Http2ErrorConnectionSpecificHeaderField, Http2ErrorCode.PROTOCOL_ERROR);
}

B) For consistency with IIS & Http.Sys which I expect removes these silently from responses.

@davidfowl
Copy link
Member

davidfowl commented Oct 13, 2020

B) For consistency with IIS & Http.Sys which I expect removes these silently from responses.

I don't think this is a good enough reason to use all of the time. Kestrel is the only server where we can control this level of behavior and that's an advantage. That said, I think we saw other servers beyond our own NOT have this behavior (go, node) so it's worth considering.

@Tratcher Tratcher force-pushed the kahbazi/HTTP2Headers branch from d6752d6 to cad4c5f Compare November 20, 2020 01:10
@Tratcher
Copy link
Member

Rebased. I spoke with @Kahbazi and volunteered to finish this PR for them.

I confirmed the behavior in Http.Sys where these headers are all silently removed. This approach allows the server to maintain both protocol compliance and app compatibility. These headers may be added by a library that is not in the developers control.

For Kestrel I've added a generic log statement that "A response header has been removed because it was invalid for the current protocol." . This is logged at the debug level because the issue is something the app can ignore unless they want to learn more about the protocol details. I've limited it to one log per response to limit the complexity and noise.

@Tratcher Tratcher dismissed their stale review November 20, 2020 01:18

Addressed

@JamesNK
Copy link
Member

JamesNK commented Nov 20, 2020

Currently the PR's behavior is checking for certain set headers when using HTTP/2+, and then clearing them.

From a performance perspective, wouldn't it be better to make setting these headers noop in HTTP/2? That avoids checking whether these headers are set in every HTTP/2+ request.

@Tratcher
Copy link
Member

The overhead should be pretty minimal, a few bitwise checks, but you've given me an idea on how to reduce that further. I'll try it and report back.

@Tratcher
Copy link
Member

From a performance perspective, wouldn't it be better to make setting these headers noop in HTTP/2? That avoids checking whether these headers are set in every HTTP/2+ request.

I've updated this to consolidate the operations for these headers and reduce overhead.

The headers collection is quite performant already, the relevant code is only setting two fields. You wouldn't be able to avoid the normal dictionary-like operations for Headers.Add to find those fields.

I'd rather keep the logic here in CreateResponseHeaders. The headers collections don't currently know about things like request protocols, the protocol specific logic for response headers is all consolidated in CreateResponseHeaders right now. I think that makes it easier to maintain.

@halter73 any thoughts on this?

@halter73
Copy link
Member

halter73 commented Nov 21, 2020

I do like keeping the main logic in CreateResponseHeaders. I think the current implementations of HasInvalidH2H3Headers and ClearInvalidH2H3Headers() are plenty efficient. You could even argue it creates less branching logic on the set-headers path compared to proactively avoiding setting them. We could microbenchmark, but I'm not too concerned about optimizing the case where invalid headers are being set as long as it isn't terrible.

@Tratcher Tratcher requested a review from halter73 November 24, 2020 23:18
@Tratcher Tratcher added this to the 6.0-preview1 milestone Nov 24, 2020
@@ -65,6 +65,7 @@ public static class HeaderNames
public static readonly string Pragma = "Pragma";
public static readonly string ProxyAuthenticate = "Proxy-Authenticate";
public static readonly string ProxyAuthorization = "Proxy-Authorization";
public static readonly string ProxyConnection = "Proxy-Connection";
Copy link
Member

Choose a reason for hiding this comment

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

Ping @dotnet/aspnet-api-review. We should probably send an email as a heads up.

@davidfowl davidfowl added the api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews label Nov 25, 2020
@ghost
Copy link

ghost commented Nov 25, 2020

Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:

  • The PR contains changes to the reference-assembly that describe the API change. Or, you have included a snippet of reference-assembly-style code that illustrates the API change.
  • The PR describes the impact to users, both positive (useful new APIs) and negative (breaking changes).
  • Someone is assigned to "champion" this change in the meeting, and they understand the impact and design of the change.

@@ -117,6 +117,10 @@ internal class KestrelTrace : IKestrelTrace
LoggerMessage.Define<string>(LogLevel.Debug, new EventId(40, nameof(Http2MaxConcurrentStreamsReached)),
@"Connection id ""{ConnectionId}"" reached the maximum number of concurrent HTTP/2 streams allowed.");

private static readonly Action<ILogger, Exception> _invalidResponseHeaderRemoved =
LoggerMessage.Define(LogLevel.Information, new EventId(41, nameof(InvalidResponseHeaderRemoved)),
Copy link
Member

Choose a reason for hiding this comment

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

I still would prefer this is a warning at least for the early previews. If we get feedback that this is too noisy or not helpful, we could lower the severity. I'll approve as-is since I don't feel super strongly about it though.

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
LoggerMessage.Define(LogLevel.Information, new EventId(41, nameof(InvalidResponseHeaderRemoved)),
LoggerMessage.Define(LogLevel.Warning, new EventId(41, nameof(InvalidResponseHeaderRemoved)),

Copy link
Member

Choose a reason for hiding this comment

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

Fair, we'll see what they say.

@@ -117,6 +117,10 @@ internal class KestrelTrace : IKestrelTrace
LoggerMessage.Define<string>(LogLevel.Debug, new EventId(40, nameof(Http2MaxConcurrentStreamsReached)),
@"Connection id ""{ConnectionId}"" reached the maximum number of concurrent HTTP/2 streams allowed.");

private static readonly Action<ILogger, Exception> _invalidResponseHeaderRemoved =
LoggerMessage.Define(LogLevel.Information, new EventId(41, nameof(InvalidResponseHeaderRemoved)),
Copy link
Member

Choose a reason for hiding this comment

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

nit: in the rest of the stack we try to avoid using nameof for the EventId because changing the name is technically a breaking change and it's easier to accidentally change it when renaming a method.

Copy link
Member

@Tratcher Tratcher Nov 25, 2020

Choose a reason for hiding this comment

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

Yeah, not sure why all of the Kestrel ones use nameof. Changing them is out of scope here. I'll file something.
#28159

@ghost
Copy link

ghost commented Nov 25, 2020

Hello @Tratcher!

Because this pull request has the auto-merge label, I will be glad to assist with helping to merge this pull request once all check-in policies pass.

p.s. you can customize the way I help with merging this pull request, such as holding this pull request until a specific person approves. Simply @mention me (@msftbot) and give me an instruction to get started! Learn more here.

@ghost ghost merged commit 77fe36f into dotnet:master Nov 25, 2020
@pranavkm pranavkm removed the api-ready-for-review API is ready for formal API review - https://github.com/dotnet/apireviews label Mar 22, 2021
@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
This pull request was closed.
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 community-contribution Indicates that the PR has been added by a community member
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Remove connection-specific headers for HTTP/2 and HTTP/3
9 participants