Skip to content

Commit

Permalink
Merge pull request #37 from Blazemeter/RELEASE_V2.0.2
Browse files Browse the repository at this point in the history
Release 2.0.2
  • Loading branch information
3dgiordano authored Nov 29, 2022
2 parents d49fdaf + 3166acd commit 94060bd
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 33 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,21 @@ to true in the jmeter.properties file.

## Buffer capacity
By default, the size of the downloaded resources is set to 2 MB (2097152 bytes) but, the limit can be increased by adding the `httpJettyClient.maxBufferSize` property on the jmeter.properties file in bytes.

## Properties
This document describes JMeter properties. The properties present in jmeter.properties also should be set in the user.properties file. These properties are only taken into account after restarting JMeter as they are usually resolved when the class is loaded.

| **Attribute** | **Description** | **Default** |
|-----------------------------------------------------|----------------------------------------------------------------------------------|-------------|
| **httpJettyClient.maxBufferSize** | Maximum size of the downloaded resources in bytes | 2097152 |
| **httpJettyClient.minThreads** | Minimum number of threads per http client | 1 |
| **httpJettyClient.maxThreads** | Maximum number of threads per http client | 5 |
| **httpJettyClient.maxRequestsQueuedPerDestination** | Maximum number of requests that may be queued to a destination | 32767 |
| **httpJettyClient.maxConnectionsPerDestination** | Sets the max number of connections to open to each destinations | 1 |
| **httpJettyClient.byteBufferPoolFactor** | Factor number used in the allocation of memory in the buffer of http client | 4 |
| **httpJettyClient.strictEventOrdering** | Force request events ordering | false |
| **httpJettyClient.removeIdleDestinations** | Whether destinations that have no connections should be removed | true |
| **httpJettyClient.idleTimeout** | the max time, in milliseconds, a connection can be idle | 30000 |
| **httpJettyClient.auth.preemptive** | Use of Basic preemptive authentication results | false |
| **HTTPSampler.response_timeout** | Maximum waiting time of request without timeout defined, in milliseconds | 0 |
| **http.post_add_content_type_if_missing** | Add to POST a Header Content-type: application/x-www-form-urlencoded if missing? | false |
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
<groupId>com.blazemeter.jmeter</groupId>
<artifactId>jmeter-bzm-http2</artifactId>
<packaging>jar</packaging>
<version>2.0.1</version>
<version>2.0.2</version>
<name>HTTP/2 Sampler</name>
<description>HTTP/2 protocol sampler</description>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<jmeter.version>5.4.1</jmeter.version>
<jetty.version>11.0.6</jetty.version>
<jetty.version>11.0.10</jetty.version>
</properties>

<dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,16 @@
import org.eclipse.jetty.http2.client.http.ClientConnectionFactoryOverHTTP2;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.io.MappedByteBufferPool;
import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HTTP2JettyClient {

public static final MappedByteBufferPool BUFFER_POOL = new MappedByteBufferPool(
JMeterUtils.getPropDefault("httpJettyClient.byteBufferPoolFactor", 4));
private static final Logger LOG = LoggerFactory.getLogger(HTTP2JettyClient.class);
private static final Set<String> SUPPORTED_METHODS = new HashSet<>(Arrays
.asList(HTTPConstants.GET, HTTPConstants.POST, HTTPConstants.PUT, HTTPConstants.PATCH,
Expand All @@ -80,13 +83,27 @@ public class HTTP2JettyClient {
private static final String MULTI_PART_SEPARATOR = "--";
private static final String LINE_SEPARATOR = "\r\n";
private static final String DEFAULT_FILE_MIME_TYPE = "application/octet-stream";
private int requestTimeout = 0;
private int maxBufferSize = 2 * 1024 * 1024;
private int maxThreads = 5;
private int minThreads = 1;
private int maxRequestsQueuedPerDestination = Short.MAX_VALUE;
private int maxConnectionsPerDestination = 1;
private boolean strictEventOrdering = false;
private boolean removeIdleDestinations = true;
private int idleTimeout = 30000;
private final HttpClient httpClient;
private boolean http1UpgradeRequired;

public HTTP2JettyClient(boolean http1UpgradeRequired) {
loadProperties();
ClientConnector clientConnector = new ClientConnector();
SslContextFactory.Client sslContextFactory = new JMeterJettySslContextFactory();
clientConnector.setSslContextFactory(sslContextFactory);
QueuedThreadPool queuedThreadPool = new QueuedThreadPool(maxThreads);
queuedThreadPool.setMinThreads(minThreads);
queuedThreadPool.setName("HttpClient");
clientConnector.setExecutor(queuedThreadPool);
ClientConnectionFactory.Info http11 = HttpClientConnectionFactory.HTTP11;
HTTP2Client http2Client = new HTTP2Client(clientConnector);
ClientConnectionFactoryOverHTTP2.HTTP2 http2 = new ClientConnectionFactoryOverHTTP2.HTTP2(
Expand All @@ -96,16 +113,56 @@ public HTTP2JettyClient(boolean http1UpgradeRequired) {
: new ClientConnectionFactory.Info[]{http2, http11};
HttpClientTransport transport = new HttpClientTransportDynamic(clientConnector, protocols);
this.httpClient = new HttpClient(transport);
this.httpClient.setUserAgentField(null); // No set UA header
this.httpClient.setByteBufferPool(HTTP2JettyClient.BUFFER_POOL);
this.httpClient.setMaxRequestsQueuedPerDestination(maxRequestsQueuedPerDestination);
this.httpClient.setMaxConnectionsPerDestination(maxConnectionsPerDestination);
this.httpClient.setStrictEventOrdering(strictEventOrdering);
this.httpClient.setRemoveIdleDestinations(removeIdleDestinations);
this.httpClient.setIdleTimeout(idleTimeout);
this.http1UpgradeRequired = http1UpgradeRequired;
}

public HTTP2JettyClient() {
this(false);
}

public static void clearBufferPool() {
HTTP2JettyClient.BUFFER_POOL.clear();
}

public void loadProperties() {
requestTimeout = JMeterUtils.getPropDefault("HTTPSampler.response_timeout", 0);
maxBufferSize =
Integer.parseInt(JMeterUtils.getPropDefault("httpJettyClient.maxBufferSize",
String.valueOf(2 * 1024 * 1024)));
minThreads = Integer
.parseInt(JMeterUtils.getPropDefault("httpJettyClient.minThreads",
String.valueOf(minThreads)));
maxThreads = Integer
.parseInt(JMeterUtils.getPropDefault("httpJettyClient.maxThreads",
String.valueOf(maxThreads)));
maxRequestsQueuedPerDestination = Integer
.parseInt(JMeterUtils.getPropDefault("httpJettyClient.maxRequestsQueuedPerDestination",
String.valueOf(maxRequestsQueuedPerDestination)));
maxConnectionsPerDestination =
Integer.parseInt(JMeterUtils.getPropDefault("httpJettyClient.maxConnectionsPerDestination",
String.valueOf(maxConnectionsPerDestination)));
strictEventOrdering =
Boolean.parseBoolean(JMeterUtils.getPropDefault("httpJettyClient.strictEventOrdering",
String.valueOf(strictEventOrdering)));
removeIdleDestinations =
Boolean.parseBoolean(JMeterUtils.getPropDefault("httpJettyClient.removeIdleDestinations",
String.valueOf(removeIdleDestinations)));
idleTimeout =
Integer.parseInt(JMeterUtils.getPropDefault("httpJettyClient.idleTimeout",
String.valueOf(idleTimeout)));
}

public void start() throws Exception {
if (!httpClient.isStarted()) {
httpClient.start();
httpClient.getContentDecoderFactories().clear(); // Clear default headers
}
}

Expand Down Expand Up @@ -165,22 +222,24 @@ public HTTPSampleResult sample(HTTP2Sampler sampler, HTTPSampleResult result,

public ContentResponse send(HttpRequest request) throws InterruptedException,
TimeoutException, ExecutionException {
String maxBufferSizeString = JMeterUtils.getPropDefault("httpJettyClient.maxBufferSize",
String.valueOf(2 * 1024 * 1024));

if (LOG.isDebugEnabled()) {
LOG.debug("Sending request: {}", request);
LOG.debug("Setting max buffer size to {}", maxBufferSizeString);
LOG.debug("Setting max buffer size to {}", maxBufferSize);
}
FutureResponseListener listener =
new FutureResponseListener(request, Integer.parseInt(maxBufferSizeString));
new FutureResponseListener(request, maxBufferSize);
request.send(listener);
int timeout = JMeterUtils.getPropDefault("HTTPSampler.response_timeout", 2000);

long getStart = System.currentTimeMillis();
try {
return listener.get(timeout, TimeUnit.MILLISECONDS);
if (requestTimeout > 0) {
int extraTime = 2000;
return listener.get(requestTimeout + extraTime, TimeUnit.MILLISECONDS);
} else {
return listener.get();
}
} catch (TimeoutException e) {
throw new TimeoutException("The request took more than " + timeout
long endGet = System.currentTimeMillis();
throw new TimeoutException("The request took more than " + (endGet - getStart)
+ " milliseconds to complete");
} catch (ExecutionException e) {
if (e.getCause() != null && e.getCause() instanceof TimeoutException) {
Expand Down Expand Up @@ -242,6 +301,8 @@ private void setTimeouts(HTTP2Sampler sampler, HttpRequest request) {
}
if (sampler.getResponseTimeout() > 0) {
request.timeout(sampler.getResponseTimeout(), TimeUnit.MILLISECONDS);
} else if (requestTimeout > 0) {
request.timeout(requestTimeout, TimeUnit.MILLISECONDS);
}
}

Expand Down Expand Up @@ -542,4 +603,16 @@ private void saveCookiesInCookieManager(ContentResponse response, URL url,
}
}

public void clearCookies() {
httpClient.getCookieStore().removeAll();
}

public void clearAuthenticationResults() {
httpClient.getAuthenticationStore().clearAuthenticationResults();
}

public String dump() {
return httpClient.dump();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.apache.jmeter.testelement.ThreadListener;
import org.apache.jmeter.threads.JMeterContextService;
import org.apache.jmeter.threads.JMeterVariables;
import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -28,6 +29,8 @@ public class HTTP2Sampler extends HTTPSamplerBase implements LoopIterationListen
.withInitial(HashMap::new);
private static final String HTTP1_UPGRADE_PROPERTY = "HTTP2Sampler.http1_upgrade";
private final transient Callable<HTTP2JettyClient> clientFactory;
private final boolean dumpAtThreadEnd = JMeterUtils.getPropDefault(
"httpJettyClient.DumpAtThreadEnd", false);

public HTTP2Sampler() {
setName("HTTP2 Sampler");
Expand Down Expand Up @@ -109,7 +112,7 @@ public HTTPSampleResult resultProcessing(final boolean pAreFollowingRedirect,
public void iterationStart(LoopIterationEvent iterEvent) {
JMeterVariables jMeterVariables = JMeterContextService.getContext().getVariables();
if (!jMeterVariables.isSameUserOnNextIteration()) {
closeConnections();
clearUserStores();
}
}

Expand All @@ -125,11 +128,44 @@ private void closeConnections() {
clients.clear();
}

private void dump() {
Map<HTTP2ClientKey, HTTP2JettyClient> clients = CONNECTIONS.get();
for (HTTP2JettyClient client : clients.values()) {
try {
LOG.debug(client.dump());
} catch (Exception e) {
LOG.error("Error while dump HTTP2JettyClient", e);
}
}
}

@Override
public void testEnded() {
super.testEnded();
HTTP2JettyClient.clearBufferPool();
System.gc(); // Force free memory
}

@Override
public void threadFinished() {
if (dumpAtThreadEnd) {
dump();
}
closeConnections();
}

private void clearUserStores() {
Map<HTTP2ClientKey, HTTP2JettyClient> clients = CONNECTIONS.get();
for (HTTP2JettyClient client : clients.values()) {
try {
client.clearCookies();
client.clearAuthenticationResults();
} catch (Exception e) {
LOG.error("Error while cleaning user store", e);
}
}
}

private static final class HTTP2ClientKey {

private final String target;
Expand Down
Loading

0 comments on commit 94060bd

Please sign in to comment.