Return-Path: Delivered-To: apmail-httpd-cvs-archive@www.apache.org Received: (qmail 64951 invoked from network); 9 Sep 2005 18:39:31 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 9 Sep 2005 18:39:31 -0000 Received: (qmail 78509 invoked by uid 500); 9 Sep 2005 18:39:31 -0000 Delivered-To: apmail-httpd-cvs-archive@httpd.apache.org Received: (qmail 78314 invoked by uid 500); 9 Sep 2005 18:39:30 -0000 Mailing-List: contact cvs-help@httpd.apache.org; run by ezmlm Precedence: bulk Reply-To: dev@httpd.apache.org list-help: list-unsubscribe: List-Post: List-Id: Delivered-To: mailing list cvs@httpd.apache.org Delivered-To: moderator for cvs@httpd.apache.org Received: (qmail 10768 invoked by uid 99); 9 Sep 2005 13:34:16 -0000 X-ASF-Spam-Status: No, hits=-9.8 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Message-ID: <20050909133412.52761.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: svn commit: r279770 - in /httpd/mod_bw/trunk: ChangeLog LICENSE TODO mod_bw.c mod_bw.txt Date: Fri, 09 Sep 2005 13:34:10 -0000 To: cvs@httpd.apache.org From: ivn@apache.org X-Mailer: svnmailer-1.0.5 X-Virus-Checked: Checked by ClamAV on apache.org X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Author: ivn Date: Fri Sep 9 06:33:46 2005 New Revision: 279770 URL: http://svn.apache.org/viewcvs?rev=279770&view=rev Log: First Commit - Release Version 0.7 Added: httpd/mod_bw/trunk/ChangeLog httpd/mod_bw/trunk/LICENSE httpd/mod_bw/trunk/TODO httpd/mod_bw/trunk/mod_bw.c httpd/mod_bw/trunk/mod_bw.txt Added: httpd/mod_bw/trunk/ChangeLog URL: http://svn.apache.org/viewcvs/httpd/mod_bw/trunk/ChangeLog?rev=279770&view=auto ============================================================================== --- httpd/mod_bw/trunk/ChangeLog (added) +++ httpd/mod_bw/trunk/ChangeLog Fri Sep 9 06:33:46 2005 @@ -0,0 +1,18 @@ + +v0.7 +------------ +2005-08-29 : + +- Changed License to Apache Software License v2.0 +- Changed coded style. Using GNU indent. +- Changed function name from is_filetype to match_ext +- Removed check for "no configurations" (confid < 0) +- Removed BandwidthDebug directive. +- Removed simple allocation on Win32 platform. Using SHM now. +- Removed no-limiting for error pages. +- Added Support for APR > 0.9 +- Added Support for extra-large files (2G, 4G) with APR > 1 +- Borrowed code (in_domain and ip match) from mod_access) +- IPV6 Support + + Added: httpd/mod_bw/trunk/LICENSE URL: http://svn.apache.org/viewcvs/httpd/mod_bw/trunk/LICENSE?rev=279770&view=auto ============================================================================== --- httpd/mod_bw/trunk/LICENSE (added) +++ httpd/mod_bw/trunk/LICENSE Fri Sep 9 06:33:46 2005 @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. Added: httpd/mod_bw/trunk/TODO URL: http://svn.apache.org/viewcvs/httpd/mod_bw/trunk/TODO?rev=279770&view=auto ============================================================================== --- httpd/mod_bw/trunk/TODO (added) +++ httpd/mod_bw/trunk/TODO Fri Sep 9 06:33:46 2005 @@ -0,0 +1,5 @@ +Todo : + + - Fixing MaxConnections behaviour + + Added: httpd/mod_bw/trunk/mod_bw.c URL: http://svn.apache.org/viewcvs/httpd/mod_bw/trunk/mod_bw.c?rev=279770&view=auto ============================================================================== --- httpd/mod_bw/trunk/mod_bw.c (added) +++ httpd/mod_bw/trunk/mod_bw.c Fri Sep 9 06:33:46 2005 @@ -0,0 +1,1117 @@ +/* + +Apache2 - Mod_bw v0.7 + +Author : Ivan Barrera A. (Bruce) + +HomePage : Http://Ivn.cl/apache + +Release Date : 01-09-2005 + +Status : Functional. Tested under medium and heavy load. + +License : Licensed under the Apache Software License v2.0 + It must be included as LICENSE in this package. + +Platform : Linux/x86 (Tested with Fedora Core 4, Suse, etc) + FreeBSD/x86 (Tested on 5.2) + MacOS X/Ppc x86 (Tested on both platforms) + Solaris 8/sparc (Some notes on compile) + Microsoft Windows (Win XP, Win2003. Others should work) + +Notes : This is a stable version of mod_bw. It works fine with + almost any MPM (tested with WinNT/prefork/Worker MPM). + +Limitations : This mod doesn't know how fast is the client really + downloading a file, so it just divides the bw assigned + between the users. + MaxConnections works only for the given scope. (i.e , all + will limit maxconnections from all,not per ip or user) + +*/ + +#define VERSION "0.7" + +#include "apr_buckets.h" +#include "apr_strings.h" +#include "apr_atomic.h" +#include "apr_lib.h" +#include "apr_shm.h" +#include "ap_config.h" +#include "util_filter.h" +#include "ap_mpm.h" +#include "httpd.h" +#include "http_config.h" +#include "http_request.h" +#include "http_core.h" +#include "http_protocol.h" +#include "http_log.h" +#include "http_main.h" +#include "util_script.h" +#include "http_core.h" +#include "scoreboard.h" + +#define MIN_BW 256 /* Minimal bandwidth 256 bytes */ +#define PACKET 8192 /* Default packet at 8192 bytes */ + +#define BANDWIDTH_DISABLED 1<<0 +#define BANDWIDTH_ENABLED 1<<1 +#define SHARED_FILENAME "logs/bwmod_runtime_status" + +/* Compatibility for ARP < 1 */ +#if (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 +#endif + +/* Enum types of "from address" */ +enum from_type { + T_ALL, + T_IP, + T_HOST, +}; + +/* + - Stats of each conf + - + - id = Configuration ID + - time = Time of the last data update + - bandwidth = Estimated bandwidth measured + - bytes_count = Bytes sent last second + - connection_ = Number of simultaneos clientes downloading + - lock = Lock, to avoid simultaneous write access to shm +*/ +typedef struct +{ + apr_uint32_t id; + apr_uint32_t connection_count; + apr_uint32_t bandwidth; + apr_uint32_t bytes_count; + volatile apr_uint32_t lock; + apr_time_t time; +} bw_data; + +/* A temporal context to save our splitted brigade */ +typedef struct ctx_struct_t +{ + apr_bucket_brigade *bb; + struct timeval wait; +} ctx_struct; + +/* With sid we count the shared memory needed. + BwBase, is a holder to the shared memory base addres */ +static int sid = 0; +bw_data *bwbase; +apr_shm_t *shm; + + +/* Limits for MaxConnections based on directory */ +typedef struct +{ + apr_uint32_t sid; + union { + char *from; + apr_ipsubnet_t *ip; + } x; + enum from_type type; + apr_uint32_t max; +} bw_maxconn; + +/* Limits for bandwidth and minimal bandwidth based on directory */ +typedef struct +{ + apr_uint32_t sid; + union { + char *from; + apr_ipsubnet_t *ip; + } x; + enum from_type type; + apr_uint32_t rate; +} bw_entry; + +/* Limits for bandwidth based on file size */ +typedef struct +{ + apr_uint32_t sid; + char *file; + apr_uint32_t size; + apr_uint32_t rate; +} bw_sizel; + +/* Per directory configuration structure */ +typedef struct +{ + apr_array_header_t *limits; + apr_array_header_t *minlimits; + apr_array_header_t *sizelimits; + apr_array_header_t *maxconnection; + int packet; + int error; + char *directory; +} bandwidth_config; + +/* Per server configuration structure */ +typedef struct +{ + int state; + int force; +} bandwidth_server_config; + +/* Module declaration */ +module AP_MODULE_DECLARE_DATA bw_module; + +/*---------------------------------------------------------------------* + * Configurations Directives * + *---------------------------------------------------------------------*/ +/* 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); + sconf->state = (flag ? BANDWIDTH_ENABLED : BANDWIDTH_DISABLED); + + return NULL; +} + +/* Set force mode enabled ... or disabled */ +static const char *forcebandwidthmodule(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); + sconf->force = (flag ? BANDWIDTH_ENABLED : BANDWIDTH_DISABLED); + + return NULL; +} + +/* 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; + int temp; + + if (pack && *pack && apr_isdigit(*pack)) + temp = atol(pack); + else + return "Invalid argument"; + + if ((temp < 1024) || (temp > 131072)) + return "Packet must be a number of bytes between 1024 and 131072"; + + conf->packet = temp; + + return NULL; +} + +/* 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; + int temp; + + if (err && *err && apr_isdigit(*err)) + temp = atol(err); + else + return "Invalid argument"; + + if ((temp < 300) || (temp > 999)) + return "Error must be a number between 300 and 599"; + + conf->error = temp; + + return NULL; +} + +/* Set the maxconnections on a per host basis */ +static const char *maxconnection(cmd_parms * cmd, void *s, const char *from, + const char *maxc) +{ + bandwidth_config *conf = (bandwidth_config *) s; + bw_maxconn *a; + int temp; + char *str; + char *where = (char *) apr_pstrdup(cmd->pool, from); + apr_status_t rv; + char msgbuf[120]; + + if (maxc && *maxc && apr_isdigit(*maxc)) + temp = atoi(maxc); + else + return "Invalid argument"; + + if (temp < 0) + return + "Connections must be a number of simultaneous connections allowed/s"; + + a = (bw_maxconn *) apr_array_push(conf->maxconnection); + + a->x.from = where; + if (!strcasecmp(where, "all")) { + a->type = T_ALL; + } + else if ((str = strchr(where, '/'))) { + *str++ = '\0'; + rv = apr_ipsubnet_create(&a->x.ip, where, str, cmd->pool); + if(APR_STATUS_IS_EINVAL(rv)) { + /* looked nothing like an IP address */ + return "An IP address was expected"; + } + else if (rv != APR_SUCCESS) { + apr_strerror(rv, msgbuf, sizeof msgbuf); + 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; + } + + a->max = temp; + + return NULL; +} + +/* Set the bandwidth on a per host basis */ +static const char *bandwidth(cmd_parms * cmd, void *s, const char *from, + const char *bw) +{ + bandwidth_config *conf = (bandwidth_config *) s; + bw_entry *a; + long int temp; + char *str; + char *where = (char *) apr_pstrdup(cmd->pool, from); + apr_status_t rv; + char msgbuf[120]; + + if (bw && *bw && apr_isdigit(*bw)) + temp = atol(bw); + else + return "Invalid argument"; + + if (temp < 0) + return "BandWidth must be a number of bytes/s"; + + a = (bw_entry *) apr_array_push(conf->limits); + a->x.from = where; + if (!strcasecmp(where, "all")) { + a->type = T_ALL; + } + else if ((str = strchr(where, '/'))) { + *str++ = '\0'; + rv = apr_ipsubnet_create(&a->x.ip, where, str, cmd->pool); + if(APR_STATUS_IS_EINVAL(rv)) { + /* looked nothing like an IP address */ + return "An IP address was expected"; + } + else if (rv != APR_SUCCESS) { + apr_strerror(rv, msgbuf, sizeof msgbuf); + 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; + } + 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) +{ + bandwidth_config *conf = (bandwidth_config *) s; + bw_entry *a; + long int temp; + char *str; + char *where = (char *) apr_pstrdup(cmd->pool, from); + apr_status_t rv; + char msgbuf[120]; + + if (bw && *bw && (*bw == '-' || apr_isdigit(*bw))) + temp = atol(bw); + else + return "Invalid argument"; + + a = (bw_entry *) apr_array_push(conf->minlimits); + a->x.from = where; + if (!strcasecmp(where, "all")) { + a->type = T_ALL; + } + else if ((str = strchr(where, '/'))) { + *str++ = '\0'; + rv = apr_ipsubnet_create(&a->x.ip, where, str, cmd->pool); + if(APR_STATUS_IS_EINVAL(rv)) { + /* looked nothing like an IP address */ + return "An IP address was expected"; + } + else if (rv != APR_SUCCESS) { + apr_strerror(rv, msgbuf, sizeof msgbuf); + 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; + } + + a->rate = temp; + + return NULL; +} + +/* Set the large file bandwidth limit */ +static const char *largefilelimit(cmd_parms * cmd, void *s, const char *file, + const char *size, const char *bw) +{ + bandwidth_config *conf = (bandwidth_config *) s; + bw_sizel *a; + long int temp, tsize; + + if (strlen(file) < 1) + return "You must enter a filetype (use * for all)"; + + if (bw && *bw && (*bw == '-' || apr_isdigit(*bw))) + temp = atol(bw); + else + return "Invalid argument"; + + if (size && *size && apr_isdigit(*size)) + tsize = atol(size); + else + return "Invalid argument"; + + if (temp < 0) + return "BandWidth must be a number of bytes/s"; + + 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->size = tsize; + a->rate = temp; + a->sid = sid++; + + return NULL; +} + + +/*----------------------------------------------------------------------------* + * Helper Functions * + *----------------------------------------------------------------------------*/ + +/* 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); + + if ((wl - dl) >= 0) { + if (strcasecmp(domain, &what[wl - dl]) != 0) + return 0; + + /* Make sure we matched an *entire* subdomain --- if the user + * said 'allow from good.com', we don't want people from nogood.com + * to be able to get in. + */ + if (wl == dl) + return 1; /* matched whole thing */ + else + return (domain[0] == '.' || what[wl - dl - 1] == '.'); + } + else + return 0; +} + +/* Validate the input as an ip */ +static int is_ip(const char *host) +{ + while ((*host == '.') || (*host == '/') || apr_isdigit(*host)) + host++; + return (*host == '\0'); +} + +/* Get the bandwidth limit based on from address */ +static long get_bw_rate(request_rec * r, apr_array_header_t * a) +{ + bw_entry *e = (bw_entry *) a->elts; + const char *remotehost = NULL; + int i; + int gothost = 0; + + for (i = 0; i < a->nelts; i++) { + + switch (e[i].type) { + case T_ALL: + return e[i].rate; + + case T_IP: + if (apr_ipsubnet_test(e[i].x.ip, r->connection->remote_addr)) { + return e[i].rate; + } + break; + case T_HOST: + if (!gothost) { + int remotehost_is_ip; + + remotehost = ap_get_remote_host(r->connection, r->per_dir_config, + REMOTE_DOUBLE_REV, &remotehost_is_ip); + + if ((remotehost == NULL) || remotehost_is_ip) + gothost = 1; + else + gothost = 2; + } + + if ((gothost == 2) && in_domain(e[i].x.from, remotehost)) + return (e[i].rate); + break; + } + + } + return 0; +} + +/* + Match the pattern with the last digist from filename + An asterisk means any. +*/ +static int match_ext(const char *file, char *match) +{ + if (strlen(match) > strlen(file)) + return 0; + if (strncmp(match, "*", 1) == 0) + return 1; + + file += strlen(file) - strlen(match); + + if (strncmp(match, file, strlen(match)) == 0) + return 1; + return 0; +} + +/* Get the bandwidth limit based on filesize */ +static long get_bw_filesize(request_rec * r, apr_array_header_t * a, + off_t filesize, const char *filename) +{ + bw_sizel *e = (bw_sizel *) a->elts; + int i; + long int tmpsize = 0, tmprate = 0; + + if (!filesize) + return (0); + + filesize /= 1024; + + for (i = 0; i < a->nelts; i++) { + if ((e[i].size <= filesize) && match_ext(filename, e[i].file)) + if (tmpsize <= e[i].size) { + tmpsize = e[i].size; + tmprate = e[i].rate; + } + } + + return (tmprate); +} + +/* Get the MaxConnections allowed */ +static int get_maxconn(request_rec * r, apr_array_header_t * a) +{ + bw_maxconn *e = (bw_maxconn *) a->elts; + const char *remotehost = NULL; + int i; + int gothost = 0; + + for (i = 0; i < a->nelts; i++) { + + switch (e[i].type) { + case T_ALL: + return e[i].max; + + case T_IP: + if (apr_ipsubnet_test(e[i].x.ip, r->connection->remote_addr)) { + return e[i].max; + } + break; + case T_HOST: + if (!gothost) { + int remotehost_is_ip; + + remotehost = ap_get_remote_host(r->connection, r->per_dir_config, + REMOTE_DOUBLE_REV, &remotehost_is_ip); + + if ((remotehost == NULL) || remotehost_is_ip) + gothost = 1; + else + gothost = 2; + } + + if ((gothost == 2) && in_domain(e[i].x.from, remotehost)) + return (e[i].max); + break; + } + + } + return 0; +} + +/* Get an id based on bandwidth limit */ +static int get_sid(request_rec * r, apr_array_header_t * a) +{ + bw_entry *e = (bw_entry *) a->elts; + const char *remotehost = NULL; + int i; + int gothost = 0; + + remotehost = + ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_HOST, + NULL); + + for (i = 0; i < a->nelts; i++) { + + switch (e[i].type) { + case T_ALL: + return e[i].sid; + + case T_IP: + if (apr_ipsubnet_test(e[i].x.ip, r->connection->remote_addr)) { + return e[i].sid; + } + break; + case T_HOST: + if (!gothost) { + int remotehost_is_ip; + + remotehost = ap_get_remote_host(r->connection, r->per_dir_config, + REMOTE_DOUBLE_REV, &remotehost_is_ip); + + if ((remotehost == NULL) || remotehost_is_ip) + gothost = 1; + else + gothost = 2; + } + + if ((gothost == 2) && in_domain(e[i].x.from, remotehost)) + return (e[i].sid); + break; + } + + } + return -1; +} + +/* Get an id based on filesize limit */ +static int get_f_sid(request_rec * r, apr_array_header_t * a, off_t filesize, + const char *filename) +{ + bw_sizel *e = (bw_sizel *) a->elts; + int i; + long int tmpsize = 0, tmpsid = -1; + + if (!filesize) + return (0); + + filesize /= 1024; + + for (i = 0; i < a->nelts; i++) { + if ((e[i].size <= filesize) && match_ext(filename, e[i].file)) + if (tmpsize <= e[i].size) { + tmpsize = e[i].size; + tmpsid = e[i].sid; + } + } + + if (tmpsid < 0) + return -1; + return (tmpsid); +} + +/* Update memory (shm) counters, which holds the bw data per context */ +static void update_counters(bw_data * bwstat, ap_filter_t * f) +{ + apr_time_t nowtime; + + /* Refresh only if 1s has passed */ + nowtime = apr_time_now(); + if (bwstat->time < (nowtime - 1000000)) { + /* And if we got lock */ + if (apr_atomic_cas32(&bwstat->lock, 1, 0) == 0) { + + /* Calculate bw used in the last timeinterval */ + bwstat->bandwidth = + (bwstat->bytes_count / (double) (nowtime - bwstat->time)) * + 1000000; + + /* Reset counters */ + bwstat->bytes_count = 0; + + /* Set timestamp */ + bwstat->time = apr_time_now(); + + /* Release lock */ + apr_atomic_set32(&bwstat->lock, 0); + } + } +} + + +/*----------------------------------------------------------------------------* + * 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) +{ + bandwidth_server_config *sconf = + (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; + int confid; + + /* Only work on main request/no subrequests */ + if (r->main) + return DECLINED; + + /* Return if module is not enabled */ + if (sconf->state == BANDWIDTH_DISABLED) + return DECLINED; + + /* Get the ID of the memory space we are using */ + confid = get_sid(r, conf->limits); + + /* Only if we have a valid space */ + if (confid >= 0) { + /* We "get" the data of the current configuration */ + bwstat = bwbase + confid; + + /* If we are too busy, deny connection */ + confid = get_maxconn(r, conf->maxconnection); + if ((bwstat->connection_count >= confid) && (confid > 0)) + return conf->error; + } + + /* Add the Filter, if in forced mode */ + if (sconf->force == BANDWIDTH_ENABLED) + ap_add_output_filter("mod_bw", NULL, r, r->connection); + + /* Pass the control */ + return DECLINED; +} + +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_server_config *sconf = + (bandwidth_server_config *) ap_get_module_config(r->server-> + module_config, + &bw_module); + apr_bucket_alloc_t *bucket_alloc = apr_bucket_alloc_create(f->r->pool); + 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; + const char *buf; + + /* Only work on main request/no subrequests */ + if (r->main) { + ap_remove_output_filter(f); + return ap_pass_brigade(f->next, bb); + } + + /* Return as fast as possible if the module is not enabled */ + if (sconf->state == BANDWIDTH_DISABLED) { + ap_pass_brigade(f->next, bb); + return APR_SUCCESS; + } + + /* Get the bw rates */ + bw_rate = get_bw_rate(r, conf->limits); + bw_min = get_bw_rate(r, conf->minlimits); + confid = connid = get_sid(r, conf->limits); + + /* Get the File Rate. r->finfo.size is not used anymore. */ + bblen = r->bytes_sent; + bw_f_rate = get_bw_filesize(r, conf->sizelimits, bblen, r->finfo.fname); + + + /* Check if we've got an ilimited client */ + if ((bw_rate == 0 && bw_f_rate == 0) || bw_f_rate < 0) { + ap_pass_brigade(f->next, bb); + return APR_SUCCESS; + } + + /* + - File size limit has precedence over location limit, if it's slower. + - The minimal bw used, will never be less than the default minimal bw. + - 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, bblen, r->finfo.fname); + bw_rate = bw_f_rate; + } + + if (bw_min < 0) + bw_min = bw_rate; + else if (!bw_min) + bw_min = MIN_BW; + + /* Initialize our temporal space */ + if (ctx == NULL) { + f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx)); + ctx->bb = apr_brigade_create(f->r->pool, bucket_alloc); + } + + /* We "get" the data of the current configuration */ + bwstat = bwbase + confid; + + /* If we have a valid bandwidth limit per host, get the maxconn limit */ + if (connid >= 0) + bwmaxconn = bwbase + connid; + else + bwmaxconn = bwstat; + + /* Add 1 active connection to the record */ + apr_atomic_inc32(&bwmaxconn->connection_count); + + /* Verbose Output */ + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "ID: %ld Directory : %s Rate : %ld Minimum : %ld Size rate : %ld", + confid, conf->directory, 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); + + + /* + - We get buckets until a sentinel appears + - Read the content of the bucket, and send it to the next filter, piece + by piece + */ + while (b != APR_BRIGADE_SENTINEL(bb)) { + /* If the bucket is EOS end here */ + if (APR_BUCKET_IS_EOS(b) || APR_BUCKET_IS_FLUSH(b)) { + APR_BUCKET_REMOVE(b); + APR_BRIGADE_INSERT_TAIL(ctx->bb, b); + ap_pass_brigade(f->next, ctx->bb); + + /* Delete 1 active connection */ + apr_atomic_dec32(&bwmaxconn->connection_count); + return APR_SUCCESS; + } + + 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 + the bw available divided by the number of clients. + - 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; + + if (cur_rate > bw_rate) + cur_rate = bw_rate; + if (cur_rate < bw_min) + cur_rate = bw_min; + + /* + - Some times we got a bw that is less than packetsize. That causes to have + a delay between packets > 1s. There are some clients that will timeout if + we took too long between packets, so , we adapt the packetsize, to always + be sending data, every 1s top. + */ + if (cur_rate <= conf->packet) + packet = cur_rate; + else + packet = conf->packet; + + /* This was a really weird issue. If the bytes available are less than the + packet to send.. all bw was used. Limit that */ + if (bytes < packet) + packet = bytes; + + /* Here we get the time we need to sleep to get the specified bw */ + sleep = + (long int) (1000000 / + ((double) cur_rate / (double) packet)); + + /* + 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 + specified rate. + */ + apr_bucket_split(b, packet); + APR_BUCKET_REMOVE(b); + APR_BRIGADE_INSERT_TAIL(ctx->bb, b); + + /* Decrease our counter */ + bytes -= packet; + + /* Flush and move to the next bucket */ + ap_pass_brigade(f->next, 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); + + /* 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"); + apr_atomic_dec32(&bwmaxconn->connection_count); + return APR_SUCCESS; + } + + /* Sleep ... zZZzZzZzzzz */ + apr_sleep(sleep); + + /* Refresh counters, so we can keep working :) */ + update_counters(bwstat, f); + } + } + /* A leftover bucket. Pass it as the others */ + APR_BUCKET_REMOVE(b); + 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); + + /* Pass the final brigade */ + ap_pass_brigade(f->next, ctx->bb); + } + + /* Delete 1 active connection to the record */ + apr_atomic_dec32(&bwmaxconn->connection_count); + + /* Give the control to the next filter's */ + return APR_SUCCESS; +} + + +/*----------------------------------------------------------------------------* + * Module Init functions * + *----------------------------------------------------------------------------*/ +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; + int t; + + /* These two help ensure that we only init once. */ + void *data; + const char *userdata_key = "ivn_shm_bw_limit_module"; + + 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); + return OK; + } + + /* Init APR's atomic functions */ + status = apr_atomic_init(p); + if (status != APR_SUCCESS) + return HTTP_INTERNAL_SERVER_ERROR; + + shm_size = (apr_size_t) sizeof(bw_data) * sid; + + + /* If there was a memory block already assigned.. destroy it */ + if (shm) { + 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"); + return status; + } else { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, + "mod_bw : Old Shared memory block, destroyed."); + } + } + + /* Create shared memory block */ + 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"); + 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"); + 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"); + 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)); + + if (retsize < (sizeof(bw_data) * sid)) { + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, + "mod_bw : Not enough memory allocated!! Giving up"); + return HTTP_INTERNAL_SERVER_ERROR; + } + + for (t = 0; t < sid; t++) { + bwstat = bwbase + t; + + /* This inits the struct that will contain current bw use */ + bwstat->time = apr_time_now(); + bwstat->lock = 0; + bwstat->connection_count = 0; + bwstat->bandwidth = 0; + bwstat->bytes_count = 0; + } + + ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, s, + "mod_bw : Version %s - Initialized [%d Confs]", VERSION, + sid); + + return OK; +} + + +static void *create_bw_config(apr_pool_t * p, char *path) +{ + bandwidth_config *new = + (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->packet = PACKET; + new->error = HTTP_SERVICE_UNAVAILABLE; + + return (void *) new; +} + +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)); + new->state = BANDWIDTH_DISABLED; + new->force = BANDWIDTH_DISABLED; + + return (void *) new; +} + +/*----------------------------------------------------------------------------* + * Apache register functions * + *----------------------------------------------------------------------------*/ + +static void register_hooks(apr_pool_t * p) +{ + /* + - Register a handler, which enforces mod_bw if needed + - Register the Output Filter + - And the init function of the mod. + */ + ap_hook_handler(handle_bw, NULL, NULL, APR_HOOK_FIRST); + ap_register_output_filter("mod_bw", bw_filter, NULL, AP_FTYPE_TRANSCODE); + ap_hook_post_config(bw_init, NULL, NULL, APR_HOOK_MIDDLE); + +} + +/* Command Table */ +static const command_rec bw_cmds[] = { + AP_INIT_TAKE2("MaxConnection", maxconnection, NULL, + RSRC_CONF | ACCESS_CONF, + "a domain (or ip, or all) and the max connections allowed"), + AP_INIT_FLAG("BandWidthModule", bandwidthmodule, NULL, + RSRC_CONF | ACCESS_CONF, + "On or Off to enable or disable (default) the whole bandwidth module"), + AP_INIT_FLAG("ForceBandWidthModule", forcebandwidthmodule, NULL, + RSRC_CONF | ACCESS_CONF, + "On or Off to enable or disable (default) the mod catching every request"), + AP_INIT_TAKE1("BandWidthPacket", setpacket, NULL, RSRC_CONF | ACCESS_CONF, + "Size of the maximun packet to use."), + AP_INIT_TAKE2("BandWidth", bandwidth, NULL, RSRC_CONF | ACCESS_CONF, + "a domain (or ip, or all) and a bandwidth limit (in bytes/s)"), + AP_INIT_TAKE2("MinBandWidth", minbandwidth, NULL, RSRC_CONF | ACCESS_CONF, + "a domain (or ip, or all) and a minimal bandwidth limit (in bytes/s)"), + AP_INIT_TAKE1("BandWidthError", bandwidtherror, NULL, + RSRC_CONF | ACCESS_CONF, + "a http error number. Useful to deliver standar (or personal) error messages"), + AP_INIT_TAKE3("LargeFileLimit", largefilelimit, NULL, + RSRC_CONF | ACCESS_CONF, + "a filesize (in Kbytes) and a bandwidth limit (in bytes/s)"), + {NULL} +}; + +module AP_MODULE_DECLARE_DATA bw_module = { + STANDARD20_MODULE_STUFF, + create_bw_config, /* dir config creater */ + NULL, /* dir merger --- default is to override */ + create_bw_server_config, /* server config */ + NULL, /* merge server config */ + bw_cmds, /* command table */ + register_hooks /* register_hooks */ +}; Added: httpd/mod_bw/trunk/mod_bw.txt URL: http://svn.apache.org/viewcvs/httpd/mod_bw/trunk/mod_bw.txt?rev=279770&view=auto ============================================================================== --- httpd/mod_bw/trunk/mod_bw.txt (added) +++ httpd/mod_bw/trunk/mod_bw.txt Fri Sep 9 06:33:46 2005 @@ -0,0 +1,492 @@ +Apache2 - Mod_bw v0.7 + +Author : Ivan Barrera A. (Bruce) + +HomePage : Http://Ivn.cl/apache & http://bwmod.sf.net + +Release Date : 01-09-2005 + +Status : Functional. Tested under medium and heavy load. + +License : Licensed under the Apache Software License v2.0 + It must be included as LICENSE in this package. + +Platform : Linux/x86 (Tested with Fedora Core 4, Suse, etc) + FreeBSD/x86 (Tested on 5.2) + MacOS X/Ppc x86 (Tested on both platforms) + Solaris 8/sparc (Some notes on compile) + Microsoft Windows (Win XP, Win2003. Others should work) + +Notes : This is a stable version of mod_bw. It works fine with + almost any MPM (tested with WinNT/prefork/Worker MPM). + +Limitations : This mod doesn't know how fast is the client really + downloading a file, so it just divides the bw assigned + between the users. + MaxConnections works only for the given scope. (i.e , all + will limit maxconnections from all,not per ip or user) + + + Especials thanks goes to the "Summer Of Code 2005" program of Google! + Chris DiBona and all the team, who were helping us out since the beggining. + Thanks to Ian Holsman and Paul Querna, Mentors from Apache Foundation. + + +------------------------------------------------------------------------------ + +Contents : + +1 .- Notices + 1.1 - Summer Of Code Program + 1.2 - ChangeLog and TODO + +2 .- Installing + 2.1 - Windows + 2.2 - Linux + +3 .- Getting it to Work, Directives + 3.1 - BandWidthModule + 3.2 - ForceBandWidthModule + 3.3 - BandWidth + 3.4 - MinBandWidth + 3.5 - LargeFileLimit + 3.6 - BandWidthPacket + 3.7 - BandWidthError + 3.8 - MaxConnection + +4 .- Examples + 4.1 - Misc Examples + +5 .- FAQ + +------------------------------------------------------------------------------ + +1.- Notices + + 1.1 - Summer Of Code Program : + + Google launch Summer Of Code 2005 program, to motivate the development of + open source software. Many students around the world signed on the program + in differents areas/projects. + Mine has to do with Bandwidth limiting in current Apache webserver. The + main idea behind the program, is to incentivate the development of quality + open source program, offering a grant in + +------------------------------------------------------------------------------ + + 1.2 - ChangeLog & TODO : + + This has been moved to the files ChangeLog and TODO. Available on the + packages and CVS at SourceForge. + +------------------------------------------------------------------------------ + +2.- How To Install : + + 2.1 - Windows + + In Windows, you have to download the binary dll from the site (or compile + one yourself) that matches your apache version, and Install it under modules + on your apache tree. Then edit httpd.conf, and add the LoadModule sentence. + If there is no matching binary dll with your version of apache (the latest) + you can ask me to compile it for you. Post a message in the home site as a + feature request. + + Example : + + Download mod_bw-2.0.54.dll from the home site, to c:\apache2\modules + Edit httpd.conf, and add LoadModule bw_module modules/mod_bw-2.0.54.dll + Restart Apache. + + 2.2 - Linux + + Well.. (under *nix) you *have* to compile it.. + It might sound scary, but it's easy. There is an easy way, and the hard way. + If the easy way doesn't work for you, use the hard way. + + REQUIREMENTS : + + You will need apache2 headers in the include path. In redhat/suse or others + this mean apache-devel or httpd-devel installed. + You also need support for SHM in your OS. Usually you will have this. + + - Easy Way : + + For this to work, you MUST have apxs (or apxs2) installed. You might need + to use it full path (i.e. /usr/sbin/apxs .....). Well, you have to do this : + + apxs -i -a -c mod_bw.c + or + apxs2 -i -a -c mod_bw.c + + This will bring out some stuff... nothing to worry (unless you see error) + If it says ok, then you are ready. Restart apache (service apache restart on + redhat, mandrake, and others... apachectl restart .. rcapache2 restart or... + well, you should know) + + Note On Solaris Users : + + If you got problems compiling it on Solaris, remember to check you have + libgcc correctly installed, and the ld_library_path set up correctly. + I didnt, so i compiled this way : + + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib + apxs -i -a -c mod_bw.c -L/usr/local/lib -lgcc_s + + And then started apache, and everything worked fine. + + Note On SuSe Users : + + You NEED to have installed apache2 header files (apache2-devel rpm). + Otherwise you'll get lots of include files error. + + - Hard Way + + If using the easy way, you turned to have some .o files.. you are almost + done... skip to the part of installing the module. + Back to the hard way..... you might notice that you dont have apxs, + dont know why (maybe the dog ate it). Well. try the following (and please + do it on a newly-fresh created directory for this purpose) : + (this does not necessarily work on all cases) + + libtool --silent --mode=compile gcc -g -O2 -pthread -DLINUX=2 \ + -D_REENTRANT -D_XOPEN_SOURCE=500 -D_BSD_SOURCE -D_SVID_SOURCE \ + -I/usr/include/httpd \ + -I/usr/include/apr-0 -Wall -g\ + -prefer-pic -c mod_bw.c && touch mod_bw.slo + + + libtool --silent --mode=link gcc -g -O2 -pthread -DLINUX=2 \ + -D_REENTRANT -D_XOPEN_SOURCE=500 -D_BSD_SOURCE -D_SVID_SOURCE \ + -D_GNU_SOURCE -DAP_HAVE_DESIGNATED_INITIALIZER \ + -I/usr/include/httpd \ + -I/usr/include/apr-0 -Wall -g\ + -export-dynamic -o mod_bw.la -rpath /usr/lib/httpd/modules \ + -module -avoid-version mod_bw.lo + + (this cleans some unneeded files) + rm -rf *.a *.la *.lo *.slo *.o *.loT + mv .libs/*.so . + rm -rf .libs + + Note that you need to know where are your httpd and apr includes. + (dont ask me where they are.. you are supossed to know) + If this worked... we are good to install the module. + + * Installing the module (hey.. this is part of the hard way) + + Ok, either if the easy way didnt install the mod, or if you did the + hard way, now you must install the module by hand. + Is easy as copying the file (usually mod_bw.so) to the apache + modules directory (usually /etc/httpd/modules or + /usr/local/libexec/apache2/) and then adding the following line at + httpd.conf (better of if you add it at the last part of all LoadModule + sentences) : + + LoadModule bw_module mod_bw.so + + Please, take a look at the other LoadModule sentences. You may need + to add a path before the .so file. + (i.e LoadModule bw_module libexec/apache2/mod_bw.so) + + + + If you didn't manage to get it installed, drop me a letter. But please + be sure you tried everything. Sometimes the simplest way, is the better. +------------------------------------------------------------------------------ + +3.- Getting it to Work : + + + Ok.. this is the most confusing part. (yeh, most of us think the installing + part is the hardest xD ) + + This mod, is able to limit bandwidth usage on every virtual host or + directory. htaccess files are not supported yet, cause i've been having some + serious segfaults when implementing it. + + * Configuration Directives + ***************************************************************************** + + 3.1 - BandWidthModule [On|Off] + + You need to set this to On, for the mod to work.. By default, the mod is + disabled, and wont limit anything. + + Example : + BandWidthModule On + + 3.2 - ForceBandWidthModule [On|Off] + + By default, the mod wont catch every request. + If you enable it, every request will be processed by the mod. + + Example : + (normal use) + AddOutputFilterByType MOD_BW text/html text/plain + + (enabling Force) + ForceBandWidthModule On + + + 3.3 - BandWidth [From] [bytes/s] + + This takes 2 parameters. From is the origin of the connections. It could + be a full host, part of a domain, an ip address, a network mask (i.e + 192.168.0.0/24 or 192.168.0.0/255.255.255.0) or all. + The second parameter indicates the total speed available to the Origin. + If speed is 0, there is no limit. + + Example : + BandWidth localhost 10240 + BandWidth 192.168.218.5 0 + + ( Order is relevant. First entries have precedence ) + + 3.4 - MinBandWidth [From] [bytes/s] + + This takes 2 parameters. From is the origin of the connections. It could + be a full host, part of a domain, an ip address, a network mask (i.e + 192.168.0.0/24 or 192.168.0.0/255.255.255.0) or all. + The second parameter indicates the minimun speed each client will have. + What does this mean ? If you have a total of 100kbytes speed, and you put + MinBandWidth at 50kbytes, it doesnt matter how many clients you have, all + of them will have at minimun 50kbytes of total speed to download. + If speed is 0, you will be using the default minimun (256 bytes/s). + There is a special value of -1. This value means that each client, will + have a top speed determined by the BandWidth directive. See the examples. + + Examples : + BandWidth all 102400 + MinBandWidth all 50000 + + The example above, will have a top speed of 100kb for the 1ยบ + client. If more clients come, it will be splitted accordingly but + everyone will have at least 50kb (even if you have 50 clients) + + BandWidth all 50000 + MinBandWidth all -1 + + This example, makes everyone have 50kb as top speed. + + 3.5 - LargeFileLimit [Type] [Minimum Size] [bytes/s] + + Type, is the last part of a file, or * for all. You can use .tgz to match + only tar-compressed files, .avi to match video files, or * to match all. + Minimum Size, is the size (in kbytes) of the file, to be matched. That way + you can match huge video files that hog your bandwidth. + The last parameter... is obvious. The speed allowed. + + Example : + LargeFileLimit .avi 500 10240 + + This limits .avi files over (or equal to) 500kb to 10kbytes/s + + 3.6 - BandWidthPacket [Size] + + Probably you never need to touch this. It defaults to 8192 which is good + for almost any speed. + It must be a size between 1024 and 131072. A Small packet will cause the + top speed to be lower, and the mod using more time splitting. If you use + a Size too big, the mod will adjust it to lower speeds. + + 3.7 - BandWidthError [Error] + + This directives is useful to deliver a personalized error code. + At default, when maxconnections is reached, the mod will issue a 503 + HTTP_SERVICE_UNAVAILABLE code. For some users, it is annoying to have an + error message, and dont knowing why. You could use an ErrorDocument to + point error 503 to a page explaining that you are under a heavy load of + connections, but sometimes 503 isn't issued by the mod. + So, with this directive, you can set the error code to return when + maxconnections is reached. You can use any error code between 300 and 599. + Please note, that some of the error codes are already used, so before using + any number, take a look to a list of the codes (search for http error codes + in google). + When testing, i've used the error code 510, which hasn't been defined yet. + + And Example, with Personalized Error Page : + + ErrorDocument 510 /errors/maxconexceeded.html + BandWidthError 510 + + Note : Sometimes, the personalized page didn't appear. I'm not sure, but + in many cases, it got fixed, by making the page size over 1024bytes. + Anyways, if you need help using ErrorDocument, refer to the apache + Documentation. + + 3.8 - MaxConnection [From] [Max] + + This takes 2 parameters. From is the origin of the connections. It could + be a full host, part of a domain, an ip address, a network mask (i.e + 192.168.0.0/24 or 192.168.0.0/255.255.255.0) or all. + The second parameter, is the max connections allowed from the origin. Any + connection over Max, will get a 503 Service Temporarily Unavailable + + There is a catch. You NEED to have a BandWidth limit for the same origin. + It doesnt need to be a low limit. You can use an unlimited setting. + You might wonder why. It's because im using them same memory space of the + bandwidth limit, to count the connections, so i can save memory space. + If you dont put a BandWidth using the same origin, MaxConnections will be + ignored. + + Example : + BandWidth all 0 + MaxConnection all 20 + or + BandWidth all 0 + BandWidth 192.168.0.0/24 10240 + MaxConnection all 20 + MaxConnection 192.168.0.0/24 5 + + + Please, rememeber that every speed, will depend mostly on your connection. + You cant get more speed if you dont have it. + + Remember also.. if you dont follow the instructions, and get some weird + results, recheck your config before sending me an email. + +------------------------------------------------------------------------------ + +4.- Examples + + 4.1 - Misc examples + + Limit every user to a max of 10Kb/s on a vhost : + + + BandwidthModule On + ForceBandWidthModule On + Bandwidth all 10240 + MinBandwidth all -1 + Servername www.example.com + + + + Limit al internal users (lan) to 1000 kb/s with a minimum of 50kb/s , and + files greater than 500kb to 50kb/s. + + + BandwidthModule On + ForceBandWidthModule On + Bandwidth all 1024000 + MinBandwidth all 50000 + LargeFileLimit * 500 50000 + Servername www.example.com + + + + Limit avi and mpg extensions to 20kb/s. + + + BandwidthModule On + ForceBandWidthModule On + LargeFileLimit .avi 1 20000 + LargeFileLimit .mpg 1 20000 + Servername www.example.com + + + + Using it the "right" way, with output filter by mime type (for text) + to 5kb/s: + + + BandwidthModule On + AddOutputFilterByType MOD_BW text/html text/plain + Bandwidth all 5000 + Servername www.example.com + + + + If you need help on doing more complex setup, you could post it in sf.net + forums. + +------------------------------------------------------------------------------ + +5.- FAQ + +(No particular order) + +1.- Why should i use mod_bw ? + + If you want to restrict the top speed a site is able to use, or limit the + max connections allowed per site, or just to try the mod. + Some people told me, they use it primarily to stop small sites hogging + all the bandwidth when serving video, images, or other content. + +2.- How do i ... ? + + First, read the documentation. it is pretty straightforward to use. + If you can't make it work, or if you want to ask for a feature, visit the + home site, and post a request. Remember to read the documentation and the + faq. If the request is already posted, i'll just delete the duplicates. + +3.- What's the difference with mod_bwshare, mod_throttle, etc ? + + The main difference, is that this mod, is aimed to the Apache 2 API. + Some other differences, is, how this mod works, and the directives it + implements. + I took some ideas from other mods, as the shared memory implementation + (mod_bandwidth2 from Tim Verhoeven). Most of the directives, are in origin + from mod_bandiwdth for apache 1. + +4.- How does it works ? + + The mod, will set a shared memory holding all of the configuration you + make. In this space, it will also, keep a "count" of the info currently + using (as current connections, bw used, time, and bytes sent). + When you assign a bw limit, the mod will "split" the data, and will send + it piece by piece, with a small delay between pieces. The delay will be + adjusted so at least 1 piece is sent in a second, thus eviting browser + timeout. + If there are two or more clients downloading from the same vhost, the + limit will be "splitted" too. So, if you have a bw limit of 16Kb and two + clients, each one will have 8Kb to download. You can also set a fixed + download rate, so each client will have a fixed maximun download rate. + This is a simple explanation of how this works. Take a look at the code + if you want to know more about it. + +5.- Can you make it do ... ? + + Post a request for a feature in the home site. I'll respond there. + +6.- Can you make mod_bw work for ... ? + + See question 5. Anyways, if i dont own (or have access) to the hardware + needed, i won't be able to help you. + +7.- I'm having some trouble in windows ... + + I cannot support windows in an official way. I do not own a licensed copy + of Windows, or Visual C. + Anyways, post your problem in the home site. + +8.- The mod is not limiting certain Directory + + Ok... first, read the documentation. Then the FAQ. If it isn't working, + then look if you have defined correctly the limits within that directory. + If you have a limit in a vhost, and inside the vhost, a directory with + another limit, a new context with the configs of the directory is created, + and only have the configurations you added there. + + In example : + + + BandWidthModule On + BandWidth all 16384 + LargeFileLimit * 500 4096 + + LargeFileLimit * 100 1024 + + + This wont limit Directory / to 16384. The Directory wont "inherit" the + settings from the vhost if you use some of the mod's directives. + + +------------------------------------------------------------------------------ + + + Ivan Barrera A. + Ivn Systems Software -(Bruce@Ivn.cl)- + +