spring-cloud-gateway icon indicating copy to clipboard operation
spring-cloud-gateway copied to clipboard

ProxyExchange fails with "IOException: insufficient data written" with boot 3.2

Open jkuipers opened this issue 1 year ago • 19 comments
trafficstars

The ProxyExchange type in spring-cloud-gateway-mvc copies over the headers from the incoming request, unless they're marked as sensitive. In addition, it also uses the HTTP message converters to unmarshal the payload of the incoming request and marshal it again when sending. What we found is that in the header copying, it also includes the Content-Length header of the incoming request. However, b/o the unmarshalling and then marshalling the content length of the proxied request can be different: especially in the case of JSON where whitespace is removed, it can become less.

This causes problems with some downstream servers, as they keep waiting for additional request contents based on the erroneous Content-Length header. When not using smth like Apache Component HTTP client but using the default Java client instead, this even leads to errors in the client itself (showcased in attached sample).

This behavior was not there in previous versions: actually, with Spring Boot 3.1.6 and the underlying Spring web version this works. When you check what's being sent then you can see that a Content-Length header is always set, with the correct value. It looks like the latest Spring web code (Jackson HTTP message converter?) no longer sets its own Content-Length header when marshalling to JSON. Therefore, spring-cloud-gateway-mvc should never copy over this header from the incoming request, but should rather leave it up to the RestTemplate to either leave it out or set this to the correct value for the outgoing request.

BTW, although this isn't strictly related to this issue, I believe that the Host header of the incoming request shouldn't be copied either by default: this can also lead to issues.

Sample I've attached a project with two integration tests that showcase the problem. By configuring the content-length to be a sensitive header you can work around the issue, but users of the framework should not be required to do this. gateway-mvc-contentlength.zip

jkuipers avatar Nov 30 '23 15:11 jkuipers

Posting for those encountering, the workaround is to set

spring.cloud.gateway.proxy.sensitive=content-length

spencergibb avatar Dec 05 '23 19:12 spencergibb

Why don't you pass the body in the ProxyController?

spencergibb avatar Dec 06 '23 00:12 spencergibb

The body isn’t modified by my code, why would I pass it myself? This is done automatically. The way that we use this, we just want to pass the request with some modified headers and path to the downstream service, which I think is the typical use case for a proxy solution. The controller doesn't care about the type of a potential payload of the request, nor about the response payload type: that's why we use ProxyExchange<byte[]>.

We could explicitly add an @RequestBody byte[] body parameter to the controller to force the conversion to not use the Jackson message converter, but to me that feels like just another workaround: I think it's confusing to explicitly ask for input that you don't actually process in your controller method just to prevent the ProxyExchange from producing an invalid HTTP request (invalid in the sense that its Content-Length value is too low). In our current use case we don't really mind the fact that the JSON message conversion is done: it also provides an additional layer of validation to ensure that the payload is in fact valid JSON. In general, since this is all just Spring MVC, other uses might involve unmarshalling the request body to some type (even outside of the controller) without the proxying code being interested in the body, and you'd expect that to work without having to compensate for headers with invalid values I think.

jkuipers avatar Dec 06 '23 06:12 jkuipers

I am trying to work around this issue and I am not sure exactly how the property should look like:

  • spring.cloud.gateway.proxy.sensitive=content-length
  • spring.cloud.gateway.mvc.proxy.sensitive=content-length

It seems like for routes are spring.cloud.gateway.mvc.routes....

Any clue about that? Btw, I am not using any Java code, just the configuration in the application.yml file.

Besides, the link to the current documentation is broken https://docs.spring.io/spring-cloud-gateway/reference/index.html

estigma88 avatar Dec 11 '23 21:12 estigma88

@estigma88 docs are back. This is only for using spring-cloud-starter-gateway-mvc not either of the server variants that have routes, so your issue is unrelated. The correct property is in my previous comment, but doesn't apply to your situation.

spencergibb avatar Dec 11 '23 21:12 spencergibb

It might be related, this is an example of what's happening, the body is not passing through:

  • From the URL /other/transcoder/v1/break
  • To the URL /base/transcoder/v1/break

Request is passthrough but no body.

estigma88 avatar Dec 11 '23 22:12 estigma88

@estigma88 please open a separate issue. You code uses the gateway server mvc, this issue is around ProxyExchange, not server

spencergibb avatar Dec 11 '23 22:12 spencergibb

Would it help to create a pull request to address this? I didn't consider that when I reported the issue as it seems trivial to fix, but given that it's been open for 4 months already and that various people have encountered issues b/o this bug I think it would be good to have a released solution ASAP.

jkuipers avatar Mar 20 '24 13:03 jkuipers

Yes, PRs welcome. We have an upcoming release in a week

spencergibb avatar Mar 20 '24 14:03 spencergibb

OK, I've created https://github.com/spring-cloud/spring-cloud-gateway/pull/3313 to address the issue for both the MVC and Webflux version.

jkuipers avatar Mar 21 '24 15:03 jkuipers

I thought the idea was that a PR could be considered for this week's release, but I see that the release was already cut without any feedback on the PR that I created last week... Any chance to get this into the next release, at least? Or any feedback on what might still be needed for that to happen?

jkuipers avatar Mar 28 '24 09:03 jkuipers

Sorry about that. I'll look at it again soon

spencergibb avatar Mar 28 '24 11:03 spencergibb

Posting for those encountering, the workaround is to set

spring.cloud.gateway.proxy.sensitive=content-length

Note that setting it like this removes all the default sensitive headers and therefore is likely to let you expose secrets to downstream services that you’re proxying, so please don’t copy this as-is.

jkuipers avatar Apr 07 '24 09:04 jkuipers

Hey, it's been 5 five months since reporting this and four weeks since providing a suggested fix: any chance of getting a review on the PR? Right now both ProxyExchange modules are broken as shipped in the latest releases...

jkuipers avatar Apr 19 '24 11:04 jkuipers

@spencergibb is there any updates about bug status?

larkioking avatar Apr 26 '24 10:04 larkioking

Missed yet another release, I see. I'm going to give up on chasing this: it's very puzzling to me how an official Spring project simply ignores shipping two broken modules for half a year already when it's clear what the problem is and there's a proposed solution.

jkuipers avatar May 07 '24 10:05 jkuipers

It'll be in the May release

spencergibb avatar May 07 '24 12:05 spencergibb

It'll be in the May release

narrator: it wasn't

jkuipers avatar Jun 04 '24 15:06 jkuipers

I just come here to say that I am also in that position. I upgraded spring boot 2.2.x to 3.2.x and this ProxyExchange is the simplest way for us to replace netflix-zuul.

But because of this header problem, for which a fix is waiting to be merged for many months now (thank you @jkuipers for providing that!), this migration is in limbo again.

It would be really nice to either put it into the next release or otherwise update us if there is a problem with that proposed solution (maybe the different configuration property is? Then please say so @spencergibb )

mindhaq avatar Jun 11 '24 01:06 mindhaq

@spencergibb hello!

Can you download updated artifact (v 4.1.5) in mvnrepo please?

larkioking avatar Jul 11 '24 13:07 larkioking