Skip to content

Conversation

@0x1306e6d
Copy link
Contributor

Motivation:

HTTP/JSON transcoding for gRPC services in Armeria currently relies solely on google.api.http annotations in proto files. This requires modifying proto files to configure HTTP endpoints, which may not be desirable or possible in some scenarios (e.g., using third-party proto definitions or managing transcoding rules separately from service definitions).

Modifications:

  • Added useHttpAnnotations() to HttpJsonTranscodingOptions to control whether HTTP/JSON transcoding rules are extracted from google.api.http annotations in proto descriptors. Defaults to true to maintain backward compatibility.
  • Added additionalHttpRules() to HttpJsonTranscodingOptions to allow programmatic specification of HttpRules without modifying proto files. These rules are processed regardless of the useHttpAnnotations() setting, allowing annotation-based and programmatic rules to coexist.
  • Extracted HTTP rule processing logic from HttpJsonTranscodingService into HttpJsonTranscodingServiceBuilder.

Result:

  • Users can now programmatically configure HTTP/JSON transcoding rules without modifying proto files:
    HttpRule rule = HttpRule.newBuilder()
                            .setSelector("my.package.Service.Method")
                            .setGet("/v1/method")
                            .build();
    
    GrpcService service = GrpcService.builder()
                                     .addService(...)
                                     .enableHttpJsonTranscoding(
                                         HttpJsonTranscodingOptions.builder()
                                             .additionalHttpRules(rule)
                                             .build())
                                     .build();
  • Users can disable automatic extraction of HTTP rules from proto annotations if needed:
    GrpcService service = GrpcService.builder()
                                     .addService(...)
                                     .enableHttpJsonTranscoding(
                                         HttpJsonTranscodingOptions.builder()
                                             .useHttpAnnotations(false)
                                             .build())
                                     .build();

@coderabbitai
Copy link

coderabbitai bot commented Dec 30, 2025

📝 Walkthrough

Walkthrough

Adds programmatic HTTP/JSON transcoding controls: ignoreProtoHttpRule, additionalHttpRules, and conflictStrategy; moves transcoding spec generation into a new HttpJsonTranscodingServiceBuilder; introduces builder copy semantics and updates tests to cover rules and conflict strategies.

Changes

Cohort / File(s) Summary
Options: core types
grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingOptions.java, grpc/src/main/java/com/linecorp/armeria/server/grpc/DefaultHttpJsonTranscodingOptions.java
Added API surface and backing fields for ignoreProtoHttpRule (boolean), additionalHttpRules() (List), and conflictStrategy() (HttpJsonTranscodingConflictStrategy). Updated constructor, equals, hashCode, and toString to include new fields.
Options builder
grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingOptionsBuilder.java
Added builder methods ignoreProtoHttpRule(boolean), additionalHttpRules(HttpRule...) / (Iterable<HttpRule>), conflictStrategy(...), and a copy constructor HttpJsonTranscodingOptionsBuilder(HttpJsonTranscodingOptions). Build now passes new parameters to DefaultHttpJsonTranscodingOptions.
Service construction refactor
grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingService.java, grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingServiceBuilder.java
Extracted transcoding spec generation into new package-private HttpJsonTranscodingServiceBuilder. Removed public factory of(...) and some internal helpers from HttpJsonTranscodingService; adjusted constructor/field visibilities and added package-visible accessors for testing. New builder registers proto and additional rules and resolves conflicts using the strategy.
Integration in GrpcServiceBuilder
grpc/src/main/java/com/linecorp/armeria/server/grpc/GrpcServiceBuilder.java
Switched internal construction to use new HttpJsonTranscodingOptionsBuilder(existingOptions) when error handler present and to create transcoding service via HttpJsonTranscodingServiceBuilder.of(...).build() instead of previous direct factory method.
Conflict strategy
grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingConflictStrategy.java
New functional interface to resolve HttpRule conflicts with presets firstWins() and lastWins().
Tests: new and moved
grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingHttpRuleTest.java, grpc/src/test/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingOptionsBuilderTest.java, grpc/src/test/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingConflictStrategyTest.java
Added tests for programmatic additional rules, copy constructor semantics, and conflict strategy behaviors.
Tests: extracted service
grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingTestService.java, grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingTest.java
Extracted and moved an in-test gRPC service implementation into its own test-class file; updated tests to reference it and removed previous inline test service.
Proto/test build
grpc/src/test/proto/testing/grpc/transcoding.proto, it/grpc/protobuf4/build.gradle
Added an AdditionalBinding RPC with additional_bindings; updated test source copy task to include the extracted test service java file.

Sequence Diagram(s)

sequenceDiagram
    participant GrpcSB as GrpcServiceBuilder
    participant OptBuilder as HttpJsonTranscodingOptionsBuilder
    participant OptDef as DefaultHttpJsonTranscodingOptions
    participant SvcBuilder as HttpJsonTranscodingServiceBuilder
    participant SvcDef as HttpJsonTranscodingService

    GrpcSB->>OptBuilder: new(existingOptions)  (copy constructor)
    OptBuilder-->>OptBuilder: copy fields (ignoreProtoHttpRule, additionalHttpRules, conflictStrategy, ...)
    GrpcSB->>OptBuilder: additionalHttpRules(...) / ignoreProtoHttpRule(flag) / conflictStrategy(...)
    GrpcSB->>OptBuilder: build()
    OptBuilder->>OptDef: new(ignoreProtoHttpRule, additionalHttpRules, conflictStrategy, matchRules, errorHandler)
    OptDef-->>GrpcSB: options

    GrpcSB->>SvcBuilder: HttpJsonTranscodingServiceBuilder.of(grpcService, options)
    SvcBuilder->>SvcBuilder: scan services/methods, register proto and additional rules
    SvcBuilder->>SvcBuilder: resolve conflicts via conflictStrategy
    SvcBuilder->>SvcDef: new(delegate, routeAndSpecs, options)
    SvcDef-->>GrpcSB: transcoding service
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested reviewers

  • trustin
  • minwoox
  • ikhoon

Poem

🐰 A rabbit hops through builders and rules delight,

I copy your options and stitch them up tight,
Proto annotations I may choose to ignore,
Extra routes join the dance and conflicts settle the score,
Hopping tests confirm the pathways are right.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 18.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: allowing programmatic specification of additional HttpRules for gRPC/JSON transcoding, which is the primary feature addition in this PR.
Description check ✅ Passed The description thoroughly explains the motivation, modifications, and provides concrete code examples demonstrating the new functionality. It directly relates to all significant changes in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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.

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)
grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingTestService.java (1)

65-65: Consider using UPPER_SNAKE_CASE for the constant name.

Java convention for static final constants is to use UPPER_SNAKE_CASE. Consider renaming testBytesValue to TEST_BYTES_VALUE.

🔎 Proposed fix
-    static final String testBytesValue = "abc123!?$*&()'-=@~";
+    static final String TEST_BYTES_VALUE = "abc123!?$*&()'-=@~";

This would also require updating the reference at line 192:

assertThat(request.getBytesVal().getValue().toStringUtf8()).isEqualTo(TEST_BYTES_VALUE);
📜 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 6777284 and 6a39ea6.

📒 Files selected for processing (11)
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/DefaultHttpJsonTranscodingOptions.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/GrpcServiceBuilder.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingOptions.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingOptionsBuilder.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingService.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingServiceBuilder.java
  • grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingHttpRuleTest.java
  • grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingTest.java
  • grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingTestService.java
  • grpc/src/test/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingOptionsBuilderTest.java
  • it/grpc/protobuf4/build.gradle
🧰 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 or .testing.
    • If the class or method is located in a test source set.
    • If a public method is part of a class that is already annotated with @UnstableApi.

Files:

  • grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingHttpRuleTest.java
  • grpc/src/test/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingOptionsBuilderTest.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingOptions.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingServiceBuilder.java
  • grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingTestService.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/DefaultHttpJsonTranscodingOptions.java
  • grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingTest.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingOptionsBuilder.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingService.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/GrpcServiceBuilder.java
🧬 Code graph analysis (3)
grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingHttpRuleTest.java (1)
junit5/src/main/java/com/linecorp/armeria/testing/junit5/server/ServerExtension.java (1)
  • ServerExtension (49-405)
grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingTest.java (2)
grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingTestService.java (1)
  • HttpJsonTranscodingTestService (63-302)
grpc/src/test/java/com/linecorp/armeria/it/grpc/TranscodingVerbTest.java (1)
  • HttpJsonTranscodingTestService (38-69)
grpc/src/main/java/com/linecorp/armeria/server/grpc/GrpcServiceBuilder.java (1)
grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingServiceBuilder.java (1)
  • HttpJsonTranscodingServiceBuilder (76-456)
⏰ 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). (14)
  • GitHub Check: build-ubicloud-standard-16-jdk-11
  • GitHub Check: build-macos-latest-jdk-25
  • GitHub Check: build-ubicloud-standard-16-jdk-25
  • GitHub Check: build-ubicloud-standard-16-jdk-17-min-java-11
  • GitHub Check: build-windows-latest-jdk-25
  • GitHub Check: build-ubicloud-standard-16-jdk-17-min-java-17-coverage
  • GitHub Check: build-ubicloud-standard-16-jdk-17-leak
  • GitHub Check: build-ubicloud-standard-16-jdk-8
  • GitHub Check: build-ubicloud-standard-16-jdk-21-snapshot-blockhound
  • GitHub Check: Kubernetes Chaos test
  • GitHub Check: flaky-tests
  • GitHub Check: lint
  • GitHub Check: site
  • GitHub Check: Summary
🔇 Additional comments (18)
it/grpc/protobuf4/build.gradle (1)

30-30: LGTM!

The additional include pattern correctly ensures HttpJsonTranscodingTestService.java is copied alongside the test files for the protobuf4 integration tests.

grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingOptions.java (1)

47-60: LGTM!

The new API methods are well-documented with clear descriptions of their purpose and default behavior. Since the interface is already annotated with @UnstableApi, the new methods don't require additional annotations. Based on coding guidelines.

grpc/src/test/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingOptionsBuilderTest.java (1)

73-95: LGTM!

The test comprehensively validates the copy constructor behavior, ensuring all fields (including the new useHttpAnnotations and additionalHttpRules) are correctly copied from an existing options instance.

grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingTest.java (1)

19-19: LGTM!

Clean refactoring that imports testBytesValue from the extracted HttpJsonTranscodingTestService class, enabling reuse of the test service across multiple test classes.

grpc/src/main/java/com/linecorp/armeria/server/grpc/GrpcServiceBuilder.java (1)

1073-1080: LGTM!

The refactoring correctly uses the new builder-based construction:

  1. HttpJsonTranscodingOptionsBuilder(this.httpJsonTranscodingOptions) properly copies all existing options before applying the error handler override.
  2. HttpJsonTranscodingServiceBuilder.of(...).build() aligns with the new service construction pathway.

This maintains backward compatibility while enabling the new programmatic HTTP rules functionality.

grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingHttpRuleTest.java (1)

36-111: LGTM!

Excellent test coverage for the new HTTP/JSON transcoding rules functionality:

  • serverMixedRules validates coexistence of annotation-based and programmatic rules
  • serverOnlyAdditionalRules validates that annotation-based rules can be disabled
  • All three test methods clearly verify the expected behavior with appropriate assertions
grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingServiceBuilder.java (3)

391-410: Consider clarifying the order variable scope.

The order variable is local to the httpRule method, so each call to httpRule() resets order to 0. This means different HttpRule instances will have overlapping order values. If order is intended to provide a unique ordering across all specs, consider making it an instance field.

However, if the current behavior is intentional (i.e., order is only meaningful within the context of a single HttpRule and its additional bindings), this is fine as-is.


76-131: LGTM!

The builder is well-structured with a clean factory method that:

  1. Collects gRPC methods from the delegate service
  2. Conditionally processes google.api.http annotations based on useHttpAnnotations()
  3. Processes additional HTTP rules from additionalHttpRules()

The class being package-private is appropriate for internal implementation details.


435-455: Good optimization on RecursiveTypeException.

Overriding fillInStackTrace() to return this is a good performance optimization since this exception is used for control flow during field building rather than error reporting.

grpc/src/main/java/com/linecorp/armeria/server/grpc/DefaultHttpJsonTranscodingOptions.java (2)

30-53: LGTM!

The new fields and accessors are implemented correctly:

  • Fields are properly initialized in the constructor
  • Accessor methods correctly implement the interface contract
  • The implementation follows the existing patterns in the class

74-82: LGTM!

The equals() and hashCode() implementations correctly include the new useHttpAnnotations and additionalHttpRules fields, maintaining the contract between the two methods.

grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingTestService.java (1)

63-302: LGTM!

The test service implementation is well-structured with comprehensive coverage of various transcoding scenarios. All RPC methods follow the standard gRPC asynchronous pattern correctly.

grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingOptionsBuilder.java (3)

51-70: LGTM! Well-designed copy constructor and field additions.

The copy constructor properly transfers all configuration from an existing HttpJsonTranscodingOptions, and the fields are appropriately initialized. This enables the GrpcServiceBuilder to modify options while preserving existing settings.


100-130: LGTM! Clean API for programmatic HTTP rule configuration.

The new methods follow the existing builder patterns in this class:

  • useHttpAnnotations(boolean) provides control over annotation-based rule extraction
  • additionalHttpRules(...) overloads handle both varargs and Iterable inputs consistently

The Javadoc clearly explains that additional rules are processed regardless of the useHttpAnnotations() setting.


146-165: LGTM!

The build() method correctly integrates the new useHttpAnnotations and additionalHttpRules parameters into the DefaultHttpJsonTranscodingOptions constructor.

grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingService.java (3)

195-218: LGTM! Appropriate visibility change for builder pattern.

The constructor visibility change from private to package-private aligns with the refactoring to use HttpJsonTranscodingServiceBuilder for instantiation. The constructor logic remains unchanged and properly initializes the routes with correct ordering.


407-408: LGTM!

Adding the @Nullable annotation correctly documents that request.contentType() may return null, improving null-safety documentation.


579-631: LGTM! Visibility changes support the builder pattern refactoring.

The package-private visibility for TranscodingSpec and Field constructors enables HttpJsonTranscodingServiceBuilder to construct these objects while keeping them internal to the package.

@codecov
Copy link

codecov bot commented Dec 30, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 0.00%. Comparing base (8150425) to head (6a39ea6).
⚠️ Report is 293 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff              @@
##               main   #6567       +/-   ##
============================================
- Coverage     74.46%       0   -74.47%     
============================================
  Files          1963       0     -1963     
  Lines         82437       0    -82437     
  Branches      10764       0    -10764     
============================================
- Hits          61385       0    -61385     
+ Misses        15918       0    -15918     
+ Partials       5134       0     -5134     

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

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 looks good.

matchRule, queryParamMatchRules);
}).collect(toImmutableList());

if (routeAndSpecs.containsKey(route)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

If there are duplicate routes, would it be more flexible to let users choose one instead of simply rejecting the later one? Because some users may want to override the predefined HttpRule in proto files.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with this - it may be a good idea to collect/merge all of the HttpRule#selector -> HttpRule mappings before starting to create the GrpcService.

I'm also unsure if the useHttpAnnotations knob will be needed if overriding is possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great.

We can add a selector that chooses between the option rule and the additional rule:

builder.httpRuleSelector((selector, option, additional) -> { // called when a conflicting selector is found
  return additional;
});

Alternatively, we could introduce a simple enum such as FIRST_WINS or LAST_WINS.

The original intention of useHttpAnnotations was to give users full control over the proto definition in cases where they do not maintain the proto and do not want to accidentally expose unexpected endpoints. The main purpose, therefore, was to clear HTTP rules parsed from the proto.

Copy link
Contributor

Choose a reason for hiding this comment

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

We can add a selector that chooses between the option rule and the additional rule:

A dynamic selector sounds better to me.

The main purpose, therefore, was to clear HTTP rules parsed from the proto.

If so, I don't think useHttpAnnotations() clearly conveys its intent. Would it make sense to rename it to ignoreProtoHttpRule?

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

I have added HttpJsonTranscodingConflictStrategy to resolve conflicts when multiple HttpRules are registered for the same method selector.

Since HttpRules are converted into Routes, it is theoretically possible for different gRPC methods to be configured to use the same Route. Because standard conflicts are now handled by the selector-based resolution, I considered these route-level collisions (different gRPC methods, same path) to be abnormal, so they will now raise an exception. I am unsure whether this becomes a breaking change or a bug fix.

The default conflict strategy is currently first-wins to maintain backward compatibility. However, I noticed that google/api/http.proto states: "All service configuration rules follow 'last one wins' order." Therefore, I suggest changing the default strategy to last-wins when Armeria reaches version 2.0.

final HttpJsonTranscodingServiceBuilder builder = new HttpJsonTranscodingServiceBuilder(delegate,
methods,
options);
if (options.useHttpAnnotations()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

My understanding is that useHttpAnnotations(false) completely ignores HttpRule in proto files but instead only uses additionalHttpRules(). Could you add a description of when useHttpAnnotations(false) is useful in the Javadoc?

@ikhoon ikhoon added this to the 1.35.0 milestone Dec 30, 2025
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.

Overall looks good

Comment on lines 336 to 338
public Map<String, GrpcMethod> methods() {
return methods;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

nit; it seems like this can be removed

Suggested change
public Map<String, GrpcMethod> methods() {
return methods;
}
public Map<String, GrpcMethod> methods() {
return methods;
}

matchRule, queryParamMatchRules);
}).collect(toImmutableList());

if (routeAndSpecs.containsKey(route)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with this - it may be a good idea to collect/merge all of the HttpRule#selector -> HttpRule mappings before starting to create the GrpcService.

I'm also unsure if the useHttpAnnotations knob will be needed if overriding is possible.

@minwoox minwoox modified the milestones: 1.35.0, 1.36.0 Dec 31, 2025
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)
grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingTestService.java (1)

65-65: Consider making testBytesValue private.

The constant is only used within this class (line 192), so it could be declared private for better encapsulation.

♻️ Suggested improvement
-    static final String testBytesValue = "abc123!?$*&()'-=@~";
+    private static final String testBytesValue = "abc123!?$*&()'-=@~";
📜 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 6a39ea6 and dcfce73.

📒 Files selected for processing (12)
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/DefaultHttpJsonTranscodingOptions.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingConflictStrategy.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingOptions.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingOptionsBuilder.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingService.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingServiceBuilder.java
  • grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingHttpRuleTest.java
  • grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingTestService.java
  • grpc/src/test/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingConflictStrategyTest.java
  • grpc/src/test/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingOptionsBuilderTest.java
  • grpc/src/test/proto/testing/grpc/transcoding.proto
  • it/grpc/protobuf4/build.gradle
🚧 Files skipped from review as they are similar to previous changes (2)
  • it/grpc/protobuf4/build.gradle
  • grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingHttpRuleTest.java
🧰 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 or .testing.
    • If the class or method is located in a test source set.
    • If a public method is part of a class that is already annotated with @UnstableApi.

Files:

  • grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingConflictStrategy.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingServiceBuilder.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingOptions.java
  • grpc/src/test/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingConflictStrategyTest.java
  • grpc/src/test/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingOptionsBuilderTest.java
  • grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingTestService.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingService.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/DefaultHttpJsonTranscodingOptions.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingOptionsBuilder.java
🧬 Code graph analysis (1)
grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingTestService.java (1)
grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingTest.java (1)
  • HttpJsonTranscodingTestService (120-359)
⏰ 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). (14)
  • GitHub Check: build-ubicloud-standard-16-jdk-17-min-java-11
  • GitHub Check: build-ubicloud-standard-16-jdk-25
  • GitHub Check: build-ubicloud-standard-16-jdk-11
  • GitHub Check: build-ubicloud-standard-16-jdk-21-snapshot-blockhound
  • GitHub Check: build-ubicloud-standard-16-jdk-17-min-java-17-coverage
  • GitHub Check: build-windows-latest-jdk-25
  • GitHub Check: build-macos-latest-jdk-25
  • GitHub Check: build-ubicloud-standard-16-jdk-8
  • GitHub Check: build-ubicloud-standard-16-jdk-17-leak
  • GitHub Check: flaky-tests
  • GitHub Check: lint
  • GitHub Check: site
  • GitHub Check: Kubernetes Chaos test
  • GitHub Check: Summary
🔇 Additional comments (15)
grpc/src/test/proto/testing/grpc/transcoding.proto (1)

252-260: LGTM! Test RPC method for additional bindings.

The new AdditionalBinding RPC method with multiple HTTP bindings (POST and PUT) provides good test coverage for the additional HTTP rules and conflict resolution features introduced in this PR.

grpc/src/test/java/com/linecorp/armeria/it/grpc/HttpJsonTranscodingTestService.java (1)

67-301: LGTM! Comprehensive test service implementation.

The test service provides thorough coverage of various transcoding scenarios including path parameters, query parameters, request/response body mappings, wrapper types, and special protobuf types (Timestamp, Duration, FieldMask, Struct, Any, etc.).

grpc/src/test/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingOptionsBuilderTest.java (1)

73-95: LGTM! Comprehensive test for copy constructor.

The test thoroughly verifies that the HttpJsonTranscodingOptionsBuilder copy constructor correctly preserves all fields including the newly added ignoreProtoHttpRule, additionalHttpRules, and the existing queryParamMatchRules and errorHandler.

grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingOptions.java (1)

47-87: LGTM! Well-designed API extension with excellent documentation.

The new methods provide flexible configuration for HTTP/JSON transcoding:

  • ignoreProtoHttpRule() enables strict control over exposed endpoints
  • additionalHttpRules() supports programmatic route configuration
  • conflictStrategy() handles rule conflicts predictably

The javadoc clearly explains the behavior, use cases, and important limitations (e.g., route conflicts between different methods are not resolved by the conflict strategy).

grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingConflictStrategy.java (1)

23-60: LGTM! Clean and well-documented strategy interface.

The functional interface provides a clear extension point for conflict resolution with sensible built-in strategies (firstWins() as default, lastWins() as alternative). The javadoc effectively distinguishes between selector-level conflicts (handled by this strategy) and route-level conflicts (which remain errors).

grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingServiceBuilder.java (4)

75-130: Well-structured builder initialization.

The factory method correctly handles both proto annotation-based rules and programmatic additional rules. The conditional extraction of proto HTTP rules based on ignoreProtoHttpRule() provides the flexibility described in the PR objectives.


369-388: Excellent defensive validation for conflict resolution.

The check at lines 382-386 ensures that custom conflict strategies cannot return arbitrary HttpRule instances, which prevents potential security or correctness issues. This is a well-designed safeguard.


335-348: Clear duplicate route detection.

The validation prevents ambiguous route mappings with an informative error message. This will help developers quickly identify configuration issues.


152-250: Robust recursive type handling.

The field mapping logic correctly handles:

  • Multiple match rule strategies (ORIGINAL_FIELD, LOWER_CAMEL_CASE, JSON_NAME)
  • Recursive message types via RecursiveTypeException control flow
  • Field name conflicts via buildKeepingLast()

The implementation is thorough and well-commented.

grpc/src/test/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingConflictStrategyTest.java (1)

1-172: Comprehensive test coverage for conflict resolution.

The test suite effectively validates:

  • Both firstWins() and lastWins() strategies work as expected
  • Additional bindings are properly removed when a primary rule is replaced
  • Cross-method route conflicts are detected and rejected
  • Invalid strategies that return unexpected rules are caught

This provides strong confidence in the conflict resolution implementation.

grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingService.java (1)

195-197: Appropriate visibility adjustments for builder pattern.

The visibility changes from private to package-private are well-justified:

  • Enables HttpJsonTranscodingServiceBuilder to construct instances
  • Maintains encapsulation by limiting visibility to the package
  • Uses @VisibleForTesting appropriately for test-only accessors

These changes align well with the builder-based refactoring.

Also applies to: 272-275, 597-621, 633-641

grpc/src/main/java/com/linecorp/armeria/server/grpc/DefaultHttpJsonTranscodingOptions.java (1)

30-61: Complete and correct implementation of new fields.

All new fields (ignoreProtoHttpRule, additionalHttpRules, conflictStrategy) are properly:

  • Initialized in the constructor
  • Exposed via accessor methods
  • Included in equals, hashCode, and toString

The immutable options implementation is consistent and well-structured.

Also applies to: 82-100

grpc/src/main/java/com/linecorp/armeria/server/grpc/HttpJsonTranscodingOptionsBuilder.java (3)

65-74: Well-designed copy constructor.

The copy constructor enables creating a builder from existing HttpJsonTranscodingOptions, which is useful for modifying configurations. The implementation correctly delegates to builder methods, maintaining consistency.


104-164: Excellent documentation and API design.

The new builder methods are well-documented with:

  • Clear explanations of each option's purpose and behavior
  • Use case examples (e.g., allowlist approach, third-party proto files)
  • Interaction details between options

The API design is intuitive with both varargs and Iterable overloads for additionalHttpRules.


180-200: Build method correctly integrates new fields.

The updated build() method properly collects all new configuration options and passes them to DefaultHttpJsonTranscodingOptions. The implementation maintains backward compatibility while supporting the new features.

@0x1306e6d 0x1306e6d requested review from ikhoon and jrhee17 January 8, 2026 09:09
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.

👍 👍

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 looks good. Left some comments about the API design and the default behavior.


private boolean ignoreProtoHttpRule;

private HttpJsonTranscodingConflictStrategy conflictStrategy = firstWins();
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure whether firstWins() is a sensible default. Rather than silently ignoring duplicate rules, how about adding strict() method that rejects duplicate rules? I think strict() would be a more reasonable default.

* This is the default behavior.
*/
static HttpJsonTranscodingConflictStrategy firstWins() {
return (selector, oldRule, newRule) -> oldRule;
Copy link
Contributor

Choose a reason for hiding this comment

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

Would you add a debug message explaining which HttpRule is ignored and why?

* Returns a strategy that always replaces the existing {@link HttpRule} with the new one.
*/
static HttpJsonTranscodingConflictStrategy lastWins() {
return (selector, oldRule, newRule) -> newRule;
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto.

* occurred, the {@link HttpRule} that was registered first (oldRule), and the {@link HttpRule}
* that is being added (newRule).
*/
HttpRule resolve(String selector, HttpRule oldRule, HttpRule newRule);
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 exposing a gRPC method descriptor instead of selector, so we don't have to explain what it is.

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.

4 participants