--- ab.c.orig 2007-07-26 17:33:59.000000000 +0100 +++ ab.c 2007-07-26 20:04:10.000000000 +0100 @@ -80,6 +80,10 @@ ** 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.0.40-dev-CM + ** Added '-R' switch to support appending data to request URL for each request. + ** Contributed by Chris Miles, July 26, 2007 **/ /* 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.0.40-dev-CM" /* * BUGS: @@ -255,6 +259,7 @@ int verbosity = 0; /* no verbosity by default */ int posting = 0; /* GET by default */ +int request_append = 0; /* Whether extra data needs to be appended to requests */ int requests = 1; /* Number of requests to make */ int heartbeatres = 100; /* How often do we say we're alive */ int concurrency = 1; /* Number of multiple requests to make */ @@ -268,7 +273,10 @@ char *path; /* path name */ char postfile[1024]; /* name of file containing post data */ char *postdata; /* *buffer containing data from postfile */ +char *reqdata; /* *buffer containing data from request file */ +char *req_p; /* pointer to position in request data */ apr_size_t postlen = 0; /* length of data to be POSTed */ +apr_size_t append_req_len = 0; /* length of request data to be appended */ char content_type[1024];/* content type to put in POST header */ char *cookie, /* optional cookie line */ *auth, /* optional (basic/uuencoded) auhentication */ @@ -603,6 +611,66 @@ static void write_request(struct connection * c) { + char *eol; + char path_append[1024]; + int snprintf_res; + + /* modify request each time to append data from request file */ + if (request_append == 1) { + eol = strchr(req_p, '\n'); /* end of current line */ + if (eol == NULL) + eol = reqdata + append_req_len; /* end of reqdata */ + + apr_snprintf(path_append, eol-req_p+1, "%s", req_p); + + req_p = eol + 1; /* next line */ + if (req_p >= (reqdata + append_req_len)) + req_p = reqdata; /* back to start of request data */ + + if (posting <= 0) { + snprintf_res = apr_snprintf(request, sizeof(_request), + "%s %s%s HTTP/1.0\r\n" + "User-Agent: ApacheBench/%s\r\n" + "%s" "%s" "%s" + "Host: %s%s\r\n" + "Accept: */*\r\n" + "%s" "\r\n", + (posting == 0) ? "GET" : "HEAD", + (isproxy) ? fullurl : path, + path_append, + AP_AB_BASEREVISION, + keepalive ? "Connection: Keep-Alive\r\n" : "", + cookie, auth, host_field, colonhost, hdrs); + } + else { + snprintf_res = apr_snprintf(request, sizeof(_request), + "POST %s%s HTTP/1.0\r\n" + "User-Agent: ApacheBench/%s\r\n" + "%s" "%s" "%s" + "Host: %s%s\r\n" + "Accept: */*\r\n" + "Content-length: %" APR_SIZE_T_FMT "\r\n" + "Content-type: %s\r\n" + "%s" + "\r\n", + (isproxy) ? fullurl : path, + path_append, + AP_AB_BASEREVISION, + keepalive ? "Connection: Keep-Alive\r\n" : "", + cookie, auth, + host_field, colonhost, postlen, + (content_type[0]) ? content_type : "text/plain", hdrs); + } + if (snprintf_res >= sizeof(_request)) { + err("Request too long\n"); + } + + if (verbosity >= 2) + printf("INFO: POST header == \n---\n%s\n---\n", request); + + reqlen = strlen(request); + } + do { apr_time_t tnow = apr_time_now(); apr_size_t l = c->rwrite; @@ -1549,7 +1617,7 @@ err("Request too long\n"); } - if (verbosity >= 2) + if (verbosity >= 2 && !request_append) printf("INFO: POST header == \n---\n%s\n---\n", request); reqlen = strlen(request); @@ -1597,6 +1665,9 @@ start_connect(&con[i]); } + if (request_append == 1) + req_p = reqdata; + while (done < requests) { apr_int32_t n; apr_int32_t timed; @@ -1757,6 +1828,7 @@ 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, " -R reqfile File containing lines to append to each request URL\n"); fprintf(stderr, " -T content-type Content-type header for POSTing\n"); fprintf(stderr, " -v verbosity How much troubleshooting info to print\n"); fprintf(stderr, " -w Print out results in HTML tables\n"); @@ -1893,6 +1965,45 @@ return 0; } +/* Open a file containing lines to append to each request + * For -R option. + */ +static int open_request_file(const char *rfile) +{ + apr_file_t *reqfd; + apr_finfo_t finfo; + apr_status_t rv; + char errmsg[120]; + + rv = apr_file_open(&reqfd, rfile, APR_READ, APR_OS_DEFAULT, cntxt); + if (rv != APR_SUCCESS) { + fprintf(stderr, "ab: Could not open POST data file (%s): %s\n", rfile, + apr_strerror(rv, errmsg, sizeof errmsg)); + return rv; + } + + apr_file_info_get(&finfo, APR_FINFO_NORM, reqfd); + append_req_len = (apr_size_t)finfo.size; + reqdata = malloc(append_req_len+2); + if (!reqdata) { + fprintf(stderr, "ab: Could not allocate request data buffer\n"); + return APR_ENOMEM; + } + rv = apr_file_read_full(reqfd, reqdata, append_req_len, NULL); + if (rv != APR_SUCCESS) { + fprintf(stderr, "ab: Could not read request data file: %s\n", + apr_strerror(rv, errmsg, sizeof errmsg)); + return rv; + } + apr_file_close(reqfd); + if (*(reqdata + append_req_len-1) != '\n') + *(reqdata + (append_req_len++) - 1) = '\n'; + *(reqdata + append_req_len) = '\0'; + + return 0; +} + + /* ------------------------------------------------------- */ /* sort out command-line args and call test */ @@ -1940,7 +2051,7 @@ #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:T:p:v:kVhwix:y:z:C:H:P:A:g:X:de:SqR:" #ifdef USE_SSL "Z:f:" #endif @@ -2070,6 +2181,16 @@ use_html = 1; tdstring = optarg; break; + case 'R': + // if (posting > 0) + // err("-R only applicable for non-POST requests\n"); + if (0 == (r = open_request_file(optarg))) { + request_append = 1; + } + else { + exit(r); + } + break; case 'h': usage(argv[0]); break;