]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Mass 'ab' backports of:
authorJim Jagielski <jim@apache.org>
Tue, 27 May 2008 16:11:36 +0000 (16:11 +0000)
committerJim Jagielski <jim@apache.org>
Tue, 27 May 2008 16:11:36 +0000 (16:11 +0000)
==================
r390504,i r390511, r390519, r394765, r395228, r396300, r413861 | colm

Give users the ability to set the window size for apache bench runs. Useful
for benchmarking largefile downloads.

  * Add siege-like behaviour to ApacheBench; output the results, as they have
    accrued so far, when the user interrupts with ctrl-c. As the signal handler
    is non-reentrant, we don't need volatiles, and the operations all look
    signal-safe.

  * Update the base version, since handling a signal differently is an external
    API change of sorts.

  * set the LastChangedRevision svn property, as ab.c has $Rev $ embedded, so
    it really should be.

when compiling statistics, only interate accross requests which occured.

A keepalive response need not neccessarily have included any content-length
header, handle this case properly for 304 responses. PR39789

==================
r516175 | trawick

ab: Add -r option to continue after socket receive errors.

Submitted by: Filip Hanik <devlist hanik.com>
Reviewed by: trawick

==================
r541138 | sctemme

Explain that POST data should be sent as the correct MIME type.
Submitted by Vincent Bray noodlet at gmail dot com,
edited and reviewed by sctemme

==================
r655214 | fielding

Overhaul ab.c stats collection and reporting to avoid integer
truncation and time divisions within the test loop, retain
native time resolution until output, remove unused data,
avoid structure copies, consistently round milliseconds, and
generally avoid losing accuracy of calculation due to type casts.

Incidentally fixes output bug on gnuplot (seconds were being
output as microseconds).  It would make more sense to output
gnuplot stats as microseconds after the start of test, but
this change returns us to consistency with pre-apr versions of ab.

PR: 44878, 44931.

==================
r655637 | fielding

Don't stop sending a request if EAGAIN is returned, which will only
happen if both the write and subsequent wait are returning EAGAIN,
and count posted bytes correctly when the initial write of a request
is not complete.

PR 10038, 38861, 39679
Submitted by: Patrick McManus <mcmanus datapower.com> (in 2003)
              Stefan Fleiter <stefan.fleiter web.de>  (in 2006)
              and Davanum Srinivas (in 2006).  Committed patch
              contains parts of all three, tweaked by Roy (2008).

==================
r655654 | fielding

Improve client performance by clearing connection pool instead
of destroying it.

PR 40054
Submitted by: Brad Roberts <braddr puremagic.com>

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.2.x@660576 13f79535-47bb-0310-9956-ffa450edef68

STATUS
support/ab.c

diff --git a/STATUS b/STATUS
index 026a16aff3ed6e10060b61165e145e3ebd1abccb..f60c1f0a2061f6e2631a077c4442f8df433d8ad6 100644 (file)
--- a/STATUS
+++ b/STATUS
@@ -90,49 +90,6 @@ RELEASE SHOWSTOPPERS:
 PATCHES ACCEPTED TO BACKPORT FROM TRUNK:
   [ start all new proposals below, under PATCHES PROPOSED. ]
 
- * ab: Sync to current trunk in order to include:
-   Add siege-like behaviour to ApacheBench; output the results, as they
-   have accrued so far, when the user interrupts with ctrl-c. As the
-   signal handler is non-reentrant, we don't need volatiles, and the
-   operations all look signal-safe.  Update the base version.
-   Set the LastChangedRevision svn property, as ab.c has $Rev $ embedded.
-   Add -r option to continue after socket receive errors.
-   The apr_port_t type is unsigned, but ab was using a signed format
-   code in its reports. PR 42070.
-   Correct behavior of HTTP request headers sent by ab in presence of
-   -H command-line overrides. PR: 31268, 26554
-   Explain that POST data should be sent as the correct MIME type. 
-   Do not try to read non existing response bodies of HEAD requests. PR: 34275
-   Use a 64 bit unsigned int instead of a signed long to count the
-   bytes transferred to avoid integer overflows. PR: 44346
-   Overhaul ab.c stats collection and reporting to avoid integer
-   truncation and time divisions within the test loop, retain
-   native time resolution until output, remove unused data,
-   avoid structure copies, consistently round milliseconds, and
-   generally avoid losing accuracy of calculation due to type casts.
-   Incidentally fixes output bug on gnuplot (seconds were being
-   output as microseconds).  PR: 44878, 44931.
-   Don't stop sending a request if EAGAIN is returned, which will only
-   happen if both the write and subsequent wait are returning EAGAIN,
-   and count posted bytes correctly when the initial write of a request
-   is not complete.  PR 10038, 38861, 39679
-   Improve client performance by clearing connection pool instead
-   of destroying it. PR 40054
-   Trunk version of patch:
-       http://svn.apache.org/viewvc?view=rev&revision=390511
-       http://svn.apache.org/viewvc?view=rev&revision=390519
-       http://svn.apache.org/viewvc?view=rev&revision=516175
-       http://svn.apache.org/viewvc?view=rev&revision=526584
-       http://svn.apache.org/viewvc?view=rev&revision=526872
-       http://svn.apache.org/viewvc?view=rev&revision=541138
-       http://svn.apache.org/viewvc?view=rev&revision=612954
-       http://svn.apache.org/viewvc?view=rev&revision=617890
-       http://svn.apache.org/viewvc?view=rev&revision=655214
-       http://svn.apache.org/viewvc?view=rev&revision=655637
-       http://svn.apache.org/viewvc?view=rev&revision=655654
-  Backport version for 2.2.x of patch:
-       http://people.apache.org/~fielding/p/ab-sync.txt
-  +1: fielding, jim, wrowe
 
 PATCHES PROPOSED TO BACKPORT FROM TRUNK:
   [ New proposals should be added at the end of the list ]
index ca899cba920e0d27e26b16a110d84b3a0648f02a..0bada081017d3e5929d26efe32d3d07fe7be4803 100644 (file)
    **     Switched to the new abstract pollset API, allowing ab to
    **     take advantage of future apr_pollset_t scalability improvements.
    **     Contributed by Brian Pane, August 31, 2002
+   **
+   ** Version 2.3
+   **     SIGINT now triggers output_results().
+   **     Contributed by colm, March 30, 2006
    **/
 
 /* Note: this version string should start with \d+[\d\.]* and be a valid
@@ -91,7 +95,7 @@
  * ab - or to due to a change in the distribution it is compiled with
  * (such as an APR change in for example blocking).
  */
-#define AP_AB_BASEREVISION "2.0.40-dev"
+#define AP_AB_BASEREVISION "2.3"
 
 /*
  * BUGS:
@@ -199,7 +203,7 @@ typedef STACK_OF(X509) X509_STACK_TYPE;
 #endif
 
 /* maximum number of requests on a time limited test */
-#define MAX_REQUESTS 50000
+#define MAX_REQUESTS (INT_MAX > 50000 ? 50000 : INT_MAX)
 
 /* good old state hostname */
 #define STATE_UNCONNECTED 0
@@ -238,22 +242,22 @@ struct connection {
 };
 
 struct data {
-    int read;              /* number of bytes read */
-    apr_time_t starttime;  /* start time of connection in seconds since
-                            * Jan. 1, 1970 */
-    apr_interval_time_t waittime;   /* Between writing request and reading
-                                     * response */
-    apr_interval_time_t ctime;      /* time in ms to connect */
-    apr_interval_time_t time;       /* time in ms for connection */
+    apr_time_t starttime;         /* start time of connection */
+    apr_interval_time_t waittime; /* between request and reading response */
+    apr_interval_time_t ctime;    /* time to connect */
+    apr_interval_time_t time;     /* time for connection */
 };
 
 #define ap_min(a,b) ((a)<(b))?(a):(b)
 #define ap_max(a,b) ((a)>(b))?(a):(b)
+#define ap_round_ms(a) ((apr_time_t)((a) + 500)/1000)
+#define ap_double_ms(a) ((double)(a)/1000.0)
 #define MAX_CONCURRENCY 20000
 
 /* --------------------- GLOBALS ---------------------------- */
 
 int verbosity = 0;      /* no verbosity by default */
+int recverrok = 0;      /* ok to proceed after socket receive errors */
 int posting = 0;        /* GET by default */
 int requests = 1;       /* Number of requests to make */
 int heartbeatres = 100; /* How often do we say we're alive */
@@ -262,6 +266,7 @@ int percentile = 1;     /* Show percentile served */
 int confidence = 1;     /* Show confidence estimator and warnings */
 int tlimit = 0;         /* time limit in secs */
 int keepalive = 0;      /* try and do keepalive connections */
+int windowsize = 0;     /* we use the OS default window size */
 char servername[1024];  /* name that server reports */
 char *hostname;         /* host name from URL */
 char *host_field;       /* value of "Host:" header field */
@@ -298,15 +303,20 @@ const char *tablestring;
 const char *trstring;
 const char *tdstring;
 
-apr_size_t doclen = 0;      /* the length the document should be */
-long started = 0;           /* number of requests started, so no excess */
-apr_uint64_t totalread = 0;         /* total number of bytes read */
-apr_uint64_t totalbread = 0;        /* totoal amount of entity body read */
-apr_uint64_t totalposted = 0;       /* total number of bytes posted, inc. headers */
-long done = 0;              /* number of requests we have done */
-long doneka = 0;            /* number of keep alive connections done */
-long good = 0, bad = 0;     /* number of good and bad requests */
-long epipe = 0;             /* number of broken pipe writes */
+apr_size_t doclen = 0;     /* the length the document should be */
+apr_int64_t totalread = 0;    /* total number of bytes read */
+apr_int64_t totalbread = 0;   /* totoal amount of entity body read */
+apr_int64_t totalposted = 0;  /* total number of bytes posted, inc. headers */
+int started = 0;           /* number of requests started, so no excess */
+int done = 0;              /* number of requests we have done */
+int doneka = 0;            /* number of keep alive connections done */
+int good = 0, bad = 0;     /* number of good and bad requests */
+int epipe = 0;             /* number of broken pipe writes */
+int err_length = 0;        /* requests failed due to response length */
+int err_conn = 0;          /* requests failed due to connection drop */
+int err_recv = 0;          /* requests failed due to broken read */
+int err_except = 0;        /* requests failed due to exception */
+int err_response = 0;      /* requests with invalid or non-200 response */
 
 #ifdef USE_SSL
 int is_ssl;
@@ -316,11 +326,7 @@ char *ssl_info = NULL;
 BIO *bio_out,*bio_err;
 #endif
 
-/* store error cases */
-int err_length = 0, err_conn = 0, err_except = 0;
-int err_response = 0;
-
-apr_time_t start, endtime;
+apr_time_t start, lasttime, stoptime;
 
 /* global request (and its length) */
 char _request[2048];
@@ -334,7 +340,7 @@ char buffer[8192];
 int percs[] = {50, 66, 75, 80, 90, 95, 98, 99, 100};
 
 struct connection *con;     /* connection array */
-struct data *stats;         /* date for each request */
+struct data *stats;         /* data for each request */
 apr_pool_t *cntxt;
 
 apr_pollset_t *readbits;
@@ -356,7 +362,7 @@ static void err(char *s)
 {
     fprintf(stderr, "%s\n", s);
     if (done)
-        printf("Total of %ld requests completed\n" , done);
+        printf("Total of %d requests completed\n" , done);
     exit(1);
 }
 
@@ -370,7 +376,7 @@ static void apr_err(char *s, apr_status_t rv)
         "%s: %s (%d)\n",
         s, apr_strerror(rv, buf, sizeof buf), rv);
     if (done)
-        printf("Total of %ld requests completed\n" , done);
+        printf("Total of %d requests completed\n" , done);
     exit(rv);
 }
 
@@ -609,18 +615,20 @@ static void ssl_proceed_handshake(struct connection *c)
 static void write_request(struct connection * c)
 {
     do {
-        apr_time_t tnow = apr_time_now();
+        apr_time_t tnow;
         apr_size_t l = c->rwrite;
         apr_status_t e = APR_SUCCESS; /* prevent gcc warning */
 
+        tnow = lasttime = apr_time_now();
+
         /*
          * First time round ?
          */
         if (c->rwrite == 0) {
             apr_socket_timeout_set(c->aprsock, 0);
             c->connect = tnow;
-            c->rwrite = reqlen;
             c->rwrote = 0;
+            c->rwrite = reqlen;
             if (posting)
                 c->rwrite += postlen;
         }
@@ -647,30 +655,19 @@ static void write_request(struct connection * c)
 #endif
             e = apr_socket_send(c->aprsock, request + c->rwrote, &l);
 
-        /*
-         * Bail early on the most common case
-         */
-        if (l == c->rwrite)
-            break;
-
-        if (e != APR_SUCCESS) {
-            /*
-             * Let's hope this traps EWOULDBLOCK too !
-             */
-            if (!APR_STATUS_IS_EAGAIN(e)) {
-                epipe++;
-                printf("Send request failed!\n");
-                close_connection(c);
-            }
+        if (e != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(e)) {
+            epipe++;
+            printf("Send request failed!\n");
+            close_connection(c);
             return;
         }
+        totalposted += l;
         c->rwrote += l;
         c->rwrite -= l;
-    } while (1);
+    } while (c->rwrite);
 
-    totalposted += c->rwrite;
     c->state = STATE_READ;
-    c->endwrite = apr_time_now();
+    c->endwrite = lasttime = apr_time_now();
     {
         apr_pollfd_t new_pollfd;
         new_pollfd.desc_type = APR_POLL_SOCKET;
@@ -723,15 +720,14 @@ static int compwait(struct data * a, struct data * b)
     return 0;
 }
 
-static void output_results(void)
+static void output_results(int sig)
 {
-    apr_interval_time_t timetakenusec;
-    float timetaken;
+    double timetaken;
 
-    endtime = apr_time_now();
-    timetakenusec = endtime - start;
-    timetaken = ((float)apr_time_sec(timetakenusec)) +
-        ((float)apr_time_usec(timetakenusec)) / 1000000.0F;
+    if (sig) {
+        lasttime = apr_time_now();  /* record final time if interrupted */
+    }
+    timetaken = (double) (lasttime - start) / APR_USEC_PER_SEC;
 
     printf("\n\n");
     printf("Server Software:        %s\n", servername);
@@ -747,45 +743,43 @@ static void output_results(void)
     printf("Document Length:        %" APR_SIZE_T_FMT " bytes\n", doclen);
     printf("\n");
     printf("Concurrency Level:      %d\n", concurrency);
-    printf("Time taken for tests:   %ld.%03ld seconds\n",
-           (long) apr_time_sec(timetakenusec),
-           (long) apr_time_usec(timetakenusec));
-    printf("Complete requests:      %ld\n", done);
-    printf("Failed requests:        %ld\n", bad);
+    printf("Time taken for tests:   %.3f seconds\n", timetaken);
+    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);
-    printf("Write errors:           %ld\n", epipe);
+        printf("   (Connect: %d, Receive: %d, Length: %d, Exceptions: %d)\n",
+            err_conn, err_recv, err_length, err_except);
+    printf("Write errors:           %d\n", epipe);
     if (err_response)
         printf("Non-2xx responses:      %d\n", err_response);
     if (keepalive)
-        printf("Keep-Alive requests:    %ld\n", doneka);
-    printf("Total transferred:      %" APR_UINT64_T_FMT " bytes\n", totalread);
+        printf("Keep-Alive requests:    %d\n", doneka);
+    printf("Total transferred:      %" APR_INT64_T_FMT " bytes\n", totalread);
     if (posting > 0)
-        printf("Total POSTed:           %" APR_UINT64_T_FMT "\n", totalposted);
-    printf("HTML transferred:       %" APR_UINT64_T_FMT " bytes\n", totalbread);
+        printf("Total POSTed:           %" APR_INT64_T_FMT "\n", totalposted);
+    printf("HTML transferred:       %" APR_INT64_T_FMT " bytes\n", totalbread);
 
     /* avoid divide by zero */
-    if (timetaken) {
+    if (timetaken && done) {
         printf("Requests per second:    %.2f [#/sec] (mean)\n",
-               (float) (done / timetaken));
+               (double) done / timetaken);
         printf("Time per request:       %.3f [ms] (mean)\n",
-               (float) (1000 * concurrency * timetaken / done));
+               (double) concurrency * timetaken * 1000 / done);
         printf("Time per request:       %.3f [ms] (mean, across all concurrent requests)\n",
-           (float) (1000 * timetaken / done));
+               (double) timetaken * 1000 / done);
         printf("Transfer rate:          %.2f [Kbytes/sec] received\n",
-           (float) (totalread / 1024 / timetaken));
+               (double) totalread / 1024 / timetaken);
         if (posting > 0) {
             printf("                        %.2f kb/s sent\n",
-               (float) (totalposted / timetaken / 1024));
+               (double) totalposted / timetaken / 1024);
             printf("                        %.2f kb/s total\n",
-               (float) ((totalread + totalposted) / timetaken / 1024));
+               (double) (totalread + totalposted) / timetaken / 1024);
         }
     }
 
-    if (requests) {
+    if (done > 0) {
         /* work out connection times */
-        long i;
+        int i;
         apr_time_t totalcon = 0, total = 0, totald = 0, totalwait = 0;
         apr_time_t meancon, meantot, meand, meanwait;
         apr_interval_time_t mincon = AB_MAX, mintot = AB_MAX, mind = AB_MAX,
@@ -794,119 +788,117 @@ static void output_results(void)
         apr_interval_time_t mediancon = 0, mediantot = 0, mediand = 0, medianwait = 0;
         double sdtot = 0, sdcon = 0, sdd = 0, sdwait = 0;
 
-        for (i = 0; i < requests; i++) {
-            struct data s = stats[i];
-            mincon = ap_min(mincon, s.ctime);
-            mintot = ap_min(mintot, s.time);
-            mind = ap_min(mind, s.time - s.ctime);
-            minwait = ap_min(minwait, s.waittime);
-
-            maxcon = ap_max(maxcon, s.ctime);
-            maxtot = ap_max(maxtot, s.time);
-            maxd = ap_max(maxd, s.time - s.ctime);
-            maxwait = ap_max(maxwait, s.waittime);
-
-            totalcon += s.ctime;
-            total += s.time;
-            totald += s.time - s.ctime;
-            totalwait += s.waittime;
+        for (i = 0; i < done; i++) {
+            struct data *s = &stats[i];
+            mincon = ap_min(mincon, s->ctime);
+            mintot = ap_min(mintot, s->time);
+            mind = ap_min(mind, s->time - s->ctime);
+            minwait = ap_min(minwait, s->waittime);
+
+            maxcon = ap_max(maxcon, s->ctime);
+            maxtot = ap_max(maxtot, s->time);
+            maxd = ap_max(maxd, s->time - s->ctime);
+            maxwait = ap_max(maxwait, s->waittime);
+
+            totalcon += s->ctime;
+            total += s->time;
+            totald += s->time - s->ctime;
+            totalwait += s->waittime;
         }
-        meancon = totalcon / requests;
-        meantot = total / requests;
-        meand = totald / requests;
-        meanwait = totalwait / requests;
+        meancon = totalcon / done;
+        meantot = total / done;
+        meand = totald / done;
+        meanwait = totalwait / done;
 
         /* calculating the sample variance: the sum of the squared deviations, divided by n-1 */
-        for (i = 0; i < requests; i++) {
-            struct data s = stats[i];
+        for (i = 0; i < done; i++) {
+            struct data *s = &stats[i];
             double a;
-            a = ((double)s.time - meantot);
+            a = ((double)s->time - meantot);
             sdtot += a * a;
-            a = ((double)s.ctime - meancon);
+            a = ((double)s->ctime - meancon);
             sdcon += a * a;
-            a = ((double)s.time - (double)s.ctime - meand);
+            a = ((double)s->time - (double)s->ctime - meand);
             sdd += a * a;
-            a = ((double)s.waittime - meanwait);
+            a = ((double)s->waittime - meanwait);
             sdwait += a * a;
         }
 
-        sdtot = (requests > 1) ? sqrt(sdtot / (requests - 1)) : 0;
-        sdcon = (requests > 1) ? sqrt(sdcon / (requests - 1)) : 0;
-        sdd = (requests > 1) ? sqrt(sdd / (requests - 1)) : 0;
-        sdwait = (requests > 1) ? sqrt(sdwait / (requests - 1)) : 0;
+        sdtot = (done > 1) ? sqrt(sdtot / (done - 1)) : 0;
+        sdcon = (done > 1) ? sqrt(sdcon / (done - 1)) : 0;
+        sdd = (done > 1) ? sqrt(sdd / (done - 1)) : 0;
+        sdwait = (done > 1) ? sqrt(sdwait / (done - 1)) : 0;
 
-        if (gnuplot) {
-            FILE *out = fopen(gnuplot, "w");
-            long i;
-            apr_time_t sttime;
-            char tmstring[1024];/* XXXX */
-            if (!out) {
-                perror("Cannot open gnuplot output file");
-                exit(1);
-            }
-            fprintf(out, "starttime\tseconds\tctime\tdtime\tttime\twait\n");
-            for (i = 0; i < requests; i++) {
-                apr_time_t diff = stats[i].time - stats[i].ctime;
-
-                sttime = stats[i].starttime;
-                (void) apr_ctime(tmstring, sttime);
-                fprintf(out, "%s\t%" APR_TIME_T_FMT "\t%" APR_TIME_T_FMT "\t%" APR_TIME_T_FMT "\t%" APR_TIME_T_FMT "\t%" APR_TIME_T_FMT "\n",
-                tmstring,
-                sttime,
-                stats[i].ctime,
-                diff,
-                stats[i].time,
-                stats[i].waittime);
-            }
-            fclose(out);
-        }
         /*
          * XXX: what is better; this hideous cast of the compradre function; or
          * the four warnings during compile ? dirkx just does not know and
          * hates both/
          */
-        qsort(stats, requests, sizeof(struct data),
+        qsort(stats, done, sizeof(struct data),
               (int (*) (const void *, const void *)) compradre);
-        if ((requests > 1) && (requests % 2))
-            mediancon = (stats[requests / 2].ctime + stats[requests / 2 + 1].ctime) / 2;
+        if ((done > 1) && (done % 2))
+            mediancon = (stats[done / 2].ctime + stats[done / 2 + 1].ctime) / 2;
         else
-            mediancon = stats[requests / 2].ctime;
+            mediancon = stats[done / 2].ctime;
 
-        qsort(stats, requests, sizeof(struct data),
+        qsort(stats, done, sizeof(struct data),
               (int (*) (const void *, const void *)) compri);
-        if ((requests > 1) && (requests % 2))
-            mediand = (stats[requests / 2].time + stats[requests / 2 + 1].time \
-            -stats[requests / 2].ctime - stats[requests / 2 + 1].ctime) / 2;
+        if ((done > 1) && (done % 2))
+            mediand = (stats[done / 2].time + stats[done / 2 + 1].time \
+            -stats[done / 2].ctime - stats[done / 2 + 1].ctime) / 2;
         else
-            mediand = stats[requests / 2].time - stats[requests / 2].ctime;
+            mediand = stats[done / 2].time - stats[done / 2].ctime;
 
-        qsort(stats, requests, sizeof(struct data),
+        qsort(stats, done, sizeof(struct data),
               (int (*) (const void *, const void *)) compwait);
-        if ((requests > 1) && (requests % 2))
-            medianwait = (stats[requests / 2].waittime + stats[requests / 2 + 1].waittime) / 2;
+        if ((done > 1) && (done % 2))
+            medianwait = (stats[done / 2].waittime + stats[done / 2 + 1].waittime) / 2;
         else
-            medianwait = stats[requests / 2].waittime;
+            medianwait = stats[done / 2].waittime;
 
-        qsort(stats, requests, sizeof(struct data),
+        qsort(stats, done, sizeof(struct data),
               (int (*) (const void *, const void *)) comprando);
-        if ((requests > 1) && (requests % 2))
-            mediantot = (stats[requests / 2].time + stats[requests / 2 + 1].time) / 2;
+        if ((done > 1) && (done % 2))
+            mediantot = (stats[done / 2].time + stats[done / 2 + 1].time) / 2;
         else
-            mediantot = stats[requests / 2].time;
+            mediantot = stats[done / 2].time;
 
         printf("\nConnection Times (ms)\n");
+        /*
+         * Reduce stats from apr time to milliseconds
+         */
+        mincon     = ap_round_ms(mincon);
+        mind       = ap_round_ms(mind);
+        minwait    = ap_round_ms(minwait);
+        mintot     = ap_round_ms(mintot);
+        meancon    = ap_round_ms(meancon);
+        meand      = ap_round_ms(meand);
+        meanwait   = ap_round_ms(meanwait);
+        meantot    = ap_round_ms(meantot);
+        mediancon  = ap_round_ms(mediancon);
+        mediand    = ap_round_ms(mediand);
+        medianwait = ap_round_ms(medianwait);
+        mediantot  = ap_round_ms(mediantot);
+        maxcon     = ap_round_ms(maxcon);
+        maxd       = ap_round_ms(maxd);
+        maxwait    = ap_round_ms(maxwait);
+        maxtot     = ap_round_ms(maxtot);
+        sdcon      = ap_double_ms(sdcon);
+        sdd        = ap_double_ms(sdd);
+        sdwait     = ap_double_ms(sdwait);
+        sdtot      = ap_double_ms(sdtot);
 
         if (confidence) {
-#define CONF_FMT_STRING "%5" APR_TIME_T_FMT " %4d %5.1f %6" APR_TIME_T_FMT " %7" APR_TIME_T_FMT "\n"
+#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"
             printf("              min  mean[+/-sd] median   max\n");
             printf("Connect:    " CONF_FMT_STRING,
-                       mincon, (int) (meancon + 0.5), sdcon, mediancon, maxcon);
+                   mincon, meancon, sdcon, mediancon, maxcon);
             printf("Processing: " CONF_FMT_STRING,
-               mind, (int) (meand + 0.5), sdd, mediand, maxd);
+                   mind, meand, sdd, mediand, maxd);
             printf("Waiting:    " CONF_FMT_STRING,
-                   minwait, (int) (meanwait + 0.5), sdwait, medianwait, maxwait);
+                   minwait, meanwait, sdwait, medianwait, maxwait);
             printf("Total:      " CONF_FMT_STRING,
-               mintot, (int) (meantot + 0.5), sdtot, mediantot, maxtot);
+                   mintot, meantot, sdtot, mediantot, maxtot);
 #undef CONF_FMT_STRING
 
 #define     SANE(what,mean,median,sd) \
@@ -928,51 +920,73 @@ static void output_results(void)
         else {
             printf("              min   avg   max\n");
 #define CONF_FMT_STRING "%5" APR_TIME_T_FMT " %5" APR_TIME_T_FMT "%5" APR_TIME_T_FMT "\n"
-            printf("Connect:    " CONF_FMT_STRING,
-                mincon, meancon, maxcon);
-            printf("Processing: " CONF_FMT_STRING,
-                mintot - mincon, meantot - meancon,  maxtot - maxcon);
-            printf("Total:      " CONF_FMT_STRING,
-                mintot, meantot, maxtot);
+            printf("Connect:    " CONF_FMT_STRING, mincon, meancon, maxcon);
+            printf("Processing: " CONF_FMT_STRING, mintot - mincon,
+                                                   meantot - meancon,
+                                                   maxtot - maxcon);
+            printf("Total:      " CONF_FMT_STRING, mintot, meantot, maxtot);
 #undef CONF_FMT_STRING
         }
 
 
         /* Sorted on total connect times */
-        if (percentile && (requests > 1)) {
+        if (percentile && (done > 1)) {
             printf("\nPercentage of the requests served within a certain time (ms)\n");
             for (i = 0; i < sizeof(percs) / sizeof(int); i++) {
                 if (percs[i] <= 0)
                     printf(" 0%%  <0> (never)\n");
                 else if (percs[i] >= 100)
                     printf(" 100%%  %5" APR_TIME_T_FMT " (longest request)\n",
-                           stats[requests - 1].time);
+                           ap_round_ms(stats[done - 1].time));
                 else
                     printf("  %d%%  %5" APR_TIME_T_FMT "\n", percs[i],
-                           stats[(int) (requests * percs[i] / 100)].time);
+                           ap_round_ms(stats[(int) (done * percs[i] / 100)].time));
             }
         }
         if (csvperc) {
             FILE *out = fopen(csvperc, "w");
-            int i;
             if (!out) {
                 perror("Cannot open CSV output file");
                 exit(1);
             }
             fprintf(out, "" "Percentage served" "," "Time in ms" "\n");
             for (i = 0; i < 100; i++) {
-                apr_time_t t;
+                double t;
                 if (i == 0)
-                    t = stats[0].time;
+                    t = ap_double_ms(stats[0].time);
                 else if (i == 100)
-                    t = stats[requests - 1].time;
+                    t = ap_double_ms(stats[done - 1].time);
                 else
-                    t = stats[(int) (0.5 + requests * i / 100.0)].time;
-                fprintf(out, "%d,%e\n", i, (double)t);
+                    t = ap_double_ms(stats[(int) (0.5 + done * i / 100.0)].time);
+                fprintf(out, "%d,%.3f\n", i, t);
             }
             fclose(out);
         }
+        if (gnuplot) {
+            FILE *out = fopen(gnuplot, "w");
+            char tmstring[APR_CTIME_LEN];
+            if (!out) {
+                perror("Cannot open gnuplot output file");
+                exit(1);
+            }
+            fprintf(out, "starttime\tseconds\tctime\tdtime\tttime\twait\n");
+            for (i = 0; i < done; i++) {
+                (void) apr_ctime(tmstring, stats[i].starttime);
+                fprintf(out, "%s\t%" APR_TIME_T_FMT "\t%" APR_TIME_T_FMT
+                               "\t%" APR_TIME_T_FMT "\t%" APR_TIME_T_FMT
+                               "\t%" APR_TIME_T_FMT "\n", tmstring,
+                        apr_time_sec(stats[i].starttime),
+                        ap_round_ms(stats[i].ctime),
+                        ap_round_ms(stats[i].time - stats[i].ctime),
+                        ap_round_ms(stats[i].time),
+                        ap_round_ms(stats[i].waittime));
+            }
+            fclose(out);
+        }
+    }
 
+    if (sig) {
+        exit(1);
     }
 }
 
@@ -982,10 +996,7 @@ static void output_results(void)
 
 static void output_html_results(void)
 {
-    long timetaken;
-
-    endtime = apr_time_now();
-    timetaken = (long)((endtime - start) / 1000);
+    double timetaken = (double) (lasttime - start) / APR_USEC_PER_SEC;
 
     printf("\n\n<table %s>\n", tablestring);
     printf("<tr %s><th colspan=2 %s>Server Software:</th>"
@@ -1007,14 +1018,13 @@ static void output_html_results(void)
        "<td colspan=2 %s>%d</td></tr>\n",
        trstring, tdstring, tdstring, concurrency);
     printf("<tr %s><th colspan=2 %s>Time taken for tests:</th>"
-       "<td colspan=2 %s>%" APR_INT64_T_FMT ".%03ld seconds</td></tr>\n",
-       trstring, tdstring, tdstring, apr_time_sec(timetaken),
-           (long)apr_time_usec(timetaken));
+       "<td colspan=2 %s>%.3f seconds</td></tr>\n",
+       trstring, tdstring, tdstring, timetaken);
     printf("<tr %s><th colspan=2 %s>Complete requests:</th>"
-       "<td colspan=2 %s>%ld</td></tr>\n",
+       "<td colspan=2 %s>%d</td></tr>\n",
        trstring, tdstring, tdstring, done);
     printf("<tr %s><th colspan=2 %s>Failed requests:</th>"
-       "<td colspan=2 %s>%ld</td></tr>\n",
+       "<td colspan=2 %s>%d</td></tr>\n",
        trstring, tdstring, tdstring, bad);
     if (bad)
         printf("<tr %s><td colspan=4 %s >   (Connect: %d, Length: %d, Exceptions: %d)</td></tr>\n",
@@ -1025,56 +1035,65 @@ static void output_html_results(void)
            trstring, tdstring, tdstring, err_response);
     if (keepalive)
         printf("<tr %s><th colspan=2 %s>Keep-Alive requests:</th>"
-           "<td colspan=2 %s>%ld</td></tr>\n",
+           "<td colspan=2 %s>%d</td></tr>\n",
            trstring, tdstring, tdstring, doneka);
     printf("<tr %s><th colspan=2 %s>Total transferred:</th>"
-       "<td colspan=2 %s>%" APR_UINT64_T_FMT " bytes</td></tr>\n",
+       "<td colspan=2 %s>%" APR_INT64_T_FMT " bytes</td></tr>\n",
        trstring, tdstring, tdstring, totalread);
     if (posting > 0)
         printf("<tr %s><th colspan=2 %s>Total POSTed:</th>"
-           "<td colspan=2 %s>%" APR_UINT64_T_FMT "</td></tr>\n",
+           "<td colspan=2 %s>%" APR_INT64_T_FMT "</td></tr>\n",
            trstring, tdstring, tdstring, totalposted);
     printf("<tr %s><th colspan=2 %s>HTML transferred:</th>"
-       "<td colspan=2 %s>%" APR_UINT64_T_FMT " bytes</td></tr>\n",
+       "<td colspan=2 %s>%" APR_INT64_T_FMT " bytes</td></tr>\n",
        trstring, tdstring, tdstring, totalbread);
 
     /* avoid divide by zero */
     if (timetaken) {
         printf("<tr %s><th colspan=2 %s>Requests per second:</th>"
            "<td colspan=2 %s>%.2f</td></tr>\n",
-           trstring, tdstring, tdstring, 1000 * (float) (done) / timetaken);
+           trstring, tdstring, tdstring, (double) done * 1000 / timetaken);
         printf("<tr %s><th colspan=2 %s>Transfer rate:</th>"
            "<td colspan=2 %s>%.2f kb/s received</td></tr>\n",
-           trstring, tdstring, tdstring, (float) (totalread) / timetaken);
+           trstring, tdstring, tdstring, (double) totalread / timetaken);
         if (posting > 0) {
             printf("<tr %s><td colspan=2 %s>&nbsp;</td>"
                "<td colspan=2 %s>%.2f kb/s sent</td></tr>\n",
                trstring, tdstring, tdstring,
-               (float) (totalposted) / timetaken);
+               (double) totalposted / timetaken);
             printf("<tr %s><td colspan=2 %s>&nbsp;</td>"
                "<td colspan=2 %s>%.2f kb/s total</td></tr>\n",
                trstring, tdstring, tdstring,
-               (float) (totalread + totalposted) / timetaken);
+               (double) (totalread + totalposted) / timetaken);
         }
     }
     {
         /* work out connection times */
-        long i;
+        int i;
         apr_interval_time_t totalcon = 0, total = 0;
         apr_interval_time_t mincon = AB_MAX, mintot = AB_MAX;
         apr_interval_time_t maxcon = 0, maxtot = 0;
 
-        for (i = 0; i < requests; i++) {
-            struct data s = stats[i];
-            mincon = ap_min(mincon, s.ctime);
-            mintot = ap_min(mintot, s.time);
-            maxcon = ap_max(maxcon, s.ctime);
-            maxtot = ap_max(maxtot, s.time);
-            totalcon += s.ctime;
-            total += s.time;
+        for (i = 0; i < done; i++) {
+            struct data *s = &stats[i];
+            mincon = ap_min(mincon, s->ctime);
+            mintot = ap_min(mintot, s->time);
+            maxcon = ap_max(maxcon, s->ctime);
+            maxtot = ap_max(maxtot, s->time);
+            totalcon += s->ctime;
+            total    += s->time;
         }
-
-        if (requests > 0) { /* avoid division by zero (if 0 requests) */
+        /*
+         * Reduce stats from apr time to milliseconds
+         */
+        mincon   = ap_round_ms(mincon);
+        mintot   = ap_round_ms(mintot);
+        maxcon   = ap_round_ms(maxcon);
+        maxtot   = ap_round_ms(maxtot);
+        totalcon = ap_round_ms(totalcon);
+        total    = ap_round_ms(total);
+
+        if (done > 0) { /* avoid division by zero (if 0 done) */
             printf("<tr %s><th %s colspan=4>Connnection Times (ms)</th></tr>\n",
                trstring, tdstring);
             printf("<tr %s><th %s>&nbsp;</th> <th %s>min</th>   <th %s>avg</th>   <th %s>max</th></tr>\n",
@@ -1083,18 +1102,18 @@ static void output_html_results(void)
                "<td %s>%5" APR_TIME_T_FMT "</td>"
                "<td %s>%5" APR_TIME_T_FMT "</td>"
                "<td %s>%5" APR_TIME_T_FMT "</td></tr>\n",
-               trstring, tdstring, tdstring, mincon, tdstring, totalcon / requests, tdstring, maxcon);
+               trstring, tdstring, tdstring, mincon, tdstring, totalcon / done, tdstring, maxcon);
             printf("<tr %s><th %s>Processing:</th>"
                "<td %s>%5" APR_TIME_T_FMT "</td>"
                "<td %s>%5" APR_TIME_T_FMT "</td>"
                "<td %s>%5" APR_TIME_T_FMT "</td></tr>\n",
                trstring, tdstring, tdstring, mintot - mincon, tdstring,
-               (total / requests) - (totalcon / requests), tdstring, maxtot - maxcon);
+               (total / done) - (totalcon / done), tdstring, maxtot - maxcon);
             printf("<tr %s><th %s>Total:</th>"
                "<td %s>%5" APR_TIME_T_FMT "</td>"
                "<td %s>%5" APR_TIME_T_FMT "</td>"
                "<td %s>%5" APR_TIME_T_FMT "</td></tr>\n",
-               trstring, tdstring, tdstring, mintot, tdstring, total / requests, tdstring, maxtot);
+               trstring, tdstring, tdstring, mintot, tdstring, total / done, tdstring, maxtot);
         }
         printf("</table>\n");
     }
@@ -1118,8 +1137,9 @@ static void start_connect(struct connection * c)
     c->gotheader = 0;
     c->rwrite = 0;
     if (c->ctx)
-        apr_pool_destroy(c->ctx);
-    apr_pool_create(&c->ctx, cntxt);
+        apr_pool_clear(c->ctx);
+    else
+        apr_pool_create(&c->ctx, cntxt);
 
     if ((rv = apr_socket_create(&c->aprsock, destsa->family,
                 SOCK_STREAM, 0, c->ctx)) != APR_SUCCESS) {
@@ -1129,7 +1149,21 @@ static void start_connect(struct connection * c)
          != APR_SUCCESS) {
         apr_err("socket nonblock", rv);
     }
-    c->start = apr_time_now();
+
+    if (windowsize != 0) {
+        rv = apr_socket_opt_set(c->aprsock, APR_SO_SNDBUF, 
+                                windowsize);
+        if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
+            apr_err("socket send buffer", rv);
+        }
+        rv = apr_socket_opt_set(c->aprsock, APR_SO_RCVBUF, 
+                                windowsize);
+        if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
+            apr_err("socket receive buffer", rv);
+        }
+    }
+
+    c->start = lasttime = apr_time_now();
 #ifdef USE_SSL
     if (is_ssl) {
         BIO *bio;
@@ -1220,18 +1254,16 @@ static void close_connection(struct connection * c)
         }
         /* save out time */
         if (done < requests) {
-            struct data s;
-            if ((done) && heartbeatres && !(done % heartbeatres)) {
-                fprintf(stderr, "Completed %ld requests\n", done);
+            struct data *s = &stats[done++];
+            c->done      = lasttime = apr_time_now();
+            s->starttime = c->start;
+            s->ctime     = ap_max(0, c->connect - c->start);
+            s->time      = ap_max(0, c->done - c->start);
+            s->waittime  = ap_max(0, c->beginread - c->endwrite);
+            if (heartbeatres && !(done % heartbeatres)) {
+                fprintf(stderr, "Completed %d requests\n", done);
                 fflush(stderr);
             }
-            c->done = apr_time_now();
-            s.read = c->read;
-            s.starttime = c->start;
-            s.ctime = ap_max(0, (c->connect - c->start) / 1000);
-            s.time = ap_max(0, (c->done - c->start) / 1000);
-            s.waittime = ap_max(0, (c->beginread - c->endwrite) / 1000);
-            stats[done++] = s;
         }
     }
 
@@ -1304,10 +1336,18 @@ static void read_connection(struct connection * c)
         }
         /* catch legitimate fatal apr_socket_recv errors */
         else if (status != APR_SUCCESS) {
-            err_except++; /* XXX: is this the right error counter? */
-            /* XXX: Should errors here be fatal, or should we allow a
-             * certain number of them before completely failing? -aaron */
-            apr_err("apr_socket_recv", status);
+            err_recv++;
+            if (recverrok) {
+                bad++;
+                close_connection(c);
+                if (verbosity >= 1) {
+                    char buf[120];
+                    fprintf(stderr,"%s: %s (%d)\n", "apr_socket_recv", apr_strerror(status, buf, sizeof buf), status);
+                }
+                return;
+            } else {
+                apr_err("apr_socket_recv", status);
+            }
         }
     }
 
@@ -1329,8 +1369,8 @@ static void read_connection(struct connection * c)
         status = apr_xlate_conv_buffer(from_ascii, 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);
+            fprintf(stderr, "only simple translation is supported (%d/%" APR_SIZE_T_FMT
+                            "/%" APR_SIZE_T_FMT ")\n", status, inbytes_left, outbytes_left);
             exit(1);
         }
 #else
@@ -1427,6 +1467,11 @@ static void read_connection(struct connection * c)
                     /* response to HEAD doesn't have entity body */
                     c->length = posting >= 0 ? atoi(cl + 16) : 0;
                 }
+                /* The response may not have a Content-Length header */
+                if (!cl) {
+                    c->keepalive = 1;
+                    c->length = 0; 
+                }      
             }
             c->bread += c->cbx - (s + l - c->cbuff) + r - tocopy;
             totalbread += c->bread;
@@ -1451,26 +1496,25 @@ static void read_connection(struct connection * c)
             err_length++;
         }
         if (done < requests) {
-            struct data s;
+            struct data *s = &stats[done++];
             doneka++;
-            if (done && heartbeatres && !(done % heartbeatres)) {
-                fprintf(stderr, "Completed %ld requests\n", done);
+            c->done      = apr_time_now();
+            s->starttime = c->start;
+            s->ctime     = ap_max(0, c->connect - c->start);
+            s->time      = ap_max(0, c->done - c->start);
+            s->waittime  = ap_max(0, c->beginread - c->endwrite);
+            if (heartbeatres && !(done % heartbeatres)) {
+                fprintf(stderr, "Completed %d requests\n", done);
                 fflush(stderr);
             }
-            c->done = apr_time_now();
-            s.read = c->read;
-            s.starttime = c->start;
-            s.ctime = ap_max(0, (c->connect - c->start) / 1000);
-            s.waittime = ap_max(0, (c->beginread - c->endwrite) / 1000);
-            s.time = ap_max(0, (c->done - c->start) / 1000);
-            stats[done++] = s;
         }
         c->keepalive = 0;
         c->length = 0;
         c->gotheader = 0;
         c->cbx = 0;
         c->read = c->bread = 0;
-        c->start = c->connect = apr_time_now(); /* zero connect time with keep-alive */
+        /* zero connect time with keep-alive */
+        c->start = c->connect = lasttime = apr_time_now();
         write_request(c);
     }
 }
@@ -1481,9 +1525,9 @@ static void read_connection(struct connection * c)
 
 static void test(void)
 {
-    apr_time_t now;
+    apr_time_t stoptime;
     apr_int16_t rv;
-    long i;
+    int i;
     apr_status_t status;
     int snprintf_res = 0;
 #ifdef NOT_ASCII
@@ -1508,8 +1552,6 @@ static void test(void)
     fflush(stdout);
     }
 
-    now = apr_time_now();
-
     con = calloc(concurrency, sizeof(struct connection));
 
     stats = calloc(requests, sizeof(struct data));
@@ -1596,8 +1638,9 @@ static void test(void)
     status = apr_xlate_conv_buffer(to_ascii, 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);
+        fprintf(stderr, "only simple translation is supported (%d/%"
+                        APR_SIZE_T_FMT "/%" APR_SIZE_T_FMT ")\n",
+                        status, inbytes_left, outbytes_left);
         exit(1);
     }
 #endif              /* NOT_ASCII */
@@ -1612,7 +1655,13 @@ static void test(void)
     }
 
     /* ok - lets start */
-    start = apr_time_now();
+    start = lasttime = apr_time_now();
+    stoptime = tlimit ? (start + apr_time_from_sec(tlimit)) : AB_MAX;
+
+#ifdef SIGINT 
+    /* Output the results if the user terminates the run early. */
+    apr_signal(SIGINT, output_results);
+#endif
 
     /* initialise lots of requests */
     for (i = 0; i < concurrency; i++) {
@@ -1620,18 +1669,9 @@ static void test(void)
         start_connect(&con[i]);
     }
 
-    while (done < requests) {
+    do {
         apr_int32_t n;
-        apr_int32_t timed;
-            const apr_pollfd_t *pollresults;
-
-        /* check for time limit expiry */
-        now = apr_time_now();
-        timed = (apr_int32_t)apr_time_sec(now - start);
-        if (tlimit && timed >= tlimit) {
-            requests = done;    /* so stats are correct */
-            break;      /* no need to do another round */
-        }
+        const apr_pollfd_t *pollresults;
 
         n = concurrency;
         status = apr_pollset_poll(readbits, aprtimeout, &n, &pollresults);
@@ -1646,7 +1686,7 @@ static void test(void)
             const apr_pollfd_t *next_fd = &(pollresults[i]);
             struct connection *c;
 
-                c = next_fd->client_data;
+            c = next_fd->client_data;
 
             /*
              * If the connection isn't connected how can we check it?
@@ -1734,17 +1774,17 @@ static void test(void)
                     apr_pollset_add(readbits, &new_pollfd);
                 }
         }
-    }
-
+    } while (lasttime < stoptime && done < requests);
+    
     if (heartbeatres)
-        fprintf(stderr, "Finished %ld requests\n", done);
+        fprintf(stderr, "Finished %d requests\n", done);
     else
         printf("..done\n");
 
     if (use_html)
         output_html_results();
     else
-        output_results();
+        output_results(0);
 }
 
 /* ------------------------------------------------------- */
@@ -1753,16 +1793,16 @@ static void test(void)
 static void copyright(void)
 {
     if (!use_html) {
-        printf("This is ApacheBench, Version %s\n", AP_AB_BASEREVISION " <$Revision: 1.146 $> apache-2.0");
+        printf("This is ApacheBench, Version %s\n", AP_AB_BASEREVISION " <$Revision: 655654 $>");
         printf("Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/\n");
-        printf("Copyright 2006 The Apache Software Foundation, http://www.apache.org/\n");
+        printf("Licensed to 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", AP_AB_BASEREVISION, "$Revision: 1.146 $");
+        printf(" This is ApacheBench, Version %s <i>&lt;%s&gt;</i><br>\n", AP_AB_BASEREVISION, "$Revision: 655654 $");
         printf(" Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/<br>\n");
-        printf(" Copyright 2006 The Apache Software Foundation, http://www.apache.org/<br>\n");
+        printf(" Licensed to The Apache Software Foundation, http://www.apache.org/<br>\n");
         printf("</p>\n<p>\n");
     }
 }
@@ -1775,12 +1815,17 @@ static void usage(const char *progname)
         "[s]"
 #endif
         "://]hostname[:port]/path\n", progname);
+/* 80 column ruler:  ********************************************************************************
+ */
     fprintf(stderr, "Options are:\n");
     fprintf(stderr, "    -n requests     Number of requests to perform\n");
     fprintf(stderr, "    -c concurrency  Number of multiple requests to make\n");
     fprintf(stderr, "    -t timelimit    Seconds to max. wait for responses\n");
-    fprintf(stderr, "    -p postfile     File containing data to POST\n");
-    fprintf(stderr, "    -T content-type Content-type header for POSTing\n");
+    fprintf(stderr, "    -b windowsize   Size of TCP send/receive buffer, in bytes\n");
+    fprintf(stderr, "    -p postfile     File containing data to POST. Remember also to set -T\n");
+    fprintf(stderr, "    -T content-type Content-type header for POSTing, eg.\n");
+    fprintf(stderr, "                    'application/x-www-form-urlencoded'\n");
+    fprintf(stderr, "                    Default is 'text/plain'\n");
     fprintf(stderr, "    -v verbosity    How much troubleshooting info to print\n");
     fprintf(stderr, "    -w              Print out results in HTML tables\n");
     fprintf(stderr, "    -i              Use HEAD instead of GET\n");
@@ -1801,6 +1846,7 @@ static void usage(const char *progname)
     fprintf(stderr, "    -S              Do not show confidence estimators and warnings.\n");
     fprintf(stderr, "    -g filename     Output collected data to gnuplot format file.\n");
     fprintf(stderr, "    -e filename     Output CSV file with percentages served\n");
+    fprintf(stderr, "    -r              Don't exit on socket receive errors.\n");
     fprintf(stderr, "    -h              Display usage information (this message)\n");
 #ifdef USE_SSL
     fprintf(stderr, "    -Z ciphersuite  Specify SSL/TLS cipher suite (See openssl ciphers)\n");
@@ -1963,7 +2009,7 @@ int main(int argc, const char * const argv[])
 #endif
 
     apr_getopt_init(&opt, cntxt, argc, argv);
-    while ((status = apr_getopt(opt, "n:c:t:T:p:v:kVhwix:y:z:C:H:P:A:g:X:de:Sq"
+    while ((status = apr_getopt(opt, "n:c:t:b:T:p:v:rkVhwix:y:z:C:H:P:A:g:X:de:Sq"
 #ifdef USE_SSL
             "Z:f:"
 #endif
@@ -1971,7 +2017,7 @@ int main(int argc, const char * const argv[])
         switch (c) {
             case 'n':
                 requests = atoi(optarg);
-                if (!requests) {
+                if (requests <= 0) {
                     err("Invalid number of requests\n");
                 }
                 break;
@@ -1984,6 +2030,9 @@ int main(int argc, const char * const argv[])
             case 'c':
                 concurrency = atoi(optarg);
                 break;
+            case 'b':
+                windowsize = atoi(optarg);
+                break;
             case 'i':
                 if (posting == 1)
                 err("Cannot mix POST and HEAD\n");
@@ -2011,6 +2060,9 @@ int main(int argc, const char * const argv[])
                     exit(r);
                 }
                 break;
+            case 'r':
+                recverrok = 1;
+                break;
             case 'v':
                 verbosity = atoi(optarg);
                 break;