Skip to content

servlet: force always sending end_stream in trailer frame (Fixes #10124) - #10125

Closed
hypnoce wants to merge 1 commit into
grpc:masterfrom
hypnoce:tomcat_empty_response
Closed

hypnoce wants to merge 1 commit into
grpc:masterfrom
hypnoce:tomcat_empty_response

Conversation

@hypnoce

@hypnoce hypnoce commented May 3, 2023 •

Copy link
Copy Markdown
Contributor

Add option to force servlet transport to send trailers to avoid empty data frame with end_stream flag from servlet containers implementation (currently Tomcat).
fixes #10124

@linux-foundation-easycla

linux-foundation-easycla Bot commented May 3, 2023 •

Copy link
Copy Markdown

CLA Signed

The committers listed above are authorized under a signed CLA.

  • ✅ login: hypnoce / name: François JACQUES (6713b8d)

@hypnoce
hypnoce force-pushed the tomcat_empty_response branch 2 times, most recently from c96be19 to 0d0d716 Compare May 3, 2023 06:05
@ejona86

ejona86 commented May 3, 2023

Copy link
Copy Markdown
Member

Hmm... this is a troublesome issue. We might want to fix support for trailers-only with a zero-length data frame following.

For the short-term: nice job on this PR. I think it is pretty solid. But I'm not sure we want to go this way. I'm a hesitant to have the transport introduce the headers, because that is changing the RPC semantics and interceptors wouldn't react.

A potentially easier workaround in terms of semantics and implementation would be "do this same approach in an interceptor." That way it can be positioned such that other interceptors could add headers if they need to. But it would require applications to use the interceptor.

Thoughts?

@hypnoce
hypnoce force-pushed the tomcat_empty_response branch from 0d0d716 to 1cd5746 Compare May 3, 2023 19:59
@hypnoce

hypnoce commented May 3, 2023 •

Copy link
Copy Markdown
Contributor Author

We might want to fix support for trailers-only with a zero-length data frame following.

True. Now it changes a lot the control flows because we need to wait for the next data frame to ensure to handle headers as trailers if end_stream flag is set and this data frame is empty.

nice job on this PR. I think it is pretty solid.

Thanks a lot :)

I'm a hesitant to have the transport introduce the headers, because that is changing the RPC semantics and interceptors wouldn't react.

From what I understood from the gRPC flow, when writeTrailer is called with headerSent=false, that means that no headers were added by any interceptor. Also, some interceptors might decide to directly close the call without calling call.sendHeader. As far as I know, this PR does not break interceptor semantic. Do you have a use case in mind ?

Now we could add an interceptor at the very beginning of the interceptors chain and implement the sendHeader and close methods of the SimpleForwardingServerCall and force sending 'empty' headers if sendHeader was not called upon closing the call.
I can work on a prototype tomorrow. WDYT ?

@ejona86

ejona86 commented May 3, 2023

Copy link
Copy Markdown
Member

As far as I know, this PR does not break interceptor semantic.

On server-side, everything looks normal. But on client-side headers appear and those were not seen on server-side. So client-side can then get confused (e.g., a key should have been in headers, but wasn't).

Now we could add an interceptor at the very beginning of the interceptors chain and implement the sendHeader and close methods of the SimpleForwardingServerCall and force sending 'empty' headers if sendHeader was not called upon closing the call.

Yes, that is what I was suggesting. I think that should turn out to be really easy.

@hypnoce
hypnoce force-pushed the tomcat_empty_response branch from 1cd5746 to a617e77 Compare May 4, 2023 07:20
@hypnoce

hypnoce commented May 4, 2023 •

Copy link
Copy Markdown
Contributor Author

On server-side, everything looks normal. But on client-side headers appear and those were not seen on server-side. So client-side can then get confused (e.g., a key should have been in headers, but wasn't).

True, I get the point. The only weird case I can think of is that the client calls ClientCall.Listener#onHeaders with an empty Metadata (stripped from :status, grpc-message and grpc-status keys). Just for my information, is there a case where a key should have been in headers but are in trailers with the transport-level fix ? I'm turning the problem in my head and I cannot think of any reproducible case.

Yes, that is what I was suggesting. I think that should turn out to be really easy.

@ejona86 I changed the PR to use a server interceptor. Obviously the transport level test do fail again but the interop tests do pass. Let me know what you think. Also, it cannot run before any global server interceptors. One final limitation are runtime exceptions. Handling them seems to be a bit tricky when not implemented at transport level.

@hypnoce
hypnoce force-pushed the tomcat_empty_response branch 3 times, most recently from a61ae5e to 492f610 Compare May 5, 2023 13:30
Comment thread servlet/src/main/java/io/grpc/servlet/ForceTrailersServerInterceptor.java Outdated
Comment thread servlet/src/main/java/io/grpc/servlet/ForceTrailersServerInterceptor.java Outdated
import io.grpc.ServerInterceptor;
import io.grpc.Status;

class ForceTrailersServerInterceptor implements ServerInterceptor {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I had thought we'd allow the user to use this themselves. They may need to use it two or more times, between various interceptors.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

To be clear here, I thought we'd make this public (and so it'd also need an @ExperimentalApi annotation).

@hypnoce
hypnoce force-pushed the tomcat_empty_response branch 3 times, most recently from c6f858b to 3f797f7 Compare May 5, 2023 14:51
Comment thread servlet/src/main/java/io/grpc/servlet/ForceTrailersServerInterceptor.java Outdated
import io.grpc.ServerInterceptor;
import io.grpc.Status;

class ForceTrailersServerInterceptor implements ServerInterceptor {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

To be clear here, I thought we'd make this public (and so it'd also need an @ExperimentalApi annotation).

@ejona86 ejona86 added the kokoro:run Add this label to a PR to tell Kokoro the code is safe and tests can be run label May 9, 2023
@grpc-kokoro grpc-kokoro removed the kokoro:run Add this label to a PR to tell Kokoro the code is safe and tests can be run label May 9, 2023
@hypnoce
hypnoce force-pushed the tomcat_empty_response branch 5 times, most recently from 4b0d14f to 63221e3 Compare May 12, 2023 10:53
@larry-safran
larry-safran self-requested a review June 1, 2023 20:39
@larry-safran

Copy link
Copy Markdown
Contributor

Just need to fix formatting of TomcatInteropTest:

Error: eckstyle] [ERROR] /home/runner/work/grpc-java/grpc-java/servlet/src/tomcatTest/java/io/grpc/servlet/TomcatInteropTest.java:40: Extra separation in import group before 'java.io.File' [CustomImportOrder]

Error: eckstyle] [ERROR] /home/runner/work/grpc-java/grpc-java/servlet/src/tomcatTest/java/io/grpc/servlet/TomcatInteropTest.java:40: Wrong lexicographical order for 'java.io.File' import. Should be before 'org.junit.Test'. [CustomImportOrder]
Error: eckstyle] [ERROR] /home/runner/work/grpc-java/grpc-java/servlet/src/tomcatTest/java/io/grpc/servlet/TomcatInteropTest.java:132: Line is longer than 100 characters (found 153). [LineLength]
Error: eckstyle] [ERROR] /home/runner/work/grpc-java/grpc-java/servlet/src/tomcatTest/java/io/grpc/servlet/TomcatInteropTest.java:133: Line is longer than 100 characters (found 121). [LineLength]
Error: eckstyle] [ERROR] /home/runner/work/grpc-java/grpc-java/servlet/src/tomcatTest/java/io/grpc/servlet/TomcatInteropTest.java:147: 'METHOD_DEF' should be separated from previous statement. [EmptyLineSeparator]
Error: eckstyle] [ERROR] /home/runner/work/grpc-java/grpc-java/servlet/src/tomcatTest/java/io/grpc/servlet/TomcatInteropTest.java:148: Line is longer than 100 characters (found 151). [LineLength]

@hypnoce

hypnoce commented Jul 28, 2023

Copy link
Copy Markdown
Contributor Author

@ejona86 Sorry to ping you again. Where do you stand regarding this issue ?

@hypnoce

hypnoce commented Dec 7, 2023

Copy link
Copy Markdown
Contributor Author

@ejona86 I put back the initial fix here: hypnoce@2eda56a
it seems to cover more use cases than the 'interceptor' one. Let me know. Thanks !

@sergiitk sergiitk added this to the 1.62 milestone Jan 11, 2024
@hypnoce
hypnoce force-pushed the tomcat_empty_response branch 2 times, most recently from 5a69eb7 to 15542f9 Compare July 1, 2024 08:36
…#10124)

Signed-off-by: hypnoce <hypnoce@donarproject.org>
@hypnoce
hypnoce force-pushed the tomcat_empty_response branch from 15542f9 to 6713b8d Compare July 1, 2024 08:44
@hypnoce

hypnoce commented Jul 10, 2024

Copy link
Copy Markdown
Contributor Author

@ejona86 Do you still think this PR can be merged ? We are maintaining a gRPC servlet fork on our side and going back to the mainstream gRPC would be a plus. Thanks !

@kannanjgithub kannanjgithub modified the milestones: 1.66, 1.67 Aug 1, 2024
@shivaspeaks shivaspeaks modified the milestones: 1.67, 1.68 Sep 16, 2024
@ejona86 ejona86 modified the milestones: 1.68, 1.69 Oct 24, 2024
@long76

long76 commented Oct 31, 2024

Copy link
Copy Markdown

up vote fo this PR

@trinque

trinque commented Dec 1, 2024

Copy link
Copy Markdown

Friendly ping for merge. It seems like this is both approved and passing.

@ejona86 ejona86 modified the milestones: 1.69, 1.70 Dec 6, 2024
@shivaspeaks shivaspeaks modified the milestones: 1.70, 1.71 Jan 8, 2025
@kannanjgithub kannanjgithub modified the milestones: 1.71, 1.72 Feb 18, 2025
@AgraVator AgraVator modified the milestones: 1.72, 1.73 Apr 3, 2025
@ejona86
ejona86 self-requested a review April 4, 2025 20:18
@shivaspeaks shivaspeaks modified the milestones: 1.73, 1.74 May 14, 2025
@ejona86 ejona86 removed this from the 1.74 milestone Jun 25, 2025
@stefanpieper

Copy link
Copy Markdown

Hi there, stumbled over the issue a few days ago and finally found this fix. We applied it to our scenario and it seems to work just fine. Would you consider to merge hypnoce's fix so that we may use the servlet-based server?

@ejona86

ejona86 commented Nov 12, 2025

Copy link
Copy Markdown
Member

In my comments for this PR I kept talking about the interceptor, as that's the only safe approach to this problem on the server-side. However, that does nothing for deadlines which are really important, and so with #12510 I'm essentially giving up on Tomcat.

I've written some in #10124 and #12510 about what's going on. This really needs a client-side fix, but Tomcat is simply not important enough for me to convince others to allow its valid behavior.

I know this will disappoint people and there will be replies. Let's have those discussions in #10124; not here.

@ejona86 ejona86 closed this Nov 12, 2025
@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Feb 11, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

servlet: tomcat does not support trailer only