Skip to content
Open
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 @@ -44,6 +44,7 @@

import org.springframework.cloud.gateway.server.mvc.common.MvcUtils;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
Expand All @@ -55,6 +56,7 @@
* parameters and form parameters the same, but a proxy should not care.
*
* @author Spencer Gibb
* @author Hutiefang Hu
*/
@SuppressWarnings("unchecked")
public class FormFilter implements Filter, Ordered {
Expand Down Expand Up @@ -141,9 +143,10 @@ static HttpServletRequest getRequestWithBodyFromRequestParameters(HttpServletReq
}
writer.flush();

byte[] body = bos.toByteArray();
ByteArrayServletInputStream servletInputStream = new ByteArrayServletInputStream(
new ByteArrayInputStream(bos.toByteArray()));
return new FormContentRequestWrapper(request, queryParams) {
new ByteArrayInputStream(body));
return new FormContentRequestWrapper(request, queryParams, body.length) {
@Override
public ServletInputStream getInputStream() throws IOException {
return servletInputStream;
Expand Down Expand Up @@ -186,9 +189,50 @@ private static class FormContentRequestWrapper extends HttpServletRequestWrapper

private final MultiValueMap<String, String> queryParams;

FormContentRequestWrapper(HttpServletRequest request, MultiValueMap<String, String> params) {
private final int contentLength;

private final boolean contentLengthHeaderPresent;

FormContentRequestWrapper(HttpServletRequest request, MultiValueMap<String, String> params, int contentLength) {
super(request);
this.queryParams = params;
this.contentLength = contentLength;
this.contentLengthHeaderPresent = request.getHeader(HttpHeaders.CONTENT_LENGTH) != null;
}

@Override
public int getContentLength() {
return this.contentLength;
}

@Override
public long getContentLengthLong() {
return this.contentLength;
}

@Override
@Nullable
public String getHeader(String name) {
if (this.contentLengthHeaderPresent && HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) {
return String.valueOf(this.contentLength);
}
return super.getHeader(name);
}

@Override
public Enumeration<String> getHeaders(String name) {
if (this.contentLengthHeaderPresent && HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) {
return Collections.enumeration(List.of(String.valueOf(this.contentLength)));
}
return super.getHeaders(name);
}

@Override
public int getIntHeader(String name) {
if (this.contentLengthHeaderPresent && HttpHeaders.CONTENT_LENGTH.equalsIgnoreCase(name)) {
return this.contentLength;
}
return super.getIntHeader(name);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;

Expand All @@ -33,6 +34,7 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;

import org.springframework.http.HttpHeaders;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.util.LinkedMultiValueMap;
Expand All @@ -45,9 +47,61 @@

/**
* @author shawyeok
* @author Hutiefang Hu
*/
class FormFilterTests {

@Test
void updateContentLengthAfterRebuildingFormBody() throws ServletException, IOException {
byte[] originalBody = "formArg=!".getBytes(StandardCharsets.UTF_8);
MockHttpServletRequest request = MockMvcRequestBuilders.post(URI.create("http://localhost/test"))
.contentType("application/x-www-form-urlencoded")
.header(HttpHeaders.CONTENT_LENGTH, originalBody.length)
.content(originalBody)
.buildRequest(null);
HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
FilterChain chain = Mockito.mock(FilterChain.class);

new FormFilter().doFilter(request, response, chain);

ArgumentCaptor<ServletRequest> captor = ArgumentCaptor.forClass(ServletRequest.class);
verify(chain).doFilter(captor.capture(), Mockito.eq(response));
HttpServletRequest wrappedRequest = (HttpServletRequest) captor.getValue();
byte[] rebuiltBody = "formArg=%21".getBytes(StandardCharsets.UTF_8);
assertThat(StreamUtils.copyToByteArray(wrappedRequest.getInputStream())).isEqualTo(rebuiltBody);
assertThat(wrappedRequest.getContentLength()).isEqualTo(rebuiltBody.length);
assertThat(wrappedRequest.getContentLengthLong()).isEqualTo(rebuiltBody.length);
assertThat(wrappedRequest.getHeader(HttpHeaders.CONTENT_LENGTH)).isEqualTo(String.valueOf(rebuiltBody.length));
assertThat(Collections.list(wrappedRequest.getHeaders(HttpHeaders.CONTENT_LENGTH)))
.containsExactly(String.valueOf(rebuiltBody.length));
assertThat(wrappedRequest.getIntHeader("content-length")).isEqualTo(rebuiltBody.length);
assertThat(Collections.list(wrappedRequest.getHeaderNames())).contains(HttpHeaders.CONTENT_LENGTH);
}

@Test
void preserveMissingContentLengthHeaderAfterRebuildingFormBody() throws ServletException, IOException {
MockHttpServletRequest request = MockMvcRequestBuilders.post(URI.create("http://localhost/test"))
.contentType("application/x-www-form-urlencoded")
.content("formArg=!")
.buildRequest(null);
request.removeHeader(HttpHeaders.CONTENT_LENGTH);
HttpServletResponse response = Mockito.mock(HttpServletResponse.class);
FilterChain chain = Mockito.mock(FilterChain.class);

new FormFilter().doFilter(request, response, chain);

ArgumentCaptor<ServletRequest> captor = ArgumentCaptor.forClass(ServletRequest.class);
verify(chain).doFilter(captor.capture(), Mockito.eq(response));
HttpServletRequest wrappedRequest = (HttpServletRequest) captor.getValue();
int rebuiltContentLength = "formArg=%21".getBytes(StandardCharsets.UTF_8).length;
assertThat(wrappedRequest.getContentLength()).isEqualTo(rebuiltContentLength);
assertThat(wrappedRequest.getContentLengthLong()).isEqualTo(rebuiltContentLength);
assertThat(wrappedRequest.getHeader("content-length")).isNull();
assertThat(Collections.list(wrappedRequest.getHeaders("content-length"))).isEmpty();
assertThat(wrappedRequest.getIntHeader("content-length")).isEqualTo(-1);
assertThat(Collections.list(wrappedRequest.getHeaderNames())).doesNotContain(HttpHeaders.CONTENT_LENGTH);
}

@Test
void hideFormParameterFromParameterMap() throws ServletException, IOException {
FormFilter filter = new FormFilter();
Expand Down