spring-cloud / spring-cloud/spring-cloud-gateway

InvalidMediaTypeException: Invalid mime type \"text/plain; charset=x-user-defined\": unsupported charset 'x-user-defined'

Open
#3,695 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

waiting-for-triage
Dominant language
Java
Stars
4.9k
Forks
3.5k
Avg merge
20h 57m
Merged PRs (30d)
8

Description

Describe the bug
Upgrade spring-cloud-starter-gateway-mvc from 4.1.2 to 4.2.0, an InvalidMediaTypeException occurred in the gateway application.
For details, we have a Web UI which sends Ajax request with mimeType = 'text/plain; charset=x-user-defined' to the gateway application, the request would be routed to another service behind and response with file content that being encrypted(this is why we use the non standard charset x-user-defined). The error logs are:

org.springframework.http.InvalidMediaTypeException: Invalid mime type "text/plain; charset=x-user-defined": unsupported charset 'x-user-defined'
at org.springframework.http.MediaType.parseMediaType(MediaType.java:760) ~[spring-web-6.2.2.jar:6.2.2]
at org.springframework.http.HttpHeaders.getContentType(HttpHeaders.java:1048) ~[spring-web-6.2.2.jar:6.2.2]
at org.springframework.http.ReadOnlyHttpHeaders.getContentType(ReadOnlyHttpHeaders.java:65) ~[spring-web-6.2.2.jar:6.2.2]
at org.springframework.cloud.gateway.server.mvc.common.AbstractProxyExchange.copyResponseBody(AbstractProxyExchange.java:45) ~[spring-cloud-gateway-server-mvc-4.2.0.jar:4.2.0]
at org.springframework.cloud.gateway.server.mvc.handler.RestClientProxyExchange.lambda$doExchange$3(RestClientProxyExchange.java:83) ~[spring-cloud-gateway-server-mvc-4.2.0.jar:4.2.0]
at org.springframework.cloud.gateway.server.mvc.handler.GatewayServerResponseBuilder$WriteFunctionResponse.writeToInternal(GatewayServerResponseBuilder.java:231) ~[spring-cloud-gateway-server-mvc-4.2.0.jar:4.2.0]
at org.springframework.cloud.gateway.server.mvc.handler.AbstractGatewayServerResponse.writeTo(AbstractGatewayServerResponse.java:103) ~[spring-cloud-gateway-server-mvc-4.2.0.jar:4.2.0]
at org.springframework.web.servlet.function.support.HandlerFunctionAdapter.handle(HandlerFunctionAdapter.java:112) ~[spring-webmvc-6.2.2.jar:6.2.2]
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1088) ~[spring-webmvc-6.2.2.jar:6.2.2]
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:978) ~[spring-webmvc-6.2.2.jar:6.2.2]
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-6.2.2.jar:6.2.2]
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) ~[spring-webmvc-6.2.2.jar:6.2.2]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) ~[servlet-api.jar:6.0]
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[spring-webmvc-6.2.2.jar:6.2.2]
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[servlet-api.jar:6.0]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) ~[catalina.jar:10.1.31]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) ~[catalina.jar:10.1.31]
...

It looks like the new version of spring-cloud-starter-gateway-mvc has added something kind of validation to check if the Http response contains non-standard mime type or charset. In our case, the service behind gateway would return an Http header Content-Type: 'text/plain; charset=x-user-defined'.

I've looked into the AbstractProxyExchange#copyResponseBody method, this was introduced in 4.1.6 to support some streaming media types -
AbstractProxyExchange.java

	protected int copyResponseBody(ClientHttpResponse clientResponse, InputStream inputStream,
			OutputStream outputStream) throws IOException {
                ...
		int transferredBytes;

		if (properties.getStreamingMediaTypes().contains(clientResponse.getHeaders().getContentType())) {
			transferredBytes = copyResponseBodyWithFlushing(inputStream, outputStream);
		}
		else {
			transferredBytes = StreamUtils.copy(inputStream, outputStream);
		}

		return transferredBytes;
	}

The error was caused by clientResponse.getHeaders().getContentType(), this de facto checks that all Http responses do not contain a charset that is not part of the JRE standard, which is a breaking change to some legacy or specialized services behind Spring Cloud Gateway.

In this case, I expect it goes with the else block for just doing IO without validation. I can override this with a customized RestClientProxyExchange bean as a temp workaround though, would you consider to support non-standard charsets or media types in the Content-Type header in a future release? Thanks!

Sample
N/A

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/common/AbstractProxyExchange.java, especially copyResponseBody and its call from RestClientProxyExchange. Reproduce the response with Content-Type text/plain; charset=x-user-defined and inspect the gateway MVC tests. Done means the response body can be proxied without InvalidMediaTypeException for this non-standard charset.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring, spring-boot
Domain
api, backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.