]> granicus.if.org Git - apache/blob - modules/test/mod_dialup.c
Introduce ap_(get|set)_core_module_config() functions/macros and use them
[apache] / modules / test / mod_dialup.c
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17
18
19 #include "httpd.h"
20 #include "http_core.h"
21
22 #include "util_filter.h"
23 #include "http_log.h"
24 #include "http_config.h"
25 #include "http_request.h"
26 #include "http_protocol.h"
27
28
29
30 #include "ap_mpm.h"
31
32 module AP_MODULE_DECLARE_DATA dialup_module;
33
34 typedef struct dialup_dcfg_t {
35     apr_size_t bytes_per_second;
36 } dialup_dcfg_t;
37
38 typedef struct dialup_baton_t {
39     apr_size_t bytes_per_second;
40     request_rec *r;
41     apr_file_t *fd;
42     apr_bucket_brigade *bb;
43     apr_bucket_brigade *tmpbb;
44 } dialup_baton_t;
45
46 static int
47 dialup_send_pulse(dialup_baton_t *db)
48 {
49     int status;
50     apr_off_t len = 0;
51     apr_size_t bytes_sent = 0;
52     
53     while (!APR_BRIGADE_EMPTY(db->bb) && bytes_sent < db->bytes_per_second) {
54         apr_bucket *e;
55
56         if (db->r->connection->aborted) {
57             return HTTP_INTERNAL_SERVER_ERROR;
58         }
59         
60         status = apr_brigade_partition(db->bb, db->bytes_per_second, &e);
61
62         if (status != APR_SUCCESS && status != APR_INCOMPLETE) {
63             /* XXXXXX: Log me. */
64             return HTTP_INTERNAL_SERVER_ERROR;
65         }
66
67         if (e != APR_BRIGADE_SENTINEL(db->bb)) {
68             apr_bucket *f;
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);
73         }
74         else {
75             APR_BRIGADE_CONCAT(db->tmpbb, db->bb);
76         }
77         
78         e = apr_bucket_flush_create(db->r->connection->bucket_alloc);
79         
80         APR_BRIGADE_INSERT_TAIL(db->tmpbb, e);
81
82         apr_brigade_length(db->tmpbb, 1, &len);
83         bytes_sent += len;
84         status = ap_pass_brigade(db->r->output_filters, db->tmpbb);
85
86         apr_brigade_cleanup(db->tmpbb);
87
88         if (status != OK) {
89             ap_log_rerror(APLOG_MARK, APLOG_ERR, status, db->r,
90                           "dialup: pulse: ap_pass_brigade failed:");
91             return status;
92         }
93     }
94
95     if (APR_BRIGADE_EMPTY(db->bb)) {
96         return DONE;
97     }
98     else {
99         return SUSPENDED;
100     }
101 }
102
103 static void 
104 dialup_callback(void *baton)
105 {
106     int status;
107     dialup_baton_t *db = (dialup_baton_t *)baton;
108
109     apr_thread_mutex_lock(db->r->invoke_mtx);
110
111     status = dialup_send_pulse(db);
112
113     if (status == SUSPENDED) {
114         ap_mpm_register_timed_callback(apr_time_from_sec(1), dialup_callback, baton);
115     }
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);
120         return;
121     }
122     else {
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);
127     }
128
129     apr_thread_mutex_unlock(db->r->invoke_mtx);
130 }
131
132 static int
133 dialup_handler(request_rec *r)
134 {
135     int status;
136     apr_status_t rv;
137     dialup_dcfg_t *dcfg;
138     core_dir_config *ccfg;
139     apr_file_t *fd;
140     dialup_baton_t *db;
141     apr_bucket *e;
142
143
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) {
148         return DECLINED;
149     }
150
151     dcfg = ap_get_module_config(r->per_dir_config,
152                                 &dialup_module);
153
154     if (dcfg->bytes_per_second == 0) {
155         return DECLINED;
156     }
157
158     ccfg = ap_get_core_module_config(r->per_dir_config);
159
160
161     rv = apr_file_open(&fd, r->filename, APR_READ | APR_BINARY
162 #if APR_HAS_SENDFILE
163                            | AP_SENDFILE_ENABLED(ccfg->enable_sendfile)
164 #endif
165                        , 0, r->pool);
166
167     if (rv) {
168         return DECLINED;
169     }
170
171     /* copied from default handler: */
172     ap_update_mtime(r, r->finfo.mtime);
173     ap_set_last_modified(r);
174     ap_set_etag(r);
175     apr_table_setn(r->headers_out, "Accept-Ranges", "bytes");
176     ap_set_content_length(r, r->finfo.size);
177
178     status = ap_meets_conditions(r);
179     if (status != OK) {
180         ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
181                       "dialup: declined, meets conditions, good luck core handler");
182         return DECLINED;
183     }
184
185     db = apr_palloc(r->pool, sizeof(dialup_baton_t));
186     
187     db->bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
188     db->tmpbb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
189
190     e = apr_brigade_insert_file(db->bb, fd, 0, r->finfo.size, r->pool);
191
192 #if APR_HAS_MMAP
193     if (ccfg->enable_mmap == ENABLE_MMAP_OFF) {
194         apr_bucket_file_enable_mmap(e, 0);
195     }
196 #endif
197     
198     
199     db->bytes_per_second = dcfg->bytes_per_second;
200     db->r = r;
201     db->fd = fd;
202
203     e = apr_bucket_eos_create(r->connection->bucket_alloc);
204
205     APR_BRIGADE_INSERT_TAIL(db->bb, e);
206
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");
211         return status;
212     }
213
214     ap_mpm_register_timed_callback(apr_time_from_sec(1), dialup_callback, db);
215
216     return SUSPENDED;
217 }
218
219
220
221 #ifndef APR_HOOK_ALMOST_LAST
222 #define APR_HOOK_ALMOST_LAST (APR_HOOK_REALLY_LAST - 1)
223 #endif
224
225 static void
226 dialup_register_hooks(apr_pool_t *p)
227 {
228     ap_hook_handler(dialup_handler, NULL, NULL, APR_HOOK_ALMOST_LAST);
229 }
230
231 typedef struct modem_speed_t {
232     const char *name;
233     apr_size_t bytes_per_second;
234 } modem_speed_t;
235
236 #ifndef BITRATE_TO_BYTES
237 #define BITRATE_TO_BYTES(x) ((1000 * x)/8)
238 #endif
239
240 static const modem_speed_t modem_bitrates[] =
241 {
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)},
248     {NULL, 0}
249 };
250
251 static const char *
252 cmd_modem_standard(cmd_parms *cmd,
253              void *dconf,
254              const char *input)
255 {
256     const modem_speed_t *standard;
257     int i = 0;
258     dialup_dcfg_t *dcfg = (dialup_dcfg_t*)dconf;
259     
260     dcfg->bytes_per_second = 0;
261
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;
266             break;
267         }
268         i++;
269     }
270
271     if (dcfg->bytes_per_second == 0) {
272         return "mod_diaulup: Unkonwn Modem Standard specified.";
273     }
274
275     return NULL;
276 }
277
278 static void *
279 dialup_dcfg_create(apr_pool_t *p, char *dummy)
280 {
281     dialup_dcfg_t *cfg = apr_palloc(p, sizeof(dialup_dcfg_t));
282     
283     cfg->bytes_per_second = 0;
284
285     return cfg;
286 }
287
288
289 static const command_rec dialup_cmds[] =
290 {
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'"),
294     {NULL}
295 };
296
297 AP_DECLARE_MODULE(dialup) =
298 {
299     STANDARD20_MODULE_STUFF,
300     dialup_dcfg_create,
301     NULL,
302     NULL,
303     NULL,
304     dialup_cmds,
305     dialup_register_hooks
306 };