hc-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ol...@apache.org
Subject [1/3] httpcomponents-core git commit: Folded ab module into testing
Date Mon, 28 Aug 2017 15:54:07 GMT
Repository: httpcomponents-core
Updated Branches:
  refs/heads/master f888a3327 -> 90d5d2ef2


http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/d9e2b62c/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/BenchmarkWorker.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/BenchmarkWorker.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/BenchmarkWorker.java
new file mode 100644
index 0000000..e47936d
--- /dev/null
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/BenchmarkWorker.java
@@ -0,0 +1,282 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.benchmark;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.util.Iterator;
+
+import javax.net.SocketFactory;
+
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.ConnectionReuseStrategy;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.HttpVersion;
+import org.apache.hc.core5.http.config.H1Config;
+import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
+import org.apache.hc.core5.http.impl.io.HttpRequestExecutor;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.http.protocol.HttpCoreContext;
+import org.apache.hc.core5.http.protocol.HttpProcessor;
+import org.apache.hc.core5.http.protocol.HttpProcessorBuilder;
+import org.apache.hc.core5.http.protocol.RequestConnControl;
+import org.apache.hc.core5.http.protocol.RequestContent;
+import org.apache.hc.core5.http.protocol.RequestExpectContinue;
+import org.apache.hc.core5.http.protocol.RequestTargetHost;
+import org.apache.hc.core5.http.protocol.RequestUserAgent;
+import org.apache.hc.core5.io.ShutdownType;
+
+/**
+ * Worker thread for the {@link HttpBenchmark HttpBenchmark}.
+ *
+ *
+ * @since 4.0
+ */
+class BenchmarkWorker implements Runnable {
+
+    private final byte[] buffer = new byte[4096];
+    private final HttpCoreContext context;
+    private final HttpProcessor httpProcessor;
+    private final HttpRequestExecutor httpexecutor;
+    private final ConnectionReuseStrategy connstrategy;
+    private final HttpHost host;
+    private final ClassicHttpRequest request;
+    private final Config config;
+    private final SocketFactory socketFactory;
+    private boolean shutdownSignal;
+    private final Stats stats = new Stats();
+
+    public BenchmarkWorker(
+            final HttpHost host,
+            final ClassicHttpRequest request,
+            final SocketFactory socketFactory,
+            final Config config) {
+        super();
+        this.context = new HttpCoreContext();
+        this.host = host;
+        this.request = request;
+        this.config = config;
+        final HttpProcessorBuilder builder = HttpProcessorBuilder.create()
+                .addAll(
+                        new RequestContent(),
+                        new RequestTargetHost(),
+                        new RequestConnControl(),
+                        new RequestUserAgent("HttpCore-AB/1.1"));
+        if (this.config.isUseExpectContinue()) {
+            builder.add(new RequestExpectContinue());
+        }
+        this.httpProcessor = builder.build();
+        this.httpexecutor = new HttpRequestExecutor();
+
+        this.connstrategy = DefaultConnectionReuseStrategy.INSTANCE;
+        this.socketFactory = socketFactory;
+        this.shutdownSignal = false;
+    }
+
+    @Override
+    public void run() {
+        ClassicHttpResponse response = null;
+        final HttpVersion version = config.isUseHttp1_0() ? HttpVersion.HTTP_1_0 : HttpVersion.HTTP_1_1;
+        final BenchmarkConnection conn = new BenchmarkConnection(H1Config.DEFAULT, stats);
+
+        final String scheme = this.host.getSchemeName();
+        final String hostname = this.host.getHostName();
+        int port = this.host.getPort();
+        if (port == -1) {
+            if (scheme.equalsIgnoreCase("https")) {
+                port = 443;
+            } else {
+                port = 80;
+            }
+        }
+
+        // Populate the execution context
+        context.setProtocolVersion(version);
+
+        stats.start();
+        final int count = config.getRequests();
+        for (int i = 0; i < count; i++) {
+
+            try {
+                resetHeader(request);
+                if (!conn.isOpen()) {
+
+                    final Socket socket;
+                    if (socketFactory != null) {
+                        socket = socketFactory.createSocket();
+                    } else {
+                        socket = new Socket();
+                    }
+
+                    final int timeout = config.getSocketTimeout();
+                    socket.setSoTimeout(timeout);
+                    socket.connect(new InetSocketAddress(hostname, port), timeout);
+
+                    conn.bind(socket);
+                }
+
+                try {
+                    // Prepare request
+                    this.httpexecutor.preProcess(this.request, this.httpProcessor, this.context);
+                    // Execute request and get a response
+                    response = this.httpexecutor.execute(this.request, conn, this.context);
+                    // Finalize response
+                    this.httpexecutor.postProcess(response, this.httpProcessor, this.context);
+
+                } catch (final HttpException e) {
+                    stats.incWriteErrors();
+                    if (config.getVerbosity() >= 2) {
+                        System.err.println("Failed HTTP request : " + e.getMessage());
+                    }
+                    conn.shutdown(ShutdownType.IMMEDIATE);
+                    continue;
+                }
+
+                verboseOutput(response);
+
+                if (response.getCode() == HttpStatus.SC_OK) {
+                    stats.incSuccessCount();
+                } else {
+                    stats.incFailureCount();
+                }
+
+                final HttpEntity entity = response.getEntity();
+                if (entity != null) {
+                    final ContentType ct = EntityUtils.getContentTypeOrDefault(entity);
+                    Charset charset = ct.getCharset();
+                    if (charset == null) {
+                        charset = StandardCharsets.ISO_8859_1;
+                    }
+                    long contentlen = 0;
+                    final InputStream instream = entity.getContent();
+                    int l;
+                    while ((l = instream.read(this.buffer)) != -1) {
+                        contentlen += l;
+                        if (config.getVerbosity() >= 4) {
+                            final String s = new String(this.buffer, 0, l, charset);
+                            System.out.print(s);
+                        }
+                    }
+                    instream.close();
+                    stats.setContentLength(contentlen);
+                }
+
+                if (config.getVerbosity() >= 4) {
+                    System.out.println();
+                    System.out.println();
+                }
+
+                if (!config.isKeepAlive() || !conn.isConsistent() || !this.connstrategy.keepAlive(request, response, this.context)) {
+                    conn.close();
+                } else {
+                    stats.incKeepAliveCount();
+                }
+
+            } catch (final IOException ex) {
+                stats.incFailureCount();
+                if (config.getVerbosity() >= 2) {
+                    System.err.println("I/O error: " + ex.getMessage());
+                }
+            } catch (final Exception ex) {
+                stats.incFailureCount();
+                if (config.getVerbosity() >= 2) {
+                    System.err.println("Generic error: " + ex.getMessage());
+                }
+            }
+
+            if (shutdownSignal) {
+                break;
+            }
+        }
+        stats.finish();
+
+        if (response != null) {
+            final Header header = response.getFirstHeader("Server");
+            if (header != null) {
+                stats.setServerName(header.getValue());
+            }
+        }
+
+        try {
+            conn.close();
+        } catch (final IOException ex) {
+            stats.incFailureCount();
+            if (config.getVerbosity() >= 2) {
+                System.err.println("I/O error: " + ex.getMessage());
+            }
+        }
+    }
+
+    private void verboseOutput(final ClassicHttpResponse response) {
+        if (config.getVerbosity() >= 3) {
+            System.out.println(">> " + request.getMethod() + " " + request.getRequestUri());
+            final Header[] headers = request.getAllHeaders();
+            for (final Header header : headers) {
+                System.out.println(">> " + header.toString());
+            }
+            System.out.println();
+        }
+        if (config.getVerbosity() >= 2) {
+            System.out.println(response.getCode());
+        }
+        if (config.getVerbosity() >= 3) {
+            System.out.println("<< " + response.getCode() + " " + response.getReasonPhrase());
+            final Header[] headers = response.getAllHeaders();
+            for (final Header header : headers) {
+                System.out.println("<< " + header.toString());
+            }
+            System.out.println();
+        }
+    }
+
+    private static void resetHeader(final ClassicHttpRequest request) {
+        for (final Iterator<Header> it = request.headerIterator(); it.hasNext();) {
+            final Header header = it.next();
+            if (!(header instanceof DefaultHeader)) {
+                it.remove();
+            }
+        }
+    }
+
+    public Stats getStats() {
+        return stats;
+    }
+
+    public synchronized void setShutdownSignal() {
+        this.shutdownSignal = true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/d9e2b62c/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/CommandLineUtils.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/CommandLineUtils.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/CommandLineUtils.java
new file mode 100644
index 0000000..b36b0d1
--- /dev/null
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/CommandLineUtils.java
@@ -0,0 +1,247 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.benchmark;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+
+public class CommandLineUtils {
+
+    private CommandLineUtils() {
+        // Do not allow utility class to be instantiated.
+    }
+
+    public static Options getOptions() {
+        final Option iopt = new Option("i", false, "Do HEAD requests instead of GET (deprecated)");
+        iopt.setRequired(false);
+
+        final Option oopt = new Option("o", false, "Use HTTP/S 1.0 instead of 1.1 (default)");
+        oopt.setRequired(false);
+
+        final Option kopt = new Option("k", false, "Enable the HTTP KeepAlive feature, " +
+            "i.e., perform multiple requests within one HTTP session. " +
+            "Default is no KeepAlive");
+        kopt.setRequired(false);
+
+        final Option uopt = new Option("u", false, "Chunk entity. Default is false");
+        uopt.setRequired(false);
+
+        final Option xopt = new Option("x", false, "Use Expect-Continue. Default is false");
+        xopt.setRequired(false);
+
+        final Option gopt = new Option("g", false, "Accept GZip. Default is false");
+        gopt.setRequired(false);
+
+        final Option nopt = new Option("n", true, "Number of requests to perform for the " +
+            "benchmarking session. The default is to just perform a single " +
+            "request which usually leads to non-representative benchmarking " +
+            "results");
+        nopt.setRequired(false);
+        nopt.setArgName("requests");
+
+        final Option copt = new Option("c", true, "Concurrency while performing the " +
+            "benchmarking session. The default is to just use a single thread/client");
+        copt.setRequired(false);
+        copt.setArgName("concurrency");
+
+        final Option popt = new Option("p", true, "File containing data to POST or PUT");
+        popt.setRequired(false);
+        popt.setArgName("Payload file");
+
+        final Option mopt = new Option("m", true, "HTTP Method. Default is POST. " +
+                "Possible options are GET, POST, PUT, DELETE, HEAD, OPTIONS, TRACE");
+        mopt.setRequired(false);
+        mopt.setArgName("HTTP method");
+
+        final Option Topt = new Option("T", true, "Content-type header to use for POST/PUT data");
+        Topt.setRequired(false);
+        Topt.setArgName("content-type");
+
+        final Option topt = new Option("t", true, "Client side socket timeout (in ms) - default 60 Secs");
+        topt.setRequired(false);
+        topt.setArgName("socket-Timeout");
+
+        final Option tlopt = new Option("l", true, "Time limit for the test to run (default is infinity)");
+        tlopt.setRequired(false);
+        tlopt.setArgName("time-limit");
+
+        final Option Hopt = new Option("H", true, "Add arbitrary header line, " +
+            "eg. 'Accept-Encoding: gzip' inserted after all normal " +
+            "header lines. (repeatable as -H \"h1: v1\",\"h2: v2\" etc)");
+        Hopt.setRequired(false);
+        Hopt.setArgName("header");
+
+        final Option vopt = new Option("v", true, "Set verbosity level - 4 and above " +
+            "prints response content, 3 and above prints " +
+            "information on headers, 2 and above prints response codes (404, 200, " +
+            "etc.), 1 and above prints warnings and info");
+        vopt.setRequired(false);
+        vopt.setArgName("verbosity");
+
+        final Option hopt = new Option("h", false, "Display usage information");
+        nopt.setRequired(false);
+
+        final Options options = new Options();
+        options.addOption(iopt);
+        options.addOption(mopt);
+        options.addOption(uopt);
+        options.addOption(xopt);
+        options.addOption(gopt);
+        options.addOption(kopt);
+        options.addOption(nopt);
+        options.addOption(copt);
+        options.addOption(popt);
+        options.addOption(Topt);
+        options.addOption(vopt);
+        options.addOption(Hopt);
+        options.addOption(hopt);
+        options.addOption(topt);
+        options.addOption(oopt);
+        options.addOption(tlopt);
+        return options;
+    }
+
+    public static void parseCommandLine(final CommandLine cmd, final Config config) {
+        if (cmd.hasOption('v')) {
+            final String s = cmd.getOptionValue('v');
+            try {
+                config.setVerbosity(Integer.parseInt(s));
+            } catch (final NumberFormatException ex) {
+                printError("Invalid verbosity level: " + s);
+            }
+        }
+
+        if (cmd.hasOption('k')) {
+            config.setKeepAlive(true);
+        }
+
+        if (cmd.hasOption('c')) {
+            final String s = cmd.getOptionValue('c');
+            try {
+                config.setThreads(Integer.parseInt(s));
+            } catch (final NumberFormatException ex) {
+                printError("Invalid number for concurrency: " + s);
+            }
+        }
+
+        if (cmd.hasOption('n')) {
+            final String s = cmd.getOptionValue('n');
+            try {
+                config.setRequests(Integer.parseInt(s));
+            } catch (final NumberFormatException ex) {
+                printError("Invalid number of requests: " + s);
+            }
+        }
+
+        if (cmd.hasOption('p')) {
+            final File file = new File(cmd.getOptionValue('p'));
+            if (!file.exists()) {
+                printError("File not found: " + file);
+            }
+            config.setPayloadFile(file);
+        }
+
+        if (cmd.hasOption('T')) {
+            config.setContentType(cmd.getOptionValue('T'));
+        }
+
+        if (cmd.hasOption('i')) {
+            config.setHeadInsteadOfGet(true);
+        }
+
+        if (cmd.hasOption('H')) {
+            final String headerStr = cmd.getOptionValue('H');
+            config.setHeaders(headerStr.split(","));
+        }
+
+        if (cmd.hasOption('t')) {
+            final String t = cmd.getOptionValue('t');
+            try {
+                config.setSocketTimeout(Integer.parseInt(t));
+            } catch (final NumberFormatException ex) {
+                printError("Invalid socket timeout: " + t);
+            }
+        }
+
+        if (cmd.hasOption('l')) {
+            final String l = cmd.getOptionValue('l');
+            try {
+                config.setTimeLimit(Integer.parseInt(l));
+            } catch (final NumberFormatException ex) {
+                printError("Invalid time limit: " + l);
+            }
+        }
+
+        if (cmd.hasOption('o')) {
+            config.setUseHttp1_0(true);
+        }
+
+        if (cmd.hasOption('m')) {
+            config.setMethod(cmd.getOptionValue('m'));
+        } else if (cmd.hasOption('p')) {
+            config.setMethod("POST");
+        }
+
+        if (cmd.hasOption('u')) {
+            config.setUseChunking(true);
+        }
+
+        if (cmd.hasOption('x')) {
+            config.setUseExpectContinue(true);
+        }
+
+        if (cmd.hasOption('g')) {
+            config.setUseAcceptGZip(true);
+        }
+
+        final String[] cmdargs = cmd.getArgs();
+        if (cmdargs.length > 0) {
+            try {
+                config.setUrl(new URL(cmdargs[0]));
+            } catch (final MalformedURLException e) {
+                printError("Invalid request URL : " + cmdargs[0]);
+            }
+        }
+    }
+
+    static void showUsage(final Options options) {
+        final HelpFormatter formatter = new HelpFormatter();
+        formatter.printHelp("HttpBenchmark [options] [http://]hostname[:port]/path?query", options);
+    }
+
+    static void printError(final String msg) {
+        System.err.println(msg);
+        showUsage(getOptions());
+        System.exit(-1);
+    }
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/d9e2b62c/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/Config.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/Config.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/Config.java
new file mode 100644
index 0000000..9388618
--- /dev/null
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/Config.java
@@ -0,0 +1,288 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.benchmark;
+
+import java.io.File;
+import java.net.URL;
+
+public class Config {
+
+    private URL url;
+    private int requests;
+    private int threads;
+    private boolean keepAlive;
+    private int verbosity;
+    private boolean headInsteadOfGet;
+    private boolean useHttp1_0;
+    private String contentType;
+    private String[] headers;
+    private int socketTimeout;
+    private String method = "GET";
+    private boolean useChunking;
+    private boolean useExpectContinue;
+    private boolean useAcceptGZip;
+    private File payloadFile = null;
+    private String payloadText = null;
+    private String soapAction = null;
+    private int timeLimit = -1;
+
+    private boolean disableSSLVerification = true;
+    private String trustStorePath = null;
+    private String identityStorePath = null;
+    private String trustStorePassword = null;
+    private String identityStorePassword = null;
+
+    public Config() {
+        super();
+        this.url = null;
+        this.requests = 1;
+        this.threads = 1;
+        this.keepAlive = false;
+        this.verbosity = 0;
+        this.headInsteadOfGet = false;
+        this.useHttp1_0 = false;
+        this.payloadFile = null;
+        this.payloadText = null;
+        this.contentType = null;
+        this.headers = null;
+        this.socketTimeout = 60000;
+    }
+
+    public URL getUrl() {
+        return url;
+    }
+
+    public void setUrl(final URL url) {
+        this.url = url;
+    }
+
+    public int getRequests() {
+        return requests;
+    }
+
+    public void setRequests(final int requests) {
+        this.requests = requests;
+    }
+
+    public int getThreads() {
+        return threads;
+    }
+
+    public void setThreads(final int threads) {
+        this.threads = threads;
+    }
+
+    public boolean isKeepAlive() {
+        return keepAlive;
+    }
+
+    public void setKeepAlive(final boolean keepAlive) {
+        this.keepAlive = keepAlive;
+    }
+
+    public int getVerbosity() {
+        return verbosity;
+    }
+
+    public void setVerbosity(final int verbosity) {
+        this.verbosity = verbosity;
+    }
+
+    public boolean isHeadInsteadOfGet() {
+        return headInsteadOfGet;
+    }
+
+    public void setHeadInsteadOfGet(final boolean headInsteadOfGet) {
+        this.headInsteadOfGet = headInsteadOfGet;
+        this.method = "HEAD";
+    }
+
+    public boolean isUseHttp1_0() {
+        return useHttp1_0;
+    }
+
+    public void setUseHttp1_0(final boolean useHttp1_0) {
+        this.useHttp1_0 = useHttp1_0;
+    }
+
+    public File getPayloadFile() {
+        return payloadFile;
+    }
+
+    public void setPayloadFile(final File payloadFile) {
+        this.payloadFile = payloadFile;
+    }
+
+    public String getContentType() {
+        return contentType;
+    }
+
+    public void setContentType(final String contentType) {
+        this.contentType = contentType;
+    }
+
+    public String[] getHeaders() {
+        return headers;
+    }
+
+    public void setHeaders(final String[] headers) {
+        this.headers = headers;
+    }
+
+    public int getSocketTimeout() {
+        return socketTimeout;
+    }
+
+    public void setSocketTimeout(final int socketTimeout) {
+        this.socketTimeout = socketTimeout;
+    }
+
+    public void setMethod(final String method) {
+        this.method = method;
+    }
+
+    public void setUseChunking(final boolean useChunking) {
+        this.useChunking = useChunking;
+    }
+
+    public void setUseExpectContinue(final boolean useExpectContinue) {
+        this.useExpectContinue = useExpectContinue;
+    }
+
+    public void setUseAcceptGZip(final boolean useAcceptGZip) {
+        this.useAcceptGZip = useAcceptGZip;
+    }
+
+    public String getMethod() {
+        return method;
+    }
+
+    public boolean isUseChunking() {
+        return useChunking;
+    }
+
+    public boolean isUseExpectContinue() {
+        return useExpectContinue;
+    }
+
+    public boolean isUseAcceptGZip() {
+        return useAcceptGZip;
+    }
+
+    public String getPayloadText() {
+        return payloadText;
+    }
+
+    public String getSoapAction() {
+        return soapAction;
+    }
+
+    public boolean isDisableSSLVerification() {
+        return disableSSLVerification;
+    }
+
+    public String getTrustStorePath() {
+        return trustStorePath;
+    }
+
+    public String getIdentityStorePath() {
+        return identityStorePath;
+    }
+
+    public String getTrustStorePassword() {
+        return trustStorePassword;
+    }
+
+    public String getIdentityStorePassword() {
+        return identityStorePassword;
+    }
+
+    public void setPayloadText(final String payloadText) {
+        this.payloadText = payloadText;
+    }
+
+    public void setSoapAction(final String soapAction) {
+        this.soapAction = soapAction;
+    }
+
+    public void setDisableSSLVerification(final boolean disableSSLVerification) {
+        this.disableSSLVerification = disableSSLVerification;
+    }
+
+    public void setTrustStorePath(final String trustStorePath) {
+        this.trustStorePath = trustStorePath;
+    }
+
+    public void setIdentityStorePath(final String identityStorePath) {
+        this.identityStorePath = identityStorePath;
+    }
+
+    public void setTrustStorePassword(final String trustStorePassword) {
+        this.trustStorePassword = trustStorePassword;
+    }
+
+    public void setIdentityStorePassword(final String identityStorePassword) {
+        this.identityStorePassword = identityStorePassword;
+    }
+
+    public void setTimeLimit(final int timeLimit) {
+        this.timeLimit = timeLimit;
+    }
+
+    public int getTimeLimit() {
+        return timeLimit;
+    }
+
+    public Config copy() {
+        final Config copy = new Config();
+        copy.url = this.url;
+        copy.requests = this.requests;
+        copy.threads = this.threads;
+        copy.keepAlive = this.keepAlive;
+        copy.verbosity = this.verbosity;
+        copy.headInsteadOfGet = this.headInsteadOfGet;
+        copy.useHttp1_0 = this.useHttp1_0;
+        copy.contentType = this.contentType;
+        copy.headers = this.headers;
+        copy.socketTimeout = this.socketTimeout;
+        copy.method = this.method;
+        copy.useChunking = this.useChunking;
+        copy.useExpectContinue = this.useExpectContinue;
+        copy.useAcceptGZip = this.useAcceptGZip;
+        copy.payloadFile = this.payloadFile;
+        copy.payloadText = this.payloadText;
+        copy.soapAction = this.soapAction;
+
+        copy.disableSSLVerification = this.disableSSLVerification;
+        copy.trustStorePath = this.trustStorePath;
+        copy.identityStorePath = this.identityStorePath;
+        copy.trustStorePassword = this.trustStorePassword;
+        copy.identityStorePassword = this.identityStorePassword;
+        return copy;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/d9e2b62c/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/CountingInputStream.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/CountingInputStream.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/CountingInputStream.java
new file mode 100644
index 0000000..b64d7b8
--- /dev/null
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/CountingInputStream.java
@@ -0,0 +1,78 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.benchmark;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+class CountingInputStream extends FilterInputStream {
+
+    private final Stats stats;
+
+    CountingInputStream(final InputStream instream, final Stats stats) {
+        super(instream);
+        this.stats = stats;
+    }
+
+    @Override
+    public int read() throws IOException {
+        final int b = this.in.read();
+        if (b != -1) {
+            this.stats.incTotalBytesRecv(1);
+        }
+        return b;
+    }
+
+    @Override
+    public int read(final byte[] b) throws IOException {
+        final int bytesRead = this.in.read(b);
+        if (bytesRead > 0) {
+            this.stats.incTotalBytesRecv(bytesRead);
+        }
+        return bytesRead;
+    }
+
+    @Override
+    public int read(final byte[] b, final int off, final int len) throws IOException {
+        final int bytesRead = this.in.read(b, off, len);
+        if (bytesRead > 0) {
+            this.stats.incTotalBytesRecv(bytesRead);
+        }
+        return bytesRead;
+    }
+
+    @Override
+    public long skip(final long n) throws IOException {
+        final long bytesRead = this.in.skip(n);
+        if (bytesRead > 0) {
+            this.stats.incTotalBytesRecv(bytesRead);
+        }
+        return bytesRead;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/d9e2b62c/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/CountingOutputStream.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/CountingOutputStream.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/CountingOutputStream.java
new file mode 100644
index 0000000..a47c537
--- /dev/null
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/CountingOutputStream.java
@@ -0,0 +1,60 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.benchmark;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+class CountingOutputStream extends FilterOutputStream {
+
+    private final Stats stats;
+
+    CountingOutputStream(final OutputStream outstream, final Stats stats) {
+        super(outstream);
+        this.stats = stats;
+    }
+
+    @Override
+    public void write(final int b) throws IOException {
+        this.out.write(b);
+        this.stats.incTotalBytesSent(1);
+    }
+
+    @Override
+    public void write(final byte[] b) throws IOException {
+        this.out.write(b);
+        this.stats.incTotalBytesSent(b.length);
+    }
+
+    @Override
+    public void write(final byte[] b, final int off, final int len) throws IOException {
+        this.out.write(b, off, len);
+        this.stats.incTotalBytesSent(len);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/d9e2b62c/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/DefaultHeader.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/DefaultHeader.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/DefaultHeader.java
new file mode 100644
index 0000000..aa5473b
--- /dev/null
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/DefaultHeader.java
@@ -0,0 +1,40 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.benchmark;
+
+import org.apache.hc.core5.http.message.BasicHeader;
+
+class DefaultHeader extends BasicHeader {
+
+    private static final long serialVersionUID = 3465786867105185103L;
+
+    public DefaultHeader(final String name, final String value) {
+        super(name, value);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/d9e2b62c/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/HttpBenchmark.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/HttpBenchmark.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/HttpBenchmark.java
new file mode 100644
index 0000000..df30ba1
--- /dev/null
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/HttpBenchmark.java
@@ -0,0 +1,240 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.benchmark;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.PosixParser;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HeaderElements;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.HttpVersion;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.io.entity.FileEntity;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
+import org.apache.hc.core5.net.URIAuthority;
+import org.apache.hc.core5.ssl.SSLContextBuilder;
+import org.apache.hc.core5.ssl.TrustStrategy;
+
+import java.io.File;
+import java.net.URL;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLContext;
+
+/**
+ * Main program of the HTTP benchmark.
+ *
+ *
+ * @since 4.0
+ */
+public class HttpBenchmark {
+
+    private final Config config;
+
+    public static void main(final String[] args) throws Exception {
+
+        final Options options = CommandLineUtils.getOptions();
+        final CommandLineParser parser = new PosixParser();
+        final CommandLine cmd = parser.parse(options, args);
+
+        if (args.length == 0 || cmd.hasOption('h') || cmd.getArgs().length != 1) {
+            CommandLineUtils.showUsage(options);
+            System.exit(1);
+        }
+
+        final Config config = new Config();
+        CommandLineUtils.parseCommandLine(cmd, config);
+
+        if (config.getUrl() == null) {
+            CommandLineUtils.showUsage(options);
+            System.exit(1);
+        }
+
+        final HttpBenchmark httpBenchmark = new HttpBenchmark(config);
+        httpBenchmark.execute();
+    }
+
+    public HttpBenchmark(final Config config) {
+        super();
+        this.config = config != null ? config : new Config();
+    }
+
+    private ClassicHttpRequest createRequest(final HttpHost host) {
+        final URL url = config.getUrl();
+        HttpEntity entity = null;
+
+        // Prepare requests for each thread
+        if (config.getPayloadFile() != null) {
+            final FileEntity fe = new FileEntity(config.getPayloadFile());
+            fe.setContentType(config.getContentType());
+            fe.setChunked(config.isUseChunking());
+            entity = fe;
+        } else if (config.getPayloadText() != null) {
+            final StringEntity se = new StringEntity(config.getPayloadText(),
+                    ContentType.parse(config.getContentType()));
+            se.setChunked(config.isUseChunking());
+            entity = se;
+        }
+        final ClassicHttpRequest request;
+        if ("POST".equals(config.getMethod())) {
+            final ClassicHttpRequest httppost = new BasicClassicHttpRequest("POST", url.getPath());
+            httppost.setEntity(entity);
+            request = httppost;
+        } else if ("PUT".equals(config.getMethod())) {
+            final ClassicHttpRequest httpput = new BasicClassicHttpRequest("PUT", url.getPath());
+            httpput.setEntity(entity);
+            request = httpput;
+        } else {
+            String path = url.getPath();
+            if (url.getQuery() != null && url.getQuery().length() > 0) {
+                path += "?" + url.getQuery();
+            } else if (path.trim().length() == 0) {
+                path = "/";
+            }
+            request = new BasicClassicHttpRequest(config.getMethod(), path);
+        }
+        request.setVersion(config.isUseHttp1_0() ? HttpVersion.HTTP_1_0 : HttpVersion.HTTP_1_1);
+
+        if (!config.isKeepAlive()) {
+            request.addHeader(new DefaultHeader(HttpHeaders.CONNECTION, HeaderElements.CLOSE));
+        }
+
+        final String[] headers = config.getHeaders();
+        if (headers != null) {
+            for (final String s : headers) {
+                final int pos = s.indexOf(':');
+                if (pos != -1) {
+                    final Header header = new DefaultHeader(s.substring(0, pos).trim(), s.substring(pos + 1));
+                    request.addHeader(header);
+                }
+            }
+        }
+
+        if (config.isUseAcceptGZip()) {
+            request.addHeader(new DefaultHeader("Accept-Encoding", "gzip"));
+        }
+
+        if (config.getSoapAction() != null && config.getSoapAction().length() > 0) {
+            request.addHeader(new DefaultHeader("SOAPAction", config.getSoapAction()));
+        }
+        request.setScheme(host.getSchemeName());
+        request.setAuthority(new URIAuthority(host));
+        return request;
+    }
+
+    public String execute() throws Exception {
+        final Results results = doExecute();
+        ResultProcessor.printResults(results);
+        return "";
+    }
+
+    public Results doExecute() throws Exception {
+
+        final URL url = config.getUrl();
+        final long endTime = System.currentTimeMillis() + config.getTimeLimit()*1000;
+        final HttpHost host = new HttpHost(url.getHost(), url.getPort(), url.getProtocol());
+        final ThreadPoolExecutor workerPool = new ThreadPoolExecutor(
+                config.getThreads(), config.getThreads(), 5, TimeUnit.SECONDS,
+            new LinkedBlockingQueue<Runnable>(),
+            new ThreadFactory() {
+
+                @Override
+                public Thread newThread(final Runnable r) {
+                    return new Thread(r, "ClientPool");
+                }
+
+            });
+        workerPool.prestartAllCoreThreads();
+
+        SocketFactory socketFactory = null;
+        if ("https".equals(host.getSchemeName())) {
+            final SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
+            sslContextBuilder.setProtocol("SSL");
+            if (config.isDisableSSLVerification()) {
+                sslContextBuilder.loadTrustMaterial(null, new TrustStrategy() {
+
+                    @Override
+                    public boolean isTrusted(
+                            final X509Certificate[] chain, final String authType) throws CertificateException {
+                        return true;
+                    }
+
+                });
+            } else if (config.getTrustStorePath() != null) {
+                sslContextBuilder.loadTrustMaterial(
+                        new File(config.getTrustStorePath()),
+                        config.getTrustStorePassword() != null ? config.getTrustStorePassword().toCharArray() : null);
+            }
+            if (config.getIdentityStorePath() != null) {
+                sslContextBuilder.loadKeyMaterial(
+                        new File(config.getIdentityStorePath()),
+                        config.getIdentityStorePassword() != null ? config.getIdentityStorePassword().toCharArray() : null,
+                        config.getIdentityStorePassword() != null ? config.getIdentityStorePassword().toCharArray() : null);
+            }
+            final SSLContext sslContext = sslContextBuilder.build();
+            socketFactory = sslContext.getSocketFactory();
+        }
+
+        final BenchmarkWorker[] workers = new BenchmarkWorker[config.getThreads()];
+        for (int i = 0; i < workers.length; i++) {
+            workers[i] = new BenchmarkWorker(
+                    host,
+                    createRequest(host),
+                    socketFactory,
+                    config);
+            workerPool.execute(workers[i]);
+        }
+
+        while (workerPool.getCompletedTaskCount() < config.getThreads()) {
+            Thread.yield();
+            try {
+                Thread.sleep(1000);
+            } catch (final InterruptedException ignore) {
+            }
+            if (config.getTimeLimit() != -1 && System.currentTimeMillis() > endTime) {
+                for (int i = 0; i < workers.length; i++) {
+                    workers[i].setShutdownSignal();
+                }
+            }
+        }
+
+        workerPool.shutdown();
+        return ResultProcessor.collectResults(workers, host, config.getUrl().toString());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/d9e2b62c/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/ResultProcessor.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/ResultProcessor.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/ResultProcessor.java
new file mode 100644
index 0000000..93d39bf
--- /dev/null
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/ResultProcessor.java
@@ -0,0 +1,126 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.benchmark;
+
+import java.text.NumberFormat;
+
+import org.apache.hc.core5.http.HttpHost;
+
+public class ResultProcessor {
+
+    private ResultProcessor() {
+        // Do not allow utility class to be instantiated.
+    }
+
+    static NumberFormat nf2 = NumberFormat.getInstance();
+    static NumberFormat nf3 = NumberFormat.getInstance();
+    static NumberFormat nf6 = NumberFormat.getInstance();
+
+    static {
+        nf2.setMaximumFractionDigits(2);
+        nf2.setMinimumFractionDigits(2);
+        nf3.setMaximumFractionDigits(3);
+        nf3.setMinimumFractionDigits(3);
+        nf6.setMaximumFractionDigits(6);
+        nf6.setMinimumFractionDigits(6);
+    }
+
+    static Results collectResults(final BenchmarkWorker[] workers, final HttpHost host, final String uri) {
+        long totalTimeNano = 0;
+        long successCount    = 0;
+        long failureCount    = 0;
+        long writeErrors     = 0;
+        long keepAliveCount  = 0;
+        long totalBytesRcvd  = 0;
+        long totalBytesSent  = 0;
+
+        final Stats stats = workers[0].getStats();
+
+        for (final BenchmarkWorker worker : workers) {
+            final Stats s = worker.getStats();
+            totalTimeNano  += s.getDuration();
+            successCount   += s.getSuccessCount();
+            failureCount   += s.getFailureCount();
+            writeErrors    += s.getWriteErrors();
+            keepAliveCount += s.getKeepAliveCount();
+            totalBytesRcvd += s.getTotalBytesRecv();
+            totalBytesSent += s.getTotalBytesSent();
+        }
+
+        final Results results = new Results();
+        results.serverName = stats.getServerName();
+        results.hostName = host.getHostName();
+        results.hostPort = host.getPort() > 0 ? host.getPort() :
+            host.getSchemeName().equalsIgnoreCase("https") ? 443 : 80;
+        results.documentPath = uri;
+        results.contentLength = stats.getContentLength();
+        results.concurrencyLevel = workers.length;
+        results.totalTimeNano = totalTimeNano;
+        results.successCount = successCount;
+        results.failureCount = failureCount;
+        results.writeErrors = writeErrors;
+        results.keepAliveCount = keepAliveCount;
+        results.totalBytesRcvd = totalBytesRcvd;
+        results.totalBytesSent = totalBytesSent;
+        results.totalBytes = totalBytesRcvd + (totalBytesSent > 0 ? totalBytesSent : 0);
+        return results;
+    }
+
+    static void printResults(final Results results) {
+        final int threads = results.getConcurrencyLevel();
+        final double totalTimeMs  = (results.getTotalTimeNano() / threads) / 1000000; // convert nano secs to milli secs
+        final double timePerReqMs = totalTimeMs / results.getSuccessCount();
+        final double totalTimeSec = totalTimeMs / 1000;
+        final double reqsPerSec   = results.getSuccessCount() / totalTimeSec;
+
+        System.out.println("\nServer Software:\t\t" + results.getServerName());
+        System.out.println( "Server Hostname:\t\t" + results.getHostName());
+        System.out.println( "Server Port:\t\t\t" + Integer.valueOf(results.getHostPort()));
+        System.out.println( "Document Path:\t\t\t" + results.getDocumentPath());
+        System.out.println( "Document Length:\t\t" + results.getContentLength() + " bytes\n");
+        System.out.println( "Concurrency Level:\t\t" + results.getConcurrencyLevel());
+        System.out.println( "Time taken for tests:\t\t" + nf6.format(totalTimeSec) + " seconds");
+        System.out.println( "Complete requests:\t\t" + results.getSuccessCount());
+        System.out.println( "Failed requests:\t\t" + results.getFailureCount());
+        System.out.println( "Write errors:\t\t\t" + results.getWriteErrors());
+        System.out.println( "Kept alive:\t\t\t" + results.getKeepAliveCount());
+        System.out.println( "Total transferred:\t\t" + results.getTotalBytes() + " bytes");
+        System.out.println( "Requests per second:\t\t" + nf2.format(reqsPerSec) + " [#/sec] (mean)");
+        System.out.println( "Time per request:\t\t" + nf3.format(timePerReqMs
+                * results.getConcurrencyLevel()) + " [ms] (mean)");
+        System.out.println( "Time per request:\t\t" + nf3.format(timePerReqMs) +
+            " [ms] (mean, across all concurrent requests)");
+        System.out.println( "Transfer rate:\t\t\t" +
+            nf2.format(results.getTotalBytesRcvd() / 1000 / totalTimeSec) + " [Kbytes/sec] received");
+        System.out.println( "\t\t\t\t" +
+            (results.getTotalBytesSent() > 0 ? nf2.format(results.getTotalBytesSent()
+                    / 1000 / totalTimeSec) : Integer.valueOf(-1)) + " kb/s sent");
+        System.out.println( "\t\t\t\t" +
+            nf2.format(results.getTotalBytes() / 1000 / totalTimeSec) + " kb/s total");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/d9e2b62c/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/Results.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/Results.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/Results.java
new file mode 100644
index 0000000..78c444f
--- /dev/null
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/Results.java
@@ -0,0 +1,133 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.benchmark;
+
+/**
+ * Benchmark results
+ *
+ * @since 4.3
+ */
+public final class Results {
+
+    String serverName;
+    String hostName;
+    int hostPort;
+    String documentPath;
+    long contentLength;
+    int concurrencyLevel;
+    long totalTimeNano;
+    long successCount;
+    long failureCount;
+    long writeErrors;
+    long keepAliveCount;
+    long totalBytesRcvd;
+    long totalBytesSent;
+    long totalBytes;
+
+    Results() {
+        super();
+        this.contentLength = -1;
+    }
+
+    public String getServerName() {
+        return serverName;
+    }
+
+    public String getHostName() {
+        return hostName;
+    }
+
+    public int getHostPort() {
+        return hostPort;
+    }
+
+    public String getDocumentPath() {
+        return documentPath;
+    }
+
+    public long getContentLength() {
+        return contentLength;
+    }
+
+    public int getConcurrencyLevel() {
+        return concurrencyLevel;
+    }
+
+    public long getTotalTimeNano() {
+        return totalTimeNano;
+    }
+
+    public long getSuccessCount() {
+        return successCount;
+    }
+
+    public long getFailureCount() {
+        return failureCount;
+    }
+
+    public long getWriteErrors() {
+        return writeErrors;
+    }
+
+    public long getKeepAliveCount() {
+        return keepAliveCount;
+    }
+
+    public long getTotalBytesRcvd() {
+        return totalBytesRcvd;
+    }
+
+    public long getTotalBytesSent() {
+        return totalBytesSent;
+    }
+
+    public long getTotalBytes() {
+        return totalBytes;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder();
+        builder.append("[serverName=").append(serverName)
+                .append(", hostName=").append(hostName)
+                .append(", hostPort=").append(hostPort)
+                .append(", documentPath=").append(documentPath)
+                .append(", contentLength=").append(contentLength)
+                .append(", concurrencyLevel=").append(concurrencyLevel)
+                .append(", totalTimeNano=").append(totalTimeNano)
+                .append(", successCount=").append(successCount)
+                .append(", failureCount=").append(failureCount)
+                .append(", writeErrors=").append(writeErrors)
+                .append(", keepAliveCount=").append(keepAliveCount)
+                .append(", totalBytesRcvd=").append(totalBytesRcvd)
+                .append(", totalBytesSent=").append(totalBytesSent)
+                .append(", totalBytes=").append(totalBytes)
+                .append("]");
+        return builder.toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/d9e2b62c/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/Stats.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/Stats.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/Stats.java
new file mode 100644
index 0000000..0551d87
--- /dev/null
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/Stats.java
@@ -0,0 +1,143 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.benchmark;
+
+/**
+ * Helper to gather statistics for an {@link HttpBenchmark HttpBenchmark}.
+ *
+ *
+ * @since 4.0
+ */
+public class Stats {
+
+    private long startTime = -1;    // nano seconds - does not represent an actual time
+    private long finishTime = -1;   // nano seconds - does not represent an actual time
+    private int successCount = 0;
+    private int failureCount = 0;
+    private int writeErrors = 0;
+    private int keepAliveCount = 0;
+    private String serverName = null;
+    private long totalBytesRecv = 0;
+    private long totalBytesSent = 0;
+    private long contentLength = -1;
+
+    public Stats() {
+        super();
+    }
+
+    public void start() {
+        this.startTime = System.nanoTime();
+    }
+
+    public void finish() {
+        this.finishTime = System.nanoTime();
+    }
+
+    public long getFinishTime() {
+        return this.finishTime;
+    }
+
+    public long getStartTime() {
+        return this.startTime;
+    }
+
+    /**
+     * Total execution time measured in nano seconds
+     *
+     * @return duration in nanoseconds
+     */
+    public long getDuration() {
+        // we are using System.nanoTime() and the return values could be negative
+        // but its only the difference that we are concerned about
+        return this.finishTime - this.startTime;
+    }
+
+    public void incSuccessCount() {
+        this.successCount++;
+    }
+
+    public int getSuccessCount() {
+        return this.successCount;
+    }
+
+    public void incFailureCount() {
+        this.failureCount++;
+    }
+
+    public int getFailureCount() {
+        return this.failureCount;
+    }
+
+    public void incWriteErrors() {
+        this.writeErrors++;
+    }
+
+    public int getWriteErrors() {
+        return this.writeErrors;
+    }
+
+    public void incKeepAliveCount() {
+        this.keepAliveCount++;
+    }
+
+    public int getKeepAliveCount() {
+        return this.keepAliveCount;
+    }
+
+    public long getTotalBytesRecv() {
+        return this.totalBytesRecv;
+    }
+
+    public void incTotalBytesRecv(final long n) {
+        this.totalBytesRecv += n;
+    }
+
+    public long getTotalBytesSent() {
+        return this.totalBytesSent;
+    }
+
+    public void incTotalBytesSent(final long n) {
+        this.totalBytesSent += n;
+    }
+
+    public long getContentLength() {
+        return this.contentLength;
+    }
+
+    public void setContentLength(final long contentLength) {
+        this.contentLength = contentLength;
+    }
+
+    public String getServerName() {
+        return this.serverName;
+    }
+
+    public void setServerName(final String serverName) {
+        this.serverName = serverName;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/d9e2b62c/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/package-info.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/package-info.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/package-info.java
new file mode 100644
index 0000000..5f87cb5
--- /dev/null
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/benchmark/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+/**
+ * Apache Bench (AB) clone based on HttpCore.
+ */
+package org.apache.hc.core5.benchmark;

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/d9e2b62c/httpcore5-testing/src/test/java/org/apache/hc/core5/benchmark/BenchmarkToolTest.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/benchmark/BenchmarkToolTest.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/benchmark/BenchmarkToolTest.java
new file mode 100644
index 0000000..19355f6
--- /dev/null
+++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/benchmark/BenchmarkToolTest.java
@@ -0,0 +1,97 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.benchmark;
+
+import java.io.IOException;
+import java.net.URL;
+
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.impl.bootstrap.HttpServer;
+import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap;
+import org.apache.hc.core5.http.io.HttpRequestHandler;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.apache.hc.core5.io.ShutdownType;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class BenchmarkToolTest {
+
+    private HttpServer server;
+
+    @Before
+    public void setup() throws Exception {
+        server = ServerBootstrap.bootstrap()
+                .register("/", new HttpRequestHandler() {
+                    @Override
+                    public void handle(
+                            final ClassicHttpRequest request,
+                            final ClassicHttpResponse response,
+                            final HttpContext context) throws HttpException, IOException {
+                        response.setCode(HttpStatus.SC_OK);
+                        response.setEntity(new StringEntity("0123456789ABCDEF", ContentType.TEXT_PLAIN));
+                    }
+                })
+                .create();
+        server.start();
+    }
+
+    @After
+    public void shutdown() throws Exception {
+        if (server != null) {
+            server.shutdown(ShutdownType.IMMEDIATE);
+        }
+    }
+
+    @Test
+    public void testBasics() throws Exception {
+        final Config config = new Config();
+        config.setKeepAlive(true);
+        config.setMethod("GET");
+        config.setUrl(new URL("http://localhost:" + server.getLocalPort() + "/"));
+        config.setThreads(3);
+        config.setRequests(100);
+        final HttpBenchmark httpBenchmark = new HttpBenchmark(config);
+        final Results results = httpBenchmark.doExecute();
+        Assert.assertNotNull(results);
+        Assert.assertEquals(16, results.getContentLength());
+        Assert.assertEquals(3, results.getConcurrencyLevel());
+        Assert.assertEquals(300, results.getKeepAliveCount());
+        Assert.assertEquals(300, results.getSuccessCount());
+        Assert.assertEquals(0, results.getFailureCount());
+        Assert.assertEquals(0, results.getWriteErrors());
+        Assert.assertEquals(300 * 16, results.getTotalBytes());
+        Assert.assertEquals(300 * 16, results.getTotalBytesRcvd());
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/d9e2b62c/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 57a91fa..82f1db6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -68,7 +68,6 @@
   <modules>
     <module>httpcore5</module>
     <module>httpcore5-h2</module>
-    <module>httpcore5-ab</module>
     <module>httpcore5-osgi</module>
     <module>httpcore5-testing</module>
   </modules>


Mime
View raw message