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.IOException;
030import java.io.InputStream;
031import java.net.InetSocketAddress;
032import java.net.Socket;
033import java.nio.charset.Charset;
034
035import javax.net.SocketFactory;
036
037import org.apache.http.ConnectionReuseStrategy;
038import org.apache.http.Header;
039import org.apache.http.HeaderIterator;
040import org.apache.http.HttpEntity;
041import org.apache.http.HttpException;
042import org.apache.http.HttpHost;
043import org.apache.http.HttpRequest;
044import org.apache.http.HttpResponse;
045import org.apache.http.HttpStatus;
046import org.apache.http.entity.ContentType;
047import org.apache.http.impl.DefaultConnectionReuseStrategy;
048import org.apache.http.protocol.HTTP;
049import org.apache.http.protocol.HttpCoreContext;
050import org.apache.http.protocol.HttpProcessor;
051import org.apache.http.protocol.HttpRequestExecutor;
052import org.apache.http.protocol.ImmutableHttpProcessor;
053import org.apache.http.protocol.RequestConnControl;
054import org.apache.http.protocol.RequestContent;
055import org.apache.http.protocol.RequestExpectContinue;
056import org.apache.http.protocol.RequestTargetHost;
057import org.apache.http.protocol.RequestUserAgent;
058
059/**
060 * Worker thread for the {@link HttpBenchmark HttpBenchmark}.
061 *
062 *
063 * @since 4.0
064 */
065class BenchmarkWorker implements Runnable {
066
067    private final byte[] buffer = new byte[4096];
068    private final HttpCoreContext context;
069    private final HttpProcessor httpProcessor;
070    private final HttpRequestExecutor httpexecutor;
071    private final ConnectionReuseStrategy connstrategy;
072    private final HttpRequest request;
073    private final HttpHost targetHost;
074    private final Config config;
075    private final SocketFactory socketFactory;
076    private final Stats stats = new Stats();
077
078    public BenchmarkWorker(
079            final HttpRequest request,
080            final HttpHost targetHost,
081            final SocketFactory socketFactory,
082            final Config config) {
083        super();
084        this.context = new HttpCoreContext();
085        this.request = request;
086        this.targetHost = targetHost;
087        this.config = config;
088        this.httpProcessor = new ImmutableHttpProcessor(
089                new RequestContent(),
090                new RequestTargetHost(),
091                new RequestConnControl(),
092                new RequestUserAgent("HttpCore-AB/1.1"),
093                new RequestExpectContinue(this.config.isUseExpectContinue()));
094        this.httpexecutor = new HttpRequestExecutor();
095
096        this.connstrategy = DefaultConnectionReuseStrategy.INSTANCE;
097        this.socketFactory = socketFactory;
098    }
099
100    @Override
101    public void run() {
102        HttpResponse response = null;
103        final BenchmarkConnection conn = new BenchmarkConnection(8 * 1024, stats);
104
105        final String scheme = targetHost.getSchemeName();
106        final String hostname = targetHost.getHostName();
107        int port = targetHost.getPort();
108        if (port == -1) {
109            if (scheme.equalsIgnoreCase("https")) {
110                port = 443;
111            } else {
112                port = 80;
113            }
114        }
115
116        // Populate the execution context
117        this.context.setTargetHost(this.targetHost);
118
119        stats.start();
120        final int count = config.getRequests();
121        for (int i = 0; i < count; i++) {
122
123            try {
124                resetHeader(request);
125                if (!conn.isOpen()) {
126
127                    final Socket socket;
128                    if (socketFactory != null) {
129                        socket = socketFactory.createSocket();
130                    } else {
131                        socket = new Socket();
132                    }
133
134                    final int timeout = config.getSocketTimeout();
135                    socket.setSoTimeout(timeout);
136                    socket.connect(new InetSocketAddress(hostname, port), timeout);
137
138                    conn.bind(socket);
139                }
140
141                try {
142                    // Prepare request
143                    this.httpexecutor.preProcess(this.request, this.httpProcessor, this.context);
144                    // Execute request and get a response
145                    response = this.httpexecutor.execute(this.request, conn, this.context);
146                    // Finalize response
147                    this.httpexecutor.postProcess(response, this.httpProcessor, this.context);
148
149                } catch (final HttpException e) {
150                    stats.incWriteErrors();
151                    if (config.getVerbosity() >= 2) {
152                        System.err.println("Failed HTTP request : " + e.getMessage());
153                    }
154                    conn.shutdown();
155                    continue;
156                }
157
158                verboseOutput(response);
159
160                if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
161                    stats.incSuccessCount();
162                } else {
163                    stats.incFailureCount();
164                }
165
166                final HttpEntity entity = response.getEntity();
167                if (entity != null) {
168                    final ContentType ct = ContentType.getOrDefault(entity);
169                    Charset charset = ct.getCharset();
170                    if (charset == null) {
171                        charset = HTTP.DEF_CONTENT_CHARSET;
172                    }
173                    long contentlen = 0;
174                    final InputStream instream = entity.getContent();
175                    int l;
176                    while ((l = instream.read(this.buffer)) != -1) {
177                        contentlen += l;
178                        if (config.getVerbosity() >= 4) {
179                            final String s = new String(this.buffer, 0, l, charset);
180                            System.out.print(s);
181                        }
182                    }
183                    instream.close();
184                    stats.setContentLength(contentlen);
185                }
186
187                if (config.getVerbosity() >= 4) {
188                    System.out.println();
189                    System.out.println();
190                }
191
192                if (!config.isKeepAlive() || !this.connstrategy.keepAlive(response, this.context)) {
193                    conn.close();
194                } else {
195                    stats.incKeepAliveCount();
196                }
197
198            } catch (final IOException ex) {
199                stats.incFailureCount();
200                if (config.getVerbosity() >= 2) {
201                    System.err.println("I/O error: " + ex.getMessage());
202                }
203            } catch (final Exception ex) {
204                stats.incFailureCount();
205                if (config.getVerbosity() >= 2) {
206                    System.err.println("Generic error: " + ex.getMessage());
207                }
208            }
209
210        }
211        stats.finish();
212
213        if (response != null) {
214            final Header header = response.getFirstHeader("Server");
215            if (header != null) {
216                stats.setServerName(header.getValue());
217            }
218        }
219
220        try {
221            conn.close();
222        } catch (final IOException ex) {
223            stats.incFailureCount();
224            if (config.getVerbosity() >= 2) {
225                System.err.println("I/O error: " + ex.getMessage());
226            }
227        }
228    }
229
230    private void verboseOutput(final HttpResponse response) {
231        if (config.getVerbosity() >= 3) {
232            System.out.println(">> " + request.getRequestLine().toString());
233            final Header[] headers = request.getAllHeaders();
234            for (final Header header : headers) {
235                System.out.println(">> " + header.toString());
236            }
237            System.out.println();
238        }
239        if (config.getVerbosity() >= 2) {
240            System.out.println(response.getStatusLine().getStatusCode());
241        }
242        if (config.getVerbosity() >= 3) {
243            System.out.println("<< " + response.getStatusLine().toString());
244            final Header[] headers = response.getAllHeaders();
245            for (final Header header : headers) {
246                System.out.println("<< " + header.toString());
247            }
248            System.out.println();
249        }
250    }
251
252    private static void resetHeader(final HttpRequest request) {
253        for (final HeaderIterator it = request.headerIterator(); it.hasNext();) {
254            final Header header = it.nextHeader();
255            if (!(header instanceof DefaultHeader)) {
256                it.remove();
257            }
258        }
259    }
260
261    public Stats getStats() {
262        return stats;
263    }
264}