diff options
Diffstat (limited to 'modules/test')
-rw-r--r-- | modules/test/config.m4 | 2 | ||||
-rw-r--r-- | modules/test/mod_dialup.c | 308 |
2 files changed, 310 insertions, 0 deletions
diff --git a/modules/test/config.m4 b/modules/test/config.m4 index 01bc0fa971..9c150f488f 100644 --- a/modules/test/config.m4 +++ b/modules/test/config.m4 @@ -6,4 +6,6 @@ APACHE_MODULE(optional_hook_import, example optional hook importer, , , no) APACHE_MODULE(optional_fn_import, example optional function importer, , , no) APACHE_MODULE(optional_fn_export, example optional function exporter, , , no) +APACHE_MODULE(dialup, rate limits static files to dialup modem speeds, , , no) + APACHE_MODPATH_FINISH diff --git a/modules/test/mod_dialup.c b/modules/test/mod_dialup.c new file mode 100644 index 0000000000..349cd2c6e6 --- /dev/null +++ b/modules/test/mod_dialup.c @@ -0,0 +1,308 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + + + +#include "httpd.h" +#include "util_filter.h" +#include "http_log.h" +#include "http_config.h" +#include "http_request.h" + +/* to detect sendfile enabled, we need CORE_PRIVATE. Someone should fix this. */ +#define CORE_PRIVATE +#include "http_core.h" + + +module AP_MODULE_DECLARE_DATA dialup_module; + +#ifndef apr_time_from_msec +#define apr_time_from_msec(x) (x * 1000) +#endif + + +typedef struct dialup_dcfg_t { + apr_size_t bytes_per_second; +} dialup_dcfg_t; + +typedef struct dialup_baton_t { + apr_size_t bytes_per_second; + request_rec *r; + apr_file_t *fd; + apr_bucket_brigade *bb; + apr_bucket_brigade *tmpbb; +} dialup_baton_t; + +static int +dialup_send_pulse(dialup_baton_t *db) +{ + int status; + apr_off_t len = 0; + apr_size_t bytes_sent = 0; + + while (!APR_BRIGADE_EMPTY(db->bb) && bytes_sent < db->bytes_per_second) { + apr_bucket *e; + + if (db->r->connection->aborted) { + return HTTP_INTERNAL_SERVER_ERROR; + } + + status = apr_brigade_partition(db->bb, db->bytes_per_second, &e); + + if (status != APR_SUCCESS && status != APR_INCOMPLETE) { + /* XXXXXX: Log me. */ + return HTTP_INTERNAL_SERVER_ERROR; + } + + if (e != APR_BRIGADE_SENTINEL(db->bb)) { + apr_bucket *f; + apr_bucket *b = APR_BUCKET_PREV(e); + f = APR_RING_FIRST(&db->bb->list); + APR_RING_UNSPLICE(f, b, link); + APR_RING_SPLICE_HEAD(&db->tmpbb->list, f, b, apr_bucket, link); + } + else { + APR_BRIGADE_CONCAT(db->tmpbb, db->bb); + } + + e = apr_bucket_flush_create(db->r->connection->bucket_alloc); + + APR_BRIGADE_INSERT_TAIL(db->tmpbb, e); + + apr_brigade_length(db->tmpbb, 1, &len); + bytes_sent += len; + status = ap_pass_brigade(db->r->output_filters, db->tmpbb); + + apr_brigade_cleanup(db->tmpbb); + + if (status != OK) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, db->r, + "dialup: pulse: ap_pass_brigade failed:"); + return status; + } + } + + if (APR_BRIGADE_EMPTY(db->bb)) { + return DONE; + } + else { + return SUSPENDED; + } +} + +void +dialup_callback(void *baton) +{ + int status; + dialup_baton_t *db = (dialup_baton_t *)baton; + + apr_thread_mutex_lock(db->r->invoke_mtx); + + status = dialup_send_pulse(db); + + if (status == SUSPENDED) { + ap_mpm_register_timed_callback(apr_time_from_sec(1), dialup_callback, baton); + } + else if (status == DONE) { + apr_thread_mutex_unlock(db->r->invoke_mtx); + ap_finalize_request_protocol(db->r); + ap_process_request_after_handler(db->r); + return; + } + else { + ap_log_rerror(APLOG_MARK, APLOG_ERR, status, db->r, + "dialup: pulse returned: %d", status); + db->r->status = HTTP_OK; + ap_die(status, db->r); + } + + apr_thread_mutex_unlock(db->r->invoke_mtx); +} + +static int +dialup_handler(request_rec *r) +{ + int status; + apr_status_t rv; + + /* See core.c, default handler for all of the cases we just decline. */ + if (r->method_number != M_GET || + r->finfo.filetype == 0 || + r->finfo.filetype == APR_DIR) { + return DECLINED; + } + + dialup_dcfg_t *dcfg = ap_get_module_config(r->per_dir_config, + &dialup_module); + if (dcfg->bytes_per_second == 0) { + return DECLINED; + } + core_dir_config *ccfg = ap_get_module_config(r->per_dir_config, + &core_module); + + apr_file_t *fd; + + rv = apr_file_open(&fd, r->filename, APR_READ | APR_BINARY +#if APR_HAS_SENDFILE + | ((ccfg->enable_sendfile == ENABLE_SENDFILE_OFF) + ? 0 : APR_SENDFILE_ENABLED) +#endif + , 0, r->pool); + + if (rv) { + return DECLINED; + } + + /* copied from default handler: */ + ap_update_mtime(r, r->finfo.mtime); + ap_set_last_modified(r); + ap_set_etag(r); + apr_table_setn(r->headers_out, "Accept-Ranges", "bytes"); + ap_set_content_length(r, r->finfo.size); + + status = ap_meets_conditions(r); + if (status != OK) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "dialup: declined, meets conditions, good luck core handler"); + return DECLINED; + } + + apr_bucket_brigade *bb; + + dialup_baton_t *db = apr_palloc(r->pool, sizeof(dialup_baton_t)); + + db->bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); + db->tmpbb = apr_brigade_create(r->pool, r->connection->bucket_alloc); + + apr_bucket *e; + + e = apr_brigade_insert_file(db->bb, fd, 0, r->finfo.size, r->pool); + +#if APR_HAS_MMAP + if (ccfg->enable_mmap == ENABLE_MMAP_OFF) { + apr_bucket_file_enable_mmap(e, 0); + } +#endif + + + db->bytes_per_second = dcfg->bytes_per_second; + db->r = r; + db->fd = fd; + + e = apr_bucket_eos_create(r->connection->bucket_alloc); + + APR_BRIGADE_INSERT_TAIL(db->bb, e); + + status = dialup_send_pulse(db); + if (status != SUSPENDED && status != DONE) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "dialup: failed, send pulse"); + return status; + } + + ap_mpm_register_timed_callback(apr_time_from_sec(1), dialup_callback, db); + + return SUSPENDED; +} + + + +#ifndef APR_HOOK_ALMOST_LAST +#define APR_HOOK_ALMOST_LAST (APR_HOOK_REALLY_LAST - 1) +#endif + +static void +dialup_register_hooks(apr_pool_t *p) +{ + ap_hook_handler(dialup_handler, NULL, NULL, APR_HOOK_ALMOST_LAST); +} + +typedef struct modem_speed_t { + const char *name; + apr_size_t bytes_per_second; +} modem_speed_t; + +#ifndef BITRATE_TO_BYTES +#define BITRATE_TO_BYTES(x) ((1000 * x)/8) +#endif + +static const modem_speed_t modem_bitrates[] = +{ + {"V.21", BITRATE_TO_BYTES(0.1)}, + {"V.26bis", BITRATE_TO_BYTES(2.4)}, + {"V.32", BITRATE_TO_BYTES(9.6)}, + {"V.34", BITRATE_TO_BYTES(28.8)}, + {"V.92", BITRATE_TO_BYTES(56.0)}, + {"i-was-rich-and-got-a-leased-line", BITRATE_TO_BYTES(1500)}, + {NULL, 0} +}; + +static const char * +cmd_modem_standard(cmd_parms *cmd, + void *dconf, + const char *input) +{ + const modem_speed_t *standard; + int i = 0; + dialup_dcfg_t *dcfg = (dialup_dcfg_t*)dconf; + + dcfg->bytes_per_second = 0; + + while (modem_bitrates[i].name != NULL) { + standard = &modem_bitrates[i]; + if (strcasecmp(standard->name, input) == 0) { + dcfg->bytes_per_second = standard->bytes_per_second; + break; + } + i++; + } + + if (dcfg->bytes_per_second == 0) { + return "mod_diaulup: Unkonwn Modem Standard specified."; + } + + return NULL; +} + +static void * +dialup_dcfg_create(apr_pool_t *p, char *dummy) +{ + dialup_dcfg_t *cfg = apr_palloc(p, sizeof(dialup_dcfg_t)); + + cfg->bytes_per_second = 0; + + return cfg; +} + + +static const command_rec dialup_cmds[] = +{ + AP_INIT_TAKE1("ModemStandard", cmd_modem_standard, NULL, ACCESS_CONF, + "Modem Standard to.. simulate. " + "Must be one of: 'V.21', 'V.26bis', 'V.32', 'V.34', or 'V.92'"), + NULL +}; + +module AP_MODULE_DECLARE_DATA dialup_module = +{ + STANDARD20_MODULE_STUFF, + dialup_dcfg_create, + NULL, + NULL, + NULL, + dialup_cmds, + dialup_register_hooks +}; |