diff --git a/mod_bw.c b/mod_bw.c index 2b24696..22e038c 100644 --- a/mod_bw.c +++ b/mod_bw.c @@ -39,8 +39,8 @@ Changelog : 2010-07-20 : Fixed ap_get_server_banner unknown on older apache version 2010-05-27 : Fixed weird behaviour on Windows Hosts. (mod_bw.txt) Added high resolution timers for windows. (speed improvements) - Fixed stupid bug that caused crash when mod is enabled but there is - not a single limit. + Fixed stupid bug that caused crash when mod is enabled but there is + not a single limit. 2010-05-24 : Code Cleanup. No more warnings or stuff in Visual Studio 2010-04-28 : Bruce's Birthday Gift : A callback to the stats of the mod :) 2010-04-06 : Fixed "Invisible" memory leak. Only seen when serving HUGE streams. @@ -71,11 +71,10 @@ Changelog : #include "scoreboard.h" #if defined(WIN32) - #include - #include + #include + #include #endif - #define MIN_BW 256 /* Minimal bandwidth 256 bytes */ #define PACKET 8192 /* Default packet at 8192 bytes */ #define MAX_VHOSTS 1024 /* Default number of vhosts to show in stats */ @@ -86,23 +85,23 @@ Changelog : /* Compatibility with regex on apache less than 2.1 */ #if !AP_MODULE_MAGIC_AT_LEAST(20050127,0) - typedef regex_t ap_regex_t; - #define AP_REG_EXTENDED REG_EXTENDED - #define AP_REG_ICASE REG_ICASE + typedef regex_t ap_regex_t; + #define AP_REG_EXTENDED REG_EXTENDED + #define AP_REG_ICASE REG_ICASE #endif /* Compatibility with obsolete ap_get_server_version() */ #if !AP_MODULE_MAGIC_AT_LEAST(20051115,4) - #define ap_get_server_banner ap_get_server_version + #define ap_get_server_banner ap_get_server_version #endif /* Compatibility for APR < 1 */ #if ( defined(APR_MAJOR_VERSION) && (APR_MAJOR_VERSION < 1) ) - #define apr_atomic_inc32 apr_atomic_inc - #define apr_atomic_dec32 apr_atomic_dec - #define apr_atomic_add32 apr_atomic_add - #define apr_atomic_cas32 apr_atomic_cas - #define apr_atomic_set32 apr_atomic_set + #define apr_atomic_inc32 apr_atomic_inc + #define apr_atomic_dec32 apr_atomic_dec + #define apr_atomic_add32 apr_atomic_add + #define apr_atomic_cas32 apr_atomic_cas + #define apr_atomic_set32 apr_atomic_set #endif /* Enum types of "from address" */ @@ -130,7 +129,7 @@ typedef struct char *v_name; apr_uint32_t connection_count; apr_uint32_t bandwidth; - apr_uint32_t bytes_count; + unsigned long long bytes_count; apr_uint32_t counter; volatile apr_uint32_t lock; apr_time_t time; @@ -141,16 +140,18 @@ typedef struct ctx_struct_t { apr_bucket_brigade *bb; struct timeval wait; + unsigned long long bw_interval_bytes, bw_interval_slept, client_bw; + apr_time_t bw_interval_start_time; + long sleep_bypasses_left, sleep_bypasses_total; } ctx_struct; -/* With sid we count the shared memory needed. +/* With sid we count the shared memory needed. BwBase, is a holder to the shared memory base addres */ static char *vnames[MAX_VHOSTS]; static int sid = 0; bw_data *bwbase; apr_shm_t *shm; - /* Limits for MaxConnections based on directory */ typedef struct { @@ -193,7 +194,7 @@ typedef struct apr_array_header_t *minlimits; apr_array_header_t *sizelimits; apr_array_header_t *maxconnection; - int packet; + unsigned int packet; int error; char *directory; } bandwidth_config; @@ -211,15 +212,15 @@ module AP_MODULE_DECLARE_DATA bw_module; /*---------------------------------------------------------------------* * Configurations Directives * *---------------------------------------------------------------------*/ -/* Set the mod enabled ... or disabled */ + /* Set the mod enabled ... or disabled */ static const char *bandwidthmodule(cmd_parms * cmd, void *dconf, int flag) { bandwidth_server_config *sconf; sconf = - (bandwidth_server_config *) ap_get_module_config(cmd->server-> - module_config, - &bw_module); + (bandwidth_server_config *)ap_get_module_config(cmd->server-> + module_config, + &bw_module); sconf->state = (flag ? BANDWIDTH_ENABLED : BANDWIDTH_DISABLED); return NULL; @@ -227,14 +228,14 @@ static const char *bandwidthmodule(cmd_parms * cmd, void *dconf, int flag) /* Set force mode enabled ... or disabled */ static const char *forcebandwidthmodule(cmd_parms * cmd, void *dconf, - int flag) + int flag) { bandwidth_server_config *sconf; sconf = - (bandwidth_server_config *) ap_get_module_config(cmd->server-> - module_config, - &bw_module); + (bandwidth_server_config *)ap_get_module_config(cmd->server-> + module_config, + &bw_module); sconf->force = (flag ? BANDWIDTH_ENABLED : BANDWIDTH_DISABLED); return NULL; @@ -243,7 +244,7 @@ static const char *forcebandwidthmodule(cmd_parms * cmd, void *dconf, /* Set the packetsize used in the context */ static const char *setpacket(cmd_parms * cmd, void *s, const char *pack) { - bandwidth_config *conf = (bandwidth_config *) s; + bandwidth_config *conf = (bandwidth_config *)s; int temp; if (pack && *pack && apr_isdigit(*pack)) @@ -262,7 +263,7 @@ static const char *setpacket(cmd_parms * cmd, void *s, const char *pack) /* Set the error to send when maxconnections is reached */ static const char *bandwidtherror(cmd_parms * cmd, void *s, const char *err) { - bandwidth_config *conf = (bandwidth_config *) s; + bandwidth_config *conf = (bandwidth_config *)s; int temp; if (err && *err && apr_isdigit(*err)) @@ -280,13 +281,13 @@ static const char *bandwidtherror(cmd_parms * cmd, void *s, const char *err) /* Set the maxconnections on a per host basis */ static const char *maxconnection(cmd_parms * cmd, void *s, const char *from, - const char *maxc) + const char *maxc) { - bandwidth_config *conf = (bandwidth_config *) s; + bandwidth_config *conf = (bandwidth_config *)s; bw_maxconn *a; int temp; char *str; - char *where = (char *) apr_pstrdup(cmd->pool, from); + char *where = (char *)apr_pstrdup(cmd->pool, from); apr_status_t rv; char msgbuf[MAX_BUF]; @@ -299,17 +300,17 @@ static const char *maxconnection(cmd_parms * cmd, void *s, const char *from, return "Connections must be a number of simultaneous connections allowed/s"; - a = (bw_maxconn *) apr_array_push(conf->maxconnection); + a = (bw_maxconn *)apr_array_push(conf->maxconnection); a->x.from = where; - if (!strncasecmp(where,"u:",2)) - { + if (!strncasecmp(where, "u:", 2)) + { /* Do not limit based on origin, but on user agent */ a->type = T_AGENT; a->agent = ap_pregcomp(cmd->pool, where+2, 0); if (a->agent == NULL) return "Regular expression could not be compiled."; - + } else if (!strcasecmp(where, "all")) { a->type = T_ALL; @@ -317,7 +318,7 @@ static const char *maxconnection(cmd_parms * cmd, void *s, const char *from, else if ((str = strchr(where, '/'))) { *str++ = '\0'; rv = apr_ipsubnet_create(&a->x.ip, where, str, cmd->pool); - if(APR_STATUS_IS_EINVAL(rv)) { + if (APR_STATUS_IS_EINVAL(rv)) { /* looked nothing like an IP address */ return "An IP address was expected"; } @@ -326,14 +327,14 @@ static const char *maxconnection(cmd_parms * cmd, void *s, const char *from, return apr_pstrdup(cmd->pool, msgbuf); } a->type = T_IP; - } + } else if (!APR_STATUS_IS_EINVAL(rv = apr_ipsubnet_create(&a->x.ip, where, NULL, cmd->pool))) { if (rv != APR_SUCCESS) { apr_strerror(rv, msgbuf, sizeof msgbuf); return apr_pstrdup(cmd->pool, msgbuf); } a->type = T_IP; - } + } else { /* no slash, didn't look like an IP address => must be a host */ a->type = T_HOST; } @@ -345,13 +346,13 @@ static const char *maxconnection(cmd_parms * cmd, void *s, const char *from, /* Set the bandwidth on a per host basis */ static const char *bandwidth(cmd_parms * cmd, void *s, const char *from, - const char *bw) + const char *bw) { - bandwidth_config *conf = (bandwidth_config *) s; + bandwidth_config *conf = (bandwidth_config *)s; bw_entry *a; long int temp; char *str; - char *where = (char *) apr_pstrdup(cmd->pool, from); + char *where = (char *)apr_pstrdup(cmd->pool, from); apr_status_t rv; char msgbuf[MAX_BUF]; @@ -364,16 +365,16 @@ static const char *bandwidth(cmd_parms * cmd, void *s, const char *from, if (temp < 0) return "BandWidth must be a number of bytes/s"; - a = (bw_entry *) apr_array_push(conf->limits); + a = (bw_entry *)apr_array_push(conf->limits); a->x.from = where; - if (!strncasecmp(where,"u:",2)) + if (!strncasecmp(where, "u:", 2)) { /* Do not limit based on origin, but on user agent */ a->type = T_AGENT; - a->agent = ap_pregcomp(cmd->pool, where+2, 0); + a->agent = ap_pregcomp(cmd->pool, where + 2, 0); if (a->agent == NULL) - return "Regular expression could not be compiled."; - + return "Regular expression could not be compiled."; + } else if (!strcasecmp(where, "all")) { a->type = T_ALL; @@ -381,7 +382,7 @@ static const char *bandwidth(cmd_parms * cmd, void *s, const char *from, else if ((str = strchr(where, '/'))) { *str++ = '\0'; rv = apr_ipsubnet_create(&a->x.ip, where, str, cmd->pool); - if(APR_STATUS_IS_EINVAL(rv)) { + if (APR_STATUS_IS_EINVAL(rv)) { /* looked nothing like an IP address */ return "An IP address was expected"; } @@ -403,25 +404,23 @@ static const char *bandwidth(cmd_parms * cmd, void *s, const char *from, } if (sid < MAX_VHOSTS) { - vnames[sid] = apr_pcalloc(cmd->pool,apr_snprintf(msgbuf,MAX_BUF,"%s,%s",cmd->server->server_hostname,where) ); - vnames[sid] = (char *) apr_pstrdup(cmd->pool, msgbuf); + vnames[sid] = apr_pcalloc(cmd->pool, apr_snprintf(msgbuf, MAX_BUF, "%s,%s", cmd->server->server_hostname, where)); + vnames[sid] = (char*)apr_pstrdup(cmd->pool, msgbuf); } a->rate = temp; a->sid = sid++; - return NULL; } /* Set the minimum bandwidth to send */ -static const char *minbandwidth(cmd_parms * cmd, void *s, const char *from, - const char *bw) +static const char *minbandwidth(cmd_parms * cmd, void *s, const char *from, const char *bw) { - bandwidth_config *conf = (bandwidth_config *) s; + bandwidth_config *conf = (bandwidth_config *)s; bw_entry *a; long int temp; char *str; - char *where = (char *) apr_pstrdup(cmd->pool, from); + char *where = (char *)apr_pstrdup(cmd->pool, from); apr_status_t rv; char msgbuf[MAX_BUF]; @@ -430,13 +429,13 @@ static const char *minbandwidth(cmd_parms * cmd, void *s, const char *from, else return "Invalid argument"; - a = (bw_entry *) apr_array_push(conf->minlimits); + a = (bw_entry *)apr_array_push(conf->minlimits); a->x.from = where; - if (!strncasecmp(where,"u:",2)) + if (!strncasecmp(where, "u:", 2)) { /* Do not limit based on origin, but on user agent */ a->type = T_AGENT; - a->agent = ap_pregcomp(cmd->pool, where+2, 0); + a->agent = ap_pregcomp(cmd->pool, where + 2, 0); if (a->agent == NULL) return "Regular expression could not be compiled."; @@ -447,7 +446,7 @@ static const char *minbandwidth(cmd_parms * cmd, void *s, const char *from, else if ((str = strchr(where, '/'))) { *str++ = '\0'; rv = apr_ipsubnet_create(&a->x.ip, where, str, cmd->pool); - if(APR_STATUS_IS_EINVAL(rv)) { + if (APR_STATUS_IS_EINVAL(rv)) { /* looked nothing like an IP address */ return "An IP address was expected"; } @@ -475,9 +474,9 @@ static const char *minbandwidth(cmd_parms * cmd, void *s, const char *from, /* Set the large file bandwidth limit */ static const char *largefilelimit(cmd_parms * cmd, void *s, const char *file, - const char *size, const char *bw) + const char *size, const char *bw) { - bandwidth_config *conf = (bandwidth_config *) s; + bandwidth_config *conf = (bandwidth_config *)s; bw_sizel *a; long int temp, tsize; char msgbuf[MAX_BUF]; @@ -501,14 +500,14 @@ static const char *largefilelimit(cmd_parms * cmd, void *s, const char *file, if (tsize < 0) return "File size must be a number of Kbytes"; - a = (bw_sizel *) apr_array_push(conf->sizelimits); - a->file = (char *) file; + a = (bw_sizel *)apr_array_push(conf->sizelimits); + a->file = (char*)file; a->size = tsize; a->rate = temp; if (sid < MAX_VHOSTS) { - vnames[sid] = apr_pcalloc(cmd->pool,apr_snprintf(msgbuf,MAX_BUF,"%s,%s",cmd->server->server_hostname,file) ); - vnames[sid] = (char *) apr_pstrdup(cmd->pool, msgbuf); + vnames[sid] = apr_pcalloc(cmd->pool, apr_snprintf(msgbuf, MAX_BUF, "%s,%s", cmd->server->server_hostname, file)); + vnames[sid] = (char *)apr_pstrdup(cmd->pool, msgbuf); } a->sid = sid++; @@ -520,11 +519,11 @@ static const char *largefilelimit(cmd_parms * cmd, void *s, const char *file, * Helper Functions * *----------------------------------------------------------------------------*/ -/* Match the input, as part of a domain */ + /* Match the input, as part of a domain */ static int in_domain(const char *domain, const char *what) { - int dl = strlen(domain); - int wl = strlen(what); + size_t dl = strlen(domain); + size_t wl = strlen(what); if ((wl - dl) >= 0) { if (strcasecmp(domain, &what[wl - dl]) != 0) @@ -544,9 +543,9 @@ static int in_domain(const char *domain, const char *what) } /* Get the bandwidth limit based on from address */ -static long get_bw_rate(request_rec * r, apr_array_header_t * a) +static long get_bw_rate(request_rec *r, apr_array_header_t *a) { - bw_entry *e = (bw_entry *) a->elts; + bw_entry *e = (bw_entry *)a->elts; const char *remotehost = NULL; int i; int gothost = 0; @@ -557,14 +556,14 @@ static long get_bw_rate(request_rec * r, apr_array_header_t * a) switch (e[i].type) { case T_AGENT: uastr = apr_table_get(r->headers_in, "User-Agent"); - if (e[i].agent && uastr && ap_regexec(e[i].agent, uastr, 0, NULL, 0)==0 ) + if (e[i].agent && uastr && ap_regexec(e[i].agent, uastr, 0, NULL, 0) == 0) return (e[i].rate); break; case T_ALL: return e[i].rate; case T_IP: - if (apr_ipsubnet_test(e[i].x.ip, r->connection->remote_addr)) { + if (apr_ipsubnet_test(e[i].x.ip, r->connection->client_addr)) { return e[i].rate; } break; @@ -573,7 +572,7 @@ static long get_bw_rate(request_rec * r, apr_array_header_t * a) int remotehost_is_ip; remotehost = ap_get_remote_host(r->connection, r->per_dir_config, - REMOTE_DOUBLE_REV, &remotehost_is_ip); + REMOTE_DOUBLE_REV, &remotehost_is_ip); if ((remotehost == NULL) || remotehost_is_ip) gothost = 1; @@ -585,14 +584,14 @@ static long get_bw_rate(request_rec * r, apr_array_header_t * a) return (e[i].rate); break; } - + } return 0; } -/* - Match the pattern with the last digist from filename - An asterisk means any. +/* + Match the pattern with the last digist from filename + An asterisk means any. */ static int match_ext(const char *file, char *match) { @@ -611,10 +610,10 @@ static int match_ext(const char *file, char *match) } /* Get the bandwidth limit based on filesize */ -static long get_bw_filesize(request_rec * r, apr_array_header_t * a, - apr_uint32_t filesize, const char *filename) +static long get_bw_filesize(request_rec *r, apr_array_header_t *a, + apr_uint32_t filesize, const char *filename) { - bw_sizel *e = (bw_sizel *) a->elts; + bw_sizel *e = (bw_sizel *)a->elts; int i; apr_uint32_t tmpsize = 0, tmprate = 0; @@ -635,9 +634,9 @@ static long get_bw_filesize(request_rec * r, apr_array_header_t * a, } /* Get the MaxConnections allowed */ -static int get_maxconn(request_rec * r, apr_array_header_t * a) +static int get_maxconn(request_rec *r, apr_array_header_t *a) { - bw_maxconn *e = (bw_maxconn *) a->elts; + bw_maxconn *e = (bw_maxconn *)a->elts; const char *remotehost = NULL; int i; int gothost = 0; @@ -648,14 +647,14 @@ static int get_maxconn(request_rec * r, apr_array_header_t * a) switch (e[i].type) { case T_AGENT: uastr = apr_table_get(r->headers_in, "User-Agent"); - if (e[i].agent && uastr && ap_regexec(e[i].agent, uastr, 0, NULL, 0)==0 ) + if (e[i].agent && uastr && ap_regexec(e[i].agent, uastr, 0, NULL, 0) == 0) return (e[i].max); break; case T_ALL: return e[i].max; case T_IP: - if (apr_ipsubnet_test(e[i].x.ip, r->connection->remote_addr)) { + if (apr_ipsubnet_test(e[i].x.ip, r->connection->client_addr)) { return e[i].max; } break; @@ -664,7 +663,7 @@ static int get_maxconn(request_rec * r, apr_array_header_t * a) int remotehost_is_ip; remotehost = ap_get_remote_host(r->connection, r->per_dir_config, - REMOTE_DOUBLE_REV, &remotehost_is_ip); + REMOTE_DOUBLE_REV, &remotehost_is_ip); if ((remotehost == NULL) || remotehost_is_ip) gothost = 1; @@ -682,9 +681,9 @@ static int get_maxconn(request_rec * r, apr_array_header_t * a) } /* Get an id based on bandwidth limit */ -static int get_sid(request_rec * r, apr_array_header_t * a) +static int get_sid(request_rec *r, apr_array_header_t *a) { - bw_entry *e = (bw_entry *) a->elts; + bw_entry *e = (bw_entry *)a->elts; const char *remotehost = NULL; int i; int gothost = 0; @@ -692,21 +691,21 @@ static int get_sid(request_rec * r, apr_array_header_t * a) remotehost = ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_HOST, - NULL); + NULL); for (i = 0; i < a->nelts; i++) { switch (e[i].type) { case T_AGENT: uastr = apr_table_get(r->headers_in, "User-Agent"); - if (e[i].agent && uastr && ap_regexec(e[i].agent, uastr, 0, NULL, 0)==0 ) + if (e[i].agent && uastr && ap_regexec(e[i].agent, uastr, 0, NULL, 0) == 0) return (e[i].sid); break; case T_ALL: return e[i].sid; case T_IP: - if (apr_ipsubnet_test(e[i].x.ip, r->connection->remote_addr)) { + if (apr_ipsubnet_test(e[i].x.ip, r->connection->client_addr)) { return e[i].sid; } break; @@ -715,7 +714,7 @@ static int get_sid(request_rec * r, apr_array_header_t * a) int remotehost_is_ip; remotehost = ap_get_remote_host(r->connection, r->per_dir_config, - REMOTE_DOUBLE_REV, &remotehost_is_ip); + REMOTE_DOUBLE_REV, &remotehost_is_ip); if ((remotehost == NULL) || remotehost_is_ip) gothost = 1; @@ -733,10 +732,10 @@ static int get_sid(request_rec * r, apr_array_header_t * a) } /* Get an id based on filesize limit */ -static int get_f_sid(request_rec * r, apr_array_header_t * a, apr_uint32_t filesize, - const char *filename) +static int get_f_sid(request_rec *r, apr_array_header_t *a, apr_uint32_t filesize, + const char *filename) { - bw_sizel *e = (bw_sizel *) a->elts; + bw_sizel *e = (bw_sizel *)a->elts; int i; apr_uint32_t tmpsize = 0, tmpsid = -1; @@ -759,7 +758,7 @@ static int get_f_sid(request_rec * r, apr_array_header_t * a, apr_uint32_t files } /* Update memory (shm) counters, which holds the bw data per context */ -static void update_counters(bw_data * bwstat, ap_filter_t * f) +static void update_counters(bw_data *bwstat, ap_filter_t *f) { apr_time_t nowtime; @@ -770,8 +769,8 @@ static void update_counters(bw_data * bwstat, ap_filter_t * f) if (apr_atomic_cas32(&bwstat->lock, 1, 0) == 0) { /* Calculate bw used in the last timeinterval */ - bwstat->bandwidth = (apr_uint32_t) ( - (bwstat->bytes_count / (double) (nowtime - bwstat->time)) * + bwstat->bandwidth = (apr_uint32_t)( + (bwstat->bytes_count / (double)(nowtime - bwstat->time)) * 1000000); /* Reset counters */ @@ -786,7 +785,7 @@ static void update_counters(bw_data * bwstat, ap_filter_t * f) } } -static int callback(request_rec * r) +static int callback(request_rec *r) { int t; bw_data *bwstat; @@ -795,20 +794,20 @@ static int callback(request_rec * r) return OK; } - if (r->args && !strncasecmp(r->args,"csv",3)) + if (r->args && !strncasecmp(r->args, "csv", 3)) { ap_set_content_type(r, "text/plain"); - ap_rputs("id,vhost,scope,lock,count,bw,bytes,hits\n",r); - + ap_rputs("id,vhost,scope,lock,count,bw,bytes,hits\n", r); + for (t = 0; t < sid; t++) { bwstat = bwbase + t; - ap_rprintf(r,"%d,%s,%d,%d,%d,%d,%d\n",t,bwstat->v_name,bwstat->lock,bwstat->connection_count,bwstat->bandwidth,bwstat->bytes_count,bwstat->counter); + ap_rprintf(r, "%d,%s,%d,%d,%d,%d,%d\n", t, bwstat->v_name, bwstat->lock, bwstat->connection_count, bwstat->bandwidth, bwstat->bytes_count, bwstat->counter); } return OK; } - + ap_set_content_type(r, "text/html"); ap_rputs(DOCTYPE_HTML_3_2, r); @@ -821,7 +820,7 @@ static int callback(request_rec * r) ap_rputs(" \n", r); ap_rputs("

\n", r); ap_rprintf(r, " Apache HTTP Server version: \"%s\"\n", - ap_get_server_banner()); + ap_get_server_banner()); ap_rputs("
\n", r); ap_rprintf(r, " Server built: \"%s\"\n", ap_get_server_built()); ap_rputs("

\n", r);; @@ -831,47 +830,44 @@ static int callback(request_rec * r) bwstat = bwbase + t; /* This inits the struct that will contain current bw use */ - ap_rputs("
",r); - ap_rprintf(r,"id : %d
",t); - ap_rprintf(r,"name : %s
",bwstat->v_name); - ap_rprintf(r,"lock : %d
",bwstat->lock); - ap_rprintf(r,"count: %d
",bwstat->connection_count); - ap_rprintf(r,"bw : %d
",bwstat->bandwidth); - ap_rprintf(r,"bytes: %d
",bwstat->bytes_count); - ap_rprintf(r,"hits : %d
",bwstat->counter); + ap_rputs("
", r); + ap_rprintf(r, "id : %d
", t); + ap_rprintf(r, "name : %s
", bwstat->v_name); + ap_rprintf(r, "lock : %d
", bwstat->lock); + ap_rprintf(r, "count: %d
", bwstat->connection_count); + ap_rprintf(r, "bw : %d
", bwstat->bandwidth); + ap_rprintf(r, "bytes: %d
", bwstat->bytes_count); + ap_rprintf(r, "hits : %d
", bwstat->counter); } ap_rputs(" \n", r); ap_rputs("\n", r); - return OK; - - + return OK; } /*----------------------------------------------------------------------------* * The Handler and the Output Filter. Core of the mod. * *----------------------------------------------------------------------------*/ -/* With this handler, we can *force* the use of the mod. */ -static int handle_bw(request_rec * r) + /* With this handler, we can *force* the use of the mod. */ +static int handle_bw(request_rec *r) { bandwidth_server_config *sconf = - (bandwidth_server_config *) ap_get_module_config(r->server-> - module_config, - &bw_module); + (bandwidth_server_config *)ap_get_module_config(r->server-> + module_config, + &bw_module); bandwidth_config *conf = - (bandwidth_config *) ap_get_module_config(r->per_dir_config, - &bw_module); - bw_data *bwstat; + (bandwidth_config *)ap_get_module_config(r->per_dir_config, + &bw_module); + bw_data * bwstat; apr_int32_t confid; /* Only work on main request/no subrequests */ if (r->main) return DECLINED; - if (strcmp(r->handler, "modbw-handler")==0) return callback(r); - + if (strcmp(r->handler, "modbw-handler") == 0) return callback(r); /* Return if module is not enabled */ if (sconf->state == BANDWIDTH_DISABLED) @@ -889,7 +885,7 @@ static int handle_bw(request_rec * r) /* If we are too busy, deny connection */ confid = get_maxconn(r, conf->maxconnection); - if ((bwstat->connection_count >= (apr_uint32_t) confid) && (confid > 0)) + if ((bwstat->connection_count >= (apr_uint32_t)confid) && (confid > 0)) return conf->error; } @@ -901,25 +897,27 @@ static int handle_bw(request_rec * r) return DECLINED; } -static int bw_filter(ap_filter_t * f, apr_bucket_brigade * bb) +static int bw_filter(ap_filter_t *f, apr_bucket_brigade *bb) { request_rec *r = f->r; bandwidth_config *conf = - (bandwidth_config *) ap_get_module_config(r->per_dir_config, - &bw_module); + (bandwidth_config *)ap_get_module_config(r->per_dir_config, + &bw_module); bandwidth_server_config *sconf = - (bandwidth_server_config *) ap_get_module_config(r->server-> - module_config, - &bw_module); + (bandwidth_server_config *)ap_get_module_config(r->server-> + module_config, + &bw_module); ctx_struct *ctx = f->ctx; apr_bucket *b = APR_BRIGADE_FIRST(bb); bw_data *bwstat, *bwmaxconn; int confid = -1, connid = -1; apr_size_t packet = conf->packet, bytes = 0; apr_off_t bblen = 0; - long int bw_rate, bw_min, bw_f_rate, cur_rate = 0, sleep; + apr_size_t bw_rate, bw_min, bw_f_rate, cur_rate = 0; + apr_interval_time_t sleep; const char *buf; const char *filename; + long current_sleep_bypasses = 0; /* Only work on main request/no subrequests */ if (r->main) { @@ -945,7 +943,7 @@ static int bw_filter(ap_filter_t * f, apr_bucket_brigade * bb) /* Get the File Rate. r->finfo.size is not used anymore. */ bblen = r->bytes_sent; - bw_f_rate = get_bw_filesize(r, conf->sizelimits, (off_t) bblen, filename); + bw_f_rate = get_bw_filesize(r, conf->sizelimits, (off_t)bblen, filename); /* Check if we've got an ilimited client */ @@ -960,7 +958,7 @@ static int bw_filter(ap_filter_t * f, apr_bucket_brigade * bb) - If file size is zero, all files apply */ if (bw_f_rate && (bw_rate > bw_f_rate || !bw_rate)) { - confid = get_f_sid(r, conf->sizelimits, (off_t) bblen, filename); + confid = get_f_sid(r, conf->sizelimits, (off_t)bblen, filename); bw_rate = bw_f_rate; } @@ -969,11 +967,17 @@ static int bw_filter(ap_filter_t * f, apr_bucket_brigade * bb) else if (!bw_min) bw_min = MIN_BW; - /* Initialize our temporal space */ + /* Initialize our temporal space, which survives multiple invokations for the same request */ if (ctx == NULL) { - apr_bucket_alloc_t *bucket_alloc = apr_bucket_alloc_create(f->r->pool); + apr_bucket_alloc_t* bucket_alloc = apr_bucket_alloc_create(f->r->pool); f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx)); ctx->bb = apr_brigade_create(f->r->pool, bucket_alloc); + + ctx->bw_interval_start_time = apr_time_now(); + ctx->bw_interval_slept = 0; + ctx->bw_interval_bytes = 0; + ctx->sleep_bypasses_left = 0; + ctx->sleep_bypasses_total = 0; } /* We "get" the data of the current configuration */ @@ -990,13 +994,12 @@ static int bw_filter(ap_filter_t * f, apr_bucket_brigade * bb) /* Verbose Output */ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, - "ID: %i Directory : %s Rate : %ld Minimum : %ld Size rate : %ld", - confid, conf->directory, bw_rate, bw_min, bw_f_rate); + "ID: %i; Directory : %s; File : %s; Rate : %ld; Minimum : %ld; Size rate : %ld;", + confid, conf->directory, filename, bw_rate, bw_min, bw_f_rate); ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, - "clients : %d/%d rate/min : %ld,%ld", bwmaxconn->connection_count, - (connid >= 0) ? get_maxconn(r, conf->maxconnection) : 0, - bw_rate, bw_min); - + "clients : %d/%d; rate/min : %ld,%ld", bwmaxconn->connection_count, + (connid >= 0) ? get_maxconn(r, conf->maxconnection) : 0, + bw_rate, bw_min); /* - We get buckets until a sentinel appears @@ -1009,6 +1012,7 @@ static int bw_filter(ap_filter_t * f, apr_bucket_brigade * bb) APR_BUCKET_REMOVE(b); APR_BRIGADE_INSERT_TAIL(ctx->bb, b); ap_pass_brigade(f->next, ctx->bb); + apr_brigade_cleanup(ctx->bb); /* Delete 1 active connection */ apr_atomic_dec32(&bwmaxconn->connection_count); @@ -1017,15 +1021,16 @@ static int bw_filter(ap_filter_t * f, apr_bucket_brigade * bb) if (apr_bucket_read(b, &buf, &bytes, APR_NONBLOCK_READ) == APR_SUCCESS) { + /* This changed, cause of the limit handling error.. see below */ while (bytes > 0) { /* - - Ok, i'm doing lots of things here. The bw the client will have, is + - Ok, i'm doing lots of things here. The bw the client will have, is the bw available divided by the number of clients. - - The minimum bw, will always be MIN_BW. If all bw is used, and new + - The minimum bw, will always be MIN_BW. If all bw is used, and new connections arrives, they'll have MIN_BW bw available. */ - cur_rate = (long int) bw_rate / bwmaxconn->connection_count; + cur_rate = (long int)bw_rate / bwmaxconn->connection_count; if (cur_rate > bw_rate) cur_rate = bw_rate; @@ -1050,21 +1055,58 @@ static int bw_filter(ap_filter_t * f, apr_bucket_brigade * bb) /* Here we get the time we need to sleep to get the specified bw */ sleep = - (long int) (1000000 / - ((double) cur_rate / (double) packet)); + (unsigned long)(1000000 / + ((double)cur_rate / (double)packet)); + + /* if more than 15ms has elapsed, roughly calculate client's actual bandwidth */ + if ((apr_time_now() - ctx->bw_interval_slept) - ctx->bw_interval_start_time > 15000) { + ctx->client_bw = (ctx->bw_interval_bytes * 1000 / (unsigned long long)((apr_time_now() - ctx->bw_interval_slept) - ctx->bw_interval_start_time)) * 1000; + ctx->bw_interval_bytes = 0; + ctx->bw_interval_slept = 0; + ctx->bw_interval_start_time = apr_time_now(); + /* Verbose logging */ + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Client BW is ~%ld B/s. This %s than available BW (%ld). %s", ctx->client_bw, (ctx->client_bw > cur_rate ? "More" : "Less"), cur_rate, (ctx->client_bw > cur_rate ? "BW Limiting!" : "Not BW limiting!")); + } + #if defined(WIN32) - if (sleep < 200000 && cur_rate > 1024) - { - sleep = 200000; - packet = cur_rate/5; - if (bytes < packet) - packet = bytes; - } + /* For Windows, assume the minimum sleep time is 10ms. If the needed sleep time is less than 10ms, calculate + how many packets to send before sleeping for 10ms. For very small buckets, this can result in an + absurdly high number of packets to send, probably more than there are buckets, so it doesn't matter. */ + if (sleep < 10000 && ctx->client_bw > cur_rate) + { + /* calculate, based on available bandwidth right now, how many packets can be sent in 10 ms */ + current_sleep_bypasses = (long)((double)10000 / (double)(sleep + 1)); /* plus 1 microsecond to avoid divide by zero errors */ + + sleep = 10000; + + if (ctx->sleep_bypasses_left == 0) { /* If not currently counting down packets... */ + ctx->sleep_bypasses_total = current_sleep_bypasses; /* setup counters */ + ctx->sleep_bypasses_left = current_sleep_bypasses; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + "File: %s. BW: %ld B/s. Packet: %ld bytes. Bypassing sleep for %i packet(s).", filename, cur_rate, packet, ctx->sleep_bypasses_left); + } + else { + /* If number of packets to send in 10ms has changed since last calc because available bandwidth changed, recalcuate */ + if (ctx->sleep_bypasses_total != current_sleep_bypasses) { + /* new packet counter = packets_per_10ms minus packets already sent */ + ctx->sleep_bypasses_left = current_sleep_bypasses - (ctx->sleep_bypasses_total - ctx->sleep_bypasses_left); + + if (ctx->sleep_bypasses_left < 0) { + ctx->sleep_bypasses_left = 0; /* if new packets to send is less than zero, reset to zero so we sleep now*/ + } + ctx->sleep_bypasses_total = current_sleep_bypasses; /* Update the total packets to send in this period */ + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, + "File: %s. Available BW changed to %ld B/s. Now bypassing sleep for %ld more packet(s).", filename, cur_rate, ctx->sleep_bypasses_left); + } + } + } + else { + ctx->sleep_bypasses_left = 0; + } #endif - - /* + /* Here, we are going to split the bucket, and send it on piece at a time, - doing a "delay" between each piece. That way, we send the data at the + doing a "delay" between each piece. That way, we send the data at the specified rate. */ apr_bucket_split(b, packet); @@ -1073,25 +1115,36 @@ static int bw_filter(ap_filter_t * f, apr_bucket_brigade * bb) /* Decrease our counter */ bytes -= packet; + ctx->bw_interval_bytes += packet; /* Flush and move to the next bucket */ ap_pass_brigade(f->next, ctx->bb); + apr_brigade_cleanup(ctx->bb); b = APR_BRIGADE_FIRST(bb); /* Add the number of bytes transferred, so we can get an estimated bw usage */ - apr_atomic_add32(&bwstat->bytes_count, packet); + apr_atomic_add64(&bwstat->bytes_count, packet); /* If the connection goes to hell... go with it ! */ if (r->connection->aborted) { /* Verbose. Tells when the connection was ended */ - ap_log_error(APLOG_MARK, APLOG_DEBUG, - 0, r->server, "Connection to hell"); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, + 0, r, "Connection to hell"); apr_atomic_dec32(&bwmaxconn->connection_count); return APR_SUCCESS; } - /* Sleep ... zZZzZzZzzzz */ - apr_sleep(sleep); + /* Sleep ... but only if the time is right (see Windows clock resolution fix above) */ + if (ctx->sleep_bypasses_left > 0) { + ctx->sleep_bypasses_left--; + } + else { + if (ctx->client_bw > cur_rate) { /* Only sleep if the client's calculated bw is greater than available */ + apr_sleep(sleep); /* bandwidth, otherwise there's no reason to sleep. */ + ctx->bw_interval_slept += sleep; + } + ctx->sleep_bypasses_left = 0; + } /* Refresh counters, so we can keep working :) */ update_counters(bwstat, f); @@ -1102,11 +1155,12 @@ static int bw_filter(ap_filter_t * f, apr_bucket_brigade * bb) APR_BRIGADE_INSERT_TAIL(ctx->bb, b); b = APR_BRIGADE_FIRST(bb); - /* Add the number of bytes to the counter */ - apr_atomic_add32(&bwstat->bytes_count, bytes); + /* Add the number of bytes to the counter - switched to the 64bit counter for bigger files */ + apr_atomic_add64(&bwstat->bytes_count, bytes); /* Pass the final brigade */ ap_pass_brigade(f->next, ctx->bb); + apr_brigade_cleanup(ctx->bb); } /* Delete 1 active connection to the record */ @@ -1121,16 +1175,16 @@ static int bw_filter(ap_filter_t * f, apr_bucket_brigade * bb) /*----------------------------------------------------------------------------* * Module Init functions * *----------------------------------------------------------------------------*/ -static int bw_init(apr_pool_t * p, apr_pool_t * plog, apr_pool_t * ptemp, - server_rec * s) +static int bw_init(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, + server_rec *s) { apr_status_t status; apr_size_t retsize; apr_size_t shm_size; - bw_data *bwstat; + bw_data* bwstat; int t; #if defined(WIN32) - TIMECAPS resolution; + TIMECAPS resolution; #endif /* These two help ensure that we only init once. */ @@ -1139,8 +1193,8 @@ static int bw_init(apr_pool_t * p, apr_pool_t * plog, apr_pool_t * ptemp, apr_pool_userdata_get(&data, userdata_key, s->process->pool); if (!data) { - apr_pool_userdata_set((const void *) 1, userdata_key, - apr_pool_cleanup_null, s->process->pool); + apr_pool_userdata_set((const void *)1, userdata_key, + apr_pool_cleanup_null, s->process->pool); return OK; } @@ -1149,7 +1203,7 @@ static int bw_init(apr_pool_t * p, apr_pool_t * plog, apr_pool_t * ptemp, if (status != APR_SUCCESS) return HTTP_INTERNAL_SERVER_ERROR; - shm_size = (apr_size_t) sizeof(bw_data) * sid; + shm_size = (apr_size_t)sizeof(bw_data) * sid; /* If there was a memory block already assigned.. destroy it */ @@ -1157,11 +1211,12 @@ static int bw_init(apr_pool_t * p, apr_pool_t * plog, apr_pool_t * ptemp, status = apr_shm_destroy(shm); if (status != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, - "mod_bw : Couldn't destroy old memory block\n"); + "mod_bw : Couldn't destroy old memory block\n"); return status; - } else { + } + else { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, - "mod_bw : Old Shared memory block, destroyed."); + "mod_bw : Old Shared memory block, destroyed."); } } @@ -1169,32 +1224,32 @@ static int bw_init(apr_pool_t * p, apr_pool_t * plog, apr_pool_t * ptemp, status = apr_shm_create(&shm, shm_size, NULL, p); if (status != APR_SUCCESS) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, - "mod_bw : Error creating shm block\n"); + "mod_bw : Error creating shm block\n"); return status; } /* Check size of shared memory block */ retsize = apr_shm_size_get(shm); if (retsize != shm_size) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, - "mod_bw : Error allocating shared memory block\n"); + "mod_bw : Error allocating shared memory block\n"); return status; } /* Init shm block */ bwbase = apr_shm_baseaddr_get(shm); if (bwbase == NULL) { ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, - "mod_bw : Error creating status block.\n"); + "mod_bw : Error creating status block.\n"); return status; } memset(bwbase, 0, retsize); ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, - "mod_bw : Memory Allocated %d bytes (each conf takes %d bytes)", - (int) retsize, (int) sizeof(bw_data)); + "mod_bw : Memory Allocated %d bytes (each conf takes %d bytes)", + (int)retsize, (int)sizeof(bw_data)); if (retsize < (sizeof(bw_data) * sid)) { ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, - "mod_bw : Not enough memory allocated!! Giving up"); + "mod_bw : Not enough memory allocated!! Giving up"); return HTTP_INTERNAL_SERVER_ERROR; } @@ -1212,55 +1267,54 @@ static int bw_init(apr_pool_t * p, apr_pool_t * plog, apr_pool_t * ptemp, } ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, - "mod_bw : Version %s - Initialized [%d Confs]", VERSION, - sid); + "mod_bw : Version %s - Initialized [%d Confs]", VERSION, + sid); #if defined(WIN32) - // Set the timer resolution to its minimum - if (timeGetDevCaps (&resolution, sizeof (TIMECAPS)) == TIMERR_NOERROR) + // Set the timer resolution to its minimum + if (timeGetDevCaps(&resolution, sizeof(TIMECAPS)) == TIMERR_NOERROR) { - ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, - "mod_bw : Supported resolution for Timers [ Min: %d Max: %d ]",resolution.wPeriodMin,resolution.wPeriodMax); - - - if (timeBeginPeriod (resolution.wPeriodMin) == TIMERR_NOERROR) ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, - "mod_bw : Enabling High resolution timers [ %d ms ]",resolution.wPeriodMin); - else - ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, - "mod_bw : Can't enable High Resolution timers. Speed might be reduced."); - } + "mod_bw : Supported resolution for Timers [ Min: %d Max: %d ]", resolution.wPeriodMin, resolution.wPeriodMax); + + if (timeBeginPeriod(resolution.wPeriodMin) == TIMERR_NOERROR) + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, + "mod_bw : Enabling High resolution timers [ %d ms ]", resolution.wPeriodMin); + else + ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, + "mod_bw : Can't enable High Resolution timers. Speed might be reduced."); + } #endif return OK; } -static void *create_bw_config(apr_pool_t * p, char *path) +static void *create_bw_config(apr_pool_t *p, char *path) { bandwidth_config *new = - (bandwidth_config *) apr_palloc(p, sizeof(bandwidth_config)); + (bandwidth_config *)apr_palloc(p, sizeof(bandwidth_config)); new->limits = apr_array_make(p, 20, sizeof(bw_entry)); new->minlimits = apr_array_make(p, 20, sizeof(bw_entry)); new->sizelimits = apr_array_make(p, 10, sizeof(bw_sizel)); new->maxconnection = apr_array_make(p, 10, sizeof(bw_maxconn)); - new->directory = (char *) apr_pstrdup(p, path); + new->directory = (char *)apr_pstrdup(p, path); new->packet = PACKET; new->error = HTTP_SERVICE_UNAVAILABLE; return (void *) new; } -static void *create_bw_server_config(apr_pool_t * p, server_rec * s) +static void *create_bw_server_config(apr_pool_t *p, server_rec *s) { bandwidth_server_config *new; new = - (bandwidth_server_config *) apr_pcalloc(p, - sizeof - (bandwidth_server_config)); + (bandwidth_server_config *)apr_pcalloc(p, + sizeof + (bandwidth_server_config)); new->state = BANDWIDTH_DISABLED; new->force = BANDWIDTH_DISABLED; @@ -1271,7 +1325,7 @@ static void *create_bw_server_config(apr_pool_t * p, server_rec * s) * Apache register functions * *----------------------------------------------------------------------------*/ -static void register_hooks(apr_pool_t * p) +static void register_hooks(apr_pool_t *p) { /* - Register a handler, which enforces mod_bw if needed