Skip to content

Conversation

@jrhee17
Copy link
Contributor

@jrhee17 jrhee17 commented Nov 26, 2025

Motivation:

This is part of an ongoing effort to apply per-request TLS: #6516

Currently, SNI is determined as follows:

  • If an Endpoint has a hostname, the hostname is used
  • Otherwise, the :authority at the time of setting the Endpoint is used

final String authority = authority();
if (authority != null && endpoint != null && endpoint.isIpAddrOnly()) {
// The connection will be established with the IP address but `host` set to the `Endpoint`
// could be used for SNI. It would make users send HTTPS requests with CSLB or configure a reverse
// proxy based on an authority.
final String host = SchemeAndAuthority.of(null, authority).host();
if (!NetUtil.isValidIpV4Address(host) && !NetUtil.isValidIpV6Address(host)) {
endpoint = endpoint.withHost(host);
}
}

When using the :authority, it does not consider headers that might have been set after the endpoint is determined (e.g. in the decorators)

I propose that the final SNI is determined right before PoolKey generation. This will be useful in the upcoming PR to determine the exact TLS specification associated with a HTTP request.

Modifications:

  • Moved SNI determining logic to HttpClientDelegate
  • Removed unnecessary recomputation of the Origin header

Result:

  • More consistent behavior regarding SNI determination

@jrhee17 jrhee17 added this to the 1.35.0 milestone Nov 26, 2025
@jrhee17 jrhee17 added the defect label Nov 26, 2025
@coderabbitai
Copy link

coderabbitai bot commented Nov 26, 2025

Walkthrough

The changes modify endpoint normalization and TLS/SNI handling. HttpChannelPool now preserves trailing dots in endpoints. HttpClientDelegate derives server names from authority headers for proper SNI when endpoints contain only IP addresses and normalizes trailing dots for TLS compatibility. DefaultClientRequestContext refactors header computation into a reusable helper method. A new test validates Authority header handling with custom TLS trust.

Changes

Cohort / File(s) Summary
Endpoint Normalization
core/src/main/java/com/linecorp/armeria/client/HttpChannelPool.java
Removed trailing dot normalization from PoolKey constructor; endpoint now stored as-is, affecting equals() and hashCode() comparisons.
TLS/SNI Host Handling
core/src/main/java/com/linecorp/armeria/client/HttpClientDelegate.java
Adds logic to derive server names from authority headers for IP-only endpoints under TLS, normalizes trailing dots for SNI compliance, and introduces new private helper method authorityToServerName().
Header Computation Refactoring
core/src/main/java/com/linecorp/armeria/internal/client/DefaultClientRequestContext.java
Extracts header computation logic into new helper method computeInternalHeaders() that builds SCHEME and ORIGIN headers. Removes obsolete methods autoFillSchemeAuthorityAndOrigin() and origin(). Updates updateEndpoint() and failEarly() to use the new helper.
Test Coverage
core/src/test/java/com/linecorp/armeria/client/TrailingDotSniTest.java
Adds new test method usesAuthorityHeader() validating Authority header injection with custom TLS trust, asserting 200 OK response.

Sequence Diagram

sequenceDiagram
    participant Client as HttpClientDelegate
    participant Pool as HttpChannelPool
    participant Auth as Authority Parser
    participant SNI as TLS/SNI Handler
    
    Client->>Client: Detect IP-only endpoint + TLS
    Client->>Auth: authorityToServerName(authority)
    Auth-->>Client: server name (if valid)
    Client->>SNI: Normalize host (remove trailing dot)
    Client->>Client: Set endpoint host to server name
    Client->>Pool: Create PoolKey with updated endpoint
    Pool->>Pool: Store endpoint as-is (no normalization)
    Pool-->>Client: Return channel from pool
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • HttpClientDelegate: New TLS/SNI logic requires understanding of authority parsing and endpoint host substitution semantics.
  • DefaultClientRequestContext: Refactoring with method extraction and removal; verify all call sites correctly use the new computeInternalHeaders() helper.
  • Interdependencies: Changes across multiple files affect pool key construction and SNI behavior; ensure endpoint normalization changes don't break existing endpoint comparison logic.

Poem

🐰 With dots preserved and SNI refined,
IP hosts now wear a name so kind,
Headers refactored, clean and bright,
Authority guides the TLS light!
Tests ensure all paths align—
A rabbit's joy: code that's well-designed ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: fixing a bug where SNI doesn't properly reflect the :authority header of a request, which aligns with the primary modification to move SNI determination logic to HttpClientDelegate.
Description check ✅ Passed The description is well-related to the changeset, explaining the motivation for moving SNI-determination logic to HttpClientDelegate and the removal of unnecessary origin recomputation, matching the actual code changes.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link

codecov bot commented Nov 26, 2025

Codecov Report

❌ Patch coverage is 84.61538% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.14%. Comparing base (8150425) to head (20a36cc).
⚠️ Report is 275 commits behind head on main.

Files with missing lines Patch % Lines
...om/linecorp/armeria/client/HttpClientDelegate.java 73.33% 2 Missing and 2 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main    #6522      +/-   ##
============================================
- Coverage     74.46%   74.14%   -0.32%     
- Complexity    22234    23366    +1132     
============================================
  Files          1963     2100     +137     
  Lines         82437    87514    +5077     
  Branches      10764    11492     +728     
============================================
+ Hits          61385    64889    +3504     
- Misses        15918    17145    +1227     
- Partials       5134     5480     +346     

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@jrhee17 jrhee17 marked this pull request as ready for review November 27, 2025 07:23
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
core/src/main/java/com/linecorp/armeria/client/HttpClientDelegate.java (1)

260-274: Consider wrapping authority parsing in try-catch for robustness.

SchemeAndAuthority.of(null, authority) may throw an exception for malformed authority strings. Since this method is used to derive an optional server name for SNI, a parsing failure shouldn't abort the request—it should gracefully return null.

 @Nullable
 private static String authorityToServerName(@Nullable String authority) {
     if (authority == null) {
         return null;
     }
-    String serverName = SchemeAndAuthority.of(null, authority).host();
+    final String serverName;
+    try {
+        serverName = SchemeAndAuthority.of(null, authority).host();
+    } catch (Exception e) {
+        // Malformed authority; fall back to no server name.
+        return null;
+    }
     if (NetUtil.isValidIpV4Address(serverName) || NetUtil.isValidIpV6Address(serverName)) {
         return null;
     }
-    serverName = serverName.trim();
-    if (serverName.isEmpty()) {
+    final String trimmed = serverName.trim();
+    if (trimmed.isEmpty()) {
         return null;
     }
-    return serverName;
+    return trimmed;
 }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ee1836f and 20a36cc.

📒 Files selected for processing (4)
  • core/src/main/java/com/linecorp/armeria/client/HttpChannelPool.java (1 hunks)
  • core/src/main/java/com/linecorp/armeria/client/HttpClientDelegate.java (3 hunks)
  • core/src/main/java/com/linecorp/armeria/internal/client/DefaultClientRequestContext.java (3 hunks)
  • core/src/test/java/com/linecorp/armeria/client/TrailingDotSniTest.java (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.java

⚙️ CodeRabbit configuration file

**/*.java: - The primary coding conventions and style guide for this project are defined in site/src/pages/community/developer-guide.mdx. Please strictly adhere to this file as the ultimate source of truth for all style and convention-related feedback.

2. Specific check for @UnstableApi

  • Review all newly added public classes and methods to ensure they have the @UnstableApi annotation.
  • However, this annotation is NOT required under the following conditions:
    • If the class or method is located in a package containing .internal.
    • If a public method is part of a class that is already annotated with @UnstableApi.

Files:

  • core/src/test/java/com/linecorp/armeria/client/TrailingDotSniTest.java
  • core/src/main/java/com/linecorp/armeria/internal/client/DefaultClientRequestContext.java
  • core/src/main/java/com/linecorp/armeria/client/HttpClientDelegate.java
  • core/src/main/java/com/linecorp/armeria/client/HttpChannelPool.java
🧬 Code graph analysis (3)
core/src/test/java/com/linecorp/armeria/client/TrailingDotSniTest.java (1)
core/src/main/java/com/linecorp/armeria/common/HttpHeaderNames.java (1)
  • HttpHeaderNames (53-1099)
core/src/main/java/com/linecorp/armeria/internal/client/DefaultClientRequestContext.java (1)
core/src/main/java/com/linecorp/armeria/common/HttpHeaderNames.java (1)
  • HttpHeaderNames (53-1099)
core/src/main/java/com/linecorp/armeria/client/HttpClientDelegate.java (1)
core/src/main/java/com/linecorp/armeria/internal/common/SchemeAndAuthority.java (1)
  • SchemeAndAuthority (32-151)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Kubernetes Chaos test
  • GitHub Check: Summary
🔇 Additional comments (6)
core/src/test/java/com/linecorp/armeria/client/TrailingDotSniTest.java (1)

78-96: LGTM! Test validates the new SNI behavior correctly.

This test verifies that when connecting to an IP-based endpoint, the :authority header set via a decorator is used for SNI during TLS handshake. The certificate is for example.com, and setting the authority header allows the TLS handshake to succeed with the correct server name.

core/src/main/java/com/linecorp/armeria/client/HttpChannelPool.java (1)

624-628: Trailing dot normalization moved to HttpClientDelegate.

The removal of endpoint.withoutTrailingDot() here is intentional since the normalization now occurs in HttpClientDelegate.acquireConnectionAndExecute0() (line 231) right before PoolKey creation. This ensures SNI determination happens at the correct point in the request lifecycle.

core/src/main/java/com/linecorp/armeria/client/HttpClientDelegate.java (1)

219-231: Logic correctly derives SNI from authority for IP-only TLS endpoints.

The implementation properly:

  1. Checks if the protocol is TLS and endpoint contains only an IP address
  2. Derives a hostname from the :authority header when available
  3. Strips trailing dots for SNI compliance (per RFC requirements)

This ensures SNI reflects the effective :authority at request time, as described in the PR objectives.

core/src/main/java/com/linecorp/armeria/internal/client/DefaultClientRequestContext.java (3)

585-600: Clean refactoring of internal header computation.

The new computeInternalHeaders helper consolidates the logic for setting SCHEME, AUTHORITY, and ORIGIN headers. The method correctly:

  • Sets the scheme based on session protocol
  • Sets authority from the endpoint when available
  • Conditionally fills the Origin header based on autoFillOriginHeader setting

This improves maintainability by eliminating code duplication between updateEndpoint and failEarly.


512-517: LGTM!

The updateEndpoint method now delegates to computeInternalHeaders, maintaining the same behavior while improving code organization.


569-576: LGTM!

The failEarly method correctly uses computeInternalHeaders with a null endpoint parameter, ensuring the scheme header is still set even when there's no endpoint available.

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.

👍👍

Copy link
Contributor

@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.

👍 👍 👍

@jrhee17 jrhee17 merged commit 770904e into line:main Dec 8, 2025
16 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants