#ifdef SUNOS4
extern char *optarg;
extern int optind, opterr, optopt;
#endif
#define BSD_COMP
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <string.h>
#define MAX_REQUESTS 50000
#define STATE_UNCONNECTED 0
#define STATE_CONNECTING 1
#define STATE_READ 2
#define CBUFFSIZE 512
struct connection
{
int fd;
int state;
int read;
int bread;
int length;
char cbuff[CBUFFSIZE];
int cbx;
int keepalive;
int gotheader;
struct timeval start, connect, done;
};
struct data
{
int read;
int ctime;
int time;
};
#define min(a,b) ((a)<(b))?(a):(b)
#define max(a,b) ((a)>(b))?(a):(b)
int requests = 1;
int concurrency = 1;
int tlimit = 0;
int keepalive = 0;
char *machine;
char *file;
char server_name[80];
int port = 80;
int doclen = 0;
int totalread = 0;
int totalbread = 0;
int done=0;
int doneka=0;
int good=0, bad=0;
int err_length = 0, err_conn = 0, err_except = 0;
struct timeval start, endtime;
char request[512];
int reqlen;
char buffer[4096];
struct connection *con;
struct data *stats;
fd_set readbits, writebits;
struct sockaddr_in server;
static void err(char *s)
{
perror(s);
exit(errno);
}
void write_request(struct connection *c)
{
gettimeofday(&c->connect,0);
write(c->fd,request, reqlen);
c->state = STATE_READ;
FD_SET(c->fd, &readbits);
FD_CLR(c->fd, &writebits);
}
void nonblock(int fd)
{
int i=1;
ioctl(fd, FIONBIO, &i);
}
int timedif(struct timeval a, struct timeval b)
{
register int us,s;
us = a.tv_usec - b.tv_usec;
us /= 1000;
s = a.tv_sec - b.tv_sec;
s *= 1000;
return s+us;
}
void output_results()
{
int timetaken;
gettimeofday(&endtime,0);
timetaken = timedif(endtime, start);
printf("\n---\n");
printf("Server: %s\n", server_name);
printf("Document Length: %d\n", doclen);
printf("Concurency Level: %d\n", concurrency);
printf("Time taken for tests: %d.%03d seconds\n",
timetaken/1000, timetaken%1000);
printf("Complete requests: %d\n", done);
printf("Failed requests: %d\n", bad);
if(bad) printf(" (Connect: %d, Length: %d, Exceptions: %d)\n",
err_conn, err_length, err_except);
if(keepalive) printf("Keep-Alive requests: %d\n", doneka);
printf("Bytes transferred: %d\n", totalread);
printf("HTML transferred: %d\n", totalbread);
if(timetaken) {
printf("Requests per seconds: %.2f\n", 1000*(float)(done)/timetaken);
printf("Transfer rate: %.2f kb/s\n",
(float)(totalread)/timetaken);
}
{
int i;
int totalcon=0, total=0;
int mincon=9999999, mintot=999999;
int maxcon=0, maxtot=0;
for(i=0; i<requests; i++) {
struct data s = stats[i];
mincon = min(mincon, s.ctime);
mintot = min(mintot, s.time);
maxcon = max(maxcon, s.ctime);
maxtot = max(maxtot, s.time);
totalcon += s.ctime;
total += s.time;
}
printf("\nConnnection Times (ms)\n");
printf(" min avg max\n");
printf("Connect: %5d %5d %5d\n",mincon, totalcon/requests, maxcon );
printf("Total: %5d %5d %5d\n", mintot, total/requests, maxtot);
printf("---\n\n");
}
exit(0);
}
void start_connect(struct connection *c)
{
c->read = 0;
c->bread = 0;
c->keepalive = 0;
c->cbx = 0;
c->gotheader = 0;
c->fd = socket(AF_INET, SOCK_STREAM, 0);
if(c->fd<0) err("socket");
nonblock(c->fd);
gettimeofday(&c->start,0);
if(connect(c->fd, (struct sockaddr *) &server, sizeof(server))<0) {
if(errno==EINPROGRESS) {
c->state = STATE_CONNECTING;
FD_SET(c->fd, &writebits);
return;
}
else {
close(c->fd);
err_conn++;
if(bad++>10) {
printf("\nTest aborted after 10 failures\n\n");
exit(1);
}
start_connect(c);
}
}
write_request(c);
}
void close_connection(struct connection *c)
{
if(c->read == 0 && c->keepalive) {
good--;
}
else {
if(good==1) {
doclen = c->bread;
} else if (c->bread!=doclen) {
bad++;
err_length++;
}
if(done < requests) {
struct data s;
gettimeofday(&c->done,0);
s.read = c->read;
s.ctime = timedif(c->connect, c->start);
s.time = timedif(c->done, c->start);
stats[done++] = s;
}
}
close(c->fd);
FD_CLR(c->fd, &readbits);
FD_CLR(c->fd, &writebits);
start_connect(c);
return;
}
void read_connection(struct connection *c)
{
int r;
r=read(c->fd,buffer,sizeof(buffer));
if(r==0 || (r<0 && errno!=EAGAIN)) {
good++;
close_connection(c);
return;
}
if(r<0 && errno==EAGAIN) return;
c->read += r;
totalread += r;
if(!c->gotheader) {
char *s;
int l=4;
int space = CBUFFSIZE - c->cbx - 1;
int tocopy = (space<r)?space:r;
memcpy(c->cbuff+c->cbx, buffer, space);
c->cbx += tocopy; space -= tocopy;
c->cbuff[c->cbx] = 0;
s = strstr(c->cbuff, "\r\n\r\n");
if(!s) { s = strstr(c->cbuff,"\n\n"); l=2; }
if(!s) {
if(space)
return;
else {
close(c->fd);
if(bad++>10) {
printf("\nTest aborted after 10 failures\n\n");
exit(1);
}
FD_CLR(c->fd, &writebits);
start_connect(c);
}
}
else {
if(!good) {
char *p, *q;
p = strstr(c->cbuff, "Server:");
q = server_name;
if(p) { p+=8; while(*p>32) *q++ = *p++; }
*q = 0;
}
c->gotheader = 1;
*s = 0;
if(keepalive &&
(strstr(c->cbuff, "Keep-Alive")
|| strstr(c->cbuff, "keep-alive")))
{
char *cl;
cl = strstr(c->cbuff, "Content-Length:");
if(!cl) cl = strstr(c->cbuff, "Content-length:");
if(cl) {
c->keepalive=1;
c->length = atoi(cl+16);
}
}
c->bread += c->cbx - (s+l-c->cbuff) + r-tocopy;
totalbread += c->bread;
}
}
else {
c->bread += r;
totalbread += r;
}
if(c->keepalive && (c->bread >= c->length)) {
good++; doneka++;
if(good==1) {
doclen = c->bread;
} else if(c->bread!=doclen) { bad++; err_length++; }
if(done < requests) {
struct data s;
gettimeofday(&c->done,0);
s.read = c->read;
s.ctime = timedif(c->connect, c->start);
s.time = timedif(c->done, c->start);
stats[done++] = s;
}
c->keepalive = 0; c->length = 0; c->gotheader=0; c->cbx = 0;
c->read = c->bread = 0;
write_request(c);
c->start = c->connect;
}
}
int test()
{
struct timeval timeout, now;
fd_set sel_read, sel_except, sel_write;
int i;
{
struct hostent *he;
he = gethostbyname(machine);
if (!he) err("gethostbyname");
server.sin_family = he->h_addrtype;
server.sin_port = htons(port);
server.sin_addr.s_addr = ((unsigned long *)(he->h_addr_list[0]))[0];
}
con = malloc(concurrency*sizeof(struct connection));
memset(con,0,concurrency*sizeof(struct connection));
stats = malloc(requests * sizeof(struct data));
FD_ZERO(&readbits);
FD_ZERO(&writebits);
sprintf(request,"GET %s HTTP/1.0\r\nUser-Agent: ZeusBench/1.0\r\n"
"%sHost: %s\r\nAccept: */*\r\n\r\n", file,
keepalive?"Connection: Keep-Alive\r\n":"", machine );
reqlen = strlen(request);
gettimeofday(&start,0);
for(i=0; i<concurrency; i++) start_connect(&con[i]);
while(done<requests) {
int n;
memcpy(&sel_except, &readbits, sizeof(readbits));
memcpy(&sel_read, &readbits, sizeof(readbits));
memcpy(&sel_write, &writebits, sizeof(readbits));
gettimeofday(&now,0);
if(tlimit && timedif(now,start) > (tlimit*1000)) {
requests=done;
output_results();
}
timeout.tv_sec=30; timeout.tv_usec=0;
n=select(256, &sel_read, &sel_write, &sel_except, &timeout);
if(!n) {
printf("\nServer timed out\n\n");
exit(1);
}
if(n<1) err("select");
for(i=0; i<concurrency; i++) {
int s = con[i].fd;
if(FD_ISSET(s, &sel_except)) {
bad++;
err_except++;
start_connect(&con[i]);
continue;
}
if(FD_ISSET(s, &sel_read)) read_connection(&con[i]);
if(FD_ISSET(s, &sel_write)) write_request(&con[i]);
}
if(done>=requests) output_results();
}
return 0;
}
void usage(char *progname) {
printf("\nZeusBench v1.0\n\n");
printf("Usage: %s <machine> <file> [-k] [-n requests | -t timelimit (sec)]"
"\n\t\t[-c concurrency] [-p port] \n",progname);
printf("Filename should start with a '/' e.g. /index.html\n\n");
exit(EINVAL);
}
int main(int argc, char **argv) {
int c;
if (argc < 3) usage(argv[0]);
machine = argv[1];
file = argv[2];
optind = 3;
while ((c = getopt(argc,argv,"p:n:c:d:t:d:k"))>0) {
switch(c) {
case 'd':
break;
case 'n':
requests = atoi(optarg);
if(!requests) {
printf("Invalid number of requests\n");
exit(1);
}
break;
case 'k':
keepalive=1;
break;
case 'c':
concurrency = atoi(optarg);
break;
case 'p':
port = atoi(optarg);
break;
case 't':
tlimit = atoi(optarg);
requests = MAX_REQUESTS;
break;
default:
usage(argv[0]);
break;
}
}
test();
return 0;
}