├── .gitignore ├── Makefile ├── README.md ├── ab.c ├── ap_release.h ├── apr-skeleton.c ├── apr ├── Makefile ├── Makefile_apr └── Makefile_aprutil └── plot.p /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | 9 | # Compiled Static libraries 10 | *.lai 11 | *.la 12 | *.a 13 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: libapr ab apr-skeleton 2 | 3 | libapr: 4 | make -C ./apr/ all 5 | 6 | 7 | apr-skeleton: apr-skeleton.c 8 | gcc apr-skeleton.c -o apr-skeleton -I./apr/aprutil-build/include/apr-1/ -I./apr/apr-build/include/apr-1/ -L./apr/apr-build/lib/ -static -lapr-1 -Wl,-Bdynamic -lpthread -lc -D_REENTRANT -D_GNU_SOURCE -D_LARGEFILE64_SOURCE 9 | # gcc apr-skeleton.c -o apr-skeleton -I./apr/aprutil-build/include/apr-1/ -I./apr/apr-build/include/apr-1/ -L./apr/apr-build/lib/ -lapr-1 -lpthread -D_REENTRANT -D_GNU_SOURCE -D_LARGEFILE64_SOURCE 10 | 11 | ab: ab.c ap_release.h 12 | gcc ab.c ap_release.h -o ab -I./apr/aprutil-build/include/apr-1/ -I./apr/apr-build/include/apr-1/ -L./apr/apr-build/lib/ -L./apr/aprutil-build/lib/ -static -lapr-1 -laprutil-1 -Wl,-Bdynamic -lm -lc -lpthread -D_REENTRANT -D_GNU_SOURCE -D_LARGEFILE64_SOURCE 13 | # gcc ab.c ap_release.h -o ab -I./apr/aprutil-build/include/apr-1/ -I./apr/apr-build/include/apr-1/ -L./apr/apr-build/lib/ -L./apr/aprutil-build/lib/ -lapr-1 -laprutil-1 -lm -lpthread -D_REENTRANT -D_GNU_SOURCE -D_LARGEFILE64_SOURCE 14 | 15 | 16 | 17 | clean: 18 | make -C ./apr/ clean 19 | rm ab apr-skeleton 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #ApacheBench-ab# 2 | 3 | Stand alone apache bench - Apache HTTP server benchmarking tool 4 | 5 | ##Build Instructions## 6 | 7 | Build: 8 | $make 9 | Clean: 10 | $make clean 11 | 12 | ##Usage## 13 | 14 | #####Simplest Usgae##### 15 | #ab -c 25 -n 100 http://www.apache.org/ 16 | 17 | The three options are: 18 | * concurrency(-c 25) 19 | * number of requests(-n 100) 20 | * URL(http://www.apache.org) 21 | 22 | #####Using with GnuPlot##### 23 | #ab -c 25 -n 100 -r -g out.dat http://www.apache.org 24 | Use the plot.p template coming with the source 25 | #gnuplot plot.p 26 | 27 | ####SYNOPSIS#### 28 | ab [ -A auth-username:password ] [ -b windowsize ] [ -c concurrency ] [ 29 | -C cookie-name=value ] [ -d ] [ -e csv-file ] [ -f protocol ] [ -g gnu‐ 30 | plot-file ] [ -h ] [ -H custom-header ] [ -i ] [ -k ] [ -n requests ] [ 31 | -p POST-file ] [ -P proxy-auth-username:password ] [ -q ] [ -r ] [ -s ] 32 | [ -S ] [ -t timelimit ] [ -T content-type ] [ -u PUT-file ] [ -v ver‐ 33 | bosity] [ -V ] [ -w ] [ -x -attributes ] [ -X proxy[:port] ] [ 34 | -y -attributes ] [ -z
-attributes ] [ -Z ciphersuite ] 35 | [http[s]://]hostname[:port]/path 36 | 37 | 38 | 39 | ####SUMMARY#### 40 | ab is a tool for benchmarking your Apache Hypertext Transfer Protocol 41 | (HTTP) server. It is designed to give you an impression of how your 42 | current Apache installation performs. This especially shows you how 43 | many requests per second your Apache installation is capable of serv‐ 44 | ing. 45 | 46 | 47 | 48 | ####OPTIONS#### 49 | -A auth-username:password 50 | Supply BASIC Authentication credentials to the server. The user‐ 51 | name and password are separated by a single : and sent on the 52 | wire base64 encoded. The string is sent regardless of whether 53 | the server needs it (i.e., has sent an 401 authentication 54 | needed). 55 | 56 | -b windowsize 57 | Size of TCP send/receive buffer, in bytes. 58 | 59 | -c concurrency 60 | Number of multiple requests to perform at a time. Default is one 61 | request at a time. 62 | 63 | -C cookie-name=value 64 | Add a Cookie: line to the request. The argument is typically in 65 | the form of a name=value pair. This field is repeatable. 66 | 67 | -d Do not display the "percentage served within XX [ms] table". 68 | (legacy support). 69 | 70 | -e csv-file 71 | Write a Comma separated value (CSV) file which contains for each 72 | percentage (from 1% to 100%) the time (in milliseconds) it took 73 | to serve that percentage of the requests. This is usually more 74 | useful than the 'gnuplot' file; as the results are already 75 | 'binned'. 76 | 77 | -f protocol 78 | Specify SSL/TLS protocol (SSL2, SSL3, TLS1, or ALL). 79 | 80 | -g gnuplot-file 81 | Write all measured values out as a 'gnuplot' or TSV (Tab sepa‐ 82 | rate values) file. This file can easily be imported into pack‐ 83 | ages like Gnuplot, IDL, Mathematica, Igor or even Excel. The 84 | labels are on the first line of the file. 85 | 86 | -h Display usage information. 87 | 88 | -H custom-header 89 | Append extra headers to the request. The argument is typically 90 | in the form of a valid header line, containing a colon-separated 91 | field-value pair (i.e., "Accept-Encoding: zip/zop;8bit"). 92 | 93 | -i Do HEAD requests instead of GET. 94 | 95 | -k Enable the HTTP KeepAlive feature, i.e., perform multiple 96 | requests within one HTTP session. Default is no KeepAlive. 97 | 98 | -n requests 99 | Number of requests to perform for the benchmarking session. The 100 | default is to just perform a single request which usually leads 101 | to non-representative benchmarking results. 102 | 103 | -p POST-file 104 | File containing data to POST. Remember to also set -T. 105 | 106 | -P proxy-auth-username:password 107 | Supply BASIC Authentication credentials to a proxy en-route. The 108 | username and password are separated by a single : and sent on 109 | the wire base64 encoded. The string is sent regardless of 110 | whether the proxy needs it (i.e., has sent an 407 proxy authen‐ 111 | tication needed). 112 | 113 | -q When processing more than 150 requests, ab outputs a progress 114 | count on stderr every 10% or 100 requests or so. The -q flag 115 | will suppress these messages. 116 | 117 | -r Don't exit on socket receive errors. 118 | 119 | -s When compiled in (ab -h will show you) use the SSL protected 120 | https rather than the http protocol. This feature is experimen‐ 121 | tal and very rudimentary. You probably do not want to use it. 122 | 123 | -S Do not display the median and standard deviation values, nor 124 | display the warning/error messages when the average and median 125 | are more than one or two times the standard deviation apart. And 126 | default to the min/avg/max values. (legacy support). 127 | 128 | -t timelimit 129 | Maximum number of seconds to spend for benchmarking. This 130 | implies a -n 50000 internally. Use this to benchmark the server 131 | within a fixed total amount of time. Per default there is no 132 | timelimit. 133 | 134 | -T content-type 135 | Content-type header to use for POST/PUT data, eg. application/x- 136 | www-form-urlencoded. Default: text/plain. 137 | 138 | -u PUT-file 139 | File containing data to PUT. Remember to also set -T. 140 | 141 | -v verbosity 142 | Set verbosity level - 4 and above prints information on headers, 143 | 3 and above prints response codes (404, 200, etc.), 2 and above 144 | prints warnings and info. 145 | 146 | -V Display version number and exit. 147 | 148 | -w Print out results in HTML tables. Default table is two columns 149 | wide, with a white background. 150 | 151 | -x -attributes 152 | String to use as attributes for
. Attributes are inserted 153 |
. 154 | 155 | -X proxy[:port] 156 | Use a proxy server for the requests. 157 | 158 | -y -attributes 159 | String to use as attributes for . 160 | 161 | -z
-attributes 162 | String to use as attributes for . 163 | 164 | -Z ciphersuite 165 | Specify SSL/TLS cipher suite (See openssl ciphers). 166 | 167 | 168 | ####BUGS#### 169 | There are various statically declared buffers of fixed length. Combined 170 | with the lazy parsing of the command line arguments, the response head‐ 171 | ers from the server and other external inputs, this might bite you. 172 | 173 | 174 | It does not implement HTTP/1.x fully; only accepts some 'expected' 175 | forms of responses. The rather heavy use of strstr(3) shows up top in 176 | profile, which might indicate a performance problem; i.e., you would 177 | measure the ab performance rather than the server's. 178 | -------------------------------------------------------------------------------- /ab.c: -------------------------------------------------------------------------------- 1 | /* Licensed to the Apache Software Foundation (ASF) under one or more 2 | * contributor license agreements. See the NOTICE file distributed with 3 | * this work for additional information regarding copyright ownership. 4 | * The ASF licenses this file to You under the Apache License, Version 2.0 5 | * (the "License"); you may not use this file except in compliance with 6 | * the License. You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /* 18 | ** This program is based on ZeusBench V1.0 written by Adam Twiss 19 | ** which is Copyright (c) 1996 by Zeus Technology Ltd. http://www.zeustech.net/ 20 | ** 21 | ** This software is provided "as is" and any express or implied waranties, 22 | ** including but not limited to, the implied warranties of merchantability and 23 | ** fitness for a particular purpose are disclaimed. In no event shall 24 | ** Zeus Technology Ltd. be liable for any direct, indirect, incidental, special, 25 | ** exemplary, or consequential damaged (including, but not limited to, 26 | ** procurement of substitute good or services; loss of use, data, or profits; 27 | ** or business interruption) however caused and on theory of liability. Whether 28 | ** in contract, strict liability or tort (including negligence or otherwise) 29 | ** arising in any way out of the use of this software, even if advised of the 30 | ** possibility of such damage. 31 | ** 32 | */ 33 | 34 | /* 35 | ** HISTORY: 36 | ** - Originally written by Adam Twiss , March 1996 37 | ** with input from Mike Belshe and 38 | ** Michael Campanella 39 | ** - Enhanced by Dean Gaudet , November 1997 40 | ** - Cleaned up by Ralf S. Engelschall , March 1998 41 | ** - POST and verbosity by Kurt Sussman , August 1998 42 | ** - HTML table output added by David N. Welton , January 1999 43 | ** - Added Cookie, Arbitrary header and auth support. , April 1999 44 | ** Version 1.3d 45 | ** - Increased version number - as some of the socket/error handling has 46 | ** fundamentally changed - and will give fundamentally different results 47 | ** in situations where a server is dropping requests. Therefore you can 48 | ** no longer compare results of AB as easily. Hence the inc of the version. 49 | ** They should be closer to the truth though. Sander & , End 2000. 50 | ** - Fixed proxy functionality, added median/mean statistics, added gnuplot 51 | ** output option, added _experimental/rudimentary_ SSL support. Added 52 | ** confidence guestimators and warnings. Sander & , End 2000 53 | ** - Fixed serious int overflow issues which would cause realistic (longer 54 | ** than a few minutes) run's to have wrong (but believable) results. Added 55 | ** trapping of connection errors which influenced measurements. 56 | ** Contributed by Sander Temme, Early 2001 57 | ** Version 1.3e 58 | ** - Changed timeout behavour during write to work whilst the sockets 59 | ** are filling up and apr_write() does writes a few - but not all. 60 | ** This will potentially change results. , April 2001 61 | ** Version 2.0.36-dev 62 | ** Improvements to concurrent processing: 63 | ** - Enabled non-blocking connect()s. 64 | ** - Prevent blocking calls to apr_socket_recv() (thereby allowing AB to 65 | ** manage its entire set of socket descriptors). 66 | ** - Any error returned from apr_socket_recv() that is not EAGAIN or EOF 67 | ** is now treated as fatal. 68 | ** Contributed by Aaron Bannert, April 24, 2002 69 | ** 70 | ** Version 2.0.36-2 71 | ** Internalized the version string - this string is part 72 | ** of the Agent: header and the result output. 73 | ** 74 | ** Version 2.0.37-dev 75 | ** Adopted SSL code by Madhu Mathihalli 76 | ** [PATCH] ab with SSL support Posted Wed, 15 Aug 2001 20:55:06 GMT 77 | ** Introduces four 'if (int == value)' tests per non-ssl request. 78 | ** 79 | ** Version 2.0.40-dev 80 | ** Switched to the new abstract pollset API, allowing ab to 81 | ** take advantage of future apr_pollset_t scalability improvements. 82 | ** Contributed by Brian Pane, August 31, 2002 83 | ** 84 | ** Version 2.3 85 | ** SIGINT now triggers output_results(). 86 | ** Contributed by colm, March 30, 2006 87 | **/ 88 | 89 | /* Note: this version string should start with \d+[\d\.]* and be a valid 90 | * string for an HTTP Agent: header when prefixed with 'ApacheBench/'. 91 | * It should reflect the version of AB - and not that of the apache server 92 | * it happens to accompany. And it should be updated or changed whenever 93 | * the results are no longer fundamentally comparable to the results of 94 | * a previous version of ab. Either due to a change in the logic of 95 | * ab - or to due to a change in the distribution it is compiled with 96 | * (such as an APR change in for example blocking). 97 | */ 98 | #define AP_AB_BASEREVISION "2.3" 99 | 100 | /* 101 | * BUGS: 102 | * 103 | * - uses strcpy/etc. 104 | * - has various other poor buffer attacks related to the lazy parsing of 105 | * response headers from the server 106 | * - doesn't implement much of HTTP/1.x, only accepts certain forms of 107 | * responses 108 | * - (performance problem) heavy use of strstr shows up top in profile 109 | * only an issue for loopback usage 110 | */ 111 | 112 | /* -------------------------------------------------------------------- */ 113 | 114 | #if 'A' != 0x41 115 | /* Hmmm... This source code isn't being compiled in ASCII. 116 | * In order for data that flows over the network to make 117 | * sense, we need to translate to/from ASCII. 118 | */ 119 | #define NOT_ASCII 120 | #endif 121 | 122 | /* affects include files on Solaris */ 123 | #define BSD_COMP 124 | 125 | /* 126 | #include "apr.h" 127 | #include "apr_signal.h" 128 | #include "apr_strings.h" 129 | #include "apr_network_io.h" 130 | #include "apr_file_io.h" 131 | #include "apr_time.h" 132 | #include "apr_getopt.h" 133 | #include "apr_general.h" 134 | #include "apr_lib.h" 135 | #include "apr_portable.h" 136 | #include "ap_release.h" 137 | #include "apr_poll.h" 138 | */ 139 | 140 | #include 141 | #include 142 | #include 143 | #include 144 | #include 145 | #include 146 | #include 147 | #include 148 | #include 149 | #include 150 | #include 151 | #include "ap_release.h" 152 | 153 | 154 | 155 | #define APR_WANT_STRFUNC 156 | #include 157 | 158 | #include 159 | #ifdef NOT_ASCII 160 | #include 161 | #endif 162 | #if APR_HAVE_STDIO_H 163 | #include 164 | #endif 165 | #if APR_HAVE_STDLIB_H 166 | #include 167 | #endif 168 | #if APR_HAVE_UNISTD_H 169 | #include /* for getpid() */ 170 | #endif 171 | 172 | /* 173 | #if !defined(WIN32) && !defined(NETWARE) 174 | #include "ap_config_auto.h" 175 | #endif 176 | */ 177 | #if defined(HAVE_SSLC) 178 | 179 | /* Libraries for RSA SSL-C */ 180 | #include 181 | #include 182 | #include 183 | #include 184 | #include 185 | #include 186 | #include 187 | #define USE_SSL 188 | #define RSAREF 189 | #define SK_NUM(x) sk_num(x) 190 | #define SK_VALUE(x,y) sk_value(x,y) 191 | typedef STACK X509_STACK_TYPE; 192 | 193 | #elif defined(HAVE_OPENSSL) 194 | 195 | /* Libraries on most systems.. */ 196 | #include 197 | #include 198 | #include 199 | #include 200 | #include 201 | #include 202 | #include 203 | #define USE_SSL 204 | #define SK_NUM(x) sk_X509_num(x) 205 | #define SK_VALUE(x,y) sk_X509_value(x,y) 206 | typedef STACK_OF(X509) X509_STACK_TYPE; 207 | 208 | #endif 209 | 210 | #include 211 | #if APR_HAVE_CTYPE_H 212 | #include 213 | #endif 214 | #if APR_HAVE_LIMITS_H 215 | #include 216 | #endif 217 | 218 | /* ------------------- DEFINITIONS -------------------------- */ 219 | 220 | #ifndef LLONG_MAX 221 | #define AB_MAX APR_INT64_C(0x7fffffffffffffff) 222 | #else 223 | #define AB_MAX LLONG_MAX 224 | #endif 225 | 226 | /* maximum number of requests on a time limited test */ 227 | #define MAX_REQUESTS (INT_MAX > 50000 ? 50000 : INT_MAX) 228 | 229 | /* connection state 230 | * don't add enums or rearrange or otherwise change values without 231 | * visiting set_conn_state() 232 | */ 233 | typedef enum { 234 | STATE_UNCONNECTED = 0, 235 | STATE_CONNECTING, /* TCP connect initiated, but we don't 236 | * know if it worked yet 237 | */ 238 | STATE_CONNECTED, /* we know TCP connect completed */ 239 | STATE_READ 240 | } connect_state_e; 241 | 242 | #define CBUFFSIZE (2048) 243 | 244 | struct connection { 245 | apr_pool_t *ctx; 246 | apr_socket_t *aprsock; 247 | apr_pollfd_t pollfd; 248 | int state; 249 | apr_size_t read; /* amount of bytes read */ 250 | apr_size_t bread; /* amount of body read */ 251 | apr_size_t rwrite, rwrote; /* keep pointers in what we write - across 252 | * EAGAINs */ 253 | apr_size_t length; /* Content-Length value used for keep-alive */ 254 | char cbuff[CBUFFSIZE]; /* a buffer to store server response header */ 255 | int cbx; /* offset in cbuffer */ 256 | int keepalive; /* non-zero if a keep-alive request */ 257 | int gotheader; /* non-zero if we have the entire header in 258 | * cbuff */ 259 | apr_time_t start, /* Start of connection */ 260 | connect, /* Connected, start writing */ 261 | endwrite, /* Request written */ 262 | beginread, /* First byte of input */ 263 | done; /* Connection closed */ 264 | 265 | int socknum; 266 | #ifdef USE_SSL 267 | SSL *ssl; 268 | #endif 269 | }; 270 | 271 | struct data { 272 | apr_time_t starttime; /* start time of connection */ 273 | apr_interval_time_t waittime; /* between request and reading response */ 274 | apr_interval_time_t ctime; /* time to connect */ 275 | apr_interval_time_t time; /* time for connection */ 276 | }; 277 | 278 | #define ap_min(a,b) ((a)<(b))?(a):(b) 279 | #define ap_max(a,b) ((a)>(b))?(a):(b) 280 | #define ap_round_ms(a) ((apr_time_t)((a) + 500)/1000) 281 | #define ap_double_ms(a) ((double)(a)/1000.0) 282 | #define MAX_CONCURRENCY 20000 283 | 284 | /* --------------------- GLOBALS ---------------------------- */ 285 | 286 | int verbosity = 0; /* no verbosity by default */ 287 | int recverrok = 0; /* ok to proceed after socket receive errors */ 288 | int posting = 0; /* GET by default */ 289 | int requests = 1; /* Number of requests to make */ 290 | int heartbeatres = 100; /* How often do we say we're alive */ 291 | int concurrency = 1; /* Number of multiple requests to make */ 292 | int percentile = 1; /* Show percentile served */ 293 | int confidence = 1; /* Show confidence estimator and warnings */ 294 | int tlimit = 0; /* time limit in secs */ 295 | int keepalive = 0; /* try and do keepalive connections */ 296 | int windowsize = 0; /* we use the OS default window size */ 297 | char servername[1024]; /* name that server reports */ 298 | char *hostname; /* host name from URL */ 299 | char *host_field; /* value of "Host:" header field */ 300 | char *path; /* path name */ 301 | char postfile[1024]; /* name of file containing post data */ 302 | char *postdata; /* *buffer containing data from postfile */ 303 | apr_size_t postlen = 0; /* length of data to be POSTed */ 304 | char content_type[1024];/* content type to put in POST header */ 305 | char *cookie, /* optional cookie line */ 306 | *auth, /* optional (basic/uuencoded) auhentication */ 307 | *hdrs; /* optional arbitrary headers */ 308 | apr_port_t port; /* port number */ 309 | char proxyhost[1024]; /* proxy host name */ 310 | int proxyport = 0; /* proxy port */ 311 | char *connecthost; 312 | apr_port_t connectport; 313 | char *gnuplot; /* GNUplot file */ 314 | char *csvperc; /* CSV Percentile file */ 315 | char url[1024]; 316 | char * fullurl, * colonhost; 317 | int isproxy = 0; 318 | apr_interval_time_t aprtimeout = apr_time_from_sec(30); /* timeout value */ 319 | 320 | /* overrides for ab-generated common headers */ 321 | int opt_host = 0; /* was an optional "Host:" header specified? */ 322 | int opt_useragent = 0; /* was an optional "User-Agent:" header specified? */ 323 | int opt_accept = 0; /* was an optional "Accept:" header specified? */ 324 | /* 325 | * XXX - this is now a per read/write transact type of value 326 | */ 327 | 328 | int use_html = 0; /* use html in the report */ 329 | const char *tablestring; 330 | const char *trstring; 331 | const char *tdstring; 332 | 333 | apr_size_t doclen = 0; /* the length the document should be */ 334 | apr_int64_t totalread = 0; /* total number of bytes read */ 335 | apr_int64_t totalbread = 0; /* totoal amount of entity body read */ 336 | apr_int64_t totalposted = 0; /* total number of bytes posted, inc. headers */ 337 | int started = 0; /* number of requests started, so no excess */ 338 | int done = 0; /* number of requests we have done */ 339 | int doneka = 0; /* number of keep alive connections done */ 340 | int good = 0, bad = 0; /* number of good and bad requests */ 341 | int epipe = 0; /* number of broken pipe writes */ 342 | int err_length = 0; /* requests failed due to response length */ 343 | int err_conn = 0; /* requests failed due to connection drop */ 344 | int err_recv = 0; /* requests failed due to broken read */ 345 | int err_except = 0; /* requests failed due to exception */ 346 | int err_response = 0; /* requests with invalid or non-200 response */ 347 | 348 | #ifdef USE_SSL 349 | int is_ssl; 350 | SSL_CTX *ssl_ctx; 351 | char *ssl_cipher = NULL; 352 | char *ssl_info = NULL; 353 | BIO *bio_out,*bio_err; 354 | #endif 355 | 356 | apr_time_t start, lasttime, stoptime; 357 | 358 | /* global request (and its length) */ 359 | char _request[2048]; 360 | char *request = _request; 361 | apr_size_t reqlen; 362 | 363 | /* one global throw-away buffer to read stuff into */ 364 | char buffer[8192]; 365 | 366 | /* interesting percentiles */ 367 | int percs[] = {50, 66, 75, 80, 90, 95, 98, 99, 100}; 368 | 369 | struct connection *con; /* connection array */ 370 | struct data *stats; /* data for each request */ 371 | apr_pool_t *cntxt; 372 | 373 | apr_pollset_t *readbits; 374 | 375 | apr_sockaddr_t *destsa; 376 | 377 | #ifdef NOT_ASCII 378 | apr_xlate_t *from_ascii, *to_ascii; 379 | #endif 380 | 381 | static void write_request(struct connection * c); 382 | static void close_connection(struct connection * c); 383 | 384 | /* --------------------------------------------------------- */ 385 | 386 | /* simple little function to write an error string and exit */ 387 | 388 | static void err(char *s) 389 | { 390 | fprintf(stderr, "%s\n", s); 391 | if (done) 392 | printf("Total of %d requests completed\n" , done); 393 | exit(1); 394 | } 395 | 396 | /* simple little function to write an APR error string and exit */ 397 | 398 | static void apr_err(char *s, apr_status_t rv) 399 | { 400 | char buf[120]; 401 | 402 | fprintf(stderr, 403 | "%s: %s (%d)\n", 404 | s, apr_strerror(rv, buf, sizeof buf), rv); 405 | if (done) 406 | printf("Total of %d requests completed\n" , done); 407 | exit(rv); 408 | } 409 | 410 | static void set_polled_events(struct connection *c, apr_int16_t new_reqevents) 411 | { 412 | apr_status_t rv; 413 | 414 | if (c->pollfd.reqevents != new_reqevents) { 415 | if (c->pollfd.reqevents != 0) { 416 | rv = apr_pollset_remove(readbits, &c->pollfd); 417 | if (rv != APR_SUCCESS) { 418 | apr_err("apr_pollset_remove()", rv); 419 | } 420 | } 421 | 422 | if (new_reqevents != 0) { 423 | c->pollfd.reqevents = new_reqevents; 424 | rv = apr_pollset_add(readbits, &c->pollfd); 425 | if (rv != APR_SUCCESS) { 426 | apr_err("apr_pollset_add()", rv); 427 | } 428 | } 429 | } 430 | } 431 | 432 | static void set_conn_state(struct connection *c, connect_state_e new_state) 433 | { 434 | apr_int16_t events_by_state[] = { 435 | 0, /* for STATE_UNCONNECTED */ 436 | APR_POLLOUT, /* for STATE_CONNECTING */ 437 | APR_POLLIN, /* for STATE_CONNECTED; we don't poll in this state, 438 | * so prepare for polling in the following state -- 439 | * STATE_READ 440 | */ 441 | APR_POLLIN /* for STATE_READ */ 442 | }; 443 | 444 | c->state = new_state; 445 | 446 | set_polled_events(c, events_by_state[new_state]); 447 | } 448 | 449 | /* --------------------------------------------------------- */ 450 | /* write out request to a connection - assumes we can write 451 | * (small) request out in one go into our new socket buffer 452 | * 453 | */ 454 | #ifdef USE_SSL 455 | static long ssl_print_cb(BIO *bio,int cmd,const char *argp,int argi,long argl,long ret) 456 | { 457 | BIO *out; 458 | 459 | out=(BIO *)BIO_get_callback_arg(bio); 460 | if (out == NULL) return(ret); 461 | 462 | if (cmd == (BIO_CB_READ|BIO_CB_RETURN)) { 463 | BIO_printf(out,"read from %p [%p] (%d bytes => %ld (0x%lX))\n", 464 | bio, argp, argi, ret, ret); 465 | BIO_dump(out,(char *)argp,(int)ret); 466 | return(ret); 467 | } 468 | else if (cmd == (BIO_CB_WRITE|BIO_CB_RETURN)) { 469 | BIO_printf(out,"write to %p [%p] (%d bytes => %ld (0x%lX))\n", 470 | bio, argp, argi, ret, ret); 471 | BIO_dump(out,(char *)argp,(int)ret); 472 | } 473 | return ret; 474 | } 475 | 476 | static void ssl_state_cb(const SSL *s, int w, int r) 477 | { 478 | if (w & SSL_CB_ALERT) { 479 | BIO_printf(bio_err, "SSL/TLS Alert [%s] %s:%s\n", 480 | (w & SSL_CB_READ ? "read" : "write"), 481 | SSL_alert_type_string_long(r), 482 | SSL_alert_desc_string_long(r)); 483 | } else if (w & SSL_CB_LOOP) { 484 | BIO_printf(bio_err, "SSL/TLS State [%s] %s\n", 485 | (SSL_in_connect_init((SSL*)s) ? "connect" : "-"), 486 | SSL_state_string_long(s)); 487 | } else if (w & (SSL_CB_HANDSHAKE_START|SSL_CB_HANDSHAKE_DONE)) { 488 | BIO_printf(bio_err, "SSL/TLS Handshake [%s] %s\n", 489 | (w & SSL_CB_HANDSHAKE_START ? "Start" : "Done"), 490 | SSL_state_string_long(s)); 491 | } 492 | } 493 | 494 | #ifndef RAND_MAX 495 | #define RAND_MAX INT_MAX 496 | #endif 497 | 498 | static int ssl_rand_choosenum(int l, int h) 499 | { 500 | int i; 501 | char buf[50]; 502 | 503 | srand((unsigned int)time(NULL)); 504 | apr_snprintf(buf, sizeof(buf), "%.0f", 505 | (((double)(rand()%RAND_MAX)/RAND_MAX)*(h-l))); 506 | i = atoi(buf)+1; 507 | if (i < l) i = l; 508 | if (i > h) i = h; 509 | return i; 510 | } 511 | 512 | static void ssl_rand_seed(void) 513 | { 514 | int nDone = 0; 515 | int n, l; 516 | time_t t; 517 | pid_t pid; 518 | unsigned char stackdata[256]; 519 | 520 | /* 521 | * seed in the current time (usually just 4 bytes) 522 | */ 523 | t = time(NULL); 524 | l = sizeof(time_t); 525 | RAND_seed((unsigned char *)&t, l); 526 | nDone += l; 527 | 528 | /* 529 | * seed in the current process id (usually just 4 bytes) 530 | */ 531 | pid = getpid(); 532 | l = sizeof(pid_t); 533 | RAND_seed((unsigned char *)&pid, l); 534 | nDone += l; 535 | 536 | /* 537 | * seed in some current state of the run-time stack (128 bytes) 538 | */ 539 | n = ssl_rand_choosenum(0, sizeof(stackdata)-128-1); 540 | RAND_seed(stackdata+n, 128); 541 | nDone += 128; 542 | } 543 | 544 | static int ssl_print_connection_info(BIO *bio, SSL *ssl) 545 | { 546 | const SSL_CIPHER *c; 547 | int alg_bits,bits; 548 | 549 | c = SSL_get_current_cipher(ssl); 550 | BIO_printf(bio,"Cipher Suite Protocol :%s\n", SSL_CIPHER_get_version(c)); 551 | BIO_printf(bio,"Cipher Suite Name :%s\n",SSL_CIPHER_get_name(c)); 552 | 553 | bits = SSL_CIPHER_get_bits(c,&alg_bits); 554 | BIO_printf(bio,"Cipher Suite Cipher Bits:%d (%d)\n",bits,alg_bits); 555 | 556 | return(1); 557 | } 558 | 559 | static void ssl_print_cert_info(BIO *bio, X509 *cert) 560 | { 561 | X509_NAME *dn; 562 | char buf[1024]; 563 | 564 | BIO_printf(bio, "Certificate version: %ld\n", X509_get_version(cert)+1); 565 | BIO_printf(bio,"Valid from: "); 566 | ASN1_UTCTIME_print(bio, X509_get_notBefore(cert)); 567 | BIO_printf(bio,"\n"); 568 | 569 | BIO_printf(bio,"Valid to : "); 570 | ASN1_UTCTIME_print(bio, X509_get_notAfter(cert)); 571 | BIO_printf(bio,"\n"); 572 | 573 | BIO_printf(bio,"Public key is %d bits\n", 574 | EVP_PKEY_bits(X509_get_pubkey(cert))); 575 | 576 | dn = X509_get_issuer_name(cert); 577 | X509_NAME_oneline(dn, buf, sizeof(buf)); 578 | BIO_printf(bio,"The issuer name is %s\n", buf); 579 | 580 | dn=X509_get_subject_name(cert); 581 | X509_NAME_oneline(dn, buf, sizeof(buf)); 582 | BIO_printf(bio,"The subject name is %s\n", buf); 583 | 584 | /* dump the extension list too */ 585 | BIO_printf(bio, "Extension Count: %d\n", X509_get_ext_count(cert)); 586 | } 587 | 588 | static void ssl_print_info(struct connection *c) 589 | { 590 | X509_STACK_TYPE *sk; 591 | X509 *cert; 592 | int count; 593 | 594 | BIO_printf(bio_err, "\n"); 595 | sk = SSL_get_peer_cert_chain(c->ssl); 596 | if ((count = SK_NUM(sk)) > 0) { 597 | int i; 598 | for (i=1; issl); 605 | if (cert == NULL) { 606 | BIO_printf(bio_out, "Anon DH\n"); 607 | } else { 608 | BIO_printf(bio_out, "Peer certificate\n"); 609 | ssl_print_cert_info(bio_out, cert); 610 | X509_free(cert); 611 | } 612 | ssl_print_connection_info(bio_err,c->ssl); 613 | SSL_SESSION_print(bio_err, SSL_get_session(c->ssl)); 614 | } 615 | 616 | static void ssl_proceed_handshake(struct connection *c) 617 | { 618 | int do_next = 1; 619 | 620 | while (do_next) { 621 | int ret, ecode; 622 | 623 | ret = SSL_do_handshake(c->ssl); 624 | ecode = SSL_get_error(c->ssl, ret); 625 | 626 | switch (ecode) { 627 | case SSL_ERROR_NONE: 628 | if (verbosity >= 2) 629 | ssl_print_info(c); 630 | if (ssl_info == NULL) { 631 | const SSL_CIPHER *ci; 632 | X509 *cert; 633 | int sk_bits, pk_bits, swork; 634 | 635 | ci = SSL_get_current_cipher(c->ssl); 636 | sk_bits = SSL_CIPHER_get_bits(ci, &swork); 637 | cert = SSL_get_peer_certificate(c->ssl); 638 | if (cert) 639 | pk_bits = EVP_PKEY_bits(X509_get_pubkey(cert)); 640 | else 641 | pk_bits = 0; /* Anon DH */ 642 | 643 | ssl_info = malloc(128); 644 | apr_snprintf(ssl_info, 128, "%s,%s,%d,%d", 645 | SSL_CIPHER_get_version(ci), 646 | SSL_CIPHER_get_name(ci), 647 | pk_bits, sk_bits); 648 | } 649 | write_request(c); 650 | do_next = 0; 651 | break; 652 | case SSL_ERROR_WANT_READ: 653 | set_polled_events(c, APR_POLLIN); 654 | do_next = 0; 655 | break; 656 | case SSL_ERROR_WANT_WRITE: 657 | /* Try again */ 658 | do_next = 1; 659 | break; 660 | case SSL_ERROR_WANT_CONNECT: 661 | case SSL_ERROR_SSL: 662 | case SSL_ERROR_SYSCALL: 663 | /* Unexpected result */ 664 | BIO_printf(bio_err, "SSL handshake failed (%d).\n", ecode); 665 | ERR_print_errors(bio_err); 666 | close_connection(c); 667 | do_next = 0; 668 | break; 669 | } 670 | } 671 | } 672 | 673 | #endif /* USE_SSL */ 674 | 675 | static void write_request(struct connection * c) 676 | { 677 | do { 678 | apr_time_t tnow; 679 | apr_size_t l = c->rwrite; 680 | apr_status_t e = APR_SUCCESS; /* prevent gcc warning */ 681 | 682 | tnow = lasttime = apr_time_now(); 683 | 684 | /* 685 | * First time round ? 686 | */ 687 | if (c->rwrite == 0) { 688 | apr_socket_timeout_set(c->aprsock, 0); 689 | c->connect = tnow; 690 | c->rwrote = 0; 691 | c->rwrite = reqlen; 692 | if (posting) 693 | c->rwrite += postlen; 694 | } 695 | else if (tnow > c->connect + aprtimeout) { 696 | printf("Send request timed out!\n"); 697 | close_connection(c); 698 | return; 699 | } 700 | 701 | #ifdef USE_SSL 702 | if (c->ssl) { 703 | apr_size_t e_ssl; 704 | e_ssl = SSL_write(c->ssl,request + c->rwrote, l); 705 | if (e_ssl != l) { 706 | BIO_printf(bio_err, "SSL write failed - closing connection\n"); 707 | ERR_print_errors(bio_err); 708 | close_connection (c); 709 | return; 710 | } 711 | l = e_ssl; 712 | e = APR_SUCCESS; 713 | } 714 | else 715 | #endif 716 | e = apr_socket_send(c->aprsock, request + c->rwrote, &l); 717 | 718 | if (e != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(e)) { 719 | epipe++; 720 | printf("Send request failed!\n"); 721 | close_connection(c); 722 | return; 723 | } 724 | totalposted += l; 725 | c->rwrote += l; 726 | c->rwrite -= l; 727 | } while (c->rwrite); 728 | 729 | c->endwrite = lasttime = apr_time_now(); 730 | set_conn_state(c, STATE_READ); 731 | } 732 | 733 | /* --------------------------------------------------------- */ 734 | 735 | /* calculate and output results */ 736 | 737 | static int compradre(struct data * a, struct data * b) 738 | { 739 | if ((a->ctime) < (b->ctime)) 740 | return -1; 741 | if ((a->ctime) > (b->ctime)) 742 | return +1; 743 | return 0; 744 | } 745 | 746 | static int comprando(struct data * a, struct data * b) 747 | { 748 | if ((a->time) < (b->time)) 749 | return -1; 750 | if ((a->time) > (b->time)) 751 | return +1; 752 | return 0; 753 | } 754 | 755 | static int compri(struct data * a, struct data * b) 756 | { 757 | apr_interval_time_t p = a->time - a->ctime; 758 | apr_interval_time_t q = b->time - b->ctime; 759 | if (p < q) 760 | return -1; 761 | if (p > q) 762 | return +1; 763 | return 0; 764 | } 765 | 766 | static int compwait(struct data * a, struct data * b) 767 | { 768 | if ((a->waittime) < (b->waittime)) 769 | return -1; 770 | if ((a->waittime) > (b->waittime)) 771 | return 1; 772 | return 0; 773 | } 774 | 775 | static void output_results(int sig) 776 | { 777 | double timetaken; 778 | 779 | if (sig) { 780 | lasttime = apr_time_now(); /* record final time if interrupted */ 781 | } 782 | timetaken = (double) (lasttime - start) / APR_USEC_PER_SEC; 783 | 784 | printf("\n\n"); 785 | printf("Server Software: %s\n", servername); 786 | printf("Server Hostname: %s\n", hostname); 787 | printf("Server Port: %hu\n", port); 788 | #ifdef USE_SSL 789 | if (is_ssl && ssl_info) { 790 | printf("SSL/TLS Protocol: %s\n", ssl_info); 791 | } 792 | #endif 793 | printf("\n"); 794 | printf("Document Path: %s\n", path); 795 | printf("Document Length: %" APR_SIZE_T_FMT " bytes\n", doclen); 796 | printf("\n"); 797 | printf("Concurrency Level: %d\n", concurrency); 798 | printf("Time taken for tests: %.3f seconds\n", timetaken); 799 | printf("Complete requests: %d\n", done); 800 | printf("Failed requests: %d\n", bad); 801 | if (bad) 802 | printf(" (Connect: %d, Receive: %d, Length: %d, Exceptions: %d)\n", 803 | err_conn, err_recv, err_length, err_except); 804 | printf("Write errors: %d\n", epipe); 805 | if (err_response) 806 | printf("Non-2xx responses: %d\n", err_response); 807 | if (keepalive) 808 | printf("Keep-Alive requests: %d\n", doneka); 809 | printf("Total transferred: %" APR_INT64_T_FMT " bytes\n", totalread); 810 | if (posting > 0) 811 | printf("Total POSTed: %" APR_INT64_T_FMT "\n", totalposted); 812 | printf("HTML transferred: %" APR_INT64_T_FMT " bytes\n", totalbread); 813 | 814 | /* avoid divide by zero */ 815 | if (timetaken && done) { 816 | printf("Requests per second: %.2f [#/sec] (mean)\n", 817 | (double) done / timetaken); 818 | printf("Time per request: %.3f [ms] (mean)\n", 819 | (double) concurrency * timetaken * 1000 / done); 820 | printf("Time per request: %.3f [ms] (mean, across all concurrent requests)\n", 821 | (double) timetaken * 1000 / done); 822 | printf("Transfer rate: %.2f [Kbytes/sec] received\n", 823 | (double) totalread / 1024 / timetaken); 824 | if (posting > 0) { 825 | printf(" %.2f kb/s sent\n", 826 | (double) totalposted / timetaken / 1024); 827 | printf(" %.2f kb/s total\n", 828 | (double) (totalread + totalposted) / timetaken / 1024); 829 | } 830 | } 831 | 832 | if (done > 0) { 833 | /* work out connection times */ 834 | int i; 835 | apr_time_t totalcon = 0, total = 0, totald = 0, totalwait = 0; 836 | apr_time_t meancon, meantot, meand, meanwait; 837 | apr_interval_time_t mincon = AB_MAX, mintot = AB_MAX, mind = AB_MAX, 838 | minwait = AB_MAX; 839 | apr_interval_time_t maxcon = 0, maxtot = 0, maxd = 0, maxwait = 0; 840 | apr_interval_time_t mediancon = 0, mediantot = 0, mediand = 0, medianwait = 0; 841 | double sdtot = 0, sdcon = 0, sdd = 0, sdwait = 0; 842 | 843 | for (i = 0; i < done; i++) { 844 | struct data *s = &stats[i]; 845 | mincon = ap_min(mincon, s->ctime); 846 | mintot = ap_min(mintot, s->time); 847 | mind = ap_min(mind, s->time - s->ctime); 848 | minwait = ap_min(minwait, s->waittime); 849 | 850 | maxcon = ap_max(maxcon, s->ctime); 851 | maxtot = ap_max(maxtot, s->time); 852 | maxd = ap_max(maxd, s->time - s->ctime); 853 | maxwait = ap_max(maxwait, s->waittime); 854 | 855 | totalcon += s->ctime; 856 | total += s->time; 857 | totald += s->time - s->ctime; 858 | totalwait += s->waittime; 859 | } 860 | meancon = totalcon / done; 861 | meantot = total / done; 862 | meand = totald / done; 863 | meanwait = totalwait / done; 864 | 865 | /* calculating the sample variance: the sum of the squared deviations, divided by n-1 */ 866 | for (i = 0; i < done; i++) { 867 | struct data *s = &stats[i]; 868 | double a; 869 | a = ((double)s->time - meantot); 870 | sdtot += a * a; 871 | a = ((double)s->ctime - meancon); 872 | sdcon += a * a; 873 | a = ((double)s->time - (double)s->ctime - meand); 874 | sdd += a * a; 875 | a = ((double)s->waittime - meanwait); 876 | sdwait += a * a; 877 | } 878 | 879 | sdtot = (done > 1) ? sqrt(sdtot / (done - 1)) : 0; 880 | sdcon = (done > 1) ? sqrt(sdcon / (done - 1)) : 0; 881 | sdd = (done > 1) ? sqrt(sdd / (done - 1)) : 0; 882 | sdwait = (done > 1) ? sqrt(sdwait / (done - 1)) : 0; 883 | 884 | /* 885 | * XXX: what is better; this hideous cast of the compradre function; or 886 | * the four warnings during compile ? dirkx just does not know and 887 | * hates both/ 888 | */ 889 | qsort(stats, done, sizeof(struct data), 890 | (int (*) (const void *, const void *)) compradre); 891 | if ((done > 1) && (done % 2)) 892 | mediancon = (stats[done / 2].ctime + stats[done / 2 + 1].ctime) / 2; 893 | else 894 | mediancon = stats[done / 2].ctime; 895 | 896 | qsort(stats, done, sizeof(struct data), 897 | (int (*) (const void *, const void *)) compri); 898 | if ((done > 1) && (done % 2)) 899 | mediand = (stats[done / 2].time + stats[done / 2 + 1].time \ 900 | -stats[done / 2].ctime - stats[done / 2 + 1].ctime) / 2; 901 | else 902 | mediand = stats[done / 2].time - stats[done / 2].ctime; 903 | 904 | qsort(stats, done, sizeof(struct data), 905 | (int (*) (const void *, const void *)) compwait); 906 | if ((done > 1) && (done % 2)) 907 | medianwait = (stats[done / 2].waittime + stats[done / 2 + 1].waittime) / 2; 908 | else 909 | medianwait = stats[done / 2].waittime; 910 | 911 | qsort(stats, done, sizeof(struct data), 912 | (int (*) (const void *, const void *)) comprando); 913 | if ((done > 1) && (done % 2)) 914 | mediantot = (stats[done / 2].time + stats[done / 2 + 1].time) / 2; 915 | else 916 | mediantot = stats[done / 2].time; 917 | 918 | printf("\nConnection Times (ms)\n"); 919 | /* 920 | * Reduce stats from apr time to milliseconds 921 | */ 922 | mincon = ap_round_ms(mincon); 923 | mind = ap_round_ms(mind); 924 | minwait = ap_round_ms(minwait); 925 | mintot = ap_round_ms(mintot); 926 | meancon = ap_round_ms(meancon); 927 | meand = ap_round_ms(meand); 928 | meanwait = ap_round_ms(meanwait); 929 | meantot = ap_round_ms(meantot); 930 | mediancon = ap_round_ms(mediancon); 931 | mediand = ap_round_ms(mediand); 932 | medianwait = ap_round_ms(medianwait); 933 | mediantot = ap_round_ms(mediantot); 934 | maxcon = ap_round_ms(maxcon); 935 | maxd = ap_round_ms(maxd); 936 | maxwait = ap_round_ms(maxwait); 937 | maxtot = ap_round_ms(maxtot); 938 | sdcon = ap_double_ms(sdcon); 939 | sdd = ap_double_ms(sdd); 940 | sdwait = ap_double_ms(sdwait); 941 | sdtot = ap_double_ms(sdtot); 942 | 943 | if (confidence) { 944 | #define CONF_FMT_STRING "%5" APR_TIME_T_FMT " %4" APR_TIME_T_FMT " %5.1f %6" APR_TIME_T_FMT " %7" APR_TIME_T_FMT "\n" 945 | printf(" min mean[+/-sd] median max\n"); 946 | printf("Connect: " CONF_FMT_STRING, 947 | mincon, meancon, sdcon, mediancon, maxcon); 948 | printf("Processing: " CONF_FMT_STRING, 949 | mind, meand, sdd, mediand, maxd); 950 | printf("Waiting: " CONF_FMT_STRING, 951 | minwait, meanwait, sdwait, medianwait, maxwait); 952 | printf("Total: " CONF_FMT_STRING, 953 | mintot, meantot, sdtot, mediantot, maxtot); 954 | #undef CONF_FMT_STRING 955 | 956 | #define SANE(what,mean,median,sd) \ 957 | { \ 958 | double d = (double)mean - median; \ 959 | if (d < 0) d = -d; \ 960 | if (d > 2 * sd ) \ 961 | printf("ERROR: The median and mean for " what " are more than twice the standard\n" \ 962 | " deviation apart. These results are NOT reliable.\n"); \ 963 | else if (d > sd ) \ 964 | printf("WARNING: The median and mean for " what " are not within a normal deviation\n" \ 965 | " These results are probably not that reliable.\n"); \ 966 | } 967 | SANE("the initial connection time", meancon, mediancon, sdcon); 968 | SANE("the processing time", meand, mediand, sdd); 969 | SANE("the waiting time", meanwait, medianwait, sdwait); 970 | SANE("the total time", meantot, mediantot, sdtot); 971 | } 972 | else { 973 | printf(" min avg max\n"); 974 | #define CONF_FMT_STRING "%5" APR_TIME_T_FMT " %5" APR_TIME_T_FMT "%5" APR_TIME_T_FMT "\n" 975 | printf("Connect: " CONF_FMT_STRING, mincon, meancon, maxcon); 976 | printf("Processing: " CONF_FMT_STRING, mintot - mincon, 977 | meantot - meancon, 978 | maxtot - maxcon); 979 | printf("Total: " CONF_FMT_STRING, mintot, meantot, maxtot); 980 | #undef CONF_FMT_STRING 981 | } 982 | 983 | 984 | /* Sorted on total connect times */ 985 | if (percentile && (done > 1)) { 986 | printf("\nPercentage of the requests served within a certain time (ms)\n"); 987 | for (i = 0; i < sizeof(percs) / sizeof(int); i++) { 988 | if (percs[i] <= 0) 989 | printf(" 0%% <0> (never)\n"); 990 | else if (percs[i] >= 100) 991 | printf(" 100%% %5" APR_TIME_T_FMT " (longest request)\n", 992 | ap_round_ms(stats[done - 1].time)); 993 | else 994 | printf(" %d%% %5" APR_TIME_T_FMT "\n", percs[i], 995 | ap_round_ms(stats[(int) (done * percs[i] / 100)].time)); 996 | } 997 | } 998 | if (csvperc) { 999 | FILE *out = fopen(csvperc, "w"); 1000 | if (!out) { 1001 | perror("Cannot open CSV output file"); 1002 | exit(1); 1003 | } 1004 | fprintf(out, "" "Percentage served" "," "Time in ms" "\n"); 1005 | for (i = 0; i < 100; i++) { 1006 | double t; 1007 | if (i == 0) 1008 | t = ap_double_ms(stats[0].time); 1009 | else if (i == 100) 1010 | t = ap_double_ms(stats[done - 1].time); 1011 | else 1012 | t = ap_double_ms(stats[(int) (0.5 + done * i / 100.0)].time); 1013 | fprintf(out, "%d,%.3f\n", i, t); 1014 | } 1015 | fclose(out); 1016 | } 1017 | if (gnuplot) { 1018 | FILE *out = fopen(gnuplot, "w"); 1019 | char tmstring[APR_CTIME_LEN]; 1020 | if (!out) { 1021 | perror("Cannot open gnuplot output file"); 1022 | exit(1); 1023 | } 1024 | fprintf(out, "starttime\tseconds\tctime\tdtime\tttime\twait\n"); 1025 | for (i = 0; i < done; i++) { 1026 | (void) apr_ctime(tmstring, stats[i].starttime); 1027 | fprintf(out, "%s\t%" APR_TIME_T_FMT "\t%" APR_TIME_T_FMT 1028 | "\t%" APR_TIME_T_FMT "\t%" APR_TIME_T_FMT 1029 | "\t%" APR_TIME_T_FMT "\n", tmstring, 1030 | apr_time_sec(stats[i].starttime), 1031 | ap_round_ms(stats[i].ctime), 1032 | ap_round_ms(stats[i].time - stats[i].ctime), 1033 | ap_round_ms(stats[i].time), 1034 | ap_round_ms(stats[i].waittime)); 1035 | } 1036 | fclose(out); 1037 | } 1038 | } 1039 | 1040 | if (sig) { 1041 | exit(1); 1042 | } 1043 | } 1044 | 1045 | /* --------------------------------------------------------- */ 1046 | 1047 | /* calculate and output results in HTML */ 1048 | 1049 | static void output_html_results(void) 1050 | { 1051 | double timetaken = (double) (lasttime - start) / APR_USEC_PER_SEC; 1052 | 1053 | printf("\n\n\n", tablestring); 1054 | printf("" 1055 | "\n", 1056 | trstring, tdstring, tdstring, servername); 1057 | printf("" 1058 | "\n", 1059 | trstring, tdstring, tdstring, hostname); 1060 | printf("" 1061 | "\n", 1062 | trstring, tdstring, tdstring, port); 1063 | printf("" 1064 | "\n", 1065 | trstring, tdstring, tdstring, path); 1066 | printf("" 1067 | "\n", 1068 | trstring, tdstring, tdstring, doclen); 1069 | printf("" 1070 | "\n", 1071 | trstring, tdstring, tdstring, concurrency); 1072 | printf("" 1073 | "\n", 1074 | trstring, tdstring, tdstring, timetaken); 1075 | printf("" 1076 | "\n", 1077 | trstring, tdstring, tdstring, done); 1078 | printf("" 1079 | "\n", 1080 | trstring, tdstring, tdstring, bad); 1081 | if (bad) 1082 | printf("\n", 1083 | trstring, tdstring, err_conn, err_length, err_except); 1084 | if (err_response) 1085 | printf("" 1086 | "\n", 1087 | trstring, tdstring, tdstring, err_response); 1088 | if (keepalive) 1089 | printf("" 1090 | "\n", 1091 | trstring, tdstring, tdstring, doneka); 1092 | printf("" 1093 | "\n", 1094 | trstring, tdstring, tdstring, totalread); 1095 | if (posting > 0) 1096 | printf("" 1097 | "\n", 1098 | trstring, tdstring, tdstring, totalposted); 1099 | printf("" 1100 | "\n", 1101 | trstring, tdstring, tdstring, totalbread); 1102 | 1103 | /* avoid divide by zero */ 1104 | if (timetaken) { 1105 | printf("" 1106 | "\n", 1107 | trstring, tdstring, tdstring, (double) done * 1000 / timetaken); 1108 | printf("" 1109 | "\n", 1110 | trstring, tdstring, tdstring, (double) totalread / timetaken); 1111 | if (posting > 0) { 1112 | printf("" 1113 | "\n", 1114 | trstring, tdstring, tdstring, 1115 | (double) totalposted / timetaken); 1116 | printf("" 1117 | "\n", 1118 | trstring, tdstring, tdstring, 1119 | (double) (totalread + totalposted) / timetaken); 1120 | } 1121 | } 1122 | { 1123 | /* work out connection times */ 1124 | int i; 1125 | apr_interval_time_t totalcon = 0, total = 0; 1126 | apr_interval_time_t mincon = AB_MAX, mintot = AB_MAX; 1127 | apr_interval_time_t maxcon = 0, maxtot = 0; 1128 | 1129 | for (i = 0; i < done; i++) { 1130 | struct data *s = &stats[i]; 1131 | mincon = ap_min(mincon, s->ctime); 1132 | mintot = ap_min(mintot, s->time); 1133 | maxcon = ap_max(maxcon, s->ctime); 1134 | maxtot = ap_max(maxtot, s->time); 1135 | totalcon += s->ctime; 1136 | total += s->time; 1137 | } 1138 | /* 1139 | * Reduce stats from apr time to milliseconds 1140 | */ 1141 | mincon = ap_round_ms(mincon); 1142 | mintot = ap_round_ms(mintot); 1143 | maxcon = ap_round_ms(maxcon); 1144 | maxtot = ap_round_ms(maxtot); 1145 | totalcon = ap_round_ms(totalcon); 1146 | total = ap_round_ms(total); 1147 | 1148 | if (done > 0) { /* avoid division by zero (if 0 done) */ 1149 | printf("\n", 1150 | trstring, tdstring); 1151 | printf("\n", 1152 | trstring, tdstring, tdstring, tdstring, tdstring); 1153 | printf("" 1154 | "" 1155 | "" 1156 | "\n", 1157 | trstring, tdstring, tdstring, mincon, tdstring, totalcon / done, tdstring, maxcon); 1158 | printf("" 1159 | "" 1160 | "" 1161 | "\n", 1162 | trstring, tdstring, tdstring, mintot - mincon, tdstring, 1163 | (total / done) - (totalcon / done), tdstring, maxtot - maxcon); 1164 | printf("" 1165 | "" 1166 | "" 1167 | "\n", 1168 | trstring, tdstring, tdstring, mintot, tdstring, total / done, tdstring, maxtot); 1169 | } 1170 | printf("
Server Software:%s
Server Hostname:%s
Server Port:%hu
Document Path:%s
Document Length:%" APR_SIZE_T_FMT " bytes
Concurrency Level:%d
Time taken for tests:%.3f seconds
Complete requests:%d
Failed requests:%d
(Connect: %d, Length: %d, Exceptions: %d)
Non-2xx responses:%d
Keep-Alive requests:%d
Total transferred:%" APR_INT64_T_FMT " bytes
Total POSTed:%" APR_INT64_T_FMT "
HTML transferred:%" APR_INT64_T_FMT " bytes
Requests per second:%.2f
Transfer rate:%.2f kb/s received
 %.2f kb/s sent
 %.2f kb/s total
Connnection Times (ms)
  min avg max
Connect:%5" APR_TIME_T_FMT "%5" APR_TIME_T_FMT "%5" APR_TIME_T_FMT "
Processing:%5" APR_TIME_T_FMT "%5" APR_TIME_T_FMT "%5" APR_TIME_T_FMT "
Total:%5" APR_TIME_T_FMT "%5" APR_TIME_T_FMT "%5" APR_TIME_T_FMT "
\n"); 1171 | } 1172 | } 1173 | 1174 | /* --------------------------------------------------------- */ 1175 | 1176 | /* start asnchronous non-blocking connection */ 1177 | 1178 | static void start_connect(struct connection * c) 1179 | { 1180 | apr_status_t rv; 1181 | 1182 | if (!(started < requests)) 1183 | return; 1184 | 1185 | c->read = 0; 1186 | c->bread = 0; 1187 | c->keepalive = 0; 1188 | c->cbx = 0; 1189 | c->gotheader = 0; 1190 | c->rwrite = 0; 1191 | if (c->ctx) 1192 | apr_pool_clear(c->ctx); 1193 | else 1194 | apr_pool_create(&c->ctx, cntxt); 1195 | 1196 | if ((rv = apr_socket_create(&c->aprsock, destsa->family, 1197 | SOCK_STREAM, 0, c->ctx)) != APR_SUCCESS) { 1198 | apr_err("socket", rv); 1199 | } 1200 | 1201 | c->pollfd.desc_type = APR_POLL_SOCKET; 1202 | c->pollfd.desc.s = c->aprsock; 1203 | c->pollfd.reqevents = 0; 1204 | c->pollfd.client_data = c; 1205 | 1206 | if ((rv = apr_socket_opt_set(c->aprsock, APR_SO_NONBLOCK, 1)) 1207 | != APR_SUCCESS) { 1208 | apr_err("socket nonblock", rv); 1209 | } 1210 | 1211 | if (windowsize != 0) { 1212 | rv = apr_socket_opt_set(c->aprsock, APR_SO_SNDBUF, 1213 | windowsize); 1214 | if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) { 1215 | apr_err("socket send buffer", rv); 1216 | } 1217 | rv = apr_socket_opt_set(c->aprsock, APR_SO_RCVBUF, 1218 | windowsize); 1219 | if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) { 1220 | apr_err("socket receive buffer", rv); 1221 | } 1222 | } 1223 | 1224 | c->start = lasttime = apr_time_now(); 1225 | #ifdef USE_SSL 1226 | if (is_ssl) { 1227 | BIO *bio; 1228 | apr_os_sock_t fd; 1229 | 1230 | if ((c->ssl = SSL_new(ssl_ctx)) == NULL) { 1231 | BIO_printf(bio_err, "SSL_new failed.\n"); 1232 | ERR_print_errors(bio_err); 1233 | exit(1); 1234 | } 1235 | ssl_rand_seed(); 1236 | apr_os_sock_get(&fd, c->aprsock); 1237 | bio = BIO_new_socket(fd, BIO_NOCLOSE); 1238 | SSL_set_bio(c->ssl, bio, bio); 1239 | SSL_set_connect_state(c->ssl); 1240 | if (verbosity >= 4) { 1241 | BIO_set_callback(bio, ssl_print_cb); 1242 | BIO_set_callback_arg(bio, (void *)bio_err); 1243 | } 1244 | } else { 1245 | c->ssl = NULL; 1246 | } 1247 | #endif 1248 | if ((rv = apr_socket_connect(c->aprsock, destsa)) != APR_SUCCESS) { 1249 | if (APR_STATUS_IS_EINPROGRESS(rv)) { 1250 | set_conn_state(c, STATE_CONNECTING); 1251 | c->rwrite = 0; 1252 | return; 1253 | } 1254 | else { 1255 | set_conn_state(c, STATE_UNCONNECTED); 1256 | apr_socket_close(c->aprsock); 1257 | err_conn++; 1258 | if (bad++ > 10) { 1259 | fprintf(stderr, 1260 | "\nTest aborted after 10 failures\n\n"); 1261 | apr_err("apr_socket_connect()", rv); 1262 | } 1263 | 1264 | start_connect(c); 1265 | return; 1266 | } 1267 | } 1268 | 1269 | /* connected first time */ 1270 | set_conn_state(c, STATE_CONNECTED); 1271 | started++; 1272 | #ifdef USE_SSL 1273 | if (c->ssl) { 1274 | ssl_proceed_handshake(c); 1275 | } else 1276 | #endif 1277 | { 1278 | write_request(c); 1279 | } 1280 | } 1281 | 1282 | /* --------------------------------------------------------- */ 1283 | 1284 | /* close down connection and save stats */ 1285 | 1286 | static void close_connection(struct connection * c) 1287 | { 1288 | if (c->read == 0 && c->keepalive) { 1289 | /* 1290 | * server has legitimately shut down an idle keep alive request 1291 | */ 1292 | if (good) 1293 | good--; /* connection never happened */ 1294 | } 1295 | else { 1296 | if (good == 1) { 1297 | /* first time here */ 1298 | doclen = c->bread; 1299 | } 1300 | else if (c->bread != doclen) { 1301 | bad++; 1302 | err_length++; 1303 | } 1304 | /* save out time */ 1305 | if (done < requests) { 1306 | struct data *s = &stats[done++]; 1307 | c->done = lasttime = apr_time_now(); 1308 | s->starttime = c->start; 1309 | s->ctime = ap_max(0, c->connect - c->start); 1310 | s->time = ap_max(0, c->done - c->start); 1311 | s->waittime = ap_max(0, c->beginread - c->endwrite); 1312 | if (heartbeatres && !(done % heartbeatres)) { 1313 | fprintf(stderr, "Completed %d requests\n", done); 1314 | fflush(stderr); 1315 | } 1316 | } 1317 | } 1318 | 1319 | set_conn_state(c, STATE_UNCONNECTED); 1320 | #ifdef USE_SSL 1321 | if (c->ssl) { 1322 | SSL_shutdown(c->ssl); 1323 | SSL_free(c->ssl); 1324 | c->ssl = NULL; 1325 | } 1326 | #endif 1327 | apr_socket_close(c->aprsock); 1328 | 1329 | /* connect again */ 1330 | start_connect(c); 1331 | return; 1332 | } 1333 | 1334 | /* --------------------------------------------------------- */ 1335 | 1336 | /* read data from connection */ 1337 | 1338 | static void read_connection(struct connection * c) 1339 | { 1340 | apr_size_t r; 1341 | apr_status_t status; 1342 | char *part; 1343 | char respcode[4]; /* 3 digits and null */ 1344 | 1345 | r = sizeof(buffer); 1346 | #ifdef USE_SSL 1347 | if (c->ssl) { 1348 | status = SSL_read(c->ssl, buffer, r); 1349 | if (status <= 0) { 1350 | int scode = SSL_get_error(c->ssl, status); 1351 | 1352 | if (scode == SSL_ERROR_ZERO_RETURN) { 1353 | /* connection closed cleanly: */ 1354 | good++; 1355 | close_connection(c); 1356 | } 1357 | else if (scode != SSL_ERROR_WANT_WRITE 1358 | && scode != SSL_ERROR_WANT_READ) { 1359 | /* some fatal error: */ 1360 | c->read = 0; 1361 | BIO_printf(bio_err, "SSL read failed - closing connection\n"); 1362 | ERR_print_errors(bio_err); 1363 | close_connection(c); 1364 | } 1365 | return; 1366 | } 1367 | r = status; 1368 | } 1369 | else 1370 | #endif 1371 | { 1372 | status = apr_socket_recv(c->aprsock, buffer, &r); 1373 | if (APR_STATUS_IS_EAGAIN(status)) 1374 | return; 1375 | else if (r == 0 && APR_STATUS_IS_EOF(status)) { 1376 | good++; 1377 | close_connection(c); 1378 | return; 1379 | } 1380 | /* catch legitimate fatal apr_socket_recv errors */ 1381 | else if (status != APR_SUCCESS) { 1382 | err_recv++; 1383 | if (recverrok) { 1384 | bad++; 1385 | close_connection(c); 1386 | if (verbosity >= 1) { 1387 | char buf[120]; 1388 | fprintf(stderr,"%s: %s (%d)\n", "apr_socket_recv", apr_strerror(status, buf, sizeof buf), status); 1389 | } 1390 | return; 1391 | } else { 1392 | apr_err("apr_socket_recv", status); 1393 | } 1394 | } 1395 | } 1396 | 1397 | totalread += r; 1398 | if (c->read == 0) { 1399 | c->beginread = apr_time_now(); 1400 | } 1401 | c->read += r; 1402 | 1403 | 1404 | if (!c->gotheader) { 1405 | char *s; 1406 | int l = 4; 1407 | apr_size_t space = CBUFFSIZE - c->cbx - 1; /* -1 allows for \0 term */ 1408 | int tocopy = (space < r) ? space : r; 1409 | #ifdef NOT_ASCII 1410 | apr_size_t inbytes_left = space, outbytes_left = space; 1411 | 1412 | status = apr_xlate_conv_buffer(from_ascii, buffer, &inbytes_left, 1413 | c->cbuff + c->cbx, &outbytes_left); 1414 | if (status || inbytes_left || outbytes_left) { 1415 | fprintf(stderr, "only simple translation is supported (%d/%" APR_SIZE_T_FMT 1416 | "/%" APR_SIZE_T_FMT ")\n", status, inbytes_left, outbytes_left); 1417 | exit(1); 1418 | } 1419 | #else 1420 | memcpy(c->cbuff + c->cbx, buffer, space); 1421 | #endif /* NOT_ASCII */ 1422 | c->cbx += tocopy; 1423 | space -= tocopy; 1424 | c->cbuff[c->cbx] = 0; /* terminate for benefit of strstr */ 1425 | if (verbosity >= 2) { 1426 | printf("LOG: header received:\n%s\n", c->cbuff); 1427 | } 1428 | s = strstr(c->cbuff, "\r\n\r\n"); 1429 | /* 1430 | * this next line is so that we talk to NCSA 1.5 which blatantly 1431 | * breaks the http specifaction 1432 | */ 1433 | if (!s) { 1434 | s = strstr(c->cbuff, "\n\n"); 1435 | l = 2; 1436 | } 1437 | 1438 | if (!s) { 1439 | /* read rest next time */ 1440 | if (space) { 1441 | return; 1442 | } 1443 | else { 1444 | /* header is in invalid or too big - close connection */ 1445 | set_conn_state(c, STATE_UNCONNECTED); 1446 | apr_socket_close(c->aprsock); 1447 | err_response++; 1448 | if (bad++ > 10) { 1449 | err("\nTest aborted after 10 failures\n\n"); 1450 | } 1451 | start_connect(c); 1452 | } 1453 | } 1454 | else { 1455 | /* have full header */ 1456 | if (!good) { 1457 | /* 1458 | * this is first time, extract some interesting info 1459 | */ 1460 | char *p, *q; 1461 | p = strstr(c->cbuff, "Server:"); 1462 | q = servername; 1463 | if (p) { 1464 | p += 8; 1465 | while (*p > 32) 1466 | *q++ = *p++; 1467 | } 1468 | *q = 0; 1469 | } 1470 | /* 1471 | * XXX: this parsing isn't even remotely HTTP compliant... but in 1472 | * the interest of speed it doesn't totally have to be, it just 1473 | * needs to be extended to handle whatever servers folks want to 1474 | * test against. -djg 1475 | */ 1476 | 1477 | /* check response code */ 1478 | part = strstr(c->cbuff, "HTTP"); /* really HTTP/1.x_ */ 1479 | if (part && strlen(part) > strlen("HTTP/1.x_")) { 1480 | strncpy(respcode, (part + strlen("HTTP/1.x_")), 3); 1481 | respcode[3] = '\0'; 1482 | } 1483 | else { 1484 | strcpy(respcode, "500"); 1485 | } 1486 | 1487 | if (respcode[0] != '2') { 1488 | err_response++; 1489 | if (verbosity >= 2) 1490 | printf("WARNING: Response code not 2xx (%s)\n", respcode); 1491 | } 1492 | else if (verbosity >= 3) { 1493 | printf("LOG: Response code = %s\n", respcode); 1494 | } 1495 | c->gotheader = 1; 1496 | *s = 0; /* terminate at end of header */ 1497 | if (keepalive && 1498 | (strstr(c->cbuff, "Keep-Alive") 1499 | || strstr(c->cbuff, "keep-alive"))) { /* for benefit of MSIIS */ 1500 | char *cl; 1501 | cl = strstr(c->cbuff, "Content-Length:"); 1502 | /* handle NCSA, which sends Content-length: */ 1503 | if (!cl) 1504 | cl = strstr(c->cbuff, "Content-length:"); 1505 | if (cl) { 1506 | c->keepalive = 1; 1507 | /* response to HEAD doesn't have entity body */ 1508 | c->length = posting >= 0 ? atoi(cl + 16) : 0; 1509 | } 1510 | /* The response may not have a Content-Length header */ 1511 | if (!cl) { 1512 | c->keepalive = 1; 1513 | c->length = 0; 1514 | } 1515 | } 1516 | c->bread += c->cbx - (s + l - c->cbuff) + r - tocopy; 1517 | totalbread += c->bread; 1518 | } 1519 | } 1520 | else { 1521 | /* outside header, everything we have read is entity body */ 1522 | c->bread += r; 1523 | totalbread += r; 1524 | } 1525 | 1526 | if (c->keepalive && (c->bread >= c->length)) { 1527 | /* finished a keep-alive connection */ 1528 | good++; 1529 | /* save out time */ 1530 | if (good == 1) { 1531 | /* first time here */ 1532 | doclen = c->bread; 1533 | } 1534 | else if (c->bread != doclen) { 1535 | bad++; 1536 | err_length++; 1537 | } 1538 | if (done < requests) { 1539 | struct data *s = &stats[done++]; 1540 | doneka++; 1541 | c->done = apr_time_now(); 1542 | s->starttime = c->start; 1543 | s->ctime = ap_max(0, c->connect - c->start); 1544 | s->time = ap_max(0, c->done - c->start); 1545 | s->waittime = ap_max(0, c->beginread - c->endwrite); 1546 | if (heartbeatres && !(done % heartbeatres)) { 1547 | fprintf(stderr, "Completed %d requests\n", done); 1548 | fflush(stderr); 1549 | } 1550 | } 1551 | c->keepalive = 0; 1552 | c->length = 0; 1553 | c->gotheader = 0; 1554 | c->cbx = 0; 1555 | c->read = c->bread = 0; 1556 | /* zero connect time with keep-alive */ 1557 | c->start = c->connect = lasttime = apr_time_now(); 1558 | write_request(c); 1559 | } 1560 | } 1561 | 1562 | /* --------------------------------------------------------- */ 1563 | 1564 | /* run the tests */ 1565 | 1566 | static void test(void) 1567 | { 1568 | apr_time_t stoptime; 1569 | apr_int16_t rv; 1570 | int i; 1571 | apr_status_t status; 1572 | int snprintf_res = 0; 1573 | #ifdef NOT_ASCII 1574 | apr_size_t inbytes_left, outbytes_left; 1575 | #endif 1576 | 1577 | if (isproxy) { 1578 | connecthost = apr_pstrdup(cntxt, proxyhost); 1579 | connectport = proxyport; 1580 | } 1581 | else { 1582 | connecthost = apr_pstrdup(cntxt, hostname); 1583 | connectport = port; 1584 | } 1585 | 1586 | if (!use_html) { 1587 | printf("Benchmarking %s ", hostname); 1588 | if (isproxy) 1589 | printf("[through %s:%d] ", proxyhost, proxyport); 1590 | printf("(be patient)%s", 1591 | (heartbeatres ? "\n" : "...")); 1592 | fflush(stdout); 1593 | } 1594 | 1595 | con = calloc(concurrency, sizeof(struct connection)); 1596 | 1597 | stats = calloc(requests, sizeof(struct data)); 1598 | 1599 | if ((status = apr_pollset_create(&readbits, concurrency, cntxt, 1600 | APR_POLLSET_NOCOPY)) != APR_SUCCESS) { 1601 | apr_err("apr_pollset_create failed", status); 1602 | } 1603 | 1604 | /* add default headers if necessary */ 1605 | if (!opt_host) { 1606 | /* Host: header not overridden, add default value to hdrs */ 1607 | hdrs = apr_pstrcat(cntxt, hdrs, "Host: ", host_field, colonhost, "\r\n", NULL); 1608 | } 1609 | else { 1610 | /* Header overridden, no need to add, as it is already in hdrs */ 1611 | } 1612 | 1613 | if (!opt_useragent) { 1614 | /* User-Agent: header not overridden, add default value to hdrs */ 1615 | hdrs = apr_pstrcat(cntxt, hdrs, "User-Agent: ApacheBench/", AP_AB_BASEREVISION, "\r\n", NULL); 1616 | } 1617 | else { 1618 | /* Header overridden, no need to add, as it is already in hdrs */ 1619 | } 1620 | 1621 | if (!opt_accept) { 1622 | /* Accept: header not overridden, add default value to hdrs */ 1623 | hdrs = apr_pstrcat(cntxt, hdrs, "Accept: */*\r\n", NULL); 1624 | } 1625 | else { 1626 | /* Header overridden, no need to add, as it is already in hdrs */ 1627 | } 1628 | 1629 | /* setup request */ 1630 | if (posting <= 0) { 1631 | snprintf_res = apr_snprintf(request, sizeof(_request), 1632 | "%s %s HTTP/1.0\r\n" 1633 | "%s" "%s" "%s" 1634 | "%s" "\r\n", 1635 | (posting == 0) ? "GET" : "HEAD", 1636 | (isproxy) ? fullurl : path, 1637 | keepalive ? "Connection: Keep-Alive\r\n" : "", 1638 | cookie, auth, hdrs); 1639 | } 1640 | else { 1641 | snprintf_res = apr_snprintf(request, sizeof(_request), 1642 | "POST %s HTTP/1.0\r\n" 1643 | "%s" "%s" "%s" 1644 | "Content-length: %" APR_SIZE_T_FMT "\r\n" 1645 | "Content-type: %s\r\n" 1646 | "%s" 1647 | "\r\n", 1648 | (isproxy) ? fullurl : path, 1649 | keepalive ? "Connection: Keep-Alive\r\n" : "", 1650 | cookie, auth, 1651 | postlen, 1652 | (content_type[0]) ? content_type : "text/plain", hdrs); 1653 | } 1654 | if (snprintf_res >= sizeof(_request)) { 1655 | err("Request too long\n"); 1656 | } 1657 | 1658 | if (verbosity >= 2) 1659 | printf("INFO: POST header == \n---\n%s\n---\n", request); 1660 | 1661 | reqlen = strlen(request); 1662 | 1663 | /* 1664 | * Combine headers and (optional) post file into one contineous buffer 1665 | */ 1666 | if (posting == 1) { 1667 | char *buff = malloc(postlen + reqlen + 1); 1668 | if (!buff) { 1669 | fprintf(stderr, "error creating request buffer: out of memory\n"); 1670 | return; 1671 | } 1672 | strcpy(buff, request); 1673 | memcpy(buff + reqlen, postdata, postlen); 1674 | request = buff; 1675 | } 1676 | 1677 | #ifdef NOT_ASCII 1678 | inbytes_left = outbytes_left = reqlen; 1679 | status = apr_xlate_conv_buffer(to_ascii, request, &inbytes_left, 1680 | request, &outbytes_left); 1681 | if (status || inbytes_left || outbytes_left) { 1682 | fprintf(stderr, "only simple translation is supported (%d/%" 1683 | APR_SIZE_T_FMT "/%" APR_SIZE_T_FMT ")\n", 1684 | status, inbytes_left, outbytes_left); 1685 | exit(1); 1686 | } 1687 | #endif /* NOT_ASCII */ 1688 | 1689 | /* This only needs to be done once */ 1690 | if ((rv = apr_sockaddr_info_get(&destsa, connecthost, APR_UNSPEC, connectport, 0, cntxt)) 1691 | != APR_SUCCESS) { 1692 | char buf[120]; 1693 | apr_snprintf(buf, sizeof(buf), 1694 | "apr_sockaddr_info_get() for %s", connecthost); 1695 | apr_err(buf, rv); 1696 | } 1697 | 1698 | /* ok - lets start */ 1699 | start = lasttime = apr_time_now(); 1700 | stoptime = tlimit ? (start + apr_time_from_sec(tlimit)) : AB_MAX; 1701 | 1702 | #ifdef SIGINT 1703 | /* Output the results if the user terminates the run early. */ 1704 | apr_signal(SIGINT, output_results); 1705 | #endif 1706 | 1707 | /* initialise lots of requests */ 1708 | for (i = 0; i < concurrency; i++) { 1709 | con[i].socknum = i; 1710 | start_connect(&con[i]); 1711 | } 1712 | 1713 | do { 1714 | apr_int32_t n; 1715 | const apr_pollfd_t *pollresults; 1716 | 1717 | n = concurrency; 1718 | do { 1719 | status = apr_pollset_poll(readbits, aprtimeout, &n, &pollresults); 1720 | } while (APR_STATUS_IS_EINTR(status)); 1721 | if (status != APR_SUCCESS) 1722 | apr_err("apr_poll", status); 1723 | 1724 | if (!n) { 1725 | err("\nServer timed out\n\n"); 1726 | } 1727 | 1728 | for (i = 0; i < n; i++) { 1729 | const apr_pollfd_t *next_fd = &(pollresults[i]); 1730 | struct connection *c; 1731 | 1732 | c = next_fd->client_data; 1733 | 1734 | /* 1735 | * If the connection isn't connected how can we check it? 1736 | */ 1737 | if (c->state == STATE_UNCONNECTED) 1738 | continue; 1739 | 1740 | rv = next_fd->rtnevents; 1741 | 1742 | #ifdef USE_SSL 1743 | if (c->state == STATE_CONNECTED && c->ssl && SSL_in_init(c->ssl)) { 1744 | ssl_proceed_handshake(c); 1745 | continue; 1746 | } 1747 | #endif 1748 | 1749 | /* 1750 | * Notes: APR_POLLHUP is set after FIN is received on some 1751 | * systems, so treat that like APR_POLLIN so that we try to read 1752 | * again. 1753 | * 1754 | * Some systems return APR_POLLERR with APR_POLLHUP. We need to 1755 | * call read_connection() for APR_POLLHUP, so check for 1756 | * APR_POLLHUP first so that a closed connection isn't treated 1757 | * like an I/O error. If it is, we never figure out that the 1758 | * connection is done and we loop here endlessly calling 1759 | * apr_poll(). 1760 | */ 1761 | if ((rv & APR_POLLIN) || (rv & APR_POLLPRI) || (rv & APR_POLLHUP)) 1762 | read_connection(c); 1763 | if ((rv & APR_POLLERR) || (rv & APR_POLLNVAL)) { 1764 | bad++; 1765 | err_except++; 1766 | /* avoid apr_poll/EINPROGRESS loop on HP-UX, let recv discover ECONNREFUSED */ 1767 | if (c->state == STATE_CONNECTING) { 1768 | read_connection(c); 1769 | } 1770 | else { 1771 | start_connect(c); 1772 | } 1773 | continue; 1774 | } 1775 | if (rv & APR_POLLOUT) { 1776 | if (c->state == STATE_CONNECTING) { 1777 | rv = apr_socket_connect(c->aprsock, destsa); 1778 | if (rv != APR_SUCCESS) { 1779 | apr_socket_close(c->aprsock); 1780 | err_conn++; 1781 | if (bad++ > 10) { 1782 | fprintf(stderr, 1783 | "\nTest aborted after 10 failures\n\n"); 1784 | apr_err("apr_socket_connect()", rv); 1785 | } 1786 | set_conn_state(c, STATE_UNCONNECTED); 1787 | start_connect(c); 1788 | continue; 1789 | } 1790 | else { 1791 | set_conn_state(c, STATE_CONNECTED); 1792 | started++; 1793 | #ifdef USE_SSL 1794 | if (c->ssl) 1795 | ssl_proceed_handshake(c); 1796 | else 1797 | #endif 1798 | write_request(c); 1799 | } 1800 | } 1801 | else { 1802 | write_request(c); 1803 | } 1804 | } 1805 | } 1806 | } while (lasttime < stoptime && done < requests); 1807 | 1808 | if (heartbeatres) 1809 | fprintf(stderr, "Finished %d requests\n", done); 1810 | else 1811 | printf("..done\n"); 1812 | 1813 | if (use_html) 1814 | output_html_results(); 1815 | else 1816 | output_results(0); 1817 | } 1818 | 1819 | /* ------------------------------------------------------- */ 1820 | 1821 | /* display copyright information */ 1822 | static void copyright(void) 1823 | { 1824 | if (!use_html) { 1825 | printf("This is ApacheBench, Version %s\n", AP_AB_BASEREVISION " <$Revision$>"); 1826 | printf("Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/\n"); 1827 | printf("Licensed to The Apache Software Foundation, http://www.apache.org/\n"); 1828 | printf("\n"); 1829 | } 1830 | else { 1831 | printf("

\n"); 1832 | printf(" This is ApacheBench, Version %s <%s>
\n", AP_AB_BASEREVISION, "$Revision$"); 1833 | printf(" Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
\n"); 1834 | printf(" Licensed to The Apache Software Foundation, http://www.apache.org/
\n"); 1835 | printf("

\n

\n"); 1836 | } 1837 | } 1838 | 1839 | /* display usage information */ 1840 | static void usage(const char *progname) 1841 | { 1842 | fprintf(stderr, "Usage: %s [options] [http" 1843 | #ifdef USE_SSL 1844 | "[s]" 1845 | #endif 1846 | "://]hostname[:port]/path\n", progname); 1847 | /* 80 column ruler: ******************************************************************************** 1848 | */ 1849 | fprintf(stderr, "Options are:\n"); 1850 | fprintf(stderr, " -n requests Number of requests to perform\n"); 1851 | fprintf(stderr, " -c concurrency Number of multiple requests to make\n"); 1852 | fprintf(stderr, " -t timelimit Seconds to max. wait for responses\n"); 1853 | fprintf(stderr, " -b windowsize Size of TCP send/receive buffer, in bytes\n"); 1854 | fprintf(stderr, " -p postfile File containing data to POST. Remember also to set -T\n"); 1855 | fprintf(stderr, " -T content-type Content-type header for POSTing, eg.\n"); 1856 | fprintf(stderr, " 'application/x-www-form-urlencoded'\n"); 1857 | fprintf(stderr, " Default is 'text/plain'\n"); 1858 | fprintf(stderr, " -v verbosity How much troubleshooting info to print\n"); 1859 | fprintf(stderr, " -w Print out results in HTML tables\n"); 1860 | fprintf(stderr, " -i Use HEAD instead of GET\n"); 1861 | fprintf(stderr, " -x attributes String to insert as table attributes\n"); 1862 | fprintf(stderr, " -y attributes String to insert as tr attributes\n"); 1863 | fprintf(stderr, " -z attributes String to insert as td or th attributes\n"); 1864 | fprintf(stderr, " -C attribute Add cookie, eg. 'Apache=1234. (repeatable)\n"); 1865 | fprintf(stderr, " -H attribute Add Arbitrary header line, eg. 'Accept-Encoding: gzip'\n"); 1866 | fprintf(stderr, " Inserted after all normal header lines. (repeatable)\n"); 1867 | fprintf(stderr, " -A attribute Add Basic WWW Authentication, the attributes\n"); 1868 | fprintf(stderr, " are a colon separated username and password.\n"); 1869 | fprintf(stderr, " -P attribute Add Basic Proxy Authentication, the attributes\n"); 1870 | fprintf(stderr, " are a colon separated username and password.\n"); 1871 | fprintf(stderr, " -X proxy:port Proxyserver and port number to use\n"); 1872 | fprintf(stderr, " -V Print version number and exit\n"); 1873 | fprintf(stderr, " -k Use HTTP KeepAlive feature\n"); 1874 | fprintf(stderr, " -d Do not show percentiles served table.\n"); 1875 | fprintf(stderr, " -S Do not show confidence estimators and warnings.\n"); 1876 | fprintf(stderr, " -g filename Output collected data to gnuplot format file.\n"); 1877 | fprintf(stderr, " -e filename Output CSV file with percentages served\n"); 1878 | fprintf(stderr, " -r Don't exit on socket receive errors.\n"); 1879 | fprintf(stderr, " -h Display usage information (this message)\n"); 1880 | #ifdef USE_SSL 1881 | fprintf(stderr, " -Z ciphersuite Specify SSL/TLS cipher suite (See openssl ciphers)\n"); 1882 | fprintf(stderr, " -f protocol Specify SSL/TLS protocol (SSL2, SSL3, TLS1, or ALL)\n"); 1883 | #endif 1884 | exit(EINVAL); 1885 | } 1886 | 1887 | /* ------------------------------------------------------- */ 1888 | 1889 | /* split URL into parts */ 1890 | 1891 | static int parse_url(char *url) 1892 | { 1893 | char *cp; 1894 | char *h; 1895 | char *scope_id; 1896 | apr_status_t rv; 1897 | 1898 | /* Save a copy for the proxy */ 1899 | fullurl = apr_pstrdup(cntxt, url); 1900 | 1901 | if (strlen(url) > 7 && strncmp(url, "http://", 7) == 0) { 1902 | url += 7; 1903 | #ifdef USE_SSL 1904 | is_ssl = 0; 1905 | #endif 1906 | } 1907 | else 1908 | #ifdef USE_SSL 1909 | if (strlen(url) > 8 && strncmp(url, "https://", 8) == 0) { 1910 | url += 8; 1911 | is_ssl = 1; 1912 | } 1913 | #else 1914 | if (strlen(url) > 8 && strncmp(url, "https://", 8) == 0) { 1915 | fprintf(stderr, "SSL not compiled in; no https support\n"); 1916 | exit(1); 1917 | } 1918 | #endif 1919 | 1920 | if ((cp = strchr(url, '/')) == NULL) 1921 | return 1; 1922 | h = apr_pstrmemdup(cntxt, url, cp - url); 1923 | rv = apr_parse_addr_port(&hostname, &scope_id, &port, h, cntxt); 1924 | if (rv != APR_SUCCESS || !hostname || scope_id) { 1925 | return 1; 1926 | } 1927 | path = apr_pstrdup(cntxt, cp); 1928 | *cp = '\0'; 1929 | if (*url == '[') { /* IPv6 numeric address string */ 1930 | host_field = apr_psprintf(cntxt, "[%s]", hostname); 1931 | } 1932 | else { 1933 | host_field = hostname; 1934 | } 1935 | 1936 | if (port == 0) { /* no port specified */ 1937 | #ifdef USE_SSL 1938 | if (is_ssl) 1939 | port = 443; 1940 | else 1941 | #endif 1942 | port = 80; 1943 | } 1944 | 1945 | if (( 1946 | #ifdef USE_SSL 1947 | is_ssl && (port != 443)) || (!is_ssl && 1948 | #endif 1949 | (port != 80))) 1950 | { 1951 | colonhost = apr_psprintf(cntxt,":%d",port); 1952 | } else 1953 | colonhost = ""; 1954 | return 0; 1955 | } 1956 | 1957 | /* ------------------------------------------------------- */ 1958 | 1959 | /* read data to POST from file, save contents and length */ 1960 | 1961 | static int open_postfile(const char *pfile) 1962 | { 1963 | apr_file_t *postfd; 1964 | apr_finfo_t finfo; 1965 | apr_status_t rv; 1966 | char errmsg[120]; 1967 | 1968 | rv = apr_file_open(&postfd, pfile, APR_READ, APR_OS_DEFAULT, cntxt); 1969 | if (rv != APR_SUCCESS) { 1970 | fprintf(stderr, "ab: Could not open POST data file (%s): %s\n", pfile, 1971 | apr_strerror(rv, errmsg, sizeof errmsg)); 1972 | return rv; 1973 | } 1974 | 1975 | rv = apr_file_info_get(&finfo, APR_FINFO_NORM, postfd); 1976 | if (rv != APR_SUCCESS) { 1977 | fprintf(stderr, "ab: Could not stat POST data file (%s): %s\n", pfile, 1978 | apr_strerror(rv, errmsg, sizeof errmsg)); 1979 | return rv; 1980 | } 1981 | postlen = (apr_size_t)finfo.size; 1982 | postdata = malloc(postlen); 1983 | if (!postdata) { 1984 | fprintf(stderr, "ab: Could not allocate POST data buffer\n"); 1985 | return APR_ENOMEM; 1986 | } 1987 | rv = apr_file_read_full(postfd, postdata, postlen, NULL); 1988 | if (rv != APR_SUCCESS) { 1989 | fprintf(stderr, "ab: Could not read POST data file: %s\n", 1990 | apr_strerror(rv, errmsg, sizeof errmsg)); 1991 | return rv; 1992 | } 1993 | apr_file_close(postfd); 1994 | return 0; 1995 | } 1996 | 1997 | /* ------------------------------------------------------- */ 1998 | 1999 | /* sort out command-line args and call test */ 2000 | int main(int argc, const char * const argv[]) 2001 | { 2002 | int r, l; 2003 | char tmp[1024]; 2004 | apr_status_t status; 2005 | apr_getopt_t *opt; 2006 | const char *optarg; 2007 | char c; 2008 | #ifdef USE_SSL 2009 | #if OPENSSL_VERSION_NUMBER >= 0x00909000 2010 | const SSL_METHOD *meth = SSLv23_client_method(); 2011 | #else 2012 | SSL_METHOD *meth = SSLv23_client_method(); 2013 | #endif 2014 | #endif 2015 | 2016 | /* table defaults */ 2017 | tablestring = ""; 2018 | trstring = ""; 2019 | tdstring = "bgcolor=white"; 2020 | cookie = ""; 2021 | auth = ""; 2022 | proxyhost[0] = '\0'; 2023 | hdrs = ""; 2024 | 2025 | apr_app_initialize(&argc, &argv, NULL); 2026 | atexit(apr_terminate); 2027 | apr_pool_create(&cntxt, NULL); 2028 | 2029 | #ifdef NOT_ASCII 2030 | status = apr_xlate_open(&to_ascii, "ISO-8859-1", APR_DEFAULT_CHARSET, cntxt); 2031 | if (status) { 2032 | fprintf(stderr, "apr_xlate_open(to ASCII)->%d\n", status); 2033 | exit(1); 2034 | } 2035 | status = apr_xlate_open(&from_ascii, APR_DEFAULT_CHARSET, "ISO-8859-1", cntxt); 2036 | if (status) { 2037 | fprintf(stderr, "apr_xlate_open(from ASCII)->%d\n", status); 2038 | exit(1); 2039 | } 2040 | status = apr_base64init_ebcdic(to_ascii, from_ascii); 2041 | if (status) { 2042 | fprintf(stderr, "apr_base64init_ebcdic()->%d\n", status); 2043 | exit(1); 2044 | } 2045 | #endif 2046 | 2047 | apr_getopt_init(&opt, cntxt, argc, argv); 2048 | while ((status = apr_getopt(opt, "n:c:t:b:T:p:v:rkVhwix:y:z:C:H:P:A:g:X:de:Sq" 2049 | #ifdef USE_SSL 2050 | "Z:f:" 2051 | #endif 2052 | ,&c, &optarg)) == APR_SUCCESS) { 2053 | switch (c) { 2054 | case 'n': 2055 | requests = atoi(optarg); 2056 | if (requests <= 0) { 2057 | err("Invalid number of requests\n"); 2058 | } 2059 | break; 2060 | case 'k': 2061 | keepalive = 1; 2062 | break; 2063 | case 'q': 2064 | heartbeatres = 0; 2065 | break; 2066 | case 'c': 2067 | concurrency = atoi(optarg); 2068 | break; 2069 | case 'b': 2070 | windowsize = atoi(optarg); 2071 | break; 2072 | case 'i': 2073 | if (posting == 1) 2074 | err("Cannot mix POST and HEAD\n"); 2075 | posting = -1; 2076 | break; 2077 | case 'g': 2078 | gnuplot = strdup(optarg); 2079 | break; 2080 | case 'd': 2081 | percentile = 0; 2082 | break; 2083 | case 'e': 2084 | csvperc = strdup(optarg); 2085 | break; 2086 | case 'S': 2087 | confidence = 0; 2088 | break; 2089 | case 'p': 2090 | if (posting != 0) 2091 | err("Cannot mix POST and HEAD\n"); 2092 | if (0 == (r = open_postfile(optarg))) { 2093 | posting = 1; 2094 | } 2095 | else if (postdata) { 2096 | exit(r); 2097 | } 2098 | break; 2099 | case 'r': 2100 | recverrok = 1; 2101 | break; 2102 | case 'v': 2103 | verbosity = atoi(optarg); 2104 | break; 2105 | case 't': 2106 | tlimit = atoi(optarg); 2107 | requests = MAX_REQUESTS; /* need to size data array on 2108 | * something */ 2109 | break; 2110 | case 'T': 2111 | strcpy(content_type, optarg); 2112 | break; 2113 | case 'C': 2114 | cookie = apr_pstrcat(cntxt, "Cookie: ", optarg, "\r\n", NULL); 2115 | break; 2116 | case 'A': 2117 | 2118 | // assume username passwd already to be in colon separated form. 2119 | // Ready to be uu-encoded. 2120 | // 2121 | while (apr_isspace(*optarg)) 2122 | optarg++; 2123 | if (apr_base64_encode_len(strlen(optarg)) > sizeof(tmp)) { 2124 | err("Authentication credentials too long\n"); 2125 | } 2126 | l = apr_base64_encode(tmp, optarg, strlen(optarg)); 2127 | tmp[l] = '\0'; 2128 | 2129 | auth = apr_pstrcat(cntxt, auth, "Authorization: Basic ", tmp, 2130 | "\r\n", NULL); 2131 | break; 2132 | 2133 | 2134 | case 'P': 2135 | // 2136 | // assume username passwd already to be in colon separated form. 2137 | // 2138 | while (apr_isspace(*optarg)) 2139 | optarg++; 2140 | if (apr_base64_encode_len(strlen(optarg)) > sizeof(tmp)) { 2141 | err("Proxy credentials too long\n"); 2142 | } 2143 | l = apr_base64_encode(tmp, optarg, strlen(optarg)); 2144 | tmp[l] = '\0'; 2145 | 2146 | auth = apr_pstrcat(cntxt, auth, "Proxy-Authorization: Basic ", 2147 | tmp, "\r\n", NULL); 2148 | break; 2149 | 2150 | case 'H': 2151 | hdrs = apr_pstrcat(cntxt, hdrs, optarg, "\r\n", NULL); 2152 | /* 2153 | * allow override of some of the common headers that ab adds 2154 | */ 2155 | if (strncasecmp(optarg, "Host:", 5) == 0) { 2156 | opt_host = 1; 2157 | } else if (strncasecmp(optarg, "Accept:", 7) == 0) { 2158 | opt_accept = 1; 2159 | } else if (strncasecmp(optarg, "User-Agent:", 11) == 0) { 2160 | opt_useragent = 1; 2161 | } 2162 | break; 2163 | case 'w': 2164 | use_html = 1; 2165 | break; 2166 | /* 2167 | * if any of the following three are used, turn on html output 2168 | * automatically 2169 | */ 2170 | case 'x': 2171 | use_html = 1; 2172 | tablestring = optarg; 2173 | break; 2174 | case 'X': 2175 | { 2176 | char *p; 2177 | /* 2178 | * assume proxy-name[:port] 2179 | */ 2180 | if ((p = strchr(optarg, ':'))) { 2181 | *p = '\0'; 2182 | p++; 2183 | proxyport = atoi(p); 2184 | } 2185 | strcpy(proxyhost, optarg); 2186 | isproxy = 1; 2187 | } 2188 | break; 2189 | case 'y': 2190 | use_html = 1; 2191 | trstring = optarg; 2192 | break; 2193 | case 'z': 2194 | use_html = 1; 2195 | tdstring = optarg; 2196 | break; 2197 | case 'h': 2198 | usage(argv[0]); 2199 | break; 2200 | case 'V': 2201 | copyright(); 2202 | return 0; 2203 | #ifdef USE_SSL 2204 | case 'Z': 2205 | ssl_cipher = strdup(optarg); 2206 | break; 2207 | case 'f': 2208 | if (strncasecmp(optarg, "ALL", 3) == 0) { 2209 | meth = SSLv23_client_method(); 2210 | } else if (strncasecmp(optarg, "SSL2", 4) == 0) { 2211 | meth = SSLv2_client_method(); 2212 | } else if (strncasecmp(optarg, "SSL3", 4) == 0) { 2213 | meth = SSLv3_client_method(); 2214 | } else if (strncasecmp(optarg, "TLS1", 4) == 0) { 2215 | meth = TLSv1_client_method(); 2216 | } 2217 | break; 2218 | #endif 2219 | } 2220 | } 2221 | 2222 | if (opt->ind != argc - 1) { 2223 | fprintf(stderr, "%s: wrong number of arguments\n", argv[0]); 2224 | usage(argv[0]); 2225 | } 2226 | 2227 | if (parse_url(apr_pstrdup(cntxt, opt->argv[opt->ind++]))) { 2228 | fprintf(stderr, "%s: invalid URL\n", argv[0]); 2229 | usage(argv[0]); 2230 | } 2231 | 2232 | if ((concurrency < 0) || (concurrency > MAX_CONCURRENCY)) { 2233 | fprintf(stderr, "%s: Invalid Concurrency [Range 0..%d]\n", 2234 | argv[0], MAX_CONCURRENCY); 2235 | usage(argv[0]); 2236 | } 2237 | 2238 | if (concurrency > requests) { 2239 | fprintf(stderr, "%s: Cannot use concurrency level greater than " 2240 | "total number of requests\n", argv[0]); 2241 | usage(argv[0]); 2242 | } 2243 | 2244 | if ((heartbeatres) && (requests > 150)) { 2245 | heartbeatres = requests / 10; /* Print line every 10% of requests */ 2246 | if (heartbeatres < 100) 2247 | heartbeatres = 100; /* but never more often than once every 100 2248 | * connections. */ 2249 | } 2250 | else 2251 | heartbeatres = 0; 2252 | 2253 | #ifdef USE_SSL 2254 | #ifdef RSAREF 2255 | R_malloc_init(); 2256 | #else 2257 | CRYPTO_malloc_init(); 2258 | #endif 2259 | SSL_load_error_strings(); 2260 | SSL_library_init(); 2261 | bio_out=BIO_new_fp(stdout,BIO_NOCLOSE); 2262 | bio_err=BIO_new_fp(stderr,BIO_NOCLOSE); 2263 | 2264 | if (!(ssl_ctx = SSL_CTX_new(meth))) { 2265 | BIO_printf(bio_err, "Could not initialize SSL Context.\n"); 2266 | ERR_print_errors(bio_err); 2267 | exit(1); 2268 | } 2269 | SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL); 2270 | if (ssl_cipher != NULL) { 2271 | if (!SSL_CTX_set_cipher_list(ssl_ctx, ssl_cipher)) { 2272 | fprintf(stderr, "error setting cipher list [%s]\n", ssl_cipher); 2273 | ERR_print_errors_fp(stderr); 2274 | exit(1); 2275 | } 2276 | } 2277 | if (verbosity >= 3) { 2278 | SSL_CTX_set_info_callback(ssl_ctx, ssl_state_cb); 2279 | } 2280 | #endif 2281 | #ifdef SIGPIPE 2282 | apr_signal(SIGPIPE, SIG_IGN); /* Ignore writes to connections that 2283 | * have been closed at the other end. */ 2284 | #endif 2285 | copyright(); 2286 | test(); 2287 | apr_pool_destroy(cntxt); 2288 | 2289 | return 0; 2290 | } 2291 | -------------------------------------------------------------------------------- /ap_release.h: -------------------------------------------------------------------------------- 1 | /* Licensed to the Apache Software Foundation (ASF) under one or more 2 | * contributor license agreements. See the NOTICE file distributed with 3 | * this work for additional information regarding copyright ownership. 4 | * The ASF licenses this file to You under the Apache License, Version 2.0 5 | * (the "License"); you may not use this file except in compliance with 6 | * the License. You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /** 18 | * @file ap_release.h 19 | * @brief Version Release defines 20 | */ 21 | 22 | #ifndef AP_RELEASE_H 23 | #define AP_RELEASE_H 24 | 25 | #include /* stringify */ 26 | //#include "apr_general.h" /* stringify */ 27 | 28 | #define AP_SERVER_COPYRIGHT \ 29 | "Copyright 2008 The Apache Software Foundation." 30 | 31 | /* 32 | * The below defines the base string of the Server: header. Additional 33 | * tokens can be added via the ap_add_version_component() API call. 34 | * 35 | * The tokens are listed in order of their significance for identifying the 36 | * application. 37 | * 38 | * "Product tokens should be short and to the point -- use of them for 39 | * advertizing or other non-essential information is explicitly forbidden." 40 | * 41 | * Example: "Apache/1.1.0 MrWidget/0.1-alpha" 42 | */ 43 | #define AP_SERVER_BASEVENDOR "Apache Software Foundation" 44 | #define AP_SERVER_BASEPROJECT "Apache HTTP Server" 45 | #define AP_SERVER_BASEPRODUCT "Apache" 46 | 47 | #define AP_SERVER_MAJORVERSION_NUMBER 2 48 | #define AP_SERVER_MINORVERSION_NUMBER 2 49 | #define AP_SERVER_PATCHLEVEL_NUMBER 11 50 | #define AP_SERVER_DEVBUILD_BOOLEAN 0 51 | 52 | #if AP_SERVER_DEVBUILD_BOOLEAN 53 | #define AP_SERVER_ADD_STRING "-dev" 54 | #else 55 | #define AP_SERVER_ADD_STRING "" 56 | #endif 57 | 58 | /* keep old macros as well */ 59 | #define AP_SERVER_MAJORVERSION APR_STRINGIFY(AP_SERVER_MAJORVERSION_NUMBER) 60 | #define AP_SERVER_MINORVERSION APR_STRINGIFY(AP_SERVER_MINORVERSION_NUMBER) 61 | #define AP_SERVER_PATCHLEVEL APR_STRINGIFY(AP_SERVER_PATCHLEVEL_NUMBER) \ 62 | AP_SERVER_ADD_STRING 63 | 64 | #define AP_SERVER_MINORREVISION AP_SERVER_MAJORVERSION "." AP_SERVER_MINORVERSION 65 | #define AP_SERVER_BASEREVISION AP_SERVER_MINORREVISION "." AP_SERVER_PATCHLEVEL 66 | #define AP_SERVER_BASEVERSION AP_SERVER_BASEPRODUCT "/" AP_SERVER_BASEREVISION 67 | #define AP_SERVER_VERSION AP_SERVER_BASEVERSION 68 | 69 | /* macro for Win32 .rc files using numeric csv representation */ 70 | #define AP_SERVER_PATCHLEVEL_CSV AP_SERVER_MAJORVERSION_NUMBER ##, \ 71 | ##AP_SERVER_MINORVERSION_NUMBER ##, \ 72 | ##AP_SERVER_PATCHLEVEL_NUMBER 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /apr-skeleton.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | /** 9 | * apr skeleton code 10 | */ 11 | int main(int argc, const char *argv[]) 12 | { 13 | apr_status_t rv; 14 | 15 | /* per-process initialization */ 16 | rv = apr_initialize(); 17 | if (rv != APR_SUCCESS) { 18 | assert(0); 19 | return -1; 20 | } 21 | 22 | /* application impl. */ 23 | 24 | apr_terminate(); 25 | return 0; 26 | } 27 | -------------------------------------------------------------------------------- /apr/Makefile: -------------------------------------------------------------------------------- 1 | APR=apr-1.5.2 2 | APRUTIL=apr-util-1.5.4 3 | 4 | all:$(APR) $(APRUTIL) buildapr 5 | 6 | $(APR): 7 | wget http://apache.techartifact.com/mirror//apr/$(APR).tar.gz 8 | tar -xf $(APR).tar.gz 9 | $(APRUTIL): 10 | wget http://apache.techartifact.com/mirror//apr/$(APRUTIL).tar.gz 11 | tar -xf $(APRUTIL).tar.gz 12 | 13 | buildapr: 14 | cp Makefile_apr $(APR) 15 | make -C $(APR) -f Makefile_apr 16 | cp Makefile_aprutil $(APRUTIL) 17 | make -C $(APRUTIL) -f Makefile_aprutil 18 | 19 | clean: 20 | rm -rf apr-* 21 | rm -rf aprutil-* 22 | 23 | -------------------------------------------------------------------------------- /apr/Makefile_apr: -------------------------------------------------------------------------------- 1 | all:libapr-1.la 2 | 3 | libapr-1.la: 4 | PWD=`pwd` 5 | ./configure --prefix=$(PWD)/apr/apr-build 6 | make 7 | make install 8 | -------------------------------------------------------------------------------- /apr/Makefile_aprutil: -------------------------------------------------------------------------------- 1 | all:libaprutil-1.la 2 | 3 | libaprutil-1.la: 4 | PWD=`pwd` 5 | ./configure --prefix=$(PWD)/apr/aprutil-build --with-apr=$(PWD)/apr/apr-build 6 | make 7 | make install 8 | -------------------------------------------------------------------------------- /plot.p: -------------------------------------------------------------------------------- 1 | # output as png image 2 | set terminal png 3 | 4 | # save file to "out.png" 5 | set output "out.png" 6 | 7 | # graph title 8 | set title "ab -n 8000 -c 100" 9 | 10 | # nicer aspect ratio for image size 11 | set size 2,0.7 12 | 13 | # y-axis grid 14 | set grid y 15 | 16 | # x-axis label 17 | set xlabel "request" 18 | 19 | # y-axis label 20 | set ylabel "response time (ms)" 21 | 22 | # plot data from "out.dat" using column 9 with smooth sbezier lines 23 | # and title of "Vizsla" for the given data 24 | plot "out.dat" using 9 smooth sbezier with lines title "Vizsla" 25 | --------------------------------------------------------------------------------