The developers at Danni's Hard Drive recently made some small additions to apache_1.3.20 in order to support a new server module we developed to do bandwidth limiting on a per user basis. The following patch is simply two small changes to the scoreboard that allow any apache module to calculate bytes per second on a per user basis. If this patch is accepted then it would be possible for us to release our bandwidth module as a standalone module without requiring users to patch apache. These additions are small and are limited to two source files, scoreboard.h and http_main.c. Provided in the remainder of this document is a description of the changes we have made in each file. 1. Changes to scoreboard.h We have added two fields to the short_score structure. These fields are connect_time and original_client. A description of these fields is provided below. 1.1 connect_time ? This field contains the time at which the client application connected to the httpd process. Knowing the current time and the time of the connection allows us to calculate how long a client has been connected to the httpd process. This information is useful in modules that must know the duration of the connection in order to perform rate calculations. Example: To compute the rate at which data has been served to a client: time_connected = time_now ? connect_time bytes_per_second = bytes_served / time_connected 1.2 original_client ? For connections made through proxy servers this field contains the identity of the original client. This information is extracted directly from the X-Forwarded-For field in the HTTP header. This field distinguishes individual clients connecting through a common proxy server. This field, along with the existing client field, allows us to quickly and accurately scan the scoreboard and determine how many connections exist for any unique client. --- scoreboard-old.h Wed Aug 22 14:48:47 2001 +++ scoreboard.h Wed Aug 22 14:49:41 2001 @@ -151,6 +151,14 @@ struct timeval start_time; struct timeval stop_time; #endif + +#if defined(NO_GETTIMEOFDAY) + clock_t connect_time; +#else + struct timeval connect_time; +#endif + char original_client[32]; /* Client behind proxy */ + #ifndef NO_TIMES struct tms times; #endif 2. Changes to http_main.c In order to support the additions we have made to the short_score structure we have also made small additions to the ap_update_child_status function in the http_main.c file. These additions update the new fields in the short_score structure when a new connection is made or an existing connection is broken. As a brief summary, when a new connection is made the time of the connection and, if applicable, the X-Forwarded-For data are recorded in the connect_time and original_client fields of the scoreboard. When a connection is dropped these fields are cleared. The modified ap_update_child_status function is shown below. --- http_main-old.c Wed Aug 22 14:48:14 2001 +++ http_main.c Wed Aug 22 14:53:43 2001 @@ -2240,6 +2240,11 @@ int old_status; short_score *ss; + const char *address; /* Forwarded address */ +#if defined(NO_GETTIMEOFDAY) && !defined(NO_TIMES) + struct tms tms_blk; +#endif + if (child_num < 0) return -1; @@ -2271,11 +2276,44 @@ } ss->conn_count = (unsigned short) 0; ss->conn_bytes = (unsigned long) 0; - } + +#if defined(NO_GETTIMEOFDAY) + ss->connect_time = (clock_t)0; +#else + ss->connect_time.tv_sec = 0L; + ss->connect_time.tv_usec = 0L; +#endif + } else { + if (status == SERVER_BUSY_READ) { +#if defined(NO_GETTIMEOFDAY) +#ifndef NO_TIMES + if ((ss->connect_time = times(&tms_blk)) == -1) +#endif + ss->connect_time = (clock_t)0; +#else + if (gettimeofday(&(ss->connect_time), + (struct timezone *)0) < 0) { + ss->connect_time.tv_sec = 0L; + ss->connect_time.tv_usec = 0L; + } +#endif + } + } + if (r) { conn_rec *c = r->connection; ap_cpystrn(ss->client, ap_get_remote_host(c, r->per_dir_config, REMOTE_NOLOOKUP), sizeof(ss->client)); + + address = ap_table_get(r->headers_in, "X-Forwarded-For"); + if (address == 0) { /* Not set? */ + ap_cpystrn(ss->original_client, "", sizeof(ss->original_client)); + } else { + ap_cpystrn(ss->original_client, /* Copy converted name */ + address, + sizeof(ss->original_client)); + } + if (r->the_request == NULL) { ap_cpystrn(ss->request, "NULL", sizeof(ss->request)); } else if (r->parsed_uri.password == NULL) { --- Jason Burns Director of Technology Danni's Hard Drive