~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to test/zb.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
 
 
2
/*                          ZeusBench V1.01
 
3
                            ===============
 
4
 
 
5
This program is Copyright (C) Zeus Technology Limited 1996.
 
6
 
 
7
This program may be used and copied freely providing this copyright notice
 
8
is not removed.
 
9
 
 
10
This software is provided "as is" and any express or implied waranties,
 
11
including but not limited to, the implied warranties of merchantability and
 
12
fitness for a particular purpose are disclaimed.  In no event shall
 
13
Zeus Technology Ltd. be liable for any direct, indirect, incidental, special,
 
14
exemplary, or consequential damaged (including, but not limited to,
 
15
procurement of substitute good or services; loss of use, data, or profits;
 
16
or business interruption) however caused and on theory of liability.  Whether
 
17
in contract, strict liability or tort (including negligence or otherwise)
 
18
arising in any way out of the use of this software, even if advised of the
 
19
possibility of such damage.
 
20
 
 
21
     Written by Adam Twiss (adam@zeus.co.uk).  March 1996
 
22
 
 
23
Thanks to the following people for their input:
 
24
  Mike Belshe (mbelshe@netscape.com)
 
25
  Michael Campanella (campanella@stevms.enet.dec.com)
 
26
 
 
27
*/
 
28
 
 
29
/* -------------------- Notes on compiling ------------------------------
 
30
 
 
31
This should compile unmodified using gcc on HP-UX, FreeBSD, Linux,
 
32
IRIX, Solaris, AIX and Digital Unix (OSF).  On Solaris 2.x you will
 
33
need to compile with "-lnsl -lsocket" options.  If you have any
 
34
difficulties compiling then let me know.
 
35
 
 
36
On SunOS 4.x.x you may need to compile with -DSUNOS4 to add the following
 
37
two lines of code which appear not to exist in my SunOS headers */
 
38
 
 
39
#ifdef SUNOS4
 
40
extern char *optarg;
 
41
extern int optind, opterr, optopt;
 
42
#endif
 
43
 
 
44
/*  -------------------------------------------------------------------- */
 
45
 
 
46
/* affects include files on Solaris */
 
47
#define BSD_COMP
 
48
 
 
49
#include <sys/time.h>
 
50
#include <sys/ioctl.h>
 
51
#include <unistd.h>
 
52
#include <stdlib.h>
 
53
#include <stdio.h>
 
54
#include <fcntl.h>
 
55
#include <sys/socket.h>
 
56
#include <netinet/in.h>
 
57
#include <netdb.h>
 
58
#include <errno.h>
 
59
#include <sys/ioctl.h>
 
60
#include <string.h>
 
61
 
 
62
/* ------------------- DEFINITIONS -------------------------- */
 
63
 
 
64
/* maximum number of requests on a time limited test */
 
65
#define MAX_REQUESTS 50000
 
66
 
 
67
/* good old state machine */
 
68
#define STATE_UNCONNECTED 0
 
69
#define STATE_CONNECTING  1
 
70
#define STATE_READ        2
 
71
 
 
72
#define CBUFFSIZE       512
 
73
 
 
74
struct connection
 
75
{
 
76
  int fd;
 
77
  int state;
 
78
  int read;       /* amount of bytes read */
 
79
  int bread;      /* amount of body read */
 
80
  int length;     /* Content-Length value used for keep-alive */
 
81
  char cbuff[CBUFFSIZE];  /* a buffer to store server response header */
 
82
  int cbx;                /* offset in cbuffer */
 
83
  int keepalive;          /* non-zero if a keep-alive request */
 
84
  int gotheader;          /* non-zero if we have the entire header in cbuff */
 
85
  struct timeval start, connect, done;
 
86
};
 
87
 
 
88
struct data
 
89
{
 
90
  int read;      /* number of bytes read */
 
91
  int ctime;     /* time in ms to connect */
 
92
  int time;      /* time in ms for connection */
 
93
};
 
94
 
 
95
#define min(a,b) ((a)<(b))?(a):(b)
 
96
#define max(a,b) ((a)>(b))?(a):(b)
 
97
 
 
98
/* --------------------- GLOBALS ---------------------------- */
 
99
 
 
100
int requests = 1;      /* Number of requests to make */
 
101
int concurrency = 1;   /* Number of multiple requests to make */
 
102
int tlimit = 0;        /* time limit in cs */
 
103
int keepalive = 0;     /* try and do keepalive connections */
 
104
char *machine;         /* Machine name */
 
105
char *file;            /* file name to use */
 
106
char server_name[80];  /* name that server reports */
 
107
int port = 80;         /* port to use */
 
108
 
 
109
int doclen = 0;        /* the length the document should be */
 
110
int totalread = 0;     /* total number of bytes read */
 
111
int totalbread = 0;    /* totoal amount of entity body read */
 
112
int done=0;            /* number of requests we have done */
 
113
int doneka=0;          /* number of keep alive connections done */
 
114
int good=0, bad=0;     /* number of good and bad requests */
 
115
 
 
116
/* store error cases */
 
117
int err_length = 0, err_conn = 0, err_except = 0;
 
118
 
 
119
struct timeval start, endtime;
 
120
 
 
121
/* global request (and its length) */
 
122
char request[512];
 
123
int reqlen;
 
124
 
 
125
/* one global throw-away buffer to read stuff into */
 
126
char buffer[4096];
 
127
 
 
128
struct connection *con;      /* connection array */
 
129
struct data *stats;          /* date for each request */
 
130
 
 
131
fd_set readbits, writebits;  /* bits for select */
 
132
struct sockaddr_in server;   /* server addr structure */
 
133
 
 
134
/* --------------------------------------------------------- */
 
135
 
 
136
/* simple little function to perror and exit */
 
137
 
 
138
static void err(char *s)
 
139
{
 
140
  perror(s);
 
141
  exit(errno);
 
142
}
 
143
 
 
144
/* --------------------------------------------------------- */
 
145
 
 
146
/* write out request to a connection - assumes we can write
 
147
   (small) request out in one go into our new socket buffer  */
 
148
 
 
149
void write_request(struct connection *c)
 
150
{
 
151
  gettimeofday(&c->connect,0);
 
152
  write(c->fd,request, reqlen);
 
153
  c->state = STATE_READ;
 
154
  FD_SET(c->fd, &readbits);
 
155
  FD_CLR(c->fd, &writebits);
 
156
}
 
157
 
 
158
/* --------------------------------------------------------- */
 
159
 
 
160
/* make an fd non blocking */
 
161
 
 
162
void nonblock(int fd)
 
163
{
 
164
  int i=1;
 
165
  ioctl(fd, FIONBIO, &i);
 
166
}
 
167
 
 
168
/* --------------------------------------------------------- */
 
169
 
 
170
/* returns the time in ms between two timevals */
 
171
 
 
172
int timedif(struct timeval a, struct timeval b)
 
173
{
 
174
  register int us,s;
 
175
 
 
176
  us = a.tv_usec - b.tv_usec;
 
177
  us /= 1000;
 
178
  s = a.tv_sec - b.tv_sec;
 
179
  s *= 1000;
 
180
  return s+us;
 
181
}
 
182
 
 
183
/* --------------------------------------------------------- */
 
184
 
 
185
/* calculate and output results and exit */
 
186
 
 
187
void output_results()
 
188
{
 
189
  int timetaken;
 
190
 
 
191
  gettimeofday(&endtime,0);
 
192
  timetaken = timedif(endtime, start);
 
193
 
 
194
  printf("\n---\n");
 
195
  printf("Server:                 %s\n", server_name);
 
196
  printf("Document Length:        %d\n", doclen);
 
197
  printf("Concurency Level:       %d\n", concurrency);
 
198
  printf("Time taken for tests:   %d.%03d seconds\n",
 
199
         timetaken/1000, timetaken%1000);
 
200
  printf("Complete requests:      %d\n", done);
 
201
  printf("Failed requests:        %d\n", bad);
 
202
  if(bad) printf("   (Connect: %d, Length: %d, Exceptions: %d)\n",
 
203
                 err_conn, err_length, err_except);
 
204
  if(keepalive) printf("Keep-Alive requests:    %d\n", doneka);
 
205
  printf("Bytes transferred:      %d\n", totalread);
 
206
  printf("HTML transferred:       %d\n", totalbread);
 
207
 
 
208
  /* avoid divide by zero */
 
209
  if(timetaken) {
 
210
    printf("Requests per seconds:   %.2f\n", 1000*(float)(done)/timetaken);
 
211
    printf("Transfer rate:          %.2f kb/s\n",
 
212
           (float)(totalread)/timetaken);
 
213
  }
 
214
 
 
215
  {
 
216
    /* work out connection times */
 
217
    int i;
 
218
    int totalcon=0, total=0;
 
219
    int mincon=9999999, mintot=999999;
 
220
    int maxcon=0, maxtot=0;
 
221
 
 
222
    for(i=0; i<requests; i++) {
 
223
      struct data s = stats[i];
 
224
      mincon = min(mincon, s.ctime);
 
225
      mintot = min(mintot, s.time);
 
226
      maxcon = max(maxcon, s.ctime);
 
227
      maxtot = max(maxtot, s.time);
 
228
      totalcon += s.ctime;
 
229
      total += s.time;
 
230
    }
 
231
    printf("\nConnnection Times (ms)\n");
 
232
    printf("           min   avg   max\n");
 
233
    printf("Connect: %5d %5d %5d\n",mincon, totalcon/requests, maxcon );
 
234
    printf("Total:   %5d %5d %5d\n", mintot, total/requests, maxtot);
 
235
    printf("---\n\n");
 
236
  }
 
237
 
 
238
  exit(0);
 
239
}
 
240
 
 
241
/* --------------------------------------------------------- */
 
242
 
 
243
/* start asnchronous non-blocking connection */
 
244
 
 
245
void start_connect(struct connection *c)
 
246
{
 
247
  c->read = 0;
 
248
  c->bread = 0;
 
249
  c->keepalive = 0;
 
250
  c->cbx = 0;
 
251
  c->gotheader = 0;
 
252
 
 
253
  c->fd = socket(AF_INET, SOCK_STREAM, 0);
 
254
  if(c->fd<0) err("socket");
 
255
 
 
256
  nonblock(c->fd);
 
257
  gettimeofday(&c->start,0);
 
258
 
 
259
  if(connect(c->fd, (struct sockaddr *) &server, sizeof(server))<0) {
 
260
    if(errno==EINPROGRESS) {
 
261
      c->state = STATE_CONNECTING;
 
262
      FD_SET(c->fd, &writebits);
 
263
      return;
 
264
    }
 
265
    else {
 
266
      close(c->fd);
 
267
      err_conn++;
 
268
      if(bad++>10) {
 
269
        printf("\nTest aborted after 10 failures\n\n");
 
270
        exit(1);
 
271
      }
 
272
      start_connect(c);
 
273
    }
 
274
  }
 
275
 
 
276
  /* connected first time */
 
277
  write_request(c);
 
278
}
 
279
 
 
280
/* --------------------------------------------------------- */
 
281
 
 
282
/* close down connection and save stats */
 
283
 
 
284
void close_connection(struct connection *c)
 
285
{
 
286
  if(c->read == 0 && c->keepalive) {
 
287
    /* server has legitiamately shut down an idle keep alive request */
 
288
    good--;  /* connection never happend */
 
289
  }
 
290
  else {
 
291
    if(good==1) {
 
292
      /* first time here */
 
293
      doclen = c->bread;
 
294
    } else if (c->bread!=doclen) {
 
295
      bad++;
 
296
      err_length++;
 
297
    }
 
298
 
 
299
    /* save out time */
 
300
    if(done < requests) {
 
301
      struct data s;
 
302
      gettimeofday(&c->done,0);
 
303
      s.read = c->read;
 
304
      s.ctime = timedif(c->connect, c->start);
 
305
      s.time = timedif(c->done, c->start);
 
306
      stats[done++] = s;
 
307
    }
 
308
  }
 
309
 
 
310
  close(c->fd);
 
311
  FD_CLR(c->fd, &readbits);
 
312
  FD_CLR(c->fd, &writebits);
 
313
 
 
314
  /* connect again */
 
315
  start_connect(c);
 
316
  return;
 
317
}
 
318
 
 
319
/* --------------------------------------------------------- */
 
320
 
 
321
/* read data from connection */
 
322
 
 
323
void read_connection(struct connection *c)
 
324
{
 
325
  int r;
 
326
 
 
327
  r=read(c->fd,buffer,sizeof(buffer));
 
328
  if(r==0 || (r<0 && errno!=EAGAIN)) {
 
329
    good++;
 
330
    close_connection(c);
 
331
    return;
 
332
  }
 
333
 
 
334
  if(r<0 && errno==EAGAIN) return;
 
335
 
 
336
  c->read += r;
 
337
  totalread += r;
 
338
 
 
339
  if(!c->gotheader) {
 
340
    char *s;
 
341
    int l=4;
 
342
    int space = CBUFFSIZE - c->cbx - 1; /* -1 to allow for 0 terminator */
 
343
    int tocopy = (space<r)?space:r;
 
344
    memcpy(c->cbuff+c->cbx, buffer, space);
 
345
    c->cbx += tocopy; space -= tocopy;
 
346
    c->cbuff[c->cbx] = 0; /* terminate for benefit of strstr */
 
347
    s = strstr(c->cbuff, "\r\n\r\n");
 
348
    /* this next line is so that we talk to NCSA 1.5 which blatantly breaks
 
349
       the http specifaction */
 
350
    if(!s) { s = strstr(c->cbuff,"\n\n"); l=2; }
 
351
 
 
352
    if(!s) {
 
353
       /* read rest next time */
 
354
      if(space)
 
355
        return;
 
356
      else {
 
357
        /* header is in invalid or too big - close connection */
 
358
        close(c->fd);
 
359
        if(bad++>10) {
 
360
          printf("\nTest aborted after 10 failures\n\n");
 
361
          exit(1);
 
362
        }
 
363
        FD_CLR(c->fd, &writebits);
 
364
        start_connect(c);
 
365
      }
 
366
    }
 
367
    else {
 
368
      /* have full header */
 
369
      if(!good) {
 
370
        /* this is first time, extract some interesting info */
 
371
        char *p, *q;
 
372
        p = strstr(c->cbuff, "Server:");
 
373
        q = server_name;
 
374
        if(p) { p+=8; while(*p>32) *q++ = *p++; }
 
375
        *q = 0;
 
376
      }
 
377
 
 
378
      c->gotheader = 1;
 
379
      *s = 0; /* terminate at end of header */
 
380
      if(keepalive &&
 
381
         (strstr(c->cbuff, "Keep-Alive")
 
382
          || strstr(c->cbuff, "keep-alive")))  /* for benefit of MSIIS */
 
383
        {
 
384
        char *cl;
 
385
        cl = strstr(c->cbuff, "Content-Length:");
 
386
        /* for cacky servers like NCSA which break the spec and send a
 
387
           lower case 'l' */
 
388
        if(!cl) cl = strstr(c->cbuff, "Content-length:");
 
389
        if(cl) {
 
390
          c->keepalive=1;
 
391
          c->length = atoi(cl+16);
 
392
        }
 
393
      }
 
394
      c->bread += c->cbx - (s+l-c->cbuff) + r-tocopy;
 
395
      totalbread += c->bread;
 
396
    }
 
397
  }
 
398
  else {
 
399
    /* outside header, everything we have read is entity body */
 
400
    c->bread += r;
 
401
    totalbread += r;
 
402
  }
 
403
 
 
404
  if(c->keepalive && (c->bread >= c->length)) {
 
405
    /* finished a keep-alive connection */
 
406
    good++; doneka++;
 
407
    /* save out time */
 
408
    if(good==1) {
 
409
      /* first time here */
 
410
      doclen = c->bread;
 
411
    } else if(c->bread!=doclen) { bad++; err_length++; }
 
412
    if(done < requests) {
 
413
      struct data s;
 
414
      gettimeofday(&c->done,0);
 
415
      s.read = c->read;
 
416
      s.ctime = timedif(c->connect, c->start);
 
417
      s.time = timedif(c->done, c->start);
 
418
      stats[done++] = s;
 
419
    }
 
420
    c->keepalive = 0; c->length = 0; c->gotheader=0; c->cbx = 0;
 
421
    c->read = c->bread = 0;
 
422
    write_request(c);
 
423
    c->start = c->connect; /* zero connect time with keep-alive */
 
424
  }
 
425
}
 
426
 
 
427
/* --------------------------------------------------------- */
 
428
 
 
429
/* run the tests */
 
430
 
 
431
int test()
 
432
{
 
433
  struct timeval timeout, now;
 
434
  fd_set sel_read, sel_except, sel_write;
 
435
  int i;
 
436
 
 
437
  {
 
438
    /* get server information */
 
439
    struct hostent *he;
 
440
    he = gethostbyname(machine);
 
441
    if (!he) err("gethostbyname");
 
442
    server.sin_family      = he->h_addrtype;
 
443
    server.sin_port        = htons(port);
 
444
    server.sin_addr.s_addr = ((unsigned long *)(he->h_addr_list[0]))[0];
 
445
  }
 
446
 
 
447
  con = malloc(concurrency*sizeof(struct connection));
 
448
  memset(con,0,concurrency*sizeof(struct connection));
 
449
 
 
450
  stats = malloc(requests * sizeof(struct data));
 
451
 
 
452
  FD_ZERO(&readbits);
 
453
  FD_ZERO(&writebits);
 
454
 
 
455
  /* setup request */
 
456
  sprintf(request,"GET %s HTTP/1.0\r\nUser-Agent: ZeusBench/1.0\r\n"
 
457
          "%sHost: %s\r\nAccept: */*\r\n\r\n", file,
 
458
          keepalive?"Connection: Keep-Alive\r\n":"", machine );
 
459
 
 
460
  reqlen = strlen(request);
 
461
 
 
462
  /* ok - lets start */
 
463
  gettimeofday(&start,0);
 
464
 
 
465
  /* initialise lots of requests */
 
466
  for(i=0; i<concurrency; i++) start_connect(&con[i]);
 
467
 
 
468
  while(done<requests) {
 
469
    int n;
 
470
    /* setup bit arrays */
 
471
    memcpy(&sel_except, &readbits, sizeof(readbits));
 
472
    memcpy(&sel_read, &readbits, sizeof(readbits));
 
473
    memcpy(&sel_write, &writebits, sizeof(readbits));
 
474
 
 
475
    /* check for time limit expiry */
 
476
    gettimeofday(&now,0);
 
477
    if(tlimit && timedif(now,start) > (tlimit*1000)) {
 
478
      requests=done;   /* so stats are correct */
 
479
      output_results();
 
480
    }
 
481
 
 
482
    /* Timeout of 30 seconds. */
 
483
    timeout.tv_sec=30; timeout.tv_usec=0;
 
484
    n=select(256, &sel_read, &sel_write, &sel_except, &timeout);
 
485
    if(!n) {
 
486
      printf("\nServer timed out\n\n");
 
487
      exit(1);
 
488
    }
 
489
    if(n<1) err("select");
 
490
 
 
491
    for(i=0; i<concurrency; i++) {
 
492
      int s = con[i].fd;
 
493
      if(FD_ISSET(s, &sel_except)) {
 
494
        bad++;
 
495
        err_except++;
 
496
        start_connect(&con[i]);
 
497
        continue;
 
498
      }
 
499
      if(FD_ISSET(s, &sel_read)) read_connection(&con[i]);
 
500
      if(FD_ISSET(s, &sel_write)) write_request(&con[i]);
 
501
    }
 
502
    if(done>=requests) output_results();
 
503
  }
 
504
  return 0;
 
505
}
 
506
 
 
507
/* ------------------------------------------------------- */
 
508
 
 
509
/* display usage information */
 
510
 
 
511
void usage(char *progname) {
 
512
  printf("\nZeusBench v1.0\n\n");
 
513
  printf("Usage: %s <machine> <file> [-k] [-n requests | -t timelimit (sec)]"
 
514
         "\n\t\t[-c concurrency] [-p port] \n",progname);
 
515
  printf("Filename should start with a '/' e.g. /index.html\n\n");
 
516
  exit(EINVAL);
 
517
}
 
518
 
 
519
/* ------------------------------------------------------- */
 
520
 
 
521
/* sort out command-line args and call test */
 
522
 
 
523
int main(int argc, char **argv) {
 
524
  int c;
 
525
  if (argc < 3) usage(argv[0]);
 
526
 
 
527
  machine = argv[1];
 
528
  file = argv[2];
 
529
  optind = 3;
 
530
  while ((c = getopt(argc,argv,"p:n:c:d:t:d:k"))>0) {
 
531
    switch(c) {
 
532
    case 'd':
 
533
      break;
 
534
    case 'n':
 
535
      requests = atoi(optarg);
 
536
      if(!requests) {
 
537
        printf("Invalid number of requests\n");
 
538
        exit(1);
 
539
      }
 
540
      break;
 
541
    case 'k':
 
542
      keepalive=1;
 
543
      break;
 
544
    case 'c':
 
545
      concurrency = atoi(optarg);
 
546
      break;
 
547
    case 'p':
 
548
      port = atoi(optarg);
 
549
      break;
 
550
    case 't':
 
551
      tlimit = atoi(optarg);
 
552
      requests = MAX_REQUESTS;  /* need to size data array on something */
 
553
      break;
 
554
    default:
 
555
      usage(argv[0]);
 
556
      break;
 
557
    }
 
558
  }
 
559
  test();
 
560
  return 0;
 
561
}
 
562
 
 
563
 
 
564
 
 
565
 
 
566
 
 
567