Skip to content

Commit

Permalink
fix: use UTF-8 as a default encoding in HTTP sampler
Browse files Browse the repository at this point in the history
In PR 5987 HTTP sampler encoded filenames with percent encoding,
however, it should not encode all the characters.


Fixes apache#6005
This is a fixup to apache#5987
  • Loading branch information
vlsi committed Jun 26, 2023
1 parent 5f2e86d commit 93a162c
Show file tree
Hide file tree
Showing 10 changed files with 485 additions and 192 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public class SampleResult implements Serializable, Cloneable, Searchable {
* The default encoding to be used if not overridden.
* The value is ISO-8859-1.
*/
public static final String DEFAULT_HTTP_ENCODING = StandardCharsets.ISO_8859_1.name();
public static final String DEFAULT_HTTP_ENCODING = StandardCharsets.UTF_8.name();

private static final String OK_CODE = Integer.toString(HttpURLConnection.HTTP_OK);
private static final String OK_MSG = "OK"; // $NON-NLS-1$
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
import org.apache.jmeter.protocol.http.control.gui.HttpTestSampleGui;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerBase;
import org.apache.jmeter.protocol.http.sampler.HTTPSamplerFactory;
import org.apache.jmeter.protocol.http.sampler.PostWriter;
import org.apache.jmeter.protocol.http.util.ConversionUtils;
import org.apache.jmeter.protocol.http.util.GraphQLRequestParamUtils;
import org.apache.jmeter.protocol.http.util.HTTPConstants;
Expand Down Expand Up @@ -218,24 +217,10 @@ protected void computeFromPostBody(HTTPSamplerBase sampler,
final String contentType = request.getContentType();
MultipartUrlConfig urlConfig = request.getMultipartConfig(contentType);
String contentEncoding = sampler.getContentEncoding();
// Get the post data using the content encoding of the request
String postData = null;
if (log.isDebugEnabled()) {
if(!StringUtils.isEmpty(contentEncoding)) {
log.debug("Using encoding {} for request body", contentEncoding);
}
else {
log.debug("No encoding found, using JRE default encoding for request body");
}
}

log.debug("Using encoding {} for request body", contentEncoding);

if (!StringUtils.isEmpty(contentEncoding)) {
postData = new String(request.getRawPostData(), contentEncoding);
} else {
// Use default encoding
postData = new String(request.getRawPostData(), PostWriter.ENCODING);
}
// Get the post data using the content encoding of the request
String postData = new String(request.getRawPostData(), contentEncoding);

if (urlConfig != null) {
urlConfig.parseArguments(postData);
Expand Down Expand Up @@ -436,16 +421,7 @@ private static String getNumberedFormat(int httpSampleNameMode) {
* @param request {@link HttpRequestHdr}
*/
protected void computePath(HTTPSamplerBase sampler, HttpRequestHdr request) {
if(sampler.getContentEncoding() != null) {
sampler.setPath(request.getPath(), sampler.getContentEncoding());
}
else {
// Although the spec says UTF-8 should be used for encoding URL parameters,
// most browser use ISO-8859-1 for default if encoding is not known.
// We use null for contentEncoding, then the url parameters will be added
// with the value in the URL, and the "encode?" flag set to false
sampler.setPath(request.getPath(), null);
}
sampler.setPath(request.getPath(), sampler.getContentEncoding());
if (log.isDebugEnabled()) {
log.debug("Proxy: finished setting path: {}", sampler.getPath());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.FormBodyPart;
import org.apache.http.entity.mime.FormBodyPartBuilder;
import org.apache.http.entity.mime.MIME;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
Expand Down Expand Up @@ -151,7 +151,6 @@
import org.apache.jmeter.protocol.http.sampler.hc.LaxGZIPInputStream;
import org.apache.jmeter.protocol.http.sampler.hc.LazyLayeredConnectionSocketFactory;
import org.apache.jmeter.protocol.http.util.ConversionUtils;
import org.apache.jmeter.protocol.http.util.EncoderCache;
import org.apache.jmeter.protocol.http.util.HTTPArgument;
import org.apache.jmeter.protocol.http.util.HTTPConstants;
import org.apache.jmeter.protocol.http.util.HTTPFileArg;
Expand Down Expand Up @@ -1508,13 +1507,18 @@ private static class ViewableFileBody extends FileBody {
"<actual file content, not shown here>".getBytes(StandardCharsets.UTF_8);
private boolean hideFileData;

public ViewableFileBody(File file, ContentType contentType) {
// Note: HttpClient4 does not support encoding the file name, so we explicitly encode it here
public ViewableFileBody(File file, ContentType contentType, Charset charset) {
// Note: HttpClient4 does not support encoding the file name, and it always encodes names in IS88
// See https://issues.apache.org/jira/browse/HTTPCLIENT-293
super(file, contentType, ConversionUtils.percentEncode(file.getName()));
super(file, contentType, encodeFilename(file.getName(), charset));
hideFileData = false;
}

private static String encodeFilename(String fileName, Charset charset) {
return ConversionUtils.percentEncode(
ConversionUtils.encodeWithEntities(fileName, charset));
}

@Override
public void writeTo(final OutputStream out) throws IOException {
if (hideFileData) {
Expand All @@ -1535,8 +1539,9 @@ protected String setupHttpEntityEnclosingRequestData(HttpEntityEnclosingRequestB
StringBuilder postedBody = new StringBuilder(1000);
HTTPFileArg[] files = getHTTPFiles();

final String contentEncoding = getContentEncodingOrNull();
final boolean haveContentEncoding = contentEncoding != null;
final String contentEncoding = getContentEncoding();
Charset charset = Charset.forName(contentEncoding);
final boolean haveContentEncoding = true;

// Check if we should do a multipart/form-data or an
// application/x-www-form-urlencoded post request
Expand All @@ -1547,25 +1552,22 @@ protected String setupHttpEntityEnclosingRequestData(HttpEntityEnclosingRequestB
Arrays.asList(entityEnclosingRequest.getHeaders(HTTPConstants.HEADER_CONTENT_TYPE)));
entityEnclosingRequest.removeHeaders(HTTPConstants.HEADER_CONTENT_TYPE);
}
// If a content encoding is specified, we use that as the
// encoding of any parameter values
Charset charset;
if(haveContentEncoding) {
charset = Charset.forName(contentEncoding);
} else {
charset = MIME.DEFAULT_CHARSET;
}

// doBrowserCompatibleMultipart means "use charset for encoding MIME headers",
// while RFC6532 means "use UTF-8 for encoding MIME headers"
boolean doBrowserCompatibleMultipart = getDoBrowserCompatibleMultipart();
if(log.isDebugEnabled()) {
log.debug("Building multipart with:getDoBrowserCompatibleMultipart(): {}, with charset:{}, haveContentEncoding:{}",
getDoBrowserCompatibleMultipart(), charset, haveContentEncoding);
doBrowserCompatibleMultipart, charset, haveContentEncoding);
}
// Write the request to our own stream
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
if(getDoBrowserCompatibleMultipart()) {
multipartEntityBuilder.setCharset(charset);
if (doBrowserCompatibleMultipart) {
multipartEntityBuilder.setLaxMode();
} else {
multipartEntityBuilder.setStrictMode();
// Use UTF-8 for encoding header names and values
multipartEntityBuilder.setMode(HttpMultipartMode.RFC6532);
}
// Create the parts
// Add any parameters
Expand Down Expand Up @@ -1596,7 +1598,8 @@ protected String setupHttpEntityEnclosingRequestData(HttpEntityEnclosingRequestB
HTTPFileArg file = files[i];

File reservedFile = FileServer.getFileServer().getResolvedFile(file.getPath());
fileBodies[i] = new ViewableFileBody(reservedFile, ContentType.parse(file.getMimeType()));
Charset filenameCharset = doBrowserCompatibleMultipart ? charset : StandardCharsets.UTF_8;
fileBodies[i] = new ViewableFileBody(reservedFile, ContentType.create(file.getMimeType()), filenameCharset);
multipartEntityBuilder.addPart(file.getParamName(), fileBodies[i] );
}

Expand Down Expand Up @@ -1652,12 +1655,7 @@ else if(ADD_CONTENT_TYPE_TO_POST_IF_MISSING) {
StringBuilder postBody = new StringBuilder();
for (JMeterProperty jMeterProperty : getArguments()) {
HTTPArgument arg = (HTTPArgument) jMeterProperty.getObjectValue();
// Note: if "Encoded?" is not selected, arg.getEncodedValue is equivalent to arg.getValue
if (haveContentEncoding) {
postBody.append(arg.getEncodedValue(contentEncoding));
} else {
postBody.append(arg.getEncodedValue());
}
postBody.append(arg.getEncodedValue(contentEncoding));
}
// Let StringEntity perform the encoding
StringEntity requestEntity = new StringEntity(postBody.toString(), contentEncoding);
Expand All @@ -1669,8 +1667,7 @@ else if(ADD_CONTENT_TYPE_TO_POST_IF_MISSING) {
if(!hasContentTypeHeader && ADD_CONTENT_TYPE_TO_POST_IF_MISSING) {
entityEnclosingRequest.setHeader(HTTPConstants.HEADER_CONTENT_TYPE, HTTPConstants.APPLICATION_X_WWW_FORM_URLENCODED);
}
String urlContentEncoding = contentEncoding;
UrlEncodedFormEntity entity = createUrlEncodedFormEntity(urlContentEncoding);
UrlEncodedFormEntity entity = createUrlEncodedFormEntity(contentEncoding);
entityEnclosingRequest.setEntity(entity);
writeEntityToSB(postedBody, entity, EMPTY_FILE_BODIES, contentEncoding);
}
Expand Down Expand Up @@ -1742,7 +1739,7 @@ protected String sendEntityData( HttpEntityEnclosingRequestBase entity) throws I

// Check for local contentEncoding (charset) override; fall back to default for content body
// we do this here rather so we can use the same charset to retrieve the data
final String charset = getContentEncoding(HTTP.DEF_CONTENT_CHARSET.name());
final String charset = getContentEncoding();

// Only create this if we are overriding whatever default there may be
// If there are no arguments, we can send a file as the body of the request
Expand Down Expand Up @@ -1775,7 +1772,7 @@ else if(getSendParameterValuesAsPostBody()) {
entity.setEntity(requestEntity);
} else if (hasArguments()) {
hasEntityBody = true;
entity.setEntity(createUrlEncodedFormEntity(getContentEncodingOrNull()));
entity.setEntity(createUrlEncodedFormEntity(getContentEncoding()));
}
// Check if we have any content to send for body
if(hasEntityBody) {
Expand All @@ -1792,20 +1789,15 @@ else if(getSendParameterValuesAsPostBody()) {

/**
* Create UrlEncodedFormEntity from parameters
* @param contentEncoding Content encoding may be null or empty
* @param urlContentEncoding Content encoding may be null or empty
* @return {@link UrlEncodedFormEntity}
* @throws UnsupportedEncodingException
*/
private UrlEncodedFormEntity createUrlEncodedFormEntity(final String contentEncoding) throws UnsupportedEncodingException {
private UrlEncodedFormEntity createUrlEncodedFormEntity(final String urlContentEncoding) throws UnsupportedEncodingException {
// It is a normal request, with parameter names and values
// Add the parameters
PropertyIterator args = getArguments().iterator();
List<NameValuePair> nvps = new ArrayList<>();
String urlContentEncoding = contentEncoding;
if (urlContentEncoding == null || urlContentEncoding.length() == 0) {
// Use the default encoding for urls
urlContentEncoding = EncoderCache.URL_ARGUMENT_ENCODING;
}
while (args.hasNext()) {
HTTPArgument arg = (HTTPArgument) args.next().getObjectValue();
// The HTTPClient always urlencodes both name and value,
Expand All @@ -1830,27 +1822,6 @@ private UrlEncodedFormEntity createUrlEncodedFormEntity(final String contentEnco
return new UrlEncodedFormEntity(nvps, urlContentEncoding);
}


/**
* @return the value of {@link #getContentEncoding()}; forced to null if empty
*/
private String getContentEncodingOrNull() {
return getContentEncoding(null);
}

/**
* @param dflt the default to be used
* @return the value of {@link #getContentEncoding()}; default if null or empty
*/
private String getContentEncoding(String dflt) {
String ce = getContentEncoding();
if (isNullOrEmptyTrimmed(ce)) {
return dflt;
} else {
return ce;
}
}

private static void saveConnectionCookies(HttpResponse method, URL u, CookieManager cookieManager) {
if (cookieManager != null) {
Header[] hdrs = method.getHeaders(HTTPConstants.HEADER_SET_COOKIE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,11 @@ public void setContentEncoding(String charsetName) {
* @return the encoding of the content, i.e. its charset name
*/
public String getContentEncoding() {
return get(getSchema().getContentEncoding());
String encoding = get(getSchema().getContentEncoding());
if (encoding.isEmpty()) {
return getSchema().getContentEncoding().getDefaultValue();
}
return encoding;
}

public void setUseKeepAlive(boolean value) {
Expand Down
Loading

0 comments on commit 93a162c

Please sign in to comment.