Commit c65e6d20 authored by Ryan Bloom's avatar Ryan Bloom
Browse files

I have merged the two ab files together, the one from src/support and

src/lib/apr/test.  This means that the ab program in the support
directory is now portable using APR.  This has only gone through the
barest of testing, and needs to be tested much better.


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@85055 13f79535-47bb-0310-9956-ffa450edef68
parent ad523807
Loading
Loading
Loading
Loading
+638 −696
Original line number Diff line number Diff line
@@ -102,41 +102,28 @@

/*  -------------------------------------------------------------------- */

#if 'A' != 0x41
/* Hmmm... This source code isn't being compiled in ASCII.
 * In order for data that flows over the network to make
 * sense, we need to translate to/from ASCII.
 */
#define NOT_ASCII
#endif

/* affects include files on Solaris */
#define BSD_COMP

/* allow compilation outside an Apache build tree */
#ifdef NO_APACHE_INCLUDES
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/stat.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 ap_select       select
#else				/* (!)NO_APACHE_INCLUDES */
#include "ap_config.h"
#include "ap.h"
#ifdef CHARSET_EBCDIC
#include "ebcdic.h"
#endif
#include <fcntl.h>
#include <sys/time.h>

#ifndef NO_WRITEV
#include <sys/types.h>
#include <sys/uio.h>
#include "apr_network_io.h"
#include "apr_file_io.h"
#include "apr_time.h"
#include "apr_getopt.h"
#ifdef NOT_ASCII
#include "apr_xlate.h"
#endif
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#endif				/* NO_APACHE_INCLUDES */
/* ------------------- DEFINITIONS -------------------------- */

/* maximum number of requests on a time limited test */
@@ -150,7 +137,7 @@
#define CBUFFSIZE       512

struct connection {
    int fd;
    ap_socket_t *aprsock;
    int state;
    int read;        		/* amount of bytes read */
    int bread;        		/* amount of body read */
@@ -160,7 +147,8 @@ struct connection {
    int keepalive;        	/* non-zero if a keep-alive request */
    int gotheader;        	/* non-zero if we have the entire header in
        			 * cbuff */
    struct timeval start, connect, done;
    ap_time_t start, connect, done;
    int socknum;
};

struct data {
@@ -173,6 +161,8 @@ struct data {
#define ap_max(a,b) ((a)>(b))?(a):(b)

/* --------------------- GLOBALS ---------------------------- */
API_VAR_IMPORT char *ap_optarg; /* argument associated with option */
API_VAR_IMPORT int  ap_optind;

int verbosity = 0;        	/* no verbosity by default */
int posting = 0;        	/* GET by default */
@@ -185,13 +175,14 @@ char hostname[1024]; /* host name */
char path[1024];        	/* path name */
char postfile[1024];        	/* name of file containing post data */
char *postdata;        		/* *buffer containing data from postfile */
int postlen = 0;		/* length of data to be POSTed */
ap_ssize_t postlen = 0;        	/* length of data to be POSTed */
char content_type[1024];        /* content type to put in POST header */
char cookie[1024],        	/* optional cookie line */
     auth[1024],        	/* optional (basic/uuencoded)
        			 * authentification */
     hdrs[4096];        	/* optional arbitrary headers */
int port = 80;        		/* port number */
time_t aprtimeout = 30 * AP_USEC_PER_SEC; /* timeout value */

int use_html = 0;        	/* use html in the report */
char *tablestring;
@@ -210,29 +201,22 @@ int good = 0, bad = 0; /* number of good and bad requests */
int err_length = 0, err_conn = 0, err_except = 0;
int err_response = 0;

struct timeval start, endtime;
ap_time_t start, endtime;

/* global request (and its length) */
char request[512];
int reqlen;
ap_ssize_t reqlen;

/* one global throw-away buffer to read stuff into */
char buffer[8192];

struct connection *con;        	/* connection array */
struct data *stats;        	/* date for each request */
ap_pool_t *cntxt;

fd_set readbits, writebits;	/* bits for select */
struct sockaddr_in server;	/* server addr structure */

#ifndef BEOS
#define ab_close(s) close(s)
#define ab_read(a,b,c) read(a,b,c)
#define ab_write(a,b,c) write(a,b,c)
#else
#define ab_close(s) closesocket(s)
#define ab_read(a,b,c) recv(a,b,c,0)
#define ab_write(a,b,c) send(a,b,c,0)
ap_pollfd_t *readbits;
#ifdef NOT_ASCII
ap_xlate_t *fromascii, *toascii;
#endif

/* --------------------------------------------------------- */
@@ -257,61 +241,21 @@ static void err(char *s)

static void write_request(struct connection *c)
{
#ifndef NO_WRITEV
    struct iovec out[2]; int outcnt = 1;
#endif
    gettimeofday(&c->connect, 0);
#ifndef NO_WRITEV
    out[0].iov_base = request;
    out[0].iov_len = reqlen;

    if (posting>0) {
	out[1].iov_base = postdata;
	out[1].iov_len = postlen;
	outcnt = 2;
	totalposted += (reqlen + postlen);
    }
    writev(c->fd,out, outcnt);
#else
    ab_write(c->fd,request,reqlen);
    if (posting>0) {
        ab_write(c->fd,postdata,postlen);
    ap_ssize_t len = reqlen;
    c->connect = ap_now();
    ap_setsocketopt(c->aprsock, APR_SO_TIMEOUT, 30 * AP_USEC_PER_SEC);
    if (ap_send(c->aprsock, request, &reqlen) != APR_SUCCESS ||
        reqlen != len) {
        printf("Send request failed!\n");
    }
    if (posting) {
        ap_send(c->aprsock, postdata, &postlen);
        totalposted += (reqlen + postlen);
    }
#endif

    c->state = STATE_READ;
    FD_SET(c->fd, &readbits);
    FD_CLR(c->fd, &writebits);
}

/* --------------------------------------------------------- */

/* make an fd non blocking */

static void nonblock(int fd)
{
    int i = 1;
#ifdef BEOS
    setsockopt(fd, SOL_SOCKET, SO_NONBLOCK, &i, sizeof(i));
#else
    ioctl(fd, FIONBIO, &i);
#endif
}

/* --------------------------------------------------------- */

/* returns the time in ms between two timevals */

static 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;
    ap_add_poll_socket(readbits, c->aprsock, APR_POLLIN);
    ap_remove_poll_socket(readbits, c->aprsock);
}

/* --------------------------------------------------------- */
@@ -322,8 +266,8 @@ static void output_results(void)
{
    int timetaken;

    gettimeofday(&endtime, 0);
    timetaken = timedif(endtime, start);
    endtime = ap_now();
    timetaken = (endtime - start) / 1000;

    printf("\r                                                                           \r");
    printf("Server Software:        %s\n", servername);
@@ -346,7 +290,7 @@ static void output_results(void)
    if (keepalive)
        printf("Keep-Alive requests:    %d\n", doneka);
    printf("Total transferred:      %d bytes\n", totalread);
    if (posting>0)
    if (posting)
        printf("Total POSTed:           %d\n", totalposted);
    printf("HTML transferred:       %d bytes\n", totalbread);

@@ -399,8 +343,8 @@ static void output_html_results(void)
{
    int timetaken;

    gettimeofday(&endtime, 0);
    timetaken = timedif(endtime, start);
    endtime = ap_now();
    timetaken = (endtime - start) / 1000;

    printf("\n\n<table %s>\n", tablestring);
    printf("<tr %s><th colspan=2 %s>Server Software:</th>"
@@ -527,21 +471,20 @@ static void start_connect(struct connection * c)
    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) {
    if (ap_create_tcp_socket(&c->aprsock, cntxt) != APR_SUCCESS) {
        err("Socket:");
    }
    c->start = ap_now();
    if (ap_connect(c->aprsock, hostname) != APR_SUCCESS) {
        if (errno == APR_EINPROGRESS) {
            c->state = STATE_CONNECTING;
	    FD_SET(c->fd, &writebits);
            ap_add_poll_socket(readbits, c->aprsock, APR_POLLOUT);
            return;
        }
        else {
	    ab_close(c->fd);
            /* we done't have to close the socket.  If we have an error this
             * bad, ap_connect will destroy it for us.
             */
            err_conn++;
            if (bad++ > 10) {
                err("\nTest aborted after 10 failures\n\n");
@@ -551,8 +494,7 @@ static void start_connect(struct connection * c)
    }

    /* connected first time */
    c->state = STATE_CONNECTING;
    FD_SET(c->fd, &writebits);
    write_request(c);
}

/* --------------------------------------------------------- */
@@ -574,21 +516,19 @@ static void close_connection(struct connection * c)
            bad ++;
            err_length++;
        }

        /* save out time */
        if (done < requests) {
            struct data s;
	    gettimeofday(&c->done, 0);
            c->done = ap_now();
            s.read  = c->read;
	    s.ctime = timedif(c->connect, c->start);
	    s.time = timedif(c->done, c->start);
            s.ctime = (c->connect - c->start) / 1000;
            s.time  = (c->done - c->start) / 1000;
            stats[done++] = s;
        }
    }

    ab_close(c->fd);
    FD_CLR(c->fd, &readbits);
    FD_CLR(c->fd, &writebits);
    ap_remove_poll_socket(readbits, c->aprsock);
    ap_close_socket(c->aprsock);

    /* connect again */
    start_connect(c);
@@ -601,19 +541,21 @@ static void close_connection(struct connection * c)

static void read_connection(struct connection *c)
{
    int r;
    ap_ssize_t r;
    ap_status_t status;
    char *part;
    char respcode[4];        	/* 3 digits and null */

    r = ab_read(c->fd, buffer, sizeof(buffer));

    if (r == 0 || (r < 0 && errno != EAGAIN)) {
    r = sizeof(buffer);
    ap_setsocketopt(c->aprsock, APR_SO_TIMEOUT, aprtimeout);
    status = ap_recv(c->aprsock, buffer, &r);
    if (r == 0 || (status != 0 && ap_canonical_error(status) != APR_EAGAIN)) {
        good++;
        close_connection(c);
        return;
    }

    if (r < 0 && errno == EAGAIN)
    if (ap_canonical_error(status) == EAGAIN)
        return;

    c->read += r;
@@ -622,14 +564,21 @@ static void read_connection(struct connection * c)
    if (!c->gotheader) {
        char *s;
        int l = 4;
	int space = CBUFFSIZE - c->cbx - 1;	/* -1 to allow for 0
						 * terminator */
        int space = CBUFFSIZE - c->cbx - 1;  /* -1 to allow for 0 terminator */
        int tocopy = (space < r) ? space : r;
#ifndef CHARSET_EBCDIC
	memcpy(c->cbuff + c->cbx, buffer, tocopy);
#else				/* CHARSET_EBCDIC */
	ascii2ebcdic(c->cbuff + c->cbx, buffer, tocopy);
#endif				/* CHARSET_EBCDIC */
#ifdef NOT_ASCII
        ap_size_t inbytes_left = space, outbytes_left = space;

        status = ap_xlate_conv_buffer(fromascii, buffer, &inbytes_left,
                                      c->cbuff + c->cbx, &outbytes_left);
        if (status || inbytes_left || outbytes_left) {
            fprintf(stderr, "only simple translation is supported (%d/%u/%u)\n",
                    status, inbytes_left, outbytes_left);
            exit(1);
        }
#else
        memcpy(c->cbuff + c->cbx, buffer, space);
#endif /*NOT_ASCII */
        c->cbx += tocopy;
        space -= tocopy;
        c->cbuff[c->cbx] = 0;  /* terminate for benefit of strstr */
@@ -637,9 +586,8 @@ static void read_connection(struct connection * c)
            printf("LOG: header received:\n%s\n", c->cbuff);
        }
        s = strstr(c->cbuff, "\r\n\r\n");
	/*
	 * this next line is so that we talk to NCSA 1.5 which blatantly
	 * breaks the http specification
            /* this next line is so that we talk to NCSA 1.5 which blatantly 
             * breaks the http specifaction 
             */
        if (!s) {
            s = strstr(c->cbuff, "\n\n");
@@ -648,15 +596,16 @@ static void read_connection(struct connection * c)

        if (!s) {
            /* read rest next time */
	    if (space)
            if (space) {
               return;
            }
            else {
                /* header is in invalid or too big - close connection */
		ab_close(c->fd);
                ap_remove_poll_socket(readbits, c->aprsock);
                ap_close_socket(c->aprsock);
                if (bad++ > 10) {
                    err("\nTest aborted after 10 failures\n\n");
                }
		FD_CLR(c->fd, &writebits);
                start_connect(c);
            }
        }
@@ -675,12 +624,10 @@ static void read_connection(struct connection * c)
               *q = 0;
            }

	    /*
	     * XXX: this parsing isn't even remotely HTTP compliant... but in
	     * the interest of speed it doesn't totally have to be, it just
	     * needs to be extended to handle whatever servers folks want to
	     * test against. -djg
	     */
            /* XXX: this parsing isn't even remotely HTTP compliant...
             * but in the interest of speed it doesn't totally have to be,
             * it just needs to be extended to handle whatever servers
             * folks want to test against. -djg */

            /* check response code */
            part = strstr(c->cbuff, "HTTP");   /* really HTTP/1.x_ */
@@ -720,8 +667,7 @@ static void read_connection(struct connection * c)
        totalbread += r;
    }

    /* cater for the case where we're using keepalives and doing HEAD requests */
    if (c->keepalive && ((c->bread >= c->length) || (posting < 0))) {
    if (c->keepalive && (c->bread >= c->length)) {
        /* finished a keep-alive connection */
        good++;
        doneka++;
@@ -736,10 +682,10 @@ static void read_connection(struct connection * c)
        }
        if (done < requests) {
            struct data s;
	    gettimeofday(&c->done, 0);
           c->done = ap_now();
            s.read = c->read;
	    s.ctime = timedif(c->connect, c->start);
	    s.time = timedif(c->done, c->start);
           s.ctime = (c->connect - c->start) / 1000;
           s.time = (c->done - c->start) / 1000;
            stats[done++] = s;
        }
        c->keepalive = 0;
@@ -758,36 +704,30 @@ static void read_connection(struct connection * c)

static void test(void)
{
    struct timeval timeout, now;
    fd_set sel_read, sel_except, sel_write;
    ap_time_t now;
    ap_interval_time_t timeout;
    ap_int16_t rv;
    int i;
#ifdef NOT_ASCII
    ap_status_t status;
    ap_size_t inbytes_left, outbytes_left;
#endif

    if (!use_html) {
        printf("Benchmarking %s (be patient)...", hostname);
        fflush(stdout);
    }

    {
	/* get server information */
	struct hostent *he;
	he = gethostbyname(hostname);
	if (!he)
	    err("bad hostname");
	server.sin_family = he->h_addrtype;
	server.sin_port = htons(port);
	server.sin_addr.s_addr = ((unsigned long *) (he->h_addr_list[0]))[0];
    }
    now = ap_now();

    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);
    ap_setup_poll(&readbits, concurrency, cntxt);

    /* setup request */
    if (posting <= 0) {
    if (!posting) {
        sprintf(request, "%s %s HTTP/1.0\r\n"
        	"User-Agent: ApacheBench/%s\r\n"
        	"%s" "%s" "%s"
@@ -823,34 +763,42 @@ static void test(void)

    reqlen = strlen(request);

#ifdef CHARSET_EBCDIC
    ebcdic2ascii(request, request, reqlen);
#endif				/* CHARSET_EBCDIC */
#ifdef NOT_ASCII
    inbytes_left = outbytes_left = reqlen;
    status = ap_xlate_conv_buffer(toascii, request, &inbytes_left,
                                  request, &outbytes_left);
    if (status || inbytes_left || outbytes_left) {
        fprintf(stderr, "only simple translation is supported (%d/%u/%u)\n",
                status, inbytes_left, outbytes_left);
        exit(1);
    }
#endif /*NOT_ASCII*/

    /* ok - lets start */
    gettimeofday(&start, 0);
    start = ap_now();

    /* initialise lots of requests */
    for (i = 0; i < concurrency; i++)
    for (i = 0; i < concurrency; i++) {
        con[i].socknum = i;
        start_connect(&con[i]);
    }

    while (done < requests) {
	int n;
	/* setup bit arrays */
	memcpy(&sel_except, &readbits, sizeof(readbits));
	memcpy(&sel_read, &readbits, sizeof(readbits));
	memcpy(&sel_write, &writebits, sizeof(readbits));
        ap_int32_t n;
        ap_int32_t timed;

        /* check for time limit expiry */
	gettimeofday(&now, 0);
	if (tlimit && timedif(now, start) > (tlimit * 1000)) {
        now = ap_now();
        timed = (now - start) / AP_USEC_PER_SEC;
        if (tlimit && timed > (tlimit * 1000)) {
            requests = done;   /* so stats are correct */
        }

        /* Timeout of 30 seconds. */
	timeout.tv_sec = 30;
	timeout.tv_usec = 0;
	n = ap_select(FD_SETSIZE, &sel_read, &sel_write, &sel_except, &timeout);
        timeout = 30 * AP_USEC_PER_SEC;

        n = concurrency;
        ap_poll(readbits, &n, timeout);

        if (!n) {
            err("\nServer timed out\n\n");
        }
@@ -858,16 +806,21 @@ static void test(void)
            err("select");

        for (i = 0; i < concurrency; i++) {
	    int s = con[i].fd;
	    if (FD_ISSET(s, &sel_except)) {
            ap_get_revents(&rv, con[i].aprsock, readbits);

            /* Note: APR_POLLHUP is set after FIN is received on some
             * systems, so treat that like APR_POLLIN so that we try
             * to read again.
             */
            if ((rv & APR_POLLERR) || (rv & APR_POLLNVAL)) {
               bad++;
               err_except++;
               start_connect(&con[i]);
               continue;
            }
	    if (FD_ISSET(s, &sel_read))
            if ((rv & APR_POLLIN) || (rv & APR_POLLPRI) || (rv & APR_POLLHUP))
               read_connection(&con[i]);
	    if (FD_ISSET(s, &sel_write))
            if (rv & APR_POLLOUT)
               write_request(&con[i]);
        }
    }
@@ -883,14 +836,14 @@ static void test(void)
static void copyright(void)
{
    if (!use_html) {
	printf("This is ApacheBench, Version %s\n", VERSION " <$Revision: 1.7 $> apache-2.0");
        printf("This is ApacheBench, Version %s\n", VERSION " <$Revision: 1.8 $> apache-2.0");
        printf("Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/\n");
        printf("Copyright (c) 1998-2000 The Apache Software Foundation, http://www.apache.org/\n");
        printf("\n");
    }
    else {
        printf("<p>\n");
	printf(" This is ApacheBench, Version %s <i>&lt;%s&gt;</i> apache-2.0<br>\n", VERSION, "$Revision: 1.7 $");
        printf(" This is ApacheBench, Version %s <i>&lt;%s&gt;</i> apache-2.0<br>\n", VERSION, "$Revision: 1.8 $");
        printf(" Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/<br>\n");
        printf(" Copyright (c) 1998-2000 The Apache Software Foundation, http://www.apache.org/<br>\n");
        printf("</p>\n<p>\n");
@@ -934,7 +887,7 @@ static int parse_url(char *url)
{
    char *cp;
    char *h;
    char *p = NULL;
    char *p/* = NULL*/;

    if (strlen(url) > 7 && strncmp(url, "http://", 7) == 0)
        url += 7;
@@ -960,40 +913,42 @@ static int parse_url(char *url)

static int open_postfile(char *pfile)
{
    int postfd, status;
    struct stat postfilestat;
    ap_file_t *postfd = NULL;
    ap_finfo_t finfo;
    ap_fileperms_t mode = APR_OS_DEFAULT;
    ap_ssize_t length;

    if ((postfd = open(pfile, O_RDONLY)) == -1) {
    if (ap_open(&postfd, pfile, APR_READ, mode, cntxt) != APR_SUCCESS) {
        printf("Invalid postfile name (%s)\n", pfile);
        return errno;
    }
    if ((status = fstat(postfd, &postfilestat)) == -1) {
	perror("Can\'t stat postfile\n");
	return status;
    }
    postdata = malloc(postfilestat.st_size);

    ap_getfileinfo(&finfo, postfd);
    postlen = finfo.size;
    postdata = (char *)malloc(postlen);
    if (!postdata) {
        printf("Can\'t alloc postfile buffer\n");
        return ENOMEM;
    }
    if (read(postfd, postdata, postfilestat.st_size) != postfilestat.st_size) {
    length = postlen;
    if (ap_read(postfd, postdata, &length) != APR_SUCCESS &&
        length != postlen) {
        printf("error reading postfilen");
        return EIO;
    }
    postlen = postfilestat.st_size;
    return 0;
}

/* ------------------------------------------------------- */

extern char *optarg;
extern int optind, opterr, optopt;

/* sort out command-line args and call test */
int main(int argc, char **argv)
{
    int c, r, l;
    char tmp[1024];
#ifdef NOT_ASCII
    ap_status_t status;
#endif

    /* table defaults  */
    tablestring = "";
@@ -1002,11 +957,30 @@ int main(int argc, char **argv)
    cookie[0] = '\0';
    auth[0] = '\0';
    hdrs[0] = '\0';
    optind = 1;
    while ((c = getopt(argc, argv, "n:c:t:T:p:v:kVhwix:y:z:C:H:P:A:")) > 0) {

    ap_initialize();
    atexit(ap_terminate);
    ap_create_pool(&cntxt, NULL);

#ifdef NOT_ASCII
    status = ap_xlate_open(&toascii, "ISO8859-1", APR_DEFAULT_CHARSET, cntxt);
    if (status) {
        fprintf(stderr, "ap_xlate_open(to ASCII)->%d\n", status);
        exit(1);
    }
    status = ap_xlate_open(&fromascii, APR_DEFAULT_CHARSET, "ISO8859-1", cntxt)

    if (status) {
        fprintf(stderr, "ap_xlate_open(from ASCII)->%d\n", status);
        exit(1);
    }
#endif

    ap_optind = 1;
    while (ap_getopt(argc, argv, "n:c:t:T:p:v:kVhwx:y:z:", &c, cntxt) == APR_SUCCESS) {
        switch (c) {
        case 'n':
	    requests = atoi(optarg);
            requests = atoi(ap_optarg);
            if (!requests) {
               err("Invalid number of requests\n");
            }
@@ -1015,19 +989,17 @@ int main(int argc, char **argv)
            keepalive = 1;
            break;
        case 'c':
	    concurrency = atoi(optarg);
            concurrency = atoi(ap_optarg);
            break;
        case 'i':
            if (posting == 1)
                err("Cannot mix POST and HEAD");

            posting = -1;
	    break;
        case 'p':
            if (posting != 0)
                err("Cannot mix POST and HEAD");

	    if (0 == (r = open_postfile(optarg))) {
            if (0 == (r = open_postfile(ap_optarg))) {
               posting = 1;
            }
            else if (postdata) {
@@ -1035,28 +1007,27 @@ int main(int argc, char **argv)
            }
            break;
        case 'v':
	    verbosity = atoi(optarg);
            verbosity = atoi(ap_optarg);
            break;
        case 't':
	    tlimit = atoi(optarg);
	    requests = MAX_REQUESTS;	/* need to size data array on
					 * something */
            tlimit = atoi(ap_optarg);
            requests = MAX_REQUESTS;  /* need to size data array on something */
            break;
        case 'T':
	    strcpy(content_type, optarg);
            strcpy(content_type, ap_optarg);
            break;
        case 'C':
            strncat(cookie, "Cookie: ", sizeof(cookie));
	    strncat(cookie, optarg, sizeof(cookie));
            strncat(cookie, ap_optarg, sizeof(cookie));
            strncat(cookie, "\r\n", sizeof(cookie));
            break;
        case 'A':
	    /* assume username passwd already to be in colon separated form. Ready
	     * to be uu-encoded.
            /* assume username passwd already to be in colon separated form. 
             * Ready to be uu-encoded.
             */
	    while(isspace(*optarg))
		optarg++;
	    l=ap_base64encode(tmp,optarg,strlen(optarg));
            while(isspace(*ap_optarg))
                ap_optarg++;
            l=ap_base64encode(tmp, ap_optarg, strlen(ap_optarg));
            tmp[l]='\0';
 
            strncat(auth, "Authorization: basic ", sizeof(auth));
@@ -1067,9 +1038,9 @@ int main(int argc, char **argv)
            /*
             * assume username passwd already to be in colon separated form.
             */
	    while(isspace(*optarg))
		optarg++;
	    l=ap_base64encode(tmp,optarg,strlen(optarg));
            while(isspace(*ap_optarg))
                ap_optarg++;
            l=ap_base64encode(tmp, ap_optarg, strlen(ap_optarg));
            tmp[l]='\0';
 
            strncat(auth, "Proxy-Authorization: basic ", sizeof(auth));
@@ -1077,47 +1048,18 @@ int main(int argc, char **argv)
            strncat(auth, "\r\n", sizeof(auth));
            break;
        case 'H':
	    strncat(hdrs, optarg, sizeof(hdrs));
            strncat(hdrs, ap_optarg, sizeof(hdrs));
            strncat(hdrs, "\r\n", sizeof(hdrs));
	    break;
	case 'V':
	    copyright();
	    exit(0);
	    break;
	case 'w':
	    use_html = 1;
	    break;
	    /*
	     * if any of the following three are used, turn on html output
	     * automatically
	     */
	case 'x':
	    use_html = 1;
	    tablestring = optarg;
	    break;
	case 'y':
	    use_html = 1;
	    trstring = optarg;
	    break;
	case 'z':
	    use_html = 1;
	    tdstring = optarg;
	    break;
	case 'h':
	    usage(argv[0]);
	    break;
	default:
	    fprintf(stderr, "%s: invalid option `%c'\n", argv[0], c);
	    usage(argv[0]);
	    break;
            strcpy(content_type, ap_optarg);
        }
    }
    if (optind != argc - 1) {

    if (ap_optind != argc - 1) {
        fprintf(stderr, "%s: wrong number of arguments\n", argv[0]);
        usage(argv[0]);
    }

    if (parse_url(argv[optind++])) {
    if (parse_url(argv[ap_optind++])) {
        fprintf(stderr, "%s: invalid URL\n", argv[0]);
        usage(argv[0]);
    }