Skip to content

WebClient throws "java.lang.IllegalStateException: Only one connection receive subscriber allowed" on Post Method with content-type x-www-form-urlencoded #22284

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
awilhelmer opened this issue Jan 21, 2019 · 8 comments
Assignees
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: invalid An issue that we don't feel is valid

Comments

@awilhelmer
Copy link

awilhelmer commented Jan 21, 2019

Affects: Spring Boot v2.1.2

When I create a REST Controller which delegates POST requests to another web service i get the following exception when setting content-type header to x-www-form-urlencoded:

java.lang.IllegalStateException: Only one connection receive subscriber allowed.
	at reactor.netty.channel.FluxReceive.startReceiver(FluxReceive.java:271) [reactor-netty-0.8.4.RELEASE.jar:0.8.4.RELEASE]
	at reactor.netty.channel.FluxReceive.lambda$subscribe$2(FluxReceive.java:124) [reactor-netty-0.8.4.RELEASE.jar:0.8.4.RELEASE]
	at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163) ~[netty-common-4.1.31.Final.jar:4.1.31.Final]
	at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404) ~[netty-common-4.1.31.Final.jar:4.1.31.Final]
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:466) ~[netty-transport-4.1.31.Final.jar:4.1.31.Final]
	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:897) ~[netty-common-4.1.31.Final.jar:4.1.31.Final]
	at java.lang.Thread.run(Thread.java:745) ~[na:1.8.0_121]

Run the test case to reproduce the error or run application and excecute the following cURL:

curl -X POST "http://localhost:8080/test/post" -H "content-type: application/x-www-form-urlencoded"

Attachment:
one-connection.zip

Similar Issue: #22096

@sdeleuze sdeleuze added the status: waiting-for-triage An issue we've not yet triaged or decided on label Jan 21, 2019
@sdeleuze
Copy link
Contributor

I had a similar issue while migrating https://github.com/mixitconf/mixit to recent Spring Boot version, I will have a look to see if that's related.

@bclozel bclozel self-assigned this Jan 21, 2019
@bclozel bclozel added for: team-attention in: web Issues in web modules (web, webmvc, webflux, websocket) status: invalid An issue that we don't feel is valid and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Jan 21, 2019
@bclozel
Copy link
Member

bclozel commented Jan 21, 2019

Hello @awilhelmer

This is a known behavior, and we don't want to change it because it would break consistency in many other areas. In this case, what happens is:

  1. Because this is a form request, the HiddenHttpMethodFilter that's configured in Spring Boot by default is reading the form data to update the request method if needed
  2. in WebFlux, reading the form data with request.getFormData() (as done in the filter) subscribes to the request body to parse the incoming data; with reactor netty, subscribing is only allowed once by design
  3. Once read, the form data is cached in the exchange, but re-subscribing to the raw body is forbidden. Calling request.getBody() in your code does just that

Now there are several ways to consider this problem.

If the application is mainly a Spring WebFlux application using the annotation model, and sometimes binding form data to annotations and using the hidden method trick for forms sent by browsers, then the WebClient sample in your code should get the data from request.getFormData() or request.getMultiPartData() as this information is cached and won't trigger a new subscription. This is the choice we've made in #21824.

If the application is mainly a gateway, then this issue is a duplicate of spring-cloud/spring-cloud-gateway#541. In that case you could disable the HiddenHttpMethodFilter with the property spring.webflux.hiddenmethod.filter.enabled=false introduced in Spring Boot 2.1 with spring-projects/spring-boot#14520, or better use spring-cloud-gateway directly.

The only other alternative would be to disable by default this filter In Spring Boot, but we've decided against that in spring-projects/spring-boot#14520 as this would break user expectations in many other cases and feature parity with Spring MVC. It seems that even on modern browsers/front-end stacks, this is still useful to web applications.

I'm closing this as "works as designed", but feel free to continue the discussion here if you've got new ideas to share. Thanks!

@awilhelmer
Copy link
Author

Hello @bclozel ,

thanks for your fast reply. Indeed spring.webflux.hiddenmethod.filter.enabled=false helps a lot and I can handle the POST requests now.

Nevertheless, I think the exception is confusing for a framework like Spring. So if a client provides a wrong header it will result in this exception and you don't know exactly what the problem is (e.g. your API want JSON but a client sends x-www-form-urlencoded). You should at least extend the Javadoc at request.getBody() and provide an exact exception for this scenario.
Shouldn't getBody() also be cached for API consistency?

IMHO the API is broken if methods only works under certain conditions.

@bclozel
Copy link
Member

bclozel commented Jan 22, 2019

@awilhelmer I've marked this issue for team attention already - thanks for your comments.

@sdeleuze
Copy link
Contributor

Additional info from the similar error that occurred while upgrading MiXiT project to latest Boot version: it was as well caused by HiddenHttpMethodFilter enabled by default + usage of request.body(toFormData()) in the application. The error could be solved by using request.formData() or configuring spring.webflux.hiddenmethod.filter.enabled=false.

@rstoyanchev
Copy link
Contributor

One idea would be to wrap the request in DefaultServerWebExchange for form requests and raise a more helpful error.

@hmble2
Copy link

hmble2 commented Apr 23, 2019

@rstoyanchev Please see issue that @violetagg linked, I think this needs to be handled gracefully by framework reactor/reactor-netty#718

@rstoyanchev
Copy link
Contributor

@hmble2 see #22486.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: invalid An issue that we don't feel is valid
Projects
None yet
Development

No branches or pull requests

6 participants