diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/FormFilter.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/FormFilter.java index d25b2ab063..4db87c23f1 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/FormFilter.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/filter/FormFilter.java @@ -125,6 +125,7 @@ static HttpServletRequest getRequestWithBodyFromRequestParameters(HttpServletReq Map.Entry entry = entryIterator.next(); String name = entry.getKey(); List values = Arrays.asList(entry.getValue()); + boolean valueWritten = false; for (Iterator valueIterator = values.iterator(); valueIterator.hasNext();) { String value = valueIterator.next(); List queryValues = queryParams.get(name); @@ -139,9 +140,10 @@ static HttpServletRequest getRequestWithBodyFromRequestParameters(HttpServletReq writer.write('&'); } } + valueWritten = true; } } - if (entryIterator.hasNext()) { + if (valueWritten && entryIterator.hasNext()) { writer.append('&'); } } diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/FormFilterTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/FormFilterTests.java new file mode 100644 index 0000000000..8d9f67c8a3 --- /dev/null +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/filter/FormFilterTests.java @@ -0,0 +1,63 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.gateway.server.mvc.filter; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; + +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import org.junit.jupiter.api.Test; +import org.testcontainers.shaded.org.apache.commons.io.IOUtils; + +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockFilterChain; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Lu lu + */ +public class FormFilterTests { + + @Test + public void sameContentLengthWithOriginalPostFormUrlEncodeTest() throws ServletException, IOException { + + byte[] content = "baz=bam".getBytes(); + + MockHttpServletRequest request = MockMvcRequestBuilders.post("http://localhost/post?foo=fooquery") + .contentType(MediaType.APPLICATION_FORM_URLENCODED).content(content).buildRequest(null); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockFilterChain mockFilterChain = new MockFilterChain(); + FormFilter formFilter = new FormFilter(); + formFilter.doFilter(request, response, mockFilterChain); + ServletRequest filterRequest = mockFilterChain.getRequest(); + ByteBuffer buffer = ByteBuffer.allocate(1024); + + ReadableByteChannel readableByteChannel = Channels.newChannel(filterRequest.getInputStream()); + IOUtils.read(readableByteChannel, buffer); + buffer.flip(); + assertThat(content.length).isEqualTo(buffer.remaining()); + + } + +}