1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
20 #include "http_core.h"
22 #include "util_filter.h"
24 #include "http_config.h"
25 #include "http_request.h"
26 #include "http_protocol.h"
32 module AP_MODULE_DECLARE_DATA dialup_module;
34 typedef struct dialup_dcfg_t {
35 apr_size_t bytes_per_second;
38 typedef struct dialup_baton_t {
39 apr_size_t bytes_per_second;
42 apr_bucket_brigade *bb;
43 apr_bucket_brigade *tmpbb;
47 dialup_send_pulse(dialup_baton_t *db)
51 apr_size_t bytes_sent = 0;
53 while (!APR_BRIGADE_EMPTY(db->bb) && bytes_sent < db->bytes_per_second) {
56 if (db->r->connection->aborted) {
57 return HTTP_INTERNAL_SERVER_ERROR;
60 status = apr_brigade_partition(db->bb, db->bytes_per_second, &e);
62 if (status != APR_SUCCESS && status != APR_INCOMPLETE) {
64 return HTTP_INTERNAL_SERVER_ERROR;
67 if (e != APR_BRIGADE_SENTINEL(db->bb)) {
69 apr_bucket *b = APR_BUCKET_PREV(e);
70 f = APR_RING_FIRST(&db->bb->list);
71 APR_RING_UNSPLICE(f, b, link);
72 APR_RING_SPLICE_HEAD(&db->tmpbb->list, f, b, apr_bucket, link);
75 APR_BRIGADE_CONCAT(db->tmpbb, db->bb);
78 e = apr_bucket_flush_create(db->r->connection->bucket_alloc);
80 APR_BRIGADE_INSERT_TAIL(db->tmpbb, e);
82 apr_brigade_length(db->tmpbb, 1, &len);
84 status = ap_pass_brigade(db->r->output_filters, db->tmpbb);
86 apr_brigade_cleanup(db->tmpbb);
89 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, db->r,
90 "dialup: pulse: ap_pass_brigade failed:");
95 if (APR_BRIGADE_EMPTY(db->bb)) {
104 dialup_callback(void *baton)
107 dialup_baton_t *db = (dialup_baton_t *)baton;
109 apr_thread_mutex_lock(db->r->invoke_mtx);
111 status = dialup_send_pulse(db);
113 if (status == SUSPENDED) {
114 ap_mpm_register_timed_callback(apr_time_from_sec(1), dialup_callback, baton);
116 else if (status == DONE) {
117 apr_thread_mutex_unlock(db->r->invoke_mtx);
118 ap_finalize_request_protocol(db->r);
119 ap_process_request_after_handler(db->r);
123 ap_log_rerror(APLOG_MARK, APLOG_ERR, status, db->r,
124 "dialup: pulse returned: %d", status);
125 db->r->status = HTTP_OK;
126 ap_die(status, db->r);
129 apr_thread_mutex_unlock(db->r->invoke_mtx);
133 dialup_handler(request_rec *r)
138 core_dir_config *ccfg;
144 /* See core.c, default handler for all of the cases we just decline. */
145 if (r->method_number != M_GET ||
146 r->finfo.filetype == APR_NOFILE ||
147 r->finfo.filetype == APR_DIR) {
151 dcfg = ap_get_module_config(r->per_dir_config,
154 if (dcfg->bytes_per_second == 0) {
158 ccfg = ap_get_core_module_config(r->per_dir_config);
161 rv = apr_file_open(&fd, r->filename, APR_READ | APR_BINARY
163 | AP_SENDFILE_ENABLED(ccfg->enable_sendfile)
171 /* copied from default handler: */
172 ap_update_mtime(r, r->finfo.mtime);
173 ap_set_last_modified(r);
175 apr_table_setn(r->headers_out, "Accept-Ranges", "bytes");
176 ap_set_content_length(r, r->finfo.size);
178 status = ap_meets_conditions(r);
180 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
181 "dialup: declined, meets conditions, good luck core handler");
185 db = apr_palloc(r->pool, sizeof(dialup_baton_t));
187 db->bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
188 db->tmpbb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
190 e = apr_brigade_insert_file(db->bb, fd, 0, r->finfo.size, r->pool);
193 if (ccfg->enable_mmap == ENABLE_MMAP_OFF) {
194 apr_bucket_file_enable_mmap(e, 0);
199 db->bytes_per_second = dcfg->bytes_per_second;
203 e = apr_bucket_eos_create(r->connection->bucket_alloc);
205 APR_BRIGADE_INSERT_TAIL(db->bb, e);
207 status = dialup_send_pulse(db);
208 if (status != SUSPENDED && status != DONE) {
209 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
210 "dialup: failed, send pulse");
214 ap_mpm_register_timed_callback(apr_time_from_sec(1), dialup_callback, db);
221 #ifndef APR_HOOK_ALMOST_LAST
222 #define APR_HOOK_ALMOST_LAST (APR_HOOK_REALLY_LAST - 1)
226 dialup_register_hooks(apr_pool_t *p)
228 ap_hook_handler(dialup_handler, NULL, NULL, APR_HOOK_ALMOST_LAST);
231 typedef struct modem_speed_t {
233 apr_size_t bytes_per_second;
236 #ifndef BITRATE_TO_BYTES
237 #define BITRATE_TO_BYTES(x) ((1000 * x)/8)
240 static const modem_speed_t modem_bitrates[] =
242 {"V.21", BITRATE_TO_BYTES(0.1)},
243 {"V.26bis", BITRATE_TO_BYTES(2.4)},
244 {"V.32", BITRATE_TO_BYTES(9.6)},
245 {"V.34", BITRATE_TO_BYTES(28.8)},
246 {"V.92", BITRATE_TO_BYTES(56.0)},
247 {"i-was-rich-and-got-a-leased-line", BITRATE_TO_BYTES(1500)},
252 cmd_modem_standard(cmd_parms *cmd,
256 const modem_speed_t *standard;
258 dialup_dcfg_t *dcfg = (dialup_dcfg_t*)dconf;
260 dcfg->bytes_per_second = 0;
262 while (modem_bitrates[i].name != NULL) {
263 standard = &modem_bitrates[i];
264 if (strcasecmp(standard->name, input) == 0) {
265 dcfg->bytes_per_second = standard->bytes_per_second;
271 if (dcfg->bytes_per_second == 0) {
272 return "mod_diaulup: Unkonwn Modem Standard specified.";
279 dialup_dcfg_create(apr_pool_t *p, char *dummy)
281 dialup_dcfg_t *cfg = apr_palloc(p, sizeof(dialup_dcfg_t));
283 cfg->bytes_per_second = 0;
289 static const command_rec dialup_cmds[] =
291 AP_INIT_TAKE1("ModemStandard", cmd_modem_standard, NULL, ACCESS_CONF,
292 "Modem Standard to.. simulate. "
293 "Must be one of: 'V.21', 'V.26bis', 'V.32', 'V.34', or 'V.92'"),
297 AP_DECLARE_MODULE(dialup) =
299 STANDARD20_MODULE_STUFF,
305 dialup_register_hooks