Revisit request parameters access with HtmlUnit

Prior to this commit, the `HtmlUnitRequestBuilder` would "translate"
HtmlUnit web requests into Servlet requests using APIs that were not
clearly defined and meant for internal usage.

HtmlUnit 2.64.0 introduced a new `.getParameters()` API for collecting
`NameValuePair` parsed from the request URI or request body, depending
on the nature of the request. This arrangement is much more stable and
in line with HtmlUnit's expectations.

This commit uses this new API and makes HtmlUnit 2.64.0 a new minimum
requirement for using HtmlUnit integration with Spring Framework.
This also removes tests that were previously testing HtmlUnit's behavior
and using the API now marked as internal.

Closes gh-28240
This commit is contained in:
Brian Clozel
2022-09-06 13:35:58 +02:00
parent 10ade235e3
commit dd1e6b9412
4 changed files with 126 additions and 259 deletions

View File

@@ -18,7 +18,6 @@ package org.springframework.test.web.servlet.htmlunit;
import java.io.File;
import java.io.IOException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@@ -159,7 +158,7 @@ final class HtmlUnitRequestBuilder implements RequestBuilder, Mergeable {
cookies(request);
this.webRequest.getAdditionalHeaders().forEach(request::addHeader);
locales(request);
params(request, uri);
params(request);
request.setQueryString(uri.getQuery());
return postProcess(request);
@@ -364,42 +363,36 @@ final class HtmlUnitRequestBuilder implements RequestBuilder, Mergeable {
}
}
private void params(MockHttpServletRequest request, UriComponents uriComponents) {
uriComponents.getQueryParams().forEach((name, values) -> {
String urlDecodedName = urlDecode(name);
values.forEach(value -> {
value = (value != null ? urlDecode(value) : "");
request.addParameter(urlDecodedName, value);
});
});
for (NameValuePair param : this.webRequest.getRequestParameters()) {
if (param instanceof KeyDataPair pair) {
File file = pair.getFile();
MockPart part;
if (file != null) {
part = new MockPart(pair.getName(), file.getName(), readAllBytes(file));
}
else {
// Support empty file upload OR file upload via setData().
// For an empty file upload, getValue() returns an empty string, and
// getData() returns null.
// For a file upload via setData(), getData() returns the file data, and
// getValue() returns the file name (if set) or an empty string.
part = new MockPart(pair.getName(), pair.getValue(), pair.getData());
}
MediaType mediaType = (pair.getMimeType() != null ? MediaType.valueOf(pair.getMimeType()) :
MediaType.APPLICATION_OCTET_STREAM);
part.getHeaders().setContentType(mediaType);
request.addPart(part);
}
else {
request.addParameter(param.getName(), param.getValue());
}
private void params(MockHttpServletRequest request) {
for (NameValuePair param : this.webRequest.getParameters()) {
addRequestParameter(request, param);
}
}
private String urlDecode(String value) {
return URLDecoder.decode(value, StandardCharsets.UTF_8);
private void addRequestParameter(MockHttpServletRequest request, NameValuePair param) {
if (param instanceof KeyDataPair) {
KeyDataPair pair = (KeyDataPair) param;
File file = pair.getFile();
MockPart part;
if (file != null) {
part = new MockPart(pair.getName(), file.getName(), readAllBytes(file));
}
else {
// Support empty file upload OR file upload via setData().
// For an empty file upload, getValue() returns an empty string, and
// getData() returns null.
// For a file upload via setData(), getData() returns the file data, and
// getValue() returns the file name (if set) or an empty string.
part = new MockPart(pair.getName(), pair.getValue(), pair.getData());
}
MediaType mediaType = (pair.getMimeType() != null ? MediaType.valueOf(pair.getMimeType()) :
MediaType.APPLICATION_OCTET_STREAM);
part.getHeaders().setContentType(mediaType);
request.addPart(part);
}
else {
request.addParameter(param.getName(), param.getValue());
}
}
private byte[] readAllBytes(File file) {