-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("Server Software: | "
1055 | "%s | \n",
1056 | trstring, tdstring, tdstring, servername);
1057 | printf("Server Hostname: | "
1058 | "%s | \n",
1059 | trstring, tdstring, tdstring, hostname);
1060 | printf("Server Port: | "
1061 | "%hu | \n",
1062 | trstring, tdstring, tdstring, port);
1063 | printf("Document Path: | "
1064 | "%s | \n",
1065 | trstring, tdstring, tdstring, path);
1066 | printf("Document Length: | "
1067 | "%" APR_SIZE_T_FMT " bytes | \n",
1068 | trstring, tdstring, tdstring, doclen);
1069 | printf("Concurrency Level: | "
1070 | "%d | \n",
1071 | trstring, tdstring, tdstring, concurrency);
1072 | printf("Time taken for tests: | "
1073 | "%.3f seconds | \n",
1074 | trstring, tdstring, tdstring, timetaken);
1075 | printf("Complete requests: | "
1076 | "%d | \n",
1077 | trstring, tdstring, tdstring, done);
1078 | printf("Failed requests: | "
1079 | "%d | \n",
1080 | trstring, tdstring, tdstring, bad);
1081 | if (bad)
1082 | printf(" (Connect: %d, Length: %d, Exceptions: %d) | \n",
1083 | trstring, tdstring, err_conn, err_length, err_except);
1084 | if (err_response)
1085 | printf("Non-2xx responses: | "
1086 | "%d | \n",
1087 | trstring, tdstring, tdstring, err_response);
1088 | if (keepalive)
1089 | printf("Keep-Alive requests: | "
1090 | "%d | \n",
1091 | trstring, tdstring, tdstring, doneka);
1092 | printf("Total transferred: | "
1093 | "%" APR_INT64_T_FMT " bytes | \n",
1094 | trstring, tdstring, tdstring, totalread);
1095 | if (posting > 0)
1096 | printf("Total POSTed: | "
1097 | "%" APR_INT64_T_FMT " | \n",
1098 | trstring, tdstring, tdstring, totalposted);
1099 | printf("HTML transferred: | "
1100 | "%" APR_INT64_T_FMT " bytes | \n",
1101 | trstring, tdstring, tdstring, totalbread);
1102 |
1103 | /* avoid divide by zero */
1104 | if (timetaken) {
1105 | printf("Requests per second: | "
1106 | "%.2f | \n",
1107 | trstring, tdstring, tdstring, (double) done * 1000 / timetaken);
1108 | printf("Transfer rate: | "
1109 | "%.2f kb/s received | \n",
1110 | trstring, tdstring, tdstring, (double) totalread / timetaken);
1111 | if (posting > 0) {
1112 | printf(" | "
1113 | "%.2f kb/s sent | \n",
1114 | trstring, tdstring, tdstring,
1115 | (double) totalposted / timetaken);
1116 | printf(" | "
1117 | "%.2f kb/s total | \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("Connnection Times (ms) | \n",
1150 | trstring, tdstring);
1151 | printf(" | min | avg | max | \n",
1152 | trstring, tdstring, tdstring, tdstring, tdstring);
1153 | printf("Connect: | "
1154 | "%5" APR_TIME_T_FMT " | "
1155 | "%5" APR_TIME_T_FMT " | "
1156 | "%5" APR_TIME_T_FMT " | \n",
1157 | trstring, tdstring, tdstring, mincon, tdstring, totalcon / done, tdstring, maxcon);
1158 | printf("Processing: | "
1159 | "%5" APR_TIME_T_FMT " | "
1160 | "%5" APR_TIME_T_FMT " | "
1161 | "%5" APR_TIME_T_FMT " | \n",
1162 | trstring, tdstring, tdstring, mintot - mincon, tdstring,
1163 | (total / done) - (totalcon / done), tdstring, maxtot - maxcon);
1164 | printf("Total: | "
1165 | "%5" APR_TIME_T_FMT " | "
1166 | "%5" APR_TIME_T_FMT " | "
1167 | "%5" APR_TIME_T_FMT " | \n",
1168 | trstring, tdstring, tdstring, mintot, tdstring, total / done, tdstring, maxtot);
1169 | }
1170 | printf(" \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 |
--------------------------------------------------------------------------------
| |