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 )