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

Propagate the authority pseudoheader #33

Merged
merged 3 commits into from
Dec 3, 2024
Merged

Conversation

glbrntt
Copy link
Collaborator

@glbrntt glbrntt commented Nov 29, 2024

Motivation:

We should be sending the ":authority" pseudoheader, somehow this was missed when doing the stream state machine.

The strategy for propagating the authority will be to:

  • Use any override value set on the client transport, otherwise
  • Use the value provided by a name resolver (if any), otherwise
  • Derive a value from the target address.

Modifications:

  • Add client config allowing the authority to be overridden
  • Add authority to the name resolver interface
  • Propagate the authority through to the stream state machine
  • Add a percent encoder, as the authority should be percent encoded
  • Remove hostname from the client TLS config and use the authority instead. This is generally a usability win as clients generally won't have to set this value themselves.

Result:

Authority is set automatically, and can be overridden if necessary.

Motivation:

We should be sending the ":authority" pseudoheader, somehow this was
missed when doing the stream state machine.

The strategy for propagating the authority will be to:
- Use any override value set on the client transport, otherwise
- Use the value provided by a name resolver (if any), otherwise
- Derive a value from the target address.

Modifications:

- Add client config allowing the authority to be overridden
- Add authority to the name resolver interface
- Propagate the authority through to the stream state machine
- Add a percent encoder, as the authority should be percent encoded
- Remove hostname from the client TLS config and use the authority
  instead. This is generally a usability win as clients generally won't
  have to set this value themselves.

Result:

Authority is set automatically, and can be overridden if necessary.
@glbrntt glbrntt added the 🆕 semver/minor Adds new public API. label Nov 29, 2024
return try await self.http2Connector.establishConnection(to: self.address)
return try await self.http2Connector.establishConnection(
to: self.address,
authority: self.authority ?? self.address.sniHostname
Copy link
Collaborator

Choose a reason for hiding this comment

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

Here you use self.authority ?? self.address.sniHostname, but further down the file you use self.authority ?? self.address.authority - is that intentional?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, it's intentional here. The authority here is used as part of the TLS handshake, but valid hostnames for that don't include raw IP addresses, so sniHostname returns nil if address is an IPv4/IPv6 address.

Further down the authority being used is for the :authority pseudo-header.

I'll add some comments explaining this.

@@ -82,6 +86,12 @@ package final class GRPCChannel: ClientTransport {
self.input = AsyncStream.makeStream()
self.connector = connector

if let authority = config.http2.authority ?? resolver.authority {
self.authority = PercentEncoding.encodeAuthority(authority)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why does the percent-encoding happen at this level of the hierarchy rather than when it is first obtained?

Copy link
Collaborator

Choose a reason for hiding this comment

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

If it remains sometimes percent-encoded and sometimes not when stored as a property on objects I think that should be documented.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is effectively where it's first obtained: config.http2.authority is the 'global' override and resolver.authority is the first fallback option. If we have a value at this point it'll be used for all connections created by the GRPCChannel.

If we don't have a value at this point then we have to derive it from the address of each connection the GRPCChannel attempts to establish.

You're right about the documentation though, I'll add that and some comments explaining this.

isValidCharacter: (UInt8) -> Bool
) -> String {
var output: [UInt8] = []
output.reserveCapacity(input.utf8.count)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do you think it's worth reserving by some amount greater than the count here, anticipating some conversions?

Copy link
Collaborator 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 so – I think the common case will be for no additional encoding. We'll get some slack anyway because reserveCapacity should reserve at least that much capacity.

if isValidCharacter(char) {
output.append(char)
} else {
output.append(UInt8(ascii: "%"))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Will the UInt8 conversion of % be computed and stored at compile time? Is it worth doing that ourselves and hardcoding it e.g. UInt8(0x25) // %

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It'll be computed at compile time in release builds: https://godbolt.org/z/rz1Macabv

@glbrntt glbrntt requested a review from rnro December 3, 2024 11:25
Copy link
Collaborator

@rnro rnro left a comment

Choose a reason for hiding this comment

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

Thanks!

@glbrntt glbrntt enabled auto-merge (squash) December 3, 2024 13:29
@glbrntt glbrntt disabled auto-merge December 3, 2024 13:29
@glbrntt
Copy link
Collaborator Author

glbrntt commented Dec 3, 2024

API breakage is expected and okay.

@glbrntt glbrntt merged commit 76e15dc into grpc:main Dec 3, 2024
32 of 36 checks passed
@glbrntt glbrntt deleted the authority branch December 3, 2024 13:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🆕 semver/minor Adds new public API.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants