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

In spring cloud gateway mvc, an exception occurs when the request address contains spaces

Open zengqinglei opened this issue 1 year ago • 9 comments

Describe the bug Exception occurs when the request address contains spaces: Invalid character ' ' for QUERY_PARAM in "2023-12-11 00:00:00" image

Before this, I was using the webflux gateway and this problem did not occur.

Details spring boot 3.2.0 spring cloud 2023.0.0 spring cloud starter gateway mvc 4.1.0 request url: http://localhost:8080/enterprise-service/api/v3/enterprise?start_created_time=2023-12-11 00:00:00&end_created_time=2023-12-12 00:00:00

I hope your team can help us analyze the possible causes and provide some solutions as soon as possible. Thank you very much!

zengqinglei avatar Dec 14 '23 03:12 zengqinglei

In the future, please paste text rather than screenshots.

How are you accessing that URL? Through a browser it automatically URL encodes it.

spencergibb avatar Dec 19 '23 22:12 spencergibb

@spencergibb I tried using the browser and Apifox but got the same error.

URL: http://localhost:8080/enterprise-service/api/v3/enterprise?start_created_time=2023-12-11%2000%3A00%3A00&end_created_time=2023-12-12%2000%3A00%3A00

zengqinglei avatar Dec 20 '23 01:12 zengqinglei

Replacing spaces with a + things go through. I'm just not sure it's the responsibility of the gateway to do that.

Maybe @rstoyanchev might have an opinion?

spencergibb avatar Dec 20 '23 16:12 spencergibb

Hi, we're facing issues with all encoded characters in query parameters. like the norwegian æøå, among others (including space). I think this may be the same issue. I tried debugging the issue, and it seems we end up in a state where the encoded variable is true (based on the raw url) and then trying to add the decoded params (from serverRequest.params()) here:

https://github.com/spring-cloud/spring-cloud-gateway/blob/main/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/handler/ProxyExchangeHandlerFunction.java#L69-L77

Flipping the encoded value works fine. Not sure the fix is as simple as negating it, though.

Stack trace as text:

java.lang.IllegalArgumentException: Invalid character ' ' for QUERY_PARAM in " "
	at org.springframework.web.util.HierarchicalUriComponents.verifyUriComponent(HierarchicalUriComponents.java:422) ~[spring-web-6.1.1.jar:6.1.1]
	at org.springframework.web.util.HierarchicalUriComponents.lambda$verify$4(HierarchicalUriComponents.java:390) ~[spring-web-6.1.1.jar:6.1.1]
	at org.springframework.util.UnmodifiableMultiValueMap.lambda$forEach$0(UnmodifiableMultiValueMap.java:114) ~[spring-core-6.1.1.jar:6.1.1]
	at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:721) ~[?:?]
	at org.springframework.util.MultiValueMapAdapter.forEach(MultiValueMapAdapter.java:179) ~[spring-core-6.1.1.jar:6.1.1]
	at org.springframework.util.UnmodifiableMultiValueMap.forEach(UnmodifiableMultiValueMap.java:114) ~[spring-core-6.1.1.jar:6.1.1]
	at org.springframework.web.util.HierarchicalUriComponents.verify(HierarchicalUriComponents.java:387) ~[spring-web-6.1.1.jar:6.1.1]
	at org.springframework.web.util.HierarchicalUriComponents.<init>(HierarchicalUriComponents.java:146) ~[spring-web-6.1.1.jar:6.1.1]
	at org.springframework.web.util.UriComponentsBuilder.buildInternal(UriComponentsBuilder.java:445) ~[spring-web-6.1.1.jar:6.1.1]
	at org.springframework.web.util.UriComponentsBuilder.build(UriComponentsBuilder.java:433) ~[spring-web-6.1.1.jar:6.1.1]
	at org.springframework.cloud.gateway.server.mvc.handler.ProxyExchangeHandlerFunction.handle(ProxyExchangeHandlerFunction.java:76) ~[spring-cloud-gateway-server-mvc-4.1.0.jar:4.1.0]
	at org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions$LookupProxyExchangeHandlerFunction.handle(HandlerFunctions.java:98) ~[spring-cloud-gateway-server-mvc-4.1.0.jar:4.1.0]
	at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$ofRequestProcessor$3(HandlerFilterFunction.java:83) ~[spring-webmvc-6.1.1.jar:6.1.1]
	at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$andThen$0(HandlerFilterFunction.java:58) ~[spring-webmvc-6.1.1.jar:6.1.1]
	at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$ofRequestProcessor$3(HandlerFilterFunction.java:83) ~[spring-webmvc-6.1.1.jar:6.1.1]
	at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$andThen$0(HandlerFilterFunction.java:58) ~[spring-webmvc-6.1.1.jar:6.1.1]
	at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$ofRequestProcessor$3(HandlerFilterFunction.java:83) ~[spring-webmvc-6.1.1.jar:6.1.1]
	at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$andThen$1(HandlerFilterFunction.java:59) ~[spring-webmvc-6.1.1.jar:6.1.1]
	at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$andThen$1(HandlerFilterFunction.java:59) ~[spring-webmvc-6.1.1.jar:6.1.1]
	at org.springframework.web.servlet.function.HandlerFilterFunction.lambda$apply$2(HandlerFilterFunction.java:70) ~[spring-webmvc-6.1.1.jar:6.1.1]
	at org.springframework.web.servlet.function.support.HandlerFunctionAdapter.handle(HandlerFunctionAdapter.java:107) ~[spring-webmvc-6.1.1.jar:6.1.1]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) ~[spring-webmvc-6.1.1.jar:6.1.1]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) ~[spring-webmvc-6.1.1.jar:6.1.1]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) ~[spring-webmvc-6.1.1.jar:6.1.1]
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) ~[spring-webmvc-6.1.1.jar:6.1.1]
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) ~[tomcat-embed-core-10.1.16.jar:6.0]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) ~[spring-webmvc-6.1.1.jar:6.1.1]
	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) ~[tomcat-embed-core-10.1.16.jar:6.0]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-10.1.16.jar:10.1.16]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.springframework.cloud.gateway.server.mvc.filter.WeightCalculatorFilter.doFilter(WeightCalculatorFilter.java:229) ~[spring-cloud-gateway-server-mvc-4.1.0.jar:4.1.0]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.springframework.cloud.gateway.server.mvc.filter.FormFilter.doFilter(FormFilter.java:97) ~[spring-cloud-gateway-server-mvc-4.1.0.jar:4.1.0]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-6.1.1.jar:6.1.1]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.1.jar:6.1.1]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-6.1.1.jar:6.1.1]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.1.jar:6.1.1]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:109) ~[spring-web-6.1.1.jar:6.1.1]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.1.jar:6.1.1]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-6.1.1.jar:6.1.1]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) ~[spring-web-6.1.1.jar:6.1.1]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) ~[tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) [tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) [tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) [tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) [tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) [tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:673) [tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:340) [tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391) [tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) [tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896) [tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744) [tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) [tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) [tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) [tomcat-embed-core-10.1.16.jar:10.1.16]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-10.1.16.jar:10.1.16]
	at java.base/java.lang.Thread.run(Thread.java:833) [?:?]

tomgag avatar Dec 21 '23 10:12 tomgag

@spencergibb @rstoyanchev When I use Webflux-based spring-cloud-gateway, I have no problem including space characters in my requests, Therefore, I think the logic of spring-cloud-gateway-mvc processing should be consistent with the behavior of spring-cloud-gateway-webflux

zengqinglei avatar Dec 25 '23 02:12 zengqinglei

@tomgag There has been no effective official reply so far. How did you solve this problem based on the current version? Please help provide some solution ideas, thank you very much!

zengqinglei avatar Jan 11 '24 05:01 zengqinglei

@tomgag

There has been no effective official reply so far. How did you solve this problem based on the current version?

Please help provide some solution ideas, thank you very much!

By holding our change. We own both sides, so might rewrite this to a POST.

tomgag avatar Jan 11 '24 06:01 tomgag

@tomgag There has been no effective official reply so far. How did you solve this problem based on the current version? Please help provide some solution ideas, thank you very much!

By holding our change. We own both sides, so might rewrite this to a POST.

To rewrite the GET request into POST, we need to do a lot of adaptation work, and it does not conform to the style of restful api, so we hope there are other better ways to handle it.

zengqinglei avatar Jan 11 '24 15:01 zengqinglei

@spencergibb @rstoyanchev @spring-cloud-issues

I think the core cause of this issue is the inconsistent behavior of the queryParam passed in the image below: image

I hope the official can fix this problem as soon as possible. There has been no progress for three months.

zengqinglei avatar Feb 06 '24 15:02 zengqinglei

Same issue with accentuated letters.

java.lang.IllegalArgumentException: Invalid character 'é' for QUERY_PARAM in "Veszprém_HU"
	at org.springframework.web.util.HierarchicalUriComponents.verifyUriComponent(HierarchicalUriComponents.java:422) ~[spring-web-6.1.3.jar:6.1.3]
	at org.springframework.web.util.HierarchicalUriComponents.lambda$verify$4(HierarchicalUriComponents.java:390) ~[spring-web-6.1.3.jar:6.1.3]
	at org.springframework.util.UnmodifiableMultiValueMap.lambda$forEach$0(UnmodifiableMultiValueMap.java:114) ~[spring-core-6.1.3.jar:6.1.3]

frichard97 avatar Mar 01 '24 14:03 frichard97