ApacheBench
view release on metacpan or search on metacpan
src/ApacheBench.xs view on Meta::CPAN
** - various refactors, rewrites, and improvements; see Changes
**
*/
/*
* BUGS:
*
* - uses strcpy/etc.
* - has various other poor buffer attacks related to the lazy parsing of
* response headers from the server
* - doesn't implement much of HTTP/1.x, only accepts certain forms of
* responses
* - (performance problem) heavy use of strstr shows up top in profile
* only an issue for loopback usage
*/
/* ------------------ DEBUGGING --------------------------------------- */
// uncomment to turn on debugging messages
//#define AB_DEBUG 1
/* -------------------------------------------------------------------- */
#ifdef AB_DEBUG
#define AB_DEBUG_XS 1
#else
#define AB_DEBUG_XS 0
#endif
/* XS library */
#include "EXTERN.h"
#include "perl.h"
#include "XSUB.h"
/* ApacheBench source code */
#include "apachebench/util.c"
#include "apachebench/xs_util.c"
#include "apachebench/http_util.c"
#include "apachebench/socket_io.c"
#include "apachebench/execute.c"
#include "apachebench/regression_data.c"
/* ------------------- MACROS -------------------------- */
#define ap_min(a,b) ((a)<(b))?(a):(b)
#define ap_max(a,b) ((a)>(b))?(a):(b)
MODULE = HTTPD::Bench::ApacheBench PACKAGE = HTTPD::Bench::ApacheBench
PROTOTYPES: ENABLE
HV *
ab(input_hash)
SV * input_hash;
PREINIT:
char *pt,**url_keys;
int i,j,k,arrlen,arrlen2;
int def_buffersize; /* default buffersize for all runs */
int def_repeat; /* default number of repeats if unspecified in runs */
int def_memory; /* default memory setting if unspecified in runs */
bool def_keepalive = 0; /* default keepalive setting */
struct global *registry = calloc(1, sizeof(struct global));
int total_started = 0, total_good = 0, total_failed = 0;
CODE:
SV * runs;
SV * urls = 0;
SV * post_data = 0;
SV * head_requests = 0;
SV * cookies = 0;
SV * ctypes = 0;
SV * req_headers = 0;
SV * keepalive = 0;
SV * url_tlimits = 0;
AV * run_group, *tmpav, *tmpav2;
SV * tmpsv, *tmpsv2 = 0, *tmpsv3;
HV * tmphv;
STRLEN len;
if (AB_DEBUG_XS) printf("AB_DEBUG: start of ab()\n");
registry->concurrency = 1;
registry->requests = 0;
registry->tlimit = 0;
registry->min_tlimit.tv_sec = 30;
registry->min_tlimit.tv_usec = 0;
registry->tail = 0;
registry->done = 0;
registry->need_to_be_done = 0;
strcpy(registry->version, VERSION);
strcpy(registry->warn_and_error, "\nWarning messages from ab():");
registry->total_bytes_received = 0;
registry->number_of_urls = 0;
/*Get necessary initial information and initialize*/
tmphv = (HV *)SvRV(input_hash);
tmpsv = *(hv_fetch(tmphv, "concurrency", 11, 0));
registry->concurrency = SvIV(tmpsv);
tmpsv = *(hv_fetch(tmphv, "timelimit", 9, 0));
if (SvOK(tmpsv)) {
registry->tlimit = SvNV(tmpsv);
registry->min_tlimit =
double2timeval(ap_min(timeval2double(registry->min_tlimit),
registry->tlimit));
}
tmpsv = *(hv_fetch(tmphv, "buffersize", 10, 0));
def_buffersize = SvIV(tmpsv);
tmpsv = *(hv_fetch(tmphv, "repeat", 6, 0));
def_repeat = SvIV(tmpsv);
tmpsv = *(hv_fetch(tmphv, "memory", 6, 0));
def_memory = SvIV(tmpsv);
if (AB_DEBUG_XS) printf("AB_DEBUG: ab() init - stage 1\n");
tmpsv = *(hv_fetch(tmphv, "keepalive", 9, 0));
if (SvTRUE(tmpsv))
def_keepalive = 1;
if (AB_DEBUG_XS) printf("AB_DEBUG: ab() init - stage 2\n");
tmpsv = *(hv_fetch(tmphv, "priority", 8, 0));
pt = SvPV(tmpsv, len);
if (strcmp(pt, "run_priority") == 0)
registry->priority = RUN_PRIORITY;
else {
registry->priority = EQUAL_OPPORTUNITY;
if (strcmp(pt, "equal_opportunity") != 0)
myerr(registry->warn_and_error, "Unknown priority value (the only possible priorities are run_priority and equal_opportunity), using default: equal_opportunity");
}
if (AB_DEBUG_XS) printf("AB_DEBUG: ab() init - stage 3\n");
runs = *(hv_fetch(tmphv, "runs", 4, 0));
run_group = (AV *)SvRV(runs);
registry->number_of_runs = av_len(run_group) + 1;
registry->order = malloc(registry->number_of_runs * sizeof(int));
registry->repeats = malloc(registry->number_of_runs * sizeof(int));
registry->position = malloc((registry->number_of_runs+1) * sizeof(int));
registry->memory = malloc(registry->number_of_runs * sizeof(int));
registry->use_auto_cookies = malloc(registry->number_of_runs * sizeof(bool));
if (AB_DEBUG_XS) printf("AB_DEBUG: done with ab() initialization\n");
for (i = 0,j = 0; i < registry->number_of_runs; i++) {
if (AB_DEBUG_XS) printf("AB_DEBUG: starting run %d setup\n", i);
tmpsv = *(av_fetch(run_group, i, 0));
if (SvROK(tmpsv))
tmphv = (HV *)SvRV(tmpsv);
registry->memory[i] = def_memory;
if (hv_exists(tmphv, "memory", 6)) {
tmpsv = *(hv_fetch(tmphv, "memory", 6, 0));
registry->memory[i] = SvIV(tmpsv);
}
registry->repeats[i] = def_repeat;
if (hv_exists(tmphv, "repeat", 6)) {
/* Number of requests to make */
tmpsv = *(hv_fetch(tmphv, "repeat", 6, 0));
registry->repeats[i] = SvIV(tmpsv);
}
registry->use_auto_cookies[i] = 1;
if (hv_exists(tmphv, "use_auto_cookies", 16)) {
tmpsv = *(hv_fetch(tmphv, "use_auto_cookies", 16, 0));
registry->use_auto_cookies[i] = SvTRUE(tmpsv) ? 1 : 0;
}
registry->requests = ap_max(registry->requests, registry->repeats[i]);
urls = *(hv_fetch(tmphv, "urls", 4, 0));
tmpav = (AV *) SvRV(urls);
registry->position[i] = registry->number_of_urls;
registry->number_of_urls += av_len(tmpav) + 1;
src/ApacheBench.xs view on Meta::CPAN
if (hv_exists(tmphv, "order", 5)) {
tmpsv = *(hv_fetch(tmphv, "order", 5, 0));
pt = SvPV(tmpsv, len);
if (strcmp(pt, "depth_first") == 0) {
registry->order[i] = DEPTH_FIRST;
j += 1;
} else if (strcmp(pt, "breadth_first") == 0) {
registry->order[i] = BREADTH_FIRST;
j += registry->repeats[i];
} else {
myerr(registry->warn_and_error, "invalid order: order can only be depth_first or breadth_first");
registry->order[i] = BREADTH_FIRST;
j += registry->repeats[i];
}
} else {
registry->order[i] = BREADTH_FIRST;
j += registry->repeats[i];
}
}
if (registry->number_of_urls <= 0) {
myerr(registry->warn_and_error, "No urls.");
return;
}
registry->position[registry->number_of_runs] = registry->number_of_urls;
registry->concurrency = ap_min(registry->concurrency, j);
if (AB_DEBUG_XS) printf("AB_DEBUG: set all run info, ready to call initialize()\n");
initialize(registry);
url_keys = malloc(registry->number_of_urls * sizeof(char *));
for (k = 0; k < registry->number_of_runs; k++) {
if (AB_DEBUG_XS) printf("AB_DEBUG: starting run %d setup2 - postdata + cookie\n", k);
registry->buffersize[k] = def_buffersize;
tmpsv = *(av_fetch(run_group, k, 0));
if (SvROK(tmpsv)) {
tmphv = (HV *)SvRV(tmpsv);
if (hv_exists(tmphv, "buffersize", 10)) {
tmpsv = *(hv_fetch(tmphv, "buffersize", 10, 0));
registry->buffersize[k] = SvIV(tmpsv);
}
}
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 1\n", k);
/* error checking: make sure all of the run specific hashkeys exist */
if (hv_exists(tmphv, "urls", 4))
urls = *(hv_fetch(tmphv, "urls", 4, 0));
if (hv_exists(tmphv, "postdata", 8))
post_data = *(hv_fetch(tmphv, "postdata", 8, 0));
if (hv_exists(tmphv, "head_requests", 13))
head_requests = *(hv_fetch(tmphv, "head_requests", 13, 0));
if (hv_exists(tmphv, "cookies", 7))
cookies = *(hv_fetch(tmphv, "cookies", 7, 0));
if (hv_exists(tmphv, "content_types", 13))
ctypes = *(hv_fetch(tmphv, "content_types", 13, 0));
if (hv_exists(tmphv, "request_headers", 15))
req_headers = *(hv_fetch(tmphv, "request_headers", 15, 0));
if (hv_exists(tmphv, "keepalive", 9))
keepalive = *(hv_fetch(tmphv, "keepalive", 9, 0));
if (hv_exists(tmphv, "timelimits", 10))
url_tlimits = *(hv_fetch(tmphv, "timelimits", 10, 0));
/* configure urls */
for (i = registry->position[k]; i < registry->position[k+1]; i++) {
tmpav =(AV *) SvRV(urls);
tmpsv = *(av_fetch(tmpav, i - registry->position[k], 0));
if (SvPOK(tmpsv)) {
pt = SvPV(tmpsv, len);
url_keys[i] = pt;
if (parse_url(registry, pt, i)) {
char *warn = malloc(CBUFFSIZE * sizeof(char));
sprintf(warn, "Invalid url: %s, the information for this url may be wrong", pt);
myerr(registry->warn_and_error, warn);
free(warn);
}
} else {
myerr(registry->warn_and_error, "Undefined url in urls list");
}
}
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 2\n", k);
tmpav = (AV *) SvRV(post_data);
tmpav2 = (AV *) SvRV(head_requests);
/* find smaller of post_data array length and urls array length */
arrlen = av_len(tmpav);
i = ap_min(registry->position[k+1],
registry->position[k] + arrlen + 1);
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 2.1\n", k);
/* find larger of head_requests and post_data (to get the most data) */
arrlen2 = av_len(tmpav2);
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 2.1.1\n", k);
i = ap_max(i, registry->position[k] + arrlen2 + 1);
/* configure post_data or head_requests */
for (j = registry->position[k]; j < i; j++) {
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 2.2, j=%d\n", k, j);
if (j - registry->position[k] <= arrlen)
tmpsv = *(av_fetch(tmpav, j - registry->position[k], 0));
if (j - registry->position[k] <= arrlen2)
tmpsv2 = *(av_fetch(tmpav2, j - registry->position[k], 0));
if (j - registry->position[k] <= arrlen && (SvROK(tmpsv) || SvPOK(tmpsv))) {
/* this url is a POST request */
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 2.3, j=%d\n", k, j);
if (SvROK(tmpsv)) {
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 2.4a, j=%d\n", k, j);
tmpsv3 = SvRV(tmpsv);
if (SvTYPE(tmpsv3) == SVt_PVCV) {
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 2.4a.i, j=%d\n", k, j);
registry->postsubs[j] = tmpsv3;
registry->posting[j] = 2;
}
} else if (SvPOK(tmpsv)) {
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 2.4b, j=%d\n", k, j);
src/ApacheBench.xs view on Meta::CPAN
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 4\n", k);
/* find smaller of req_headers array length and urls array length */
tmpav = (AV *) SvRV(req_headers);
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 4.1\n", k);
i = ap_min(registry->position[k+1],
registry->position[k] + av_len(tmpav) + 1);
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 4.2\n", k);
/* configure arbitrary request headers */
for (j = registry->position[k]; j < i; j++) {
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 4.3, j=%d\n", k, j);
tmpsv = *(av_fetch(tmpav, j - registry->position[k], 0));
if (SvPOK(tmpsv)) {
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 4.4, j=%d\n", k, j);
pt = SvPV(tmpsv, len);
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 4.5, j=%d\n", k, j);
registry->req_headers[j] = pt;
} else
registry->req_headers[j] = 0;
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 4.6, j=%d\n", k, j);
}
/*If the number of req_headers strings is less than
that of urls, then assign NULL (undef) */
for (j = i; j < registry->position[k+1]; j++)
registry->req_headers[j] = 0;
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 5\n", k);
/* find smaller of ctypes array length and urls array length */
tmpav = (AV *) SvRV(ctypes);
i = ap_min(registry->position[k+1],
registry->position[k] + av_len(tmpav) + 1);
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 5.1\n", k);
/* configure ctypes */
for (j = registry->position[k]; j < i; j++) {
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 5.2, j=%d\n", k, j);
tmpsv = *(av_fetch(tmpav, j - registry->position[k], 0));
if (SvPOK(tmpsv)) {
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 5.3, j=%d\n", k, j);
pt = SvPV(tmpsv, len);
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 5.4, j=%d\n", k, j);
registry->ctypes[j] = pt;
} else
registry->ctypes[j] = 0;
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 5.5, j=%d\n", k, j);
}
/*If the number of ctypes strings is less than
that of urls, then assign NULL (undef) */
for (j = i; j < registry->position[k+1]; j++)
registry->ctypes[j] = 0;
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 6\n", k);
/* find smaller of keepalive array length and urls array length */
tmpav = (AV *) SvRV(keepalive);
i = ap_min(registry->position[k+1],
registry->position[k] + av_len(tmpav) + 1);
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 6.1\n", k);
/* configure keepalive */
for (j = registry->position[k]; j < i; j++) {
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 6.2, j=%d\n", k, j);
tmpsv = *(av_fetch(tmpav, j - registry->position[k], 0));
if (SvOK(tmpsv))
if (SvTRUE(tmpsv))
registry->keepalive[j] = 1;
else
registry->keepalive[j] = 0;
else
registry->keepalive[j] = def_keepalive;
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 6.3, j=%d\n", k, j);
}
/*If the number of keepalive strings is less than
that of urls, then assign object's default keepalive value */
for (j = i; j < registry->position[k+1]; j++)
registry->keepalive[j] = def_keepalive;
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 7\n", k);
/* find smaller of url_tlimits array length and urls array length */
tmpav = (AV *) SvRV(url_tlimits);
i = ap_min(registry->position[k+1],
registry->position[k] + av_len(tmpav) + 1);
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 7.1\n", k);
/* configure url_tlimits */
for (j = registry->position[k]; j < i; j++) {
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 7.2, j=%d\n", k, j);
tmpsv = *(av_fetch(tmpav, j - registry->position[k], 0));
if (SvOK(tmpsv)) {
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 7.3, j=%d\n", k, j);
registry->url_tlimit[j] = SvNV(tmpsv);
registry->min_tlimit =
double2timeval(ap_min(timeval2double(registry->min_tlimit),
registry->url_tlimit[j]));
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 7.3.1, j=%d, url_tlimit=%.3f sec, min tlimit so far = %.3f sec\n", k, j, registry->url_tlimit[j], timeval2double(registry->min_tlimit));
} else
registry->url_tlimit[j] = 0;
if (AB_DEBUG_XS) printf("AB_DEBUG: run %d setup2 - stage 7.4, j=%d\n", k, j);
}
/*If the number of url_tlimits is less than
that of urls, then assign 0 for no time limit */
for (j = i; j < registry->position[k+1]; j++)
registry->url_tlimit[j] = 0;
}
if (AB_DEBUG_XS) printf("AB_DEBUG: ready to test()\n");
test(registry);
if (AB_DEBUG_XS) printf("AB_DEBUG: done with test()\n");
RETVAL = newHV();/* ready to get information stored in global variables */
for (k = 0; k < registry->number_of_runs; k++) {
if (registry->memory[k] >= 1) {
AV *started = newAV();/* number of started requests for each url */
AV *good = newAV(); /* number of good responses for each url */
AV *failed = newAV(); /* number of bad responses for each url */
tmpav = newAV(); /* array to keep the thread information */
if (AB_DEBUG_XS) printf("AB_DEBUG: getting regression info for run %d\n", k);
for (i = 0; i < registry->repeats[k]; i++) {
AV *th_t = newAV(); /* times for processing and connecting */
AV *th_r = newAV(); /* times for http request */
AV *th_c = newAV(); /* connecting times */
AV *page_contents = newAV(); /* pages read from servers */
AV *request_headers = newAV(); /* HTTP requests sent to servers */
AV *request_body = newAV(); /* HTTP requests sent to servers */
AV *headers = newAV();
AV *bytes_posted = newAV();
( run in 0.436 second using v1.01-cache-2.11-cpan-df04353d9ac )