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}