001/*
002 * ====================================================================
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *   http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied.  See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 * ====================================================================
020 *
021 * This software consists of voluntary contributions made by many
022 * individuals on behalf of the Apache Software Foundation.  For more
023 * information on the Apache Software Foundation, please see
024 * <http://www.apache.org/>.
025 *
026 */
027package org.apache.http.benchmark;
028
029import java.io.File;
030import java.net.URL;
031import java.security.cert.CertificateException;
032import java.security.cert.X509Certificate;
033import java.util.concurrent.LinkedBlockingQueue;
034import java.util.concurrent.ThreadFactory;
035import java.util.concurrent.ThreadPoolExecutor;
036import java.util.concurrent.TimeUnit;
037
038import javax.net.SocketFactory;
039import javax.net.ssl.SSLContext;
040
041import org.apache.commons.cli.CommandLine;
042import org.apache.commons.cli.CommandLineParser;
043import org.apache.commons.cli.Options;
044import org.apache.commons.cli.PosixParser;
045import org.apache.http.Header;
046import org.apache.http.HttpEntity;
047import org.apache.http.HttpHost;
048import org.apache.http.HttpRequest;
049import org.apache.http.HttpVersion;
050import org.apache.http.entity.ContentType;
051import org.apache.http.entity.FileEntity;
052import org.apache.http.entity.StringEntity;
053import org.apache.http.message.BasicHttpEntityEnclosingRequest;
054import org.apache.http.message.BasicHttpRequest;
055import org.apache.http.protocol.HTTP;
056import org.apache.http.ssl.SSLContextBuilder;
057import org.apache.http.ssl.TrustStrategy;
058
059/**
060 * Main program of the HTTP benchmark.
061 *
062 *
063 * @since 4.0
064 */
065public class HttpBenchmark {
066
067    private final Config config;
068
069    public static void main(final String[] args) throws Exception {
070
071        final Options options = CommandLineUtils.getOptions();
072        final CommandLineParser parser = new PosixParser();
073        final CommandLine cmd = parser.parse(options, args);
074
075        if (args.length == 0 || cmd.hasOption('h') || cmd.getArgs().length != 1) {
076            CommandLineUtils.showUsage(options);
077            System.exit(1);
078        }
079
080        final Config config = new Config();
081        CommandLineUtils.parseCommandLine(cmd, config);
082
083        if (config.getUrl() == null) {
084            CommandLineUtils.showUsage(options);
085            System.exit(1);
086        }
087
088        final HttpBenchmark httpBenchmark = new HttpBenchmark(config);
089        httpBenchmark.execute();
090    }
091
092    public HttpBenchmark(final Config config) {
093        super();
094        this.config = config != null ? config : new Config();
095    }
096
097    private HttpRequest createRequest() {
098        final URL url = config.getUrl();
099        HttpEntity entity = null;
100
101        // Prepare requests for each thread
102        if (config.getPayloadFile() != null) {
103            final FileEntity fe = new FileEntity(config.getPayloadFile());
104            fe.setContentType(config.getContentType());
105            fe.setChunked(config.isUseChunking());
106            entity = fe;
107        } else if (config.getPayloadText() != null) {
108            final StringEntity se = new StringEntity(config.getPayloadText(),
109                    ContentType.parse(config.getContentType()));
110            se.setChunked(config.isUseChunking());
111            entity = se;
112        }
113        final HttpVersion ver = config.isUseHttp1_0() ? HttpVersion.HTTP_1_0 : HttpVersion.HTTP_1_1;
114        final HttpRequest request;
115        if ("POST".equals(config.getMethod())) {
116            final BasicHttpEntityEnclosingRequest httppost =
117                    new BasicHttpEntityEnclosingRequest("POST", url.getPath(), ver);
118            httppost.setEntity(entity);
119            request = httppost;
120        } else if ("PUT".equals(config.getMethod())) {
121            final BasicHttpEntityEnclosingRequest httpput =
122                    new BasicHttpEntityEnclosingRequest("PUT", url.getPath(), ver);
123            httpput.setEntity(entity);
124            request = httpput;
125        } else {
126            String path = url.getPath();
127            if (url.getQuery() != null && url.getQuery().length() > 0) {
128                path += "?" + url.getQuery();
129            } else if (path.trim().length() == 0) {
130                path = "/";
131            }
132            request = new BasicHttpRequest(config.getMethod(), path, ver);
133        }
134
135        if (!config.isKeepAlive()) {
136            request.addHeader(new DefaultHeader(HTTP.CONN_DIRECTIVE, HTTP.CONN_CLOSE));
137        }
138
139        final String[] headers = config.getHeaders();
140        if (headers != null) {
141            for (final String s : headers) {
142                final int pos = s.indexOf(':');
143                if (pos != -1) {
144                    final Header header = new DefaultHeader(s.substring(0, pos).trim(), s.substring(pos + 1));
145                    request.addHeader(header);
146                }
147            }
148        }
149
150        if (config.isUseAcceptGZip()) {
151            request.addHeader(new DefaultHeader("Accept-Encoding", "gzip"));
152        }
153
154        if (config.getSoapAction() != null && config.getSoapAction().length() > 0) {
155            request.addHeader(new DefaultHeader("SOAPAction", config.getSoapAction()));
156        }
157        return request;
158    }
159
160    public String execute() throws Exception {
161        final Results results = doExecute();
162        ResultProcessor.printResults(results);
163        return "";
164    }
165
166    public Results doExecute() throws Exception {
167
168        final URL url = config.getUrl();
169        final HttpHost host = new HttpHost(url.getHost(), url.getPort(), url.getProtocol());
170
171        final ThreadPoolExecutor workerPool = new ThreadPoolExecutor(
172                config.getThreads(), config.getThreads(), 5, TimeUnit.SECONDS,
173            new LinkedBlockingQueue<Runnable>(),
174            new ThreadFactory() {
175
176                @Override
177                public Thread newThread(final Runnable r) {
178                    return new Thread(r, "ClientPool");
179                }
180
181            });
182        workerPool.prestartAllCoreThreads();
183
184        SocketFactory socketFactory = null;
185        if ("https".equals(host.getSchemeName())) {
186            final SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
187            sslContextBuilder.setProtocol("SSL");
188            if (config.isDisableSSLVerification()) {
189                sslContextBuilder.loadTrustMaterial(null, new TrustStrategy() {
190
191                    @Override
192                    public boolean isTrusted(
193                            final X509Certificate[] chain, final String authType) throws CertificateException {
194                        return true;
195                    }
196
197                });
198            } else if (config.getTrustStorePath() != null) {
199                sslContextBuilder.loadTrustMaterial(
200                        new File(config.getTrustStorePath()),
201                        config.getTrustStorePassword() != null ? config.getTrustStorePassword().toCharArray() : null);
202            }
203            if (config.getIdentityStorePath() != null) {
204                sslContextBuilder.loadKeyMaterial(
205                        new File(config.getIdentityStorePath()),
206                        config.getIdentityStorePassword() != null ? config.getIdentityStorePassword().toCharArray() : null,
207                        config.getIdentityStorePassword() != null ? config.getIdentityStorePassword().toCharArray() : null);
208            }
209            final SSLContext sslContext = sslContextBuilder.build();
210            socketFactory = sslContext.getSocketFactory();
211        }
212
213        final BenchmarkWorker[] workers = new BenchmarkWorker[config.getThreads()];
214        for (int i = 0; i < workers.length; i++) {
215            workers[i] = new BenchmarkWorker(
216                    createRequest(),
217                    host,
218                    socketFactory,
219                    config);
220            workerPool.execute(workers[i]);
221        }
222
223        while (workerPool.getCompletedTaskCount() < config.getThreads()) {
224            Thread.yield();
225            try {
226                Thread.sleep(1000);
227            } catch (final InterruptedException ignore) {
228            }
229        }
230
231        workerPool.shutdown();
232        return ResultProcessor.collectResults(workers, host, config.getUrl().toString());
233    }
234
235}