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

Reuse VirtualHostBuilder on the same hostnamePattern #5418

Merged
merged 11 commits into from Apr 4, 2024

Conversation

yejinio
Copy link
Contributor

@yejinio yejinio commented Jan 29, 2024

Motivation:

I registered each virtual host with the same hostname, the registered routes cannot be not found. Because VirtualHostBuilder creates new builder even if hostname is the same. However, it does reuse them on the same port.
So, I think it's better to reuse them on the same hostname.

Modifications:

  • ServerBuilder.virtualHost(hostnamePattern) and ServerBuilder.virtualHost(defaultHostname, hostnamePattern) checks whether there is builders with the same hostname. If there is, returns this builder.

Result:

  • Reuse VirtualHostBuilder on the same hostname.
  • Even if you register each one on a virtual host with the same hostname, they will be merged.

@CLAassistant
Copy link

CLAassistant commented Jan 29, 2024

CLA assistant check
All committers have signed the CLA.

@ikhoon ikhoon added the defect label Jan 29, 2024
@ikhoon ikhoon added this to the 1.28.0 milestone Jan 29, 2024
Copy link
Contributor

@ikhoon ikhoon left a comment

Choose a reason for hiding this comment

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

Overall, it looks great.

return normalizeDefaultHostname(defaultHostname).equals(this.defaultHostname);
}

boolean equalsHostnamePattern(String hostnamePattern) {
Copy link
Contributor

Choose a reason for hiding this comment

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

What do you think of parsing hostnamePattern before calling this method? Otherwise, the same hostnamePattern will be parsed as many virtual hosts.

Suggested change
boolean equalsHostnamePattern(String hostnamePattern) {
boolean equalsHostnamePattern(String validhostnamePattern, int port) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for the advice. Before I modify it, I'd like to know your intend exactly.
If I modify as you suggest, hostnamePattern is parsed using HostAndPort by the part which calls this method and passed. (e.g. ServiceBuilder.virtualHost(String hostnamePattern)), is it correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@ikhoon I applied your comment as I understand. PTAL 🙏

Copy link

codecov bot commented Feb 7, 2024

Codecov Report

Attention: Patch coverage is 80.00000% with 7 lines in your changes are missing coverage. Please review.

Project coverage is 74.09%. Comparing base (8678567) to head (283b7ec).
Report is 16 commits behind head on main.

Files Patch % Lines
...om/linecorp/armeria/server/VirtualHostBuilder.java 63.63% 1 Missing and 3 partials ⚠️
.../java/com/linecorp/armeria/server/VirtualHost.java 62.50% 0 Missing and 3 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #5418      +/-   ##
============================================
+ Coverage     74.04%   74.09%   +0.04%     
- Complexity    20871    20986     +115     
============================================
  Files          1809     1819      +10     
  Lines         76857    77332     +475     
  Branches       9809     9880      +71     
============================================
+ Hits          56912    57300     +388     
- Misses        15304    15375      +71     
- Partials       4641     4657      +16     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Member

@minwoox minwoox left a comment

Choose a reason for hiding this comment

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

Hey~ thanks for the PR and good to see you here. 😆
Left some suggestions. PTAL.

final Optional<VirtualHostBuilder> vhost =
virtualHostBuilders.stream()
.filter(v -> !v.defaultVirtualHost() &&
v.equalsHostnamePattern(hostAndPort.getHost(),
Copy link
Member

Choose a reason for hiding this comment

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

The hostname is normalized thus this won't still find the builder if the name contains unicode.

hostnamePattern = IDN.toASCII(hostnamePattern, IDN.ALLOW_UNASSIGNED);

So how about validating and normalizing it before we find it?
Then we can just set the hostnamePattern and port directly to VirtualHostBuilder not to call HostAndPort.fromString() and validate it again.

Copy link
Contributor Author

@yejinio yejinio Feb 23, 2024

Choose a reason for hiding this comment

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

Good idea. But, I don't fully understand your suggestion. Could you check I understand is correct?

Then we can just set the hostnamePattern and port directly to VirtualHostBuilder not to call HostAndPort.fromString() and validate it again.

  1. validate and normalize in ServerBuilder.virtualHost(hostnamePattern)
  2. if exists on same hostname, return it
  3. if not exists, VirtualHostBuilder.hostnamePattern(hostnamePattern, port) calls
  4. VirtualHostBuilder.hostnamePatter(hostnamePattern, port) sets directly hostnamePattern and port without validation and normalization

In 4, I'm worried about the hostnamePattern is able to be set without validation somewhere.

Copy link
Member

@minwoox minwoox Feb 23, 2024

Choose a reason for hiding this comment

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

In 4, I'm worried about the hostnamePattern is able to be set without validation somewhere.

That's a good question. I believe that if we make the method package-private, we don't have to worry about it because a user can't just use the method to set the properties directly.
If you worry about it, we can also add the validation logic in the method but I don't believe we really need it. 😉

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, I didn't think about package-private! great job 👍 Thank you for the suggestion!

final int port = hostAndPort.getPortOrDefault(-1);
for (VirtualHostBuilder virtualHostBuilder : virtualHostBuilders) {
if (!virtualHostBuilder.defaultVirtualHost() &&
virtualHostBuilder.equalsDefaultHostname(defaultHostname) &&
Copy link
Member

Choose a reason for hiding this comment

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

What are your thoughts on utilizing hostnamePattern and port for the equality check and removing the defaultHostname check? Since hostnamePattern is solely used to identify the virtual host, I believe we can consider two virtual hosts as identical if their hostnamePattern matches.

return DefaultRoutingContext.of(serverConfig.findVirtualHost(hostname, port),

mappingBuilder.add(virtualHost.hostnamePattern(), virtualHost);

Copy link
Contributor

Choose a reason for hiding this comment

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

What do you think of raising an exception if a defaultHostname is registered for the same hostnamePattern before?
It may be controversial that defaultHostname is ignored silently.

Copy link
Member

Choose a reason for hiding this comment

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

That's a good suggestion. I think we can

  • set the defaultHostname if the previous virtualHostBuilder does not have the defaultHostname
  • raise an exception if the defaultHostname of the previous virtualHostBuilder not null and It's not the same with the parameter defaultHostname

Copy link
Contributor

Choose a reason for hiding this comment

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

A couple thoughts regarding the proposal:

  • Even before this PR, a defaultHostname could be ignored if there were multiple virtual hosts with the same hostnamePattern
  • The suggested behavior tries to guarantee uniqueness of (hostName, defaultHostname, port). However, the mutability of defaultHostname, hostnamePattern, port does not guarantee this.

Rather, I propose:

  • All methods that can mutate hostnamePattern are deprecated
    • VirtualHostBuilder#hostnamePattern(String) is deprecated
  • All methods that do not receive a single hostnamePattern or port are deprecated
    • ServerBuilder#virtualHost(String,String) is deprecated, since the additional defaultHostname makes it unclear which VirtualHostBuilder is returned.
    • ServerBuilder#withVirtualHost(Customizer) is deprecated. It is not compatible with the VirtualHostBuilder reuse functionality. If needed, we could add a ServerBuilder#withVirtualHost(String, Customizer), ServerBuilder#withVirtualHost(int,Customizer) instead.

This would make it clear that only hostnamePattern or port will be used for checking reusability of VirtualHostBuilder.


In terms of implementation plan, I propose we assume that the @Deprecated methods do not support VirtualHostBuilder reusability. We can simply remove the deprecated methods in the next major release which gives a straightforward upgrade plan as well.

Regarding this PR, I propose that this method is simply deprecated and we only support ServerBuilder#virtualHost(String) instead.

Thoughts @line/dx ?

Copy link
Member

Choose a reason for hiding this comment

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

That's a good idea. 👍 It's time to cleanup the API.

Copy link
Contributor

Choose a reason for hiding this comment

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

That sounds great to me.

If needed, we could add a ServerBuilder#withVirtualHost(String, Customizer)

It would be better than deprecation without a replacement. Some forks prefer explicit code blocks over the fluent style.

Copy link
Contributor Author

@yejinio yejinio Mar 28, 2024

Choose a reason for hiding this comment

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

I'm sorry for late checking.

Regarding this PR, I propose that this method is simply deprecated and we only support ServerBuilder#virtualHost(String) instead.

I understand that I need to apply changes to ServerBuilder#virtualHost(String) in this PR. I'll remove the changes of ServerBuilder#virtualHost(String, String).

Copy link
Contributor

Choose a reason for hiding this comment

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

I'll remove the changes of ServerBuilder#virtualHost(String, String).

Thanks!

Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

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

Thanks for the effort! Looks good overall 👍 👍 👍

final int port = hostAndPort.getPortOrDefault(-1);
for (VirtualHostBuilder virtualHostBuilder : virtualHostBuilders) {
if (!virtualHostBuilder.defaultVirtualHost() &&
virtualHostBuilder.equalsDefaultHostname(defaultHostname) &&
Copy link
Contributor

Choose a reason for hiding this comment

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

A couple thoughts regarding the proposal:

  • Even before this PR, a defaultHostname could be ignored if there were multiple virtual hosts with the same hostnamePattern
  • The suggested behavior tries to guarantee uniqueness of (hostName, defaultHostname, port). However, the mutability of defaultHostname, hostnamePattern, port does not guarantee this.

Rather, I propose:

  • All methods that can mutate hostnamePattern are deprecated
    • VirtualHostBuilder#hostnamePattern(String) is deprecated
  • All methods that do not receive a single hostnamePattern or port are deprecated
    • ServerBuilder#virtualHost(String,String) is deprecated, since the additional defaultHostname makes it unclear which VirtualHostBuilder is returned.
    • ServerBuilder#withVirtualHost(Customizer) is deprecated. It is not compatible with the VirtualHostBuilder reuse functionality. If needed, we could add a ServerBuilder#withVirtualHost(String, Customizer), ServerBuilder#withVirtualHost(int,Customizer) instead.

This would make it clear that only hostnamePattern or port will be used for checking reusability of VirtualHostBuilder.


In terms of implementation plan, I propose we assume that the @Deprecated methods do not support VirtualHostBuilder reusability. We can simply remove the deprecated methods in the next major release which gives a straightforward upgrade plan as well.

Regarding this PR, I propose that this method is simply deprecated and we only support ServerBuilder#virtualHost(String) instead.

Thoughts @line/dx ?

@jrhee17
Copy link
Contributor

jrhee17 commented Mar 28, 2024

gentle ping @yejinio 🙇

@yejinio yejinio requested review from minwoox and jrhee17 March 29, 2024 03:05
@jrhee17
Copy link
Contributor

jrhee17 commented Apr 2, 2024

I've added a small commit which

  • Deprecates ServerBuilder#virtualHost methods that doesn't support VirtualHostBuilder reuse
  • Adds a ServerBuilder#withVirtualHost which supports VirtualHostBuilder reuse
  • Deprecates VirtualHostBuilder#hostnamePattern which makes VirtualHostBuilder reuse ambiguous

Let me know if the changes don't make sense 🙇
0d0edef

Copy link
Contributor

@jrhee17 jrhee17 left a comment

Choose a reason for hiding this comment

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

Changes look good to me 👍 👍 👍

Copy link
Contributor

@ikhoon ikhoon left a comment

Choose a reason for hiding this comment

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

@yejinio Thanks for your contribution! 🚀🚀

Copy link
Member

@minwoox minwoox left a comment

Choose a reason for hiding this comment

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

@yejinio Thank you very much for all your hard work! 🙇

@jrhee17 jrhee17 merged commit dad02d6 into line:main Apr 4, 2024
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants