-
Notifications
You must be signed in to change notification settings - Fork 478
The New nGrinder HTTP Client
Until nGrinder 3.5.3, legacy HTTP client has not updated and exposed on many vulnerabilities. Also, modern HTTP specs were not supported like HTTP2.
From nGrinder 3.5.4, nGrinder provides new HTTP client that supports HTTP2, latest cookie specs, and HTTP methods PATCH.
The new HTTP client for nGrinder must be light-weight so the agent can focus on performance test. The new HTTP client is implemented based on Apache httpcomponents-core.
The new HTTP client supports both HTTP1 and HTTP2. You can enforce protocol version with invoking setVersionPolicy()
. Available options are FORCE_HTTP_1
, FORCE_HTTP_2
and NEGOTIATE
. Default version policy is NEGOTIATE
. So, HTTP client negotiates the protocol version with target server.
import org.ngrinder.http.HTTPRequest
import org.ngrinder.http.HTTPResponse
import org.apache.hc.core5.http2.HttpVersionPolicy
@RunWith(GrinderRunner)
class TestRunner {
public static GTest test
public static HTTPRequest request
public static Map<String, String> headers = [:]
public static Map<String, String> params = [:]
@BeforeProcess
public static void beforeProcess() {
test = new GTest(1, "www.google.com")
request = new HTTPRequest()
grinder.logger.info("before process.");
}
@BeforeThread
public void beforeThread() {
test.record(this, "test")
grinder.statistics.delayReports=true;
grinder.logger.info("before thread.");
}
@Before
public void before() {
request.setHeaders(headers)
request.setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_1) // or HttpVersionPolicy.FORCE_HTTP_2
grinder.logger.info("before. init headers and cookies");
}
@Test
public void test(){
HTTPResponse result = request.GET("https://www.google.com", params)
grinder.logger.info("protocol version: {}", result.version);
grinder.logger.info("body: {}", result.bodyText);
if (result.statusCode == 301 || result.statusCode == 302) {
grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
} else {
assertThat(result.statusCode, is(200));
}
}
}
Legacy nGrinder HTTP client supports only NVPair
as a parameter. It was enough for setting request parameters or headers but, in case you have to use bunch of parameters, you also had to write more boilerplate codes.
Now, the new HTTP client supports org.apache.hc.core5.http.NameValuePair
and org.apache.hc.core5.http.Header
as a parameter. But, you don't have to worry about instantiating implementation class. Map
is also supported as a parameter. If you pass a parameter as Map
, new HTTP client converts it to appropriate parameter type and request is executed. The NVPair
is also supported for easy to migrate existing performance test scripts.
import org.ngrinder.http.HTTPRequest
import org.ngrinder.http.HTTPResponse
@RunWith(GrinderRunner)
class TestRunner {
public static GTest test
public static HTTPRequest request
@BeforeProcess
public static void beforeProcess() {
test = new GTest(1, "www.google.com")
request = new HTTPRequest()
grinder.logger.info("before process.");
}
@BeforeThread
public void beforeThread() {
test.record(this, "test")
grinder.statistics.delayReports=true;
grinder.logger.info("before thread.");
}
@Test
public void test(){
// You can pass a parameter with Groovy Map literal
HTTPResponse result = request.GET("https://www.google.com", [ "param1": "value1" ], [ "header1": "value1" ])
if (result.statusCode == 301 || result.statusCode == 302) {
grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
} else {
assertThat(result.statusCode, is(200));
}
}
}
Or you can pass a parameter with instantiating a list of org.apache.hc.core5.http.message.BasicHeader
or org.apache.hc.core5.http.message.BasicNameValuePair
yourself.
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.message.BasicHeader;
import org.apache.hc.core5.http.message.BasicNameValuePair;
import org.ngrinder.http.HTTPRequest
import org.ngrinder.http.HTTPResponse
@RunWith(GrinderRunner)
class TestRunner {
public static GTest test
public static HTTPRequest request
public List<Header> headers = [ new BasicHeader("header1", "value1") ]
public List<NameValuePair> params = [ new BasicNameValuePair("param1", "value1") ]
@BeforeProcess
public static void beforeProcess() {
test = new GTest(1, "www.google.com")
request = new HTTPRequest()
grinder.logger.info("before process.");
}
@BeforeThread
public void beforeThread() {
test.record(this, "test")
grinder.statistics.delayReports=true;
grinder.logger.info("before thread.");
}
@Test
public void test(){
HTTPResponse result = request.GET("https://www.google.com", params, headers)
if (result.statusCode == 301 || result.statusCode == 302) {
grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
} else {
assertThat(result.statusCode, is(200));
}
}
}
In case the test target API produces a large response body, the performance test will derive humongous network traffic and it's almost same as DDOS attack. You can reduce network traffic by reading only a part of response body. The partial response reading feature is only supported on HTTP1 protocol currently.
import org.ngrinder.http.HTTPRequest
import org.ngrinder.http.HTTPResponse
import org.apache.hc.core5.http2.HttpVersionPolicy
@RunWith(GrinderRunner)
class TestRunner {
public static GTest test
public static HTTPRequest request
public static Map<String, String> headers = [:]
public static Map<String, String> params = [:]
@BeforeProcess
public static void beforeProcess() {
test = new GTest(1, "www.google.com")
request = new HTTPRequest()
grinder.logger.info("before process.");
}
@BeforeThread
public void beforeThread() {
test.record(this, "test")
grinder.statistics.delayReports=true;
grinder.logger.info("before thread.");
}
@Before
public void before() {
request.setHeaders(headers)
request.setReadBytes(1024) // Set how many bytes will you read from response body
request.setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_1)
grinder.logger.info("before. init headers and cookies");
}
@Test
public void test(){
HTTPResponse result = request.GET("https://www.google.com", params)
grinder.logger.info("protocol version: {}", result.version);
grinder.logger.info("body: {}", result.bodyText);
if (result.statusCode == 301 || result.statusCode == 302) {
grinder.logger.warn("Warning. The response may not be correct. The response code was {}.", result.statusCode);
} else {
assertThat(result.statusCode, is(200));
}
}
}