Skip to content

Fix '+' character in query parameters is not encoded when remove query parameter #3799

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@
import org.springframework.web.servlet.function.ServerRequest;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.web.util.UriTemplate;
import org.springframework.web.util.UriUtils;

import static org.springframework.cloud.gateway.server.mvc.common.MvcUtils.CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR;
import static org.springframework.util.CollectionUtils.unmodifiableMultiValueMap;
Expand Down Expand Up @@ -216,7 +215,7 @@ public static Function<ServerRequest, ServerRequest> removeRequestParameter(Stri
MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<>(request.params());
queryParams.remove(name);

MultiValueMap<String, String> encodedQueryParams = UriUtils.encodeQueryParams(queryParams);
MultiValueMap<String, String> encodedQueryParams = MvcUtils.encodeQueryParams(queryParams);

// remove from uri
URI newUri = UriComponentsBuilder.fromUri(request.uri())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ void removeRequestParameterWithEncodedRemainParameters() {
MockHttpServletRequest servletRequest = MockMvcRequestBuilders.get("http://localhost/path")
.queryParam("foo", "bar")
.queryParam("baz[]", "qux[]")
.queryParam("quux", "corge+")
.buildRequest(null);

ServerRequest request = ServerRequest.create(servletRequest, Collections.emptyList());
Expand All @@ -127,7 +128,8 @@ void removeRequestParameterWithEncodedRemainParameters() {

assertThat(result.param("foo")).isEmpty();
assertThat(result.param("baz[]")).isPresent().hasValue("qux[]");
assertThat(result.uri().toString()).hasToString("http://localhost/path?baz%5B%5D=qux%5B%5D");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this test was asserting for the presence of the bug?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ryanjbaxter
Correct. This test ensures that quux=corge+ is properly encoded.

assertThat(result.param("quux")).isPresent().hasValue("corge+");
assertThat(result.uri().toString()).hasToString("http://localhost/path?baz%5B%5D=qux%5B%5D&quux=corge%2B");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.web.util.UriUtils;

import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
import static org.springframework.util.CollectionUtils.unmodifiableMultiValueMap;
Expand Down Expand Up @@ -59,7 +59,8 @@ public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
queryParams.remove(config.getName());

try {
MultiValueMap<String, String> encodedQueryParams = UriUtils.encodeQueryParams(queryParams);
MultiValueMap<String, String> encodedQueryParams = ServerWebExchangeUtils
.encodeQueryParams(queryParams);
URI newUri = UriComponentsBuilder.fromUri(request.getURI())
.replaceQueryParams(unmodifiableMultiValueMap(encodedQueryParams))
.build(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.web.util.UriUtils;

import static org.springframework.cloud.gateway.support.GatewayToStringStyler.filterToStringCreator;
import static org.springframework.util.CollectionUtils.unmodifiableMultiValueMap;
Expand Down Expand Up @@ -71,7 +71,8 @@ public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
}

try {
MultiValueMap<String, String> encodedQueryParams = UriUtils.encodeQueryParams(queryParams);
MultiValueMap<String, String> encodedQueryParams = ServerWebExchangeUtils
.encodeQueryParams(queryParams);
URI uri = uriComponentsBuilder.replaceQueryParams(unmodifiableMultiValueMap(encodedQueryParams))
.build(true)
.toUri();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@
package org.springframework.cloud.gateway.support;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -48,9 +50,13 @@
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.DispatcherHandler;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.web.util.UriUtils;

/**
* @author Spencer Gibb
Expand Down Expand Up @@ -260,6 +266,17 @@ public static boolean containsEncodedParts(URI uri) {
return encoded;
}

public static MultiValueMap<String, String> encodeQueryParams(MultiValueMap<String, String> params) {
MultiValueMap<String, String> encodedQueryParams = new LinkedMultiValueMap<>(params.size());
for (Map.Entry<String, List<String>> entry : params.entrySet()) {
for (String value : entry.getValue()) {
encodedQueryParams.add(UriUtils.encode(entry.getKey(), StandardCharsets.UTF_8),
UriUtils.encode(value, StandardCharsets.UTF_8));
}
}
return CollectionUtils.unmodifiableMultiValueMap(encodedQueryParams);
}

public static HttpStatus parse(String statusString) {
HttpStatus httpStatus;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.springframework.cloud.gateway.filter.factory;

import java.net.URI;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
Expand All @@ -24,6 +26,7 @@
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory.NameConfig;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
import org.springframework.mock.web.server.MockServerWebExchange;
Expand Down Expand Up @@ -123,6 +126,23 @@ void removeRequestParameterFilterShouldHandleRemainingParamsWhichRequiringEncodi
assertThat(actualRequest.getQueryParams()).containsEntry("ccc", singletonList(",xyz"));
}

@Test
void removeRequestParameterFilterShouldHandleRemainingPlusSignParams() {
MockServerHttpRequest request = MockServerHttpRequest
.method(HttpMethod.GET, URI.create("http://localhost?foo=bar&aaa=%2Bxyz"))
.build();
exchange = MockServerWebExchange.from(request);
NameConfig config = new NameConfig();
config.setName("foo");
GatewayFilter filter = new RemoveRequestParameterGatewayFilterFactory().apply(config);

filter.filter(exchange, filterChain);

ServerHttpRequest actualRequest = captor.getValue().getRequest();
assertThat(actualRequest.getQueryParams()).doesNotContainKey("foo");
assertThat(actualRequest.getQueryParams()).containsEntry("aaa", singletonList("+xyz"));
}

@Test
void removeRequestParameterFilterShouldHandleEncodedParameterName() {
MockServerHttpRequest request = MockServerHttpRequest.get("http://localhost")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ void rewriteRequestParameterFilterKeepsOtherParamsEncoded() {
Map.of("campaign[]", List.of("blue"), "color", List.of("white")));
}

@Test
void rewriteRequestParameterFilterWithPlusSign() {
testRewriteRequestParameterFilter("color", "white+", "campaign=blue%2B&color=green",
Map.of("campaign", List.of("blue+"), "color", List.of("white+")));
}

private void testRewriteRequestParameterFilter(String name, String replacement, String query,
Map<String, List<String>> expectedQueryParams) {
GatewayFilter filter = new RewriteRequestParameterGatewayFilterFactory()
Expand Down
Loading