
#include "config.h"

#if defined(_SQUID_LINUX_)
#else
#define FD_SETSIZE 1024
#endif

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#if defined(_SQUID_SUNOS_)
#elif defined(_SQUID_LINUX_)
#else
#include <sys/select.h>
#endif
#include <signal.h>
#include <time.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>

#define PROXY_PORT 3128
#define PROXY_ADDR "127.0.0.1"
#define MAX_FDS 1024
#define READ_BUF_SZ 4096

static int proxy_port = PROXY_PORT;
static char *proxy_addr = PROXY_ADDR;
static char *progname;
static int reqpersec;
static int nrequests;
static int opt_ims = 0;
static int max_connections = 64;

#ifdef _SQUID_SUNOS_
extern char *optarg;
extern int optind, opterr;
extern double drand48();
#endif /* _SQUID_SUNOS_ */

typedef void (CB) (int, void *);

struct _f {
    CB *cb;
    void *data;
};

struct _f FD[MAX_FDS];
int nfds = 0;
int maxfd = 0;


char *
mkrfc850(t)
    time_t *t;
{
    static char buf[128];
    struct tm *gmt = gmtime(t);
    buf[0] = '\0';
    (void) strftime(buf, 127, "%A, %d-%b-%y %H:%M:%S GMT", gmt);
    return buf;
}

void
fd_close(int fd)
{
    close(fd);
    FD[fd].cb = NULL;
    FD[fd].data = NULL;
    nfds--;
    if (fd == maxfd) {
	while (FD[fd].cb == NULL)
		fd--;
	maxfd = fd;
    }
}

void
fd_open(int fd, CB *cb, void *data)
{
    FD[fd].cb = cb;
    FD[fd].data = data;
    if (fd > maxfd)
	maxfd = fd;
    nfds++;
}

void
sig_intr(int sig)
{
	fd_close(0);
	printf ("\rWaiting for open connections to finish...\n");
	signal(sig, SIG_DFL);
}

void 
read_reply(int fd, void *data)
{
	static char buf[READ_BUF_SZ];
	if (read(fd, buf, READ_BUF_SZ) <= 0) {
		fd_close(fd);
		reqpersec++;
		nrequests++;
	}
}

int
request(url)
    char *url;
{
    int s;
    char buf[4096];
    int len;
    time_t w;
    struct sockaddr_in S;
    if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
	perror("socket");
	return -1;
    }
    memset(&S, '\0', sizeof(struct sockaddr_in));
    S.sin_family = AF_INET;
    S.sin_port = htons(proxy_port);
    S.sin_addr.s_addr = inet_addr(proxy_addr);
    if (connect(s, (struct sockaddr *) & S, sizeof(S)) < 0) {
	close(s);
	perror("connect");
	return -1;
    }
    buf[0] = '\0';
    strcat(buf, "GET ");
    strcat(buf, url);
    strcat(buf, " HTTP/1.0\r\n");
    strcat(buf, "Accept: */*\r\n");
    if (opt_ims && (lrand48() & 0x03) == 0) {
	w = time(NULL) - (lrand48() & 0x3FFFF);
	strcat(buf, "If-Modified-Since: ");
	strcat(buf, mkrfc850(&w));
	strcat(buf, "\r\n");
    }
    strcat(buf, "\r\n");
    len = strlen(buf);
    if (write(s, buf, len) < 0) {
	close(s);
	perror("write");
	return -1;
    }
/*
    if (fcntl(s, F_SETFL, O_NDELAY) < 0)
	perror("fcntl O_NDELAY");
*/
    return s;
}

void
read_url(int fd, void *junk)
{
    static char buf[1024];
    char *t;
    int s;
    int n = 10;
    while (--n && nfds < max_connections) {
	if (fgets(buf, 1024, stdin) == NULL) {
	    printf("Done Reading URLS\n");
	    fd_close(0);
	    return;
	}
	if ((t = strchr(buf, '\n')))
	    *t = '\0';
	s = request(buf);
	if (s < 0) {
	    max_connections = nfds - 1;
	    printf("NOTE: max_connections set at %d\n", max_connections);
	    break;
	}
	fd_open(s, read_reply, NULL);
    }
}

void
usage(void)
{
	fprintf(stderr, "usage: %s: -p port -h host -n max\n", progname);
}

int 
main(argc, argv)
    int argc;
    char *argv[];
{
    int i;
    int c;
    int dt;
    fd_set R;
    struct timeval start;
    struct timeval now;
    struct timeval last;
    struct timeval to;
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);
    progname = strdup(argv[0]);
    gettimeofday(&start, NULL);
    last = start;
    while ((c = getopt(argc, argv, "p:h:n:i")) != -1) {
        switch (c) {
	case 'p':
		proxy_port = atoi(optarg);
		break;
	case 'h':
		proxy_addr = strdup(optarg);
		break;
	case 'n':
		max_connections = atoi(optarg);
		break;
	case 'i':
		opt_ims = 1;
		break;
	default:
		usage();
		return 1;
        }
    }
    fd_open(0, read_url, NULL);
    signal(SIGINT, sig_intr);
    while (nfds) {
	FD_ZERO(&R);
	to.tv_sec = 0;
	to.tv_usec = 100000;
	if (nfds < max_connections && FD[0].cb)
	    FD_SET(0, &R);
	for (i = 1; i <= maxfd; i++) {
	    if (FD[i].cb == NULL)
		continue;
	    FD_SET(i, &R);
	}
	if (select(maxfd + 1, &R, NULL, NULL, &to) < 0) {
	    if (errno != EINTR)
	        perror("select");
	    continue;
	}
	for (i = 0; i <= maxfd; i++) {
	    if (!FD_ISSET(i, &R))
		continue;
	    FD[i].cb(i, FD[i].data);
	}
	gettimeofday(&now, NULL);
	if (now.tv_sec > last.tv_sec) {
		last = now;
		dt = (int) (now.tv_sec - start.tv_sec);
		printf ("T+ %6d: %9d req (%+4d), %4d conn, %3d/sec avg\n",
			dt,
			nrequests,
			reqpersec,
			nfds,
			(int) (nrequests / dt));
		reqpersec = 0;
	}
    }
    return 0;
}
