1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package org.apache.hc.core5.benchmark;
28
29 import java.io.File;
30 import java.io.IOException;
31 import java.net.SocketAddress;
32 import java.net.URI;
33 import java.nio.ByteBuffer;
34 import java.nio.channels.ByteChannel;
35 import java.security.cert.CertificateException;
36 import java.security.cert.X509Certificate;
37 import java.util.List;
38 import java.util.concurrent.CountDownLatch;
39 import java.util.concurrent.TimeUnit;
40 import java.util.concurrent.atomic.AtomicLong;
41 import java.util.concurrent.locks.Lock;
42
43 import javax.net.ssl.SSLContext;
44
45 import org.apache.commons.cli.CommandLine;
46 import org.apache.commons.cli.CommandLineParser;
47 import org.apache.commons.cli.DefaultParser;
48 import org.apache.commons.cli.Options;
49 import org.apache.hc.core5.function.Decorator;
50 import org.apache.hc.core5.http.Header;
51 import org.apache.hc.core5.http.HttpConnection;
52 import org.apache.hc.core5.http.HttpHost;
53 import org.apache.hc.core5.http.HttpRequest;
54 import org.apache.hc.core5.http.HttpResponse;
55 import org.apache.hc.core5.http.HttpVersion;
56 import org.apache.hc.core5.http.impl.Http1StreamListener;
57 import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester;
58 import org.apache.hc.core5.http.nio.ssl.BasicClientTlsStrategy;
59 import org.apache.hc.core5.http.protocol.HttpCoreContext;
60 import org.apache.hc.core5.http.protocol.HttpProcessorBuilder;
61 import org.apache.hc.core5.http.protocol.RequestExpectContinue;
62 import org.apache.hc.core5.http.protocol.RequestUserAgent;
63 import org.apache.hc.core5.http2.HttpVersionPolicy;
64 import org.apache.hc.core5.http2.config.H2Config;
65 import org.apache.hc.core5.http2.frame.FramePrinter;
66 import org.apache.hc.core5.http2.frame.FrameType;
67 import org.apache.hc.core5.http2.frame.RawFrame;
68 import org.apache.hc.core5.http2.impl.nio.H2StreamListener;
69 import org.apache.hc.core5.http2.impl.nio.bootstrap.H2RequesterBootstrap;
70 import org.apache.hc.core5.http2.protocol.H2RequestConnControl;
71 import org.apache.hc.core5.http2.protocol.H2RequestContent;
72 import org.apache.hc.core5.http2.protocol.H2RequestTargetHost;
73 import org.apache.hc.core5.io.CloseMode;
74 import org.apache.hc.core5.reactor.Command;
75 import org.apache.hc.core5.reactor.IOEventHandler;
76 import org.apache.hc.core5.reactor.IOReactorConfig;
77 import org.apache.hc.core5.reactor.IOSession;
78 import org.apache.hc.core5.ssl.SSLContextBuilder;
79 import org.apache.hc.core5.ssl.SSLContexts;
80 import org.apache.hc.core5.ssl.TrustStrategy;
81 import org.apache.hc.core5.util.Timeout;
82
83
84
85
86
87
88 public class HttpBenchmark {
89
90 private final BenchmarkConfig config;
91
92 public static void main(final String[] args) throws Exception {
93
94 final Options options = CommandLineUtils.getOptions();
95 final CommandLineParser parser = new DefaultParser();
96 final CommandLine cmd = parser.parse(options, args);
97
98 if (args.length == 0 || cmd.hasOption('h') || cmd.getArgs().length != 1) {
99 CommandLineUtils.showUsage(options);
100 System.exit(1);
101 }
102
103 final BenchmarkConfig config = CommandLineUtils.parseCommandLine(cmd);
104
105 if (config.getUri() == null) {
106 CommandLineUtils.showUsage(options);
107 System.exit(1);
108 }
109
110 final HttpBenchmarkmark.html#HttpBenchmark">HttpBenchmark httpBenchmark = new HttpBenchmark(config);
111 final Results results = httpBenchmark.execute();
112 System.out.println();
113 ResultFormatter.print(System.out, results);
114 }
115
116 public HttpBenchmark(final BenchmarkConfig config) {
117 super();
118 this.config = config != null ? config : BenchmarkConfig.custom().build();
119 }
120
121 public Results execute() throws Exception {
122 final HttpProcessorBuilder builder = HttpProcessorBuilder.create()
123 .addAll(
124 new H2RequestContent(),
125 new H2RequestTargetHost(),
126 new H2RequestConnControl(),
127 new RequestUserAgent("HttpCore-AB/5.0"));
128 if (this.config.isUseExpectContinue()) {
129 builder.add(new RequestExpectContinue());
130 }
131
132 final SSLContext sslContext;
133 if ("https".equals(config.getUri().getScheme())) {
134 final SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
135 sslContextBuilder.setProtocol("SSL");
136 if (config.isDisableSSLVerification()) {
137 sslContextBuilder.loadTrustMaterial(null, new TrustStrategy() {
138
139 @Override
140 public boolean isTrusted(
141 final X509Certificate[] chain, final String authType) throws CertificateException {
142 return true;
143 }
144
145 });
146 } else if (config.getTrustStorePath() != null) {
147 sslContextBuilder.loadTrustMaterial(
148 new File(config.getTrustStorePath()),
149 config.getTrustStorePassword() != null ? config.getTrustStorePassword().toCharArray() : null);
150 }
151 if (config.getIdentityStorePath() != null) {
152 sslContextBuilder.loadKeyMaterial(
153 new File(config.getIdentityStorePath()),
154 config.getIdentityStorePassword() != null ? config.getIdentityStorePassword().toCharArray() : null,
155 config.getIdentityStorePassword() != null ? config.getIdentityStorePassword().toCharArray() : null);
156 }
157 sslContext = sslContextBuilder.build();
158 } else {
159 sslContext = SSLContexts.createSystemDefault();
160 }
161
162 final HttpVersionPolicy versionPolicy;
163 if (config.isForceHttp2()) {
164 versionPolicy = HttpVersionPolicy.FORCE_HTTP_2;
165 } else {
166 if (sslContext != null) {
167 versionPolicy = HttpVersionPolicy.NEGOTIATE;
168 } else {
169 versionPolicy = HttpVersionPolicy.FORCE_HTTP_1;
170 }
171 }
172
173 final Statschmark/Stats.html#Stats">Stats stats = new Stats();
174 try (final HttpAsyncRequester requester = H2RequesterBootstrap.bootstrap()
175 .setHttpProcessor(builder.build())
176 .setTlsStrategy(new BasicClientTlsStrategy(sslContext))
177 .setVersionPolicy(versionPolicy)
178 .setH2Config(H2Config.custom()
179 .setPushEnabled(false)
180 .build())
181 .setIOSessionDecorator(new Decorator<IOSession>() {
182
183 @Override
184 public IOSession decorate(final IOSession ioSession) {
185 return new IOSession() {
186
187 @Override
188 public String getId() {
189 return ioSession.getId();
190 }
191
192 @Override
193 public Lock getLock() {
194 return ioSession.getLock();
195 }
196
197 @Override
198 public void enqueue(final Command command, final Command.Priority priority) {
199 ioSession.enqueue(command, priority);
200 }
201
202 @Override
203 public boolean hasCommands() {
204 return ioSession.hasCommands();
205 }
206
207 @Override
208 public Command poll() {
209 return ioSession.poll();
210 }
211
212 @Override
213 public ByteChannel channel() {
214 return ioSession.channel();
215 }
216
217 @Override
218 public SocketAddress getRemoteAddress() {
219 return ioSession.getRemoteAddress();
220 }
221
222 @Override
223 public SocketAddress getLocalAddress() {
224 return ioSession.getLocalAddress();
225 }
226
227 @Override
228 public int getEventMask() {
229 return ioSession.getEventMask();
230 }
231
232 @Override
233 public void setEventMask(final int ops) {
234 ioSession.setEventMask(ops);
235 }
236
237 @Override
238 public void setEvent(final int op) {
239 ioSession.setEvent(op);
240 }
241
242 @Override
243 public void clearEvent(final int op) {
244 ioSession.clearEvent(op);
245 }
246
247 @Override
248 public void close() {
249 ioSession.close();
250 }
251
252 @Override
253 public Status getStatus() {
254 return ioSession.getStatus();
255 }
256
257 @Override
258 public int read(final ByteBuffer dst) throws IOException {
259 final int bytesRead = ioSession.read(dst);
260 if (bytesRead > 0) {
261 stats.incTotalBytesRecv(bytesRead);
262 }
263 return bytesRead;
264 }
265
266 @Override
267 public int write(final ByteBuffer src) throws IOException {
268 final int bytesWritten = ioSession.write(src);
269 if (bytesWritten > 0) {
270 stats.incTotalBytesSent(bytesWritten);
271 }
272 return bytesWritten;
273 }
274
275 @Override
276 public boolean isOpen() {
277 return ioSession.isOpen();
278 }
279
280 @Override
281 public Timeout getSocketTimeout() {
282 return ioSession.getSocketTimeout();
283 }
284
285 @Override
286 public void setSocketTimeout(final Timeout timeout) {
287 ioSession.setSocketTimeout(timeout);
288 }
289
290 @Override
291 public long getLastReadTime() {
292 return ioSession.getLastReadTime();
293 }
294
295 @Override
296 public long getLastWriteTime() {
297 return ioSession.getLastWriteTime();
298 }
299
300 @Override
301 public long getLastEventTime() {
302 return ioSession.getLastEventTime();
303 }
304
305 @Override
306 public void updateReadTime() {
307 ioSession.updateReadTime();
308 }
309
310 @Override
311 public void updateWriteTime() {
312 ioSession.updateWriteTime();
313 }
314
315 @Override
316 public void close(final CloseMode closeMode) {
317 ioSession.close(closeMode);
318 }
319
320 @Override
321 public IOEventHandler getHandler() {
322 return ioSession.getHandler();
323 }
324
325 @Override
326 public void upgrade(final IOEventHandler handler) {
327 ioSession.upgrade(handler);
328 }
329
330 };
331 }
332
333 })
334 .setStreamListener(new Http1StreamListener() {
335
336 @Override
337 public void onRequestHead(final HttpConnection connection, final HttpRequest request) {
338 if (config.getVerbosity() >= 3) {
339 System.out.println(">> " + request.getMethod() + " " + request.getRequestUri());
340 final Header[] headers = request.getHeaders();
341 for (final Header header : headers) {
342 System.out.println(">> " + header);
343 }
344 System.out.println();
345 }
346 }
347
348 @Override
349 public void onResponseHead(final HttpConnection connection, final HttpResponse response) {
350 if (config.getVerbosity() >= 3) {
351 System.out.println("<< " + response.getCode() + " " + response.getReasonPhrase());
352 final Header[] headers = response.getHeaders();
353 for (final Header header : headers) {
354 System.out.println("<< " + header);
355 }
356 System.out.println();
357 }
358 }
359
360 @Override
361 public void onExchangeComplete(final HttpConnection connection, final boolean keepAlive) {
362 }
363
364 })
365 .setStreamListener(new H2StreamListener() {
366
367 private final FramePrinter framePrinter = new FramePrinter();
368
369 @Override
370 public void onHeaderInput(
371 final HttpConnection connection,
372 final int streamId,
373 final List<? extends Header> headers) {
374 if (config.getVerbosity() >= 3) {
375 for (final Header header : headers) {
376 System.out.println("<< " + header);
377 }
378 System.out.println();
379 }
380 }
381
382 @Override
383 public void onHeaderOutput(
384 final HttpConnection connection,
385 final int streamId,
386 final List<? extends Header> headers) {
387 if (config.getVerbosity() >= 3) {
388 for (final Header header : headers) {
389 System.out.println(">> " + header);
390 }
391 System.out.println();
392 }
393 }
394
395 @Override
396 public void onFrameInput(
397 final HttpConnection connection,
398 final int streamId,
399 final RawFrame frame) {
400 if (config.getVerbosity() >= 4) {
401 System.out.print("<< ");
402 try {
403 framePrinter.printFrameInfo(frame, System.out);
404 System.out.println();
405 if (!frame.isType(FrameType.DATA)) {
406 framePrinter.printPayload(frame, System.out);
407 System.out.println();
408 }
409 } catch (final IOException ignore) {
410 }
411 }
412 }
413
414 @Override
415 public void onFrameOutput(
416 final HttpConnection connection,
417 final int streamId,
418 final RawFrame frame) {
419 if (config.getVerbosity() >= 4) {
420 System.out.print(">> ");
421 try {
422 framePrinter.printFrameInfo(frame, System.out);
423 System.out.println();
424 if (!frame.isType(FrameType.DATA)) {
425 framePrinter.printPayload(frame, System.out);
426 System.out.println();
427 }
428 } catch (final IOException ignore) {
429 }
430 }
431 }
432
433 @Override
434 public void onInputFlowControl(
435 final HttpConnection connection,
436 final int streamId,
437 final int delta,
438 final int actualSize) {
439 if (config.getVerbosity() >= 5) {
440 System.out.println("<< stream " + streamId + ": " + actualSize + " " + delta);
441 }
442 }
443
444 @Override
445 public void onOutputFlowControl(
446 final HttpConnection connection,
447 final int streamId,
448 final int delta,
449 final int actualSize) {
450 if (config.getVerbosity() >= 5) {
451 System.out.println(">> stream " + streamId + ": " + actualSize + " " + delta);
452 }
453 }
454
455 })
456 .setIOReactorConfig(IOReactorConfig.custom()
457 .setSoTimeout(config.getSocketTimeout())
458 .build())
459 .create()) {
460 requester.setDefaultMaxPerRoute(config.getConcurrencyLevel());
461 requester.setMaxTotal(config.getConcurrencyLevel() * 2);
462 requester.start();
463 return doExecute(requester, stats);
464 }
465 }
466
467 private Results doExecute(final HttpAsyncRequester requester, final Stats stats) throws Exception {
468
469 final URI requestUri = config.getUri();
470 final HttpHost host = new HttpHost(requestUri.getScheme(), requestUri.getHost(), requestUri.getPort());
471
472 final AtomicLong requestCount = new AtomicLong(config.getRequests());
473
474 final HttpVersion version = HttpVersion.HTTP_1_1;
475
476 final CountDownLatch completionLatch = new CountDownLatch(config.getConcurrencyLevel());
477 final BenchmarkWorkerrkWorker.html#BenchmarkWorker">BenchmarkWorker[] workers = new BenchmarkWorker[config.getConcurrencyLevel()];
478 for (int i = 0; i < workers.length; i++) {
479 final HttpCoreContext context = HttpCoreContext.create();
480 context.setProtocolVersion(version);
481 final BenchmarkWorkerhmarkWorker.html#BenchmarkWorker">BenchmarkWorker worker = new BenchmarkWorker(
482 requester,
483 host,
484 context,
485 requestCount,
486 completionLatch,
487 stats,
488 config);
489 workers[i] = worker;
490 }
491
492 final long deadline = config.getTimeLimit() != null ? config.getTimeLimit().toMilliseconds() : Long.MAX_VALUE;
493
494 final long startTime = System.currentTimeMillis();
495
496 for (int i = 0; i < workers.length; i++) {
497 workers[i].execute();
498 }
499
500 completionLatch.await(deadline, TimeUnit.MILLISECONDS);
501
502 if (config.getVerbosity() >= 3) {
503 System.out.println("...done");
504 }
505
506 final long endTime = System.currentTimeMillis();
507
508 for (int i = 0; i < workers.length; i++) {
509 workers[i].releaseResources();
510 }
511
512 return new Results(
513 stats.getServerName(),
514 stats.getVersion(),
515 host.getHostName(),
516 host.getPort() > 0 ? host.getPort() : host.getSchemeName().equalsIgnoreCase("https") ? 443 : 80,
517 requestUri.toASCIIString(),
518 stats.getContentLength(),
519 config.getConcurrencyLevel(),
520 endTime - startTime,
521 stats.getSuccessCount(),
522 stats.getFailureCount(),
523 stats.getKeepAliveCount(),
524 stats.getTotalBytesRecv(),
525 stats.getTotalBytesSent(),
526 stats.getTotalContentLength());
527 }
528
529 }