Skip to content

Commit

Permalink
Create request entity if content-length is set (#1872) (#1873)
Browse files Browse the repository at this point in the history
To enable valid POST requests without a body, an empty entity is required.
Otherwise, the proxy request will not include the header content-length.
Additionally, the content-type will only be set if it is already present
in the originalRequest.
This prevents a new, additional header from being set.

Signed-off-by: Christian von Atzigen <christian.vonatzigen@mobi.ch>

Signed-off-by: Christian von Atzigen <christian.vonatzigen@mobi.ch>
  • Loading branch information
vonatzigenc committed Sep 14, 2022
1 parent 0d98e49 commit f339447
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ public Response render(ServeEvent serveEvent) {
addRequestHeaders(httpRequest, responseDefinition);

Request originalRequest = responseDefinition.getOriginalRequest();
if (originalRequest.getBody() != null && originalRequest.getBody().length > 0) {
if ((originalRequest.getBody() != null && originalRequest.getBody().length > 0)
|| originalRequest.containsHeader(CONTENT_LENGTH)) {
httpRequest.setEntity(buildEntityFrom(originalRequest));
}
CloseableHttpClient client = buildClient(serveEvent.getRequest().isBrowserProxyRequest());
Expand Down Expand Up @@ -229,7 +230,9 @@ private static HttpEntity buildEntityFrom(Request originalRequest) {

return applyGzipWrapperIfRequired(
originalRequest,
new ByteArrayEntity(originalRequest.getBody(), ContentType.DEFAULT_BINARY));
new ByteArrayEntity(
originalRequest.getBody(),
originalRequest.contentTypeHeader().isPresent() ? contentType : null));
}

private static HttpEntity applyGzipWrapperIfRequired(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledForJreRange;
import org.junit.jupiter.api.condition.JRE;
Expand Down Expand Up @@ -199,6 +201,60 @@ void addsEntityIfNotEmptyBodyForwardProxy() throws IOException {
Mockito.verify(clientSpy).execute(argThat(request -> request.getEntity() != null));
}

@Test
void addsEmptyEntityIfEmptyBodyForwardProxyPOST() throws IOException {
ProxyResponseRenderer trustAllProxyResponseRenderer = buildProxyResponseRenderer(true);
CloseableHttpClient clientSpy =
reflectiveSpyField(
CloseableHttpClient.class, "forwardProxyClient", trustAllProxyResponseRenderer);

origin.stubFor(post("/proxied/empty-post").willReturn(aResponse().withBody("Result")));

ServeEvent serveEvent =
serveEvent(
"/proxied/empty-post",
true,
new byte[0],
RequestMethod.POST,
new HttpHeaders(new HttpHeader("Content-Length", "0")));

trustAllProxyResponseRenderer.render(serveEvent);
Mockito.verify(clientSpy).execute(argThat(request -> request.getEntity() != null));
List<LoggedRequest> requests =
origin.findAll(postRequestedFor(urlPathMatching("/proxied/empty-post")));
Assertions.assertThat(requests)
.hasSizeGreaterThan(0)
.allMatch(r -> "0".equals(r.getHeader("Content-Length")))
.noneMatch(r -> r.containsHeader("Content-Type"));
}

@Test
void addsEmptyEntityIfEmptyBodyForwardProxyGET() throws IOException {
ProxyResponseRenderer trustAllProxyResponseRenderer = buildProxyResponseRenderer(true);
CloseableHttpClient clientSpy =
reflectiveSpyField(
CloseableHttpClient.class, "forwardProxyClient", trustAllProxyResponseRenderer);

origin.stubFor(get("/proxied/empty-get").willReturn(aResponse().withBody("Result")));

ServeEvent serveEvent =
serveEvent(
"/proxied/empty-get",
true,
new byte[0],
RequestMethod.GET,
new HttpHeaders(new HttpHeader("Content-Length", "0")));

trustAllProxyResponseRenderer.render(serveEvent);
Mockito.verify(clientSpy).execute(argThat(request -> request.getEntity() != null));
List<LoggedRequest> requests =
origin.findAll(getRequestedFor(urlPathMatching("/proxied/empty-get")));
Assertions.assertThat(requests)
.hasSizeGreaterThan(0)
.allMatch(r -> "0".equals(r.getHeader("Content-Length")))
.noneMatch(r -> r.containsHeader("Content-Type"));
}

private static <T> T reflectiveSpyField(Class<T> fieldType, String fieldName, Object object) {
try {
Field field = object.getClass().getDeclaredField(fieldName);
Expand All @@ -220,13 +276,22 @@ private ServeEvent forwardProxyServeEvent(String path) {
}

private ServeEvent serveEvent(String path, boolean isBrowserProxyRequest, byte[] body) {
return serveEvent(path, isBrowserProxyRequest, body, RequestMethod.GET, new HttpHeaders());
}

private ServeEvent serveEvent(
String path,
boolean isBrowserProxyRequest,
byte[] body,
RequestMethod method,
HttpHeaders headers) {
LoggedRequest loggedRequest =
new LoggedRequest(
/* url = */ path,
/* absoluteUrl = */ origin.url(path),
/* method = */ RequestMethod.GET,
/* method = */ method,
/* clientIp = */ "127.0.0.1",
/* headers = */ new HttpHeaders(),
/* headers = */ headers,
/* cookies = */ new HashMap<String, Cookie>(),
/* isBrowserProxyRequest = */ isBrowserProxyRequest,
/* loggedDate = */ new Date(),
Expand Down

0 comments on commit f339447

Please sign in to comment.