Skip to content
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

formFilter append useless character "&" cause body length change #3330

Closed
sunhao1256 opened this issue Mar 29, 2024 · 2 comments
Closed

formFilter append useless character "&" cause body length change #3330

sunhao1256 opened this issue Mar 29, 2024 · 2 comments

Comments

@sunhao1256
Copy link
Contributor

sunhao1256 commented Mar 29, 2024

Describe the bug
Prerequisite: use property spring.cloud.gateway.mvc.remove-content-length-request-headers-filter.enabled=false disable RemoveContentLengthFilter.

post form data with query params like "/post?foo=fooquery" , the default rest client jdk client will throw error

[SimpleAsyncTaskExecutor-2] Too many bytes in request body. Expected: 7, got: 8
	at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.createResourceAccessException(DefaultRestClient.java:557) ~[spring-web-6.1.5.jar:6.1.5]
	at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.exchangeInternal(DefaultRestClient.java:482) ~[spring-web-6.1.5.jar:6.1.5]
	at org.springframework.web.client.DefaultRestClient$DefaultRequestBodyUriSpec.exchange(DefaultRestClient.java:449) ~[spring-web-6.1.5.jar:6.1.5]
	at org.springframework.cloud.gateway.server.mvc.handler.RestClientProxyExchange.exchange(RestClientProxyExchange.java:40) ~[classes/:na]

After i dive into code , the following code chunk from form filter .

	static HttpServletRequest getRequestWithBodyFromRequestParameters(HttpServletRequest request) throws IOException {
		ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
		Writer writer = new OutputStreamWriter(bos, FORM_CHARSET);

		Map<String, String[]> form = request.getParameterMap();
		String queryString = request.getQueryString();
		StringBuffer requestURL = request.getRequestURL();
		if (StringUtils.hasText(queryString)) {
			requestURL.append('?').append(queryString);
		}
		UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(requestURL.toString());
		MultiValueMap<String, String> queryParams = uriComponentsBuilder.build().getQueryParams();
		for (Iterator<Map.Entry<String, String[]>> entryIterator = form.entrySet().iterator(); entryIterator
				.hasNext();) {
			Map.Entry<String, String[]> entry = entryIterator.next();
			String name = entry.getKey();
			List<String> values = Arrays.asList(entry.getValue());
			for (Iterator<String> valueIterator = values.iterator(); valueIterator.hasNext();) {
				String value = valueIterator.next();
				List<String> queryValues = queryParams.get(name);
				boolean isQueryParam = queryParams.containsKey(name) && queryValues != null
						&& queryValues.contains(value);
				if (!isQueryParam) {
					writer.write(URLEncoder.encode(name, FORM_CHARSET));
					if (value != null) {
						writer.write('=');
						writer.write(URLEncoder.encode(value, FORM_CHARSET));
						if (valueIterator.hasNext()) {
							writer.write('&');
						}
					}
				}
			}
			if (entryIterator.hasNext()) {
				writer.append('&');
			}
		}
		writer.flush();

		ByteArrayServletInputStream servletInputStream = new ByteArrayServletInputStream(
				new ByteArrayInputStream(bos.toByteArray()));
		return new FormContentRequestWrapper(request, queryParams) {
			@Override
			public ServletInputStream getInputStream() throws IOException {
				return servletInputStream;
			}
		};

The writer if current value isQueryParam means variable isQueryParam return true ,the code still append '&' ,so the body length changed

			if (entryIterator.hasNext()) {
				writer.append('&');
			}

i perfer add a boolen variable to predicate whether should append the character "&"

Sample

@SpringBootTest(properties = {"spring.cloud.gateway.mvc.http-client.type=jdk",
		"spring.cloud.gateway.mvc.remove-content-length-request-headers-filter.enabled=false"},
		webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class DisableRemoveContentLengthFilterTests extends ServerMvcIntegrationTests {

	@Test
	void formUrlencodedWorks() {
		LinkedMultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
		formData.add("baz", "bam");

		// @formatter:off
		restClient.post().uri("/post?foo=fooquery").header("test", "formurlencoded")
				.contentType(FORM_URL_ENCODED_CONTENT_TYPE)
				.bodyValue(formData)
				.exchange()
				.expectStatus().isOk()
				.expectBody(Map.class).consumeWith(result -> {
					Map map = result.getResponseBody();
					Map<String, Object> form = getMap(map, "form");
					assertThat(form).containsEntry("baz", "bam");
				});
	}
}
@sunhao1256
Copy link
Contributor Author

sunhao1256 commented Mar 31, 2024

i made a pr to fix the issue #3329

@spencergibb
Copy link
Member

Closing in favor of #3329

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants