Skip to content

Support for Envoy gRPC HTTP/1.1 bridge#6689

Merged
ikhoon merged 3 commits intoline:mainfrom
kwondh5217:envoy-http1-bridge
Apr 6, 2026
Merged

Support for Envoy gRPC HTTP/1.1 bridge#6689
ikhoon merged 3 commits intoline:mainfrom
kwondh5217:envoy-http1-bridge

Conversation

@kwondh5217
Copy link
Copy Markdown
Contributor

Motivation:

When serving framed gRPC over HTTP/1.1, Armeria delivers gRPC trailers via chunked transfer-encoding.
Some legacy L7 proxies cannot handle chunked trailers correctly, which breaks gRPC traffic.
The Envoy gRPC HTTP/1.1 bridge specification addresses this by merging gRPC trailers into response headers, producing a plain HTTP/1.1 response that legacy proxies can handle.

Modifications:

  • Add enableEnvoyHttp1Bridge(boolean) option to GrpcServiceBuilder
  • Pass the flag through FramedGrpcService down to UnaryServerCall
  • In UnaryServerCall.doClose(), merge gRPC trailers into response
    headers when the bridge is enabled and the connection is HTTP/1.1
  • The bridge applies only to unary methods; streaming methods fall back
    to the existing chunked transfer-encoding behavior
  • Add EnvoyHttp1BridgeTest to verify the behavior

Result:

  • Closes Support for Envoy gRPC HTTP/1.1 bridge #6664
  • When enableEnvoyHttp1Bridge(true) is set, unary gRPC responses over
    HTTP/1.1 include grpc-status and other trailers in the response
    headers instead of chunked trailers, improving compatibility with
    legacy proxy servers. HTTP/2 connections are unaffected.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 31, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 876c4a46-3e5b-4387-9cee-ba0d8f663a22

📥 Commits

Reviewing files that changed from the base of the PR and between d2e7941 and 370c2be.

📒 Files selected for processing (6)
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/FramedGrpcService.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/GrpcServiceBuilder.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/UnaryServerCall.java
  • grpc/src/test/java/com/linecorp/armeria/server/grpc/DeferredListenerTest.java
  • grpc/src/test/java/com/linecorp/armeria/server/grpc/EnvoyHttp1BridgeTest.java
  • grpc/src/test/java/com/linecorp/armeria/server/grpc/UnaryServerCallTest.java
✅ Files skipped from review due to trivial changes (1)
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/UnaryServerCall.java
🚧 Files skipped from review as they are similar to previous changes (3)
  • grpc/src/test/java/com/linecorp/armeria/server/grpc/DeferredListenerTest.java
  • grpc/src/test/java/com/linecorp/armeria/server/grpc/UnaryServerCallTest.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/GrpcServiceBuilder.java

📝 Walkthrough

Walkthrough

Adds an Envoy gRPC HTTP/1.1 bridge toggle to the gRPC builder, propagates it through FramedGrpcService into server-call constructors, and updates UnaryServerCall to optionally merge gRPC trailers into HTTP/1 response headers; includes tests covering bridge behavior across protocols, call types, and transcoding.

Changes

Cohort / File(s) Summary
Builder & Service wiring
grpc/src/main/java/com/linecorp/armeria/server/grpc/GrpcServiceBuilder.java, grpc/src/main/java/com/linecorp/armeria/server/grpc/FramedGrpcService.java
Add enableEnvoyHttp1Bridge flag on the builder and pass it into FramedGrpcService so subsequent server-call instances receive the flag.
Unary response handling
grpc/src/main/java/com/linecorp/armeria/server/grpc/UnaryServerCall.java
Add enableEnvoyHttp1Bridge field/ctor param; doClose() may merge trailers into response headers for non-multiplexed HTTP/1 when the bridge is enabled.
Streaming server calls
grpc/src/main/java/com/linecorp/armeria/server/grpc/StreamingServerCall.java
Propagate the new bridge flag into streaming call construction (no streaming logic change in this diff).
Unit / integration tests
grpc/src/test/java/com/linecorp/armeria/server/grpc/EnvoyHttp1BridgeTest.java, grpc/src/test/java/com/linecorp/armeria/server/grpc/UnaryServerCallTest.java, grpc/src/test/java/com/linecorp/armeria/server/grpc/DeferredListenerTest.java
Added EnvoyHttp1BridgeTest validating header-vs-trailer behavior and updated tests/helpers to pass the new constructor argument.
Misc (test constructor sites)
grpc/src/test/...
Adjusted test helper and call sites to include the new boolean parameter when constructing UnaryServerCall/StreamingServerCall.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Client as Client
participant Server as FramedGrpcService\n/ ServerCall
participant HTTP as HTTP Layer / Proxy
Note over Client,HTTP: HTTP/1 request (POST gRPC method)
Client->>Server: gRPC request frame(s)
Server->>Server: process RPC (unary or streaming)
alt Bridge enabled AND unary AND session is HTTP/1 (non-multiplexed)
Server->>Server: buffer response, extract trailers
Server->>HTTP: send response with merged grpc-status/grpc-message in headers
HTTP->>Client: response headers + body
else Bridge disabled OR streaming OR HTTP/2
Server->>HTTP: send response body and trailers (grpc-status in trailers)
HTTP->>Client: body then trailers
end

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

improvement

Suggested reviewers

  • trustin
  • minwoox

Poem

🐇 I hopped through headers, trailers in tow,
I learnt when to merge and when to let go.
For HTTP/1 bridges I softly conspire,
Copying whispers for old nets to admire.
Hooray — tidy responses, and rabbits inspire!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.34% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: adding support for the Envoy gRPC HTTP/1.1 bridge feature.
Description check ✅ Passed The description clearly explains the motivation, modifications, and results, directly relating to the changeset and the linked issue.
Linked Issues check ✅ Passed All code requirements from issue #6664 are met: enableEnvoyHttp1Bridge() option added [GrpcServiceBuilder], flag propagated to UnaryServerCall, trailers merged into headers for HTTP/1.1 unary calls, streaming unaffected, and comprehensive tests added.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the Envoy HTTP/1.1 bridge feature specified in #6664; no extraneous modifications detected.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Copy Markdown
Contributor

@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: 1

🧹 Nitpick comments (1)
grpc/src/test/java/com/linecorp/armeria/server/grpc/EnvoyHttp1BridgeTest.java (1)

105-107: Add one explicit HTTP/2 regression assertion for bridge-enabled unary calls.

Current tests validate HTTP/1.1 behavior well, but a dedicated HTTP/2 case would lock in the “HTTP/2 unaffected” objective and prevent accidental regressions.

Suggested test addition
+    private static BlockingWebClient h2cClient(ServerExtension server) {
+        return WebClient.of(server.uri(SessionProtocol.H2C)).blocking();
+    }
+
+    `@Test`
+    void bridgeNotAppliedOverHttp2() {
+        final BlockingWebClient client = h2cClient(bridgeServer);
+        final AggregatedHttpResponse response =
+                client.prepare()
+                      .post(TestServiceGrpc.getEmptyCallMethod().getFullMethodName())
+                      .content(GrpcSerializationFormats.PROTO.mediaType(), EMPTY_GRPC_FRAME)
+                      .execute();
+
+        assertThat(response.status()).isEqualTo(HttpStatus.OK);
+        assertThat(response.headers().get(GrpcHeaderNames.GRPC_STATUS)).isNull();
+        assertThat(response.trailers().get(GrpcHeaderNames.GRPC_STATUS)).isEqualTo("0");
+    }

Also applies to: 109-169

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@grpc/src/test/java/com/linecorp/armeria/server/grpc/EnvoyHttp1BridgeTest.java`
around lines 105 - 107, Add a focused HTTP/2 regression test in
EnvoyHttp1BridgeTest to assert that bridge-enabled unary calls remain HTTP/2:
create a helper analogous to h1cClient (e.g., h2cClient using
WebClient.of(server.uri(SessionProtocol.H2C)).blocking()) and add a test method
that uses that client to invoke the same unary endpoint exercised by the
HTTP/1.1 tests and assert the response uses HTTP/2 and the payload/headers match
expectations; place the test near the existing unary tests (the block around
h1cClient and tests at lines 109-169) so it runs alongside the current cases and
catches regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@grpc/src/main/java/com/linecorp/armeria/server/grpc/GrpcServiceBuilder.java`:
- Around line 654-657: The new public builder method enableEnvoyHttp1Bridge in
class GrpcServiceBuilder must be annotated with `@UnstableApi`; add the annotation
above the method declaration and ensure the corresponding import
(com.linecorp.armeria.common.annotation.UnstableApi) is added to the file so the
method follows the repository rule for newly added public APIs.

---

Nitpick comments:
In
`@grpc/src/test/java/com/linecorp/armeria/server/grpc/EnvoyHttp1BridgeTest.java`:
- Around line 105-107: Add a focused HTTP/2 regression test in
EnvoyHttp1BridgeTest to assert that bridge-enabled unary calls remain HTTP/2:
create a helper analogous to h1cClient (e.g., h2cClient using
WebClient.of(server.uri(SessionProtocol.H2C)).blocking()) and add a test method
that uses that client to invoke the same unary endpoint exercised by the
HTTP/1.1 tests and assert the response uses HTTP/2 and the payload/headers match
expectations; place the test near the existing unary tests (the block around
h1cClient and tests at lines 109-169) so it runs alongside the current cases and
catches regressions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9b10404b-50c1-4341-a506-8faf5c8e16d8

📥 Commits

Reviewing files that changed from the base of the PR and between 61c0fbf and 0d945bf.

📒 Files selected for processing (6)
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/FramedGrpcService.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/GrpcServiceBuilder.java
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/UnaryServerCall.java
  • grpc/src/test/java/com/linecorp/armeria/server/grpc/DeferredListenerTest.java
  • grpc/src/test/java/com/linecorp/armeria/server/grpc/EnvoyHttp1BridgeTest.java
  • grpc/src/test/java/com/linecorp/armeria/server/grpc/UnaryServerCallTest.java

Copy link
Copy Markdown
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.

Looks good overall, left a minor comment

@jrhee17 jrhee17 added this to the 1.38.0 milestone Apr 1, 2026
Copy link
Copy Markdown
Contributor

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

🧹 Nitpick comments (1)
grpc/src/test/java/com/linecorp/armeria/server/grpc/EnvoyHttp1BridgeTest.java (1)

151-240: Consider extracting a small request helper to reduce repetition.

The same client.prepare().post(...).content(...).execute() flow appears multiple times. A local helper would make future additions/edits less error-prone.

Proposed refactor
+    private static AggregatedHttpResponse postGrpcEmptyFrame(BlockingWebClient client, String methodPath) {
+        return client.prepare()
+                     .post(methodPath)
+                     .content(GrpcSerializationFormats.PROTO.mediaType(), EMPTY_GRPC_FRAME)
+                     .execute();
+    }
...
-        final AggregatedHttpResponse response =
-                client.prepare()
-                      .post(TestServiceGrpc.getEmptyCallMethod().getFullMethodName())
-                      .content(GrpcSerializationFormats.PROTO.mediaType(), EMPTY_GRPC_FRAME)
-                      .execute();
+        final AggregatedHttpResponse response =
+                postGrpcEmptyFrame(client, TestServiceGrpc.getEmptyCallMethod().getFullMethodName());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@grpc/src/test/java/com/linecorp/armeria/server/grpc/EnvoyHttp1BridgeTest.java`
around lines 151 - 240, Multiple tests repeat the
client.prepare().post(...).content(...).execute() flow; extract a small helper
(e.g., sendPost(BlockingWebClient client, String pathOrMethod, MediaType
contentType, byte[] body) or an overloaded sendPost for String bodies) and
replace the repeated chains in tests like
trailersAreMergedIntoHeadersForUnaryOverHttp1,
grpcStatusInHeadersOnErrorOverHttp1, bridgeNotAppliedForStreamingMethod,
bridgeNotAppliedWhenDisabled, bridgeNotAppliedOverHttp2 and
jsonTranscodingNotAffectedByBridge to call this helper to reduce duplication and
make future edits safer. Ensure the helper returns AggregatedHttpResponse and
supports both GRPC method full names and regular URI paths.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@grpc/src/test/java/com/linecorp/armeria/server/grpc/EnvoyHttp1BridgeTest.java`:
- Around line 151-240: Multiple tests repeat the
client.prepare().post(...).content(...).execute() flow; extract a small helper
(e.g., sendPost(BlockingWebClient client, String pathOrMethod, MediaType
contentType, byte[] body) or an overloaded sendPost for String bodies) and
replace the repeated chains in tests like
trailersAreMergedIntoHeadersForUnaryOverHttp1,
grpcStatusInHeadersOnErrorOverHttp1, bridgeNotAppliedForStreamingMethod,
bridgeNotAppliedWhenDisabled, bridgeNotAppliedOverHttp2 and
jsonTranscodingNotAffectedByBridge to call this helper to reduce duplication and
make future edits safer. Ensure the helper returns AggregatedHttpResponse and
supports both GRPC method full names and regular URI paths.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b45505fc-b2eb-4b01-b368-0bdc1ac50494

📥 Commits

Reviewing files that changed from the base of the PR and between 0d945bf and d2e7941.

📒 Files selected for processing (2)
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/GrpcServiceBuilder.java
  • grpc/src/test/java/com/linecorp/armeria/server/grpc/EnvoyHttp1BridgeTest.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • grpc/src/main/java/com/linecorp/armeria/server/grpc/GrpcServiceBuilder.java

Copy link
Copy Markdown
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
Copy Markdown
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.

Thanks, @kwondh5217! 👍🚀

Signed-off-by: Daeho Kwon <trewq231@naver.com>
Signed-off-by: Daeho Kwon <trewq231@naver.com>
@kwondh5217 kwondh5217 force-pushed the envoy-http1-bridge branch from d2e7941 to 370c2be Compare April 2, 2026 11:30
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 2, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 73.97%. Comparing base (8150425) to head (370c2be).
⚠️ Report is 386 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff              @@
##               main    #6689      +/-   ##
============================================
- Coverage     74.46%   73.97%   -0.49%     
- Complexity    22234    23997    +1763     
============================================
  Files          1963     2170     +207     
  Lines         82437    90034    +7597     
  Branches      10764    11788    +1024     
============================================
+ Hits          61385    66601    +5216     
- Misses        15918    17860    +1942     
- Partials       5134     5573     +439     

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

Thanks! 👍

@ikhoon ikhoon merged commit 1472702 into line:main Apr 6, 2026
14 of 17 checks passed
@kwondh5217 kwondh5217 deleted the envoy-http1-bridge branch April 6, 2026 13:19
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.

Support for Envoy gRPC HTTP/1.1 bridge

4 participants