spring-cloud / spring-cloud/spring-cloud-gateway
[spring-cloud-gateway-mvc] GET requests fail on HTTP/2 upgrade
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 4.9k
- Forks
- 3.5k
- Avg merge
- 20h 57m
- Merged PRs (30d)
- 8
Description
Boot Version: 3.2.0-M3
Gateway Version: 4.1.0-RC1
We recently started with a new application and decided to use spring-cloud-gateway-mvc as the basis. When writing tests "around" our application with WireMock standing in for the target service we noticed that very simple GET requests were not processed successfully but failed with this error message:
java.io.EOFException: EOF reached while reading
at java.net.http/jdk.internal.net.http.Http2Connection$Http2TubeSubscriber.onComplete(Http2Connection.java:1587) ~[java.net.http:na]
at java.net.http/jdk.internal.net.http.SocketTube$InternalReadPublisher$ReadSubscription.signalCompletion(SocketTube.java:648) ~[java.net.http:na]
at java.net.http/jdk.internal.net.http.SocketTube$InternalReadPublisher$InternalReadSubscription.read(SocketTube.java:853) ~[java.net.http:na]
at java.net.http/jdk.internal.net.http.SocketTube$SocketFlowTask.run(SocketTube.java:181) ~[java.net.http:na]
at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run(SequentialScheduler.java:207) ~[java.net.http:na]
at java.net.http/jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(SequentialScheduler.java:280) ~[java.net.http:na]
at java.net.http/jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(SequentialScheduler.java:233) ~[java.net.http:na]
at java.net.http/jdk.internal.net.http.SocketTube$InternalReadPublisher$InternalReadSubscription.signalReadable(SocketTube.java:782) ~[java.net.http:na]
at java.net.http/jdk.internal.net.http.SocketTube$InternalReadPublisher$ReadEvent.signalEvent(SocketTube.java:965) ~[java.net.http:na]
at java.net.http/jdk.internal.net.http.SocketTube$SocketFlowEvent.handle(SocketTube.java:253) ~[java.net.http:na]
at java.net.http/jdk.internal.net.http.HttpClientImpl$SelectorManager.handleEvent(HttpClientImpl.java:1467) ~[java.net.http:na]
at java.net.http/jdk.internal.net.http.HttpClientImpl$SelectorManager.lambda$run$3(HttpClientImpl.java:1412) ~[java.net.http:na]
at java.base/java.util.ArrayList.forEach(ArrayList.java:1596) ~[na:na]
at java.net.http/jdk.internal.net.http.HttpClientImpl$SelectorManager.run(HttpClientImpl.java:1412) ~[java.net.http:na]
This only happens when WireMock is called through the gateway, attempts to reproduce the error with several HTTP client implementations calling WireMock directly were unsuccessful. Putting NGINX between the gateway and WireMock prevented the HTTP/2 upgrade and the request worked as expected. After some digging we found out that the JDK HTTP client used by Spring will send the GET request as stream with a Transfer-encoding: chunked header. This happens because the content-length is reported as -1 to the HTTP client as the request body that is passed to the bodyPublisher methond in HttpRequest.BodyPublisher is empty but not null.
I'm aware of the fact that the mentioned classes are residing in the spring-web part of the spring-framework project, but since it is no longer possible at this point to check for an empty body this will probably have to be fixed at an earlier stage.
What seems to trigger this error is that the server (Jetty in this case) initiates the upgrade and waits for the client to send the preface. Instead of doing so the client appears to send an empty chunk to indicate that it is done which then causes the server to send an ìnvalid_preface error and terminate the connection.
Sample
I added a small project that can be used to reproduce the issue. It just defines one route that passes everything to WireMock and contains tests that include a call to WireMock through the gateway (fails) and directly using the JDK client (succeeds). I would expect both tests to work once a fix has been applied.
http2-upgrade-demo.zip
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the attached http2-upgrade-demo.zip and compare the gateway-to-WireMock test with the direct JDK client test. Trace the Spring Cloud Gateway MVC request forwarding and the JDK HttpClient BodyPublisher handling around the HTTP/2 upgrade. Done means the gateway test succeeds without NGINX and no EOFException or invalid_preface error occurs.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java, spring, spring-boot
- Domain
- api, backend, networking
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100