602.3.3
by Dan Garner
Work on PHP7 support and dev docker file |
1 |
/* Copyright 2006-2010 by Nils Maier
|
2 |
*
|
|
3 |
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
4 |
* you may not use this file except in compliance with the License.
|
|
5 |
* You may obtain a copy of the License at
|
|
6 |
*
|
|
7 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
8 |
*
|
|
9 |
* Unless required by applicable law or agreed to in writing, software
|
|
10 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
11 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12 |
* See the License for the specific language governing permissions and
|
|
13 |
* limitations under the License.
|
|
14 |
*/
|
|
15 |
||
16 |
/*
|
|
17 |
* mod_xsendfile.c: Process X-SENDFILE header cgi/scripts may set
|
|
18 |
* Written by Nils Maier, testnutzer123 at google mail, March 2006
|
|
19 |
*
|
|
20 |
* Whenever an X-SENDFILE header occures in the response headers drop
|
|
21 |
* the body and send the replacement file idenfitied by this header instead.
|
|
22 |
*
|
|
23 |
* Method inspired by lighttpd <http://lighttpd.net/>
|
|
24 |
* Code inspired by mod_headers, mod_rewrite and such
|
|
25 |
*
|
|
26 |
* Installation:
|
|
27 |
* apxs2 -cia mod_xsendfile.c
|
|
28 |
*/
|
|
29 |
||
30 |
/*
|
|
31 |
* v0.12 (peer-review still required)
|
|
32 |
*
|
|
33 |
* $Id$
|
|
34 |
*/
|
|
35 |
||
36 |
#include "apr.h" |
|
37 |
#include "apr_lib.h" |
|
38 |
#include "apr_strings.h" |
|
39 |
#include "apr_buckets.h" |
|
40 |
#include "apr_file_io.h" |
|
41 |
||
42 |
#include "apr_hash.h" |
|
43 |
#define APR_WANT_IOVEC
|
|
44 |
#define APR_WANT_STRFUNC
|
|
45 |
#include "apr_want.h" |
|
46 |
||
47 |
#include "httpd.h" |
|
48 |
#include "http_log.h" |
|
49 |
#include "http_config.h" |
|
50 |
#include "http_log.h" |
|
51 |
#define CORE_PRIVATE
|
|
52 |
#include "http_request.h" |
|
53 |
#include "http_core.h" /* needed for per-directory core-config */ |
|
54 |
#include "util_filter.h" |
|
55 |
#include "http_protocol.h" /* ap_hook_insert_error_filter */ |
|
56 |
||
57 |
#define AP_XSENDFILE_HEADER "X-SENDFILE"
|
|
58 |
||
59 |
module AP_MODULE_DECLARE_DATA xsendfile_module; |
|
60 |
||
61 |
typedef enum { |
|
62 |
XSENDFILE_UNSET = 0, |
|
63 |
XSENDFILE_ENABLED = 1<<0, |
|
64 |
XSENDFILE_DISABLED = 1<<1 |
|
65 |
} xsendfile_conf_active_t; |
|
66 |
||
67 |
typedef struct xsendfile_conf_t { |
|
68 |
xsendfile_conf_active_t enabled; |
|
69 |
xsendfile_conf_active_t ignoreETag; |
|
70 |
xsendfile_conf_active_t ignoreLM; |
|
71 |
apr_array_header_t *paths; |
|
72 |
} xsendfile_conf_t; |
|
73 |
||
74 |
static xsendfile_conf_t *xsendfile_config_create(apr_pool_t *p) { |
|
75 |
xsendfile_conf_t *conf; |
|
76 |
||
77 |
conf = (xsendfile_conf_t *) apr_pcalloc(p, sizeof(xsendfile_conf_t)); |
|
78 |
conf->ignoreETag = |
|
79 |
conf->ignoreLM = |
|
80 |
conf->enabled = |
|
81 |
XSENDFILE_UNSET; |
|
82 |
||
83 |
conf->paths = apr_array_make(p, 1, sizeof(char*)); |
|
84 |
||
85 |
return conf; |
|
86 |
}
|
|
87 |
||
88 |
static void *xsendfile_config_server_create(apr_pool_t *p, server_rec *s) { |
|
89 |
return (void*)xsendfile_config_create(p); |
|
90 |
}
|
|
91 |
||
92 |
#define XSENDFILE_CFLAG(x) conf->x = overrides->x != XSENDFILE_UNSET ? overrides->x : base->x
|
|
93 |
||
94 |
static void *xsendfile_config_merge(apr_pool_t *p, void *basev, void *overridesv) { |
|
95 |
xsendfile_conf_t *base = (xsendfile_conf_t *)basev; |
|
96 |
xsendfile_conf_t *overrides = (xsendfile_conf_t *)overridesv; |
|
97 |
xsendfile_conf_t *conf; |
|
98 |
||
99 |
conf = (xsendfile_conf_t *) apr_pcalloc(p, sizeof(xsendfile_conf_t)); |
|
100 |
||
101 |
XSENDFILE_CFLAG(enabled); |
|
102 |
XSENDFILE_CFLAG(ignoreETag); |
|
103 |
XSENDFILE_CFLAG(ignoreLM); |
|
104 |
||
105 |
conf->paths = apr_array_append(p, overrides->paths, base->paths); |
|
106 |
||
107 |
return (void*)conf; |
|
108 |
}
|
|
109 |
||
110 |
static void *xsendfile_config_perdir_create(apr_pool_t *p, char *path) { |
|
111 |
return (void*)xsendfile_config_create(p); |
|
112 |
}
|
|
113 |
#undef XSENDFILE_CFLAG
|
|
114 |
||
115 |
static const char *xsendfile_cmd_flag(cmd_parms *cmd, void *perdir_confv, int flag) { |
|
116 |
xsendfile_conf_t *conf = (xsendfile_conf_t *)perdir_confv; |
|
117 |
if (cmd->path == NULL) { |
|
118 |
conf = (xsendfile_conf_t*)ap_get_module_config( |
|
119 |
cmd->server->module_config, |
|
120 |
&xsendfile_module |
|
121 |
);
|
|
122 |
}
|
|
123 |
if (!conf) { |
|
124 |
return "Cannot get configuration object"; |
|
125 |
}
|
|
126 |
if (!strcasecmp(cmd->cmd->name, "xsendfile")) { |
|
127 |
conf->enabled = flag ? XSENDFILE_ENABLED : XSENDFILE_DISABLED; |
|
128 |
}
|
|
129 |
else if (!strcasecmp(cmd->cmd->name, "xsendfileignoreetag")) { |
|
130 |
conf->ignoreETag = flag ? XSENDFILE_ENABLED: XSENDFILE_DISABLED; |
|
131 |
}
|
|
132 |
else if (!strcasecmp(cmd->cmd->name, "xsendfileignorelastmodified")) { |
|
133 |
conf->ignoreLM = flag ? XSENDFILE_ENABLED: XSENDFILE_DISABLED; |
|
134 |
}
|
|
135 |
else { |
|
136 |
return apr_psprintf(cmd->pool, "Not a valid command in this context: %s %s", cmd->cmd->name, flag ? "On": "Off"); |
|
137 |
}
|
|
138 |
||
139 |
return NULL; |
|
140 |
}
|
|
141 |
||
142 |
static const char *xsendfile_cmd_path(cmd_parms *cmd, void *pdc, const char *arg) { |
|
143 |
xsendfile_conf_t *conf = (xsendfile_conf_t*)ap_get_module_config( |
|
144 |
cmd->server->module_config, |
|
145 |
&xsendfile_module |
|
146 |
);
|
|
147 |
char **newpath = (char**)apr_array_push(conf->paths); |
|
148 |
*newpath = apr_pstrdup(cmd->pool, arg); |
|
149 |
||
150 |
return NULL; |
|
151 |
}
|
|
152 |
||
153 |
/*
|
|
154 |
little helper function to get the original request path
|
|
155 |
code borrowed from request.c and util_script.c
|
|
156 |
*/
|
|
157 |
static const char *ap_xsendfile_get_orginal_path(request_rec *rec) { |
|
158 |
const char |
|
159 |
*rv = rec->the_request, |
|
160 |
*last; |
|
161 |
||
162 |
int dir = 0; |
|
163 |
size_t uri_len; |
|
164 |
||
165 |
/* skip method && spaces */
|
|
166 |
while (*rv && !apr_isspace(*rv)) { |
|
167 |
++rv; |
|
168 |
}
|
|
169 |
while (apr_isspace(*rv)) { |
|
170 |
++rv; |
|
171 |
}
|
|
172 |
/* first space is the request end */
|
|
173 |
last = rv; |
|
174 |
while (*last && !apr_isspace(*last)) { |
|
175 |
++last; |
|
176 |
}
|
|
177 |
uri_len = last - rv; |
|
178 |
if (!uri_len) { |
|
179 |
return NULL; |
|
180 |
}
|
|
181 |
||
182 |
/* alright, lets see if the request_uri changed! */
|
|
183 |
if (strncmp(rv, rec->uri, uri_len) == 0) { |
|
184 |
rv = apr_pstrdup(rec->pool, rec->filename); |
|
185 |
dir = rec->finfo.filetype == APR_DIR; |
|
186 |
}
|
|
187 |
else { |
|
188 |
/* need to lookup the url again as it changed */
|
|
189 |
request_rec *sr = ap_sub_req_lookup_uri( |
|
190 |
apr_pstrmemdup(rec->pool, rv, uri_len), |
|
191 |
rec, |
|
192 |
NULL
|
|
193 |
);
|
|
194 |
if (!sr) { |
|
195 |
return NULL; |
|
196 |
}
|
|
197 |
rv = apr_pstrdup(rec->pool, sr->filename); |
|
198 |
dir = rec->finfo.filetype == APR_DIR; |
|
199 |
ap_destroy_sub_req(sr); |
|
200 |
}
|
|
201 |
||
202 |
/* now we need to truncate so we only have the directory */
|
|
203 |
if (!dir && (last = ap_strrchr(rv, '/')) != NULL) { |
|
204 |
*((char*)last + 1) = '\0'; |
|
205 |
}
|
|
206 |
return rv; |
|
207 |
}
|
|
208 |
||
209 |
/*
|
|
210 |
little helper function to build the file path if available
|
|
211 |
*/
|
|
212 |
static apr_status_t ap_xsendfile_get_filepath(request_rec *r, xsendfile_conf_t *conf, const char *file, /* out */ char **path) { |
|
213 |
||
214 |
const char *root = ap_xsendfile_get_orginal_path(r); |
|
215 |
apr_status_t rv; |
|
216 |
||
217 |
apr_array_header_t *patharr; |
|
218 |
const char **paths; |
|
219 |
int i; |
|
220 |
||
221 |
||
222 |
#ifdef _DEBUG
|
|
223 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "xsendfile: path is %s", root); |
|
224 |
#endif
|
|
225 |
||
226 |
/* merge the array */
|
|
227 |
if (root) { |
|
228 |
patharr = apr_array_make(r->pool, conf->paths->nelts + 1, sizeof(char*)); |
|
229 |
*(const char**)(apr_array_push(patharr)) = root; |
|
230 |
apr_array_cat(patharr, conf->paths); |
|
231 |
} else { |
|
232 |
patharr = conf->paths; |
|
233 |
}
|
|
234 |
if (patharr->nelts == 0) { |
|
235 |
return APR_EBADPATH; |
|
236 |
}
|
|
237 |
paths = (const char**)patharr->elts; |
|
238 |
||
239 |
for (i = 0; i < patharr->nelts; ++i) { |
|
240 |
if ((rv = apr_filepath_merge( |
|
241 |
path, |
|
242 |
paths[i], |
|
243 |
file, |
|
244 |
APR_FILEPATH_TRUENAME | APR_FILEPATH_NOTABOVEROOT, |
|
245 |
r->pool |
|
246 |
)) == OK) { |
|
247 |
break; |
|
248 |
}
|
|
249 |
}
|
|
250 |
if (rv != OK) { |
|
251 |
*path = NULL; |
|
252 |
}
|
|
253 |
return rv; |
|
254 |
}
|
|
255 |
||
256 |
static apr_status_t ap_xsendfile_output_filter(ap_filter_t *f, apr_bucket_brigade *in) { |
|
257 |
request_rec *r = f->r, *sr = NULL; |
|
258 |
||
259 |
xsendfile_conf_t
|
|
260 |
*dconf = (xsendfile_conf_t *)ap_get_module_config(r->per_dir_config, &xsendfile_module), |
|
261 |
*sconf = (xsendfile_conf_t *)ap_get_module_config(r->server->module_config, &xsendfile_module), |
|
262 |
*conf = xsendfile_config_merge(r->pool, sconf, dconf); |
|
263 |
||
264 |
core_dir_config *coreconf = (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module); |
|
265 |
||
266 |
apr_status_t rv; |
|
267 |
apr_bucket *e; |
|
268 |
||
269 |
apr_file_t *fd = NULL; |
|
270 |
apr_finfo_t finfo; |
|
271 |
||
272 |
const char *file = NULL; |
|
273 |
char *translated = NULL; |
|
274 |
||
275 |
int errcode; |
|
276 |
||
277 |
#ifdef _DEBUG
|
|
278 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "xsendfile: output_filter for %s", r->the_request); |
|
279 |
#endif
|
|
280 |
/*
|
|
281 |
should we proceed with this request?
|
|
282 |
||
283 |
* sub-requests suck
|
|
284 |
* furthermore default-handled requests suck, as they actually shouldn't be able to set headers
|
|
285 |
*/
|
|
286 |
if ( |
|
287 |
r->status != HTTP_OK |
|
288 |
|| r->main |
|
289 |
|| (r->handler && strcmp(r->handler, "default-handler") == 0) /* those table-keys are lower-case, right? */ |
|
290 |
) { |
|
291 |
#ifdef _DEBUG
|
|
292 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "xsendfile: not met [%d]", r->status); |
|
293 |
#endif
|
|
294 |
ap_remove_output_filter(f); |
|
295 |
return ap_pass_brigade(f->next, in); |
|
296 |
}
|
|
297 |
||
298 |
/*
|
|
299 |
alright, look for x-sendfile
|
|
300 |
*/
|
|
301 |
file = apr_table_get(r->headers_out, AP_XSENDFILE_HEADER); |
|
302 |
apr_table_unset(r->headers_out, AP_XSENDFILE_HEADER); |
|
303 |
||
304 |
/* cgi/fastcgi will put the stuff into err_headers_out */
|
|
305 |
if (!file || !*file) { |
|
306 |
file = apr_table_get(r->err_headers_out, AP_XSENDFILE_HEADER); |
|
307 |
apr_table_unset(r->err_headers_out, AP_XSENDFILE_HEADER); |
|
308 |
}
|
|
309 |
||
310 |
/* nothing there :p */
|
|
311 |
if (!file || !*file) { |
|
312 |
#ifdef _DEBUG
|
|
313 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "xsendfile: nothing found"); |
|
314 |
#endif
|
|
315 |
ap_remove_output_filter(f); |
|
316 |
return ap_pass_brigade(f->next, in); |
|
317 |
}
|
|
318 |
||
319 |
/*
|
|
320 |
drop *everything*
|
|
321 |
might be pretty expensive to generate content first that goes straight to the bitbucket,
|
|
322 |
but actually the scripts that might set this flag won't output too much anyway
|
|
323 |
*/
|
|
324 |
while (!APR_BRIGADE_EMPTY(in)) { |
|
325 |
e = APR_BRIGADE_FIRST(in); |
|
326 |
apr_bucket_delete(e); |
|
327 |
}
|
|
328 |
r->eos_sent = 0; |
|
329 |
||
330 |
/* as we dropped all the content this field is not valid anymore! */
|
|
331 |
apr_table_unset(r->headers_out, "Content-Length"); |
|
332 |
apr_table_unset(r->err_headers_out, "Content-Length"); |
|
333 |
apr_table_unset(r->headers_out, "Content-Encoding"); |
|
334 |
apr_table_unset(r->err_headers_out, "Content-Encoding"); |
|
335 |
||
336 |
rv = ap_xsendfile_get_filepath(r, conf, file, &translated); |
|
337 |
if (rv != OK) { |
|
338 |
ap_log_rerror( |
|
339 |
APLOG_MARK, |
|
340 |
APLOG_ERR, |
|
341 |
rv, |
|
342 |
r, |
|
343 |
"xsendfile: unable to find file: %s", |
|
344 |
file
|
|
345 |
);
|
|
346 |
ap_remove_output_filter(f); |
|
347 |
ap_die(HTTP_NOT_FOUND, r); |
|
348 |
return HTTP_NOT_FOUND; |
|
349 |
}
|
|
350 |
||
351 |
#ifdef _DEBUG
|
|
352 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "xsendfile: found %s", translated); |
|
353 |
#endif
|
|
354 |
||
355 |
/*
|
|
356 |
try open the file
|
|
357 |
*/
|
|
358 |
if ((rv = apr_file_open( |
|
359 |
&fd, |
|
360 |
translated, |
|
361 |
APR_READ | APR_BINARY |
|
362 |
#if APR_HAS_SENDFILE
|
|
363 |
| (coreconf->enable_sendfile != ENABLE_SENDFILE_OFF ? APR_SENDFILE_ENABLED : 0) |
|
364 |
#endif
|
|
365 |
,
|
|
366 |
0, |
|
367 |
r->pool |
|
368 |
)) != APR_SUCCESS) { |
|
369 |
ap_log_rerror( |
|
370 |
APLOG_MARK, |
|
371 |
APLOG_ERR, |
|
372 |
rv, |
|
373 |
r, |
|
374 |
"xsendfile: cannot open file: %s", |
|
375 |
translated
|
|
376 |
);
|
|
377 |
ap_remove_output_filter(f); |
|
378 |
ap_die(HTTP_NOT_FOUND, r); |
|
379 |
return HTTP_NOT_FOUND; |
|
380 |
}
|
|
381 |
#if APR_HAS_SENDFILE && defined(_DEBUG)
|
|
382 |
if (coreconf->enable_sendfile == ENABLE_SENDFILE_OFF) { |
|
383 |
ap_log_error( |
|
384 |
APLOG_MARK, |
|
385 |
APLOG_WARNING, |
|
386 |
0, |
|
387 |
r->server, |
|
388 |
"xsendfile: sendfile configured, but not active %d", |
|
389 |
coreconf->enable_sendfile |
|
390 |
);
|
|
391 |
}
|
|
392 |
#endif
|
|
393 |
/* stat (for etag/cache/content-length stuff) */
|
|
394 |
if ((rv = apr_file_info_get(&finfo, APR_FINFO_NORM, fd)) != APR_SUCCESS) { |
|
395 |
ap_log_rerror( |
|
396 |
APLOG_MARK, |
|
397 |
APLOG_ERR, |
|
398 |
rv, |
|
399 |
r, |
|
400 |
"xsendfile: unable to stat file: %s", |
|
401 |
translated
|
|
402 |
);
|
|
403 |
apr_file_close(fd); |
|
404 |
ap_remove_output_filter(f); |
|
405 |
ap_die(HTTP_FORBIDDEN, r); |
|
406 |
return HTTP_FORBIDDEN; |
|
407 |
}
|
|
408 |
/* no inclusion of directories! we're serving files! */
|
|
409 |
if (finfo.filetype != APR_REG) { |
|
410 |
ap_log_rerror( |
|
411 |
APLOG_MARK, |
|
412 |
APLOG_ERR, |
|
413 |
APR_EBADPATH, |
|
414 |
r, |
|
415 |
"xsendfile: not a file %s", |
|
416 |
translated
|
|
417 |
);
|
|
418 |
apr_file_close(fd); |
|
419 |
ap_remove_output_filter(f); |
|
420 |
ap_die(HTTP_NOT_FOUND, r); |
|
421 |
return HTTP_NOT_FOUND; |
|
422 |
}
|
|
423 |
||
424 |
/*
|
|
425 |
need to cheat here a bit
|
|
426 |
as etag generator will use those ;)
|
|
427 |
and we want local_copy and cache
|
|
428 |
*/
|
|
429 |
r->finfo.inode = finfo.inode; |
|
430 |
r->finfo.size = finfo.size; |
|
431 |
||
432 |
/*
|
|
433 |
caching? why not :p
|
|
434 |
*/
|
|
435 |
r->no_cache = r->no_local_copy = 0; |
|
436 |
||
437 |
/* some script (f?cgi) place stuff in err_headers_out */
|
|
438 |
if ( |
|
439 |
conf->ignoreLM == XSENDFILE_ENABLED |
|
440 |
|| ( |
|
441 |
!apr_table_get(r->headers_out, "last-modified") |
|
442 |
&& !apr_table_get(r->headers_out, "last-modified") |
|
443 |
)
|
|
444 |
) { |
|
445 |
apr_table_unset(r->err_headers_out, "last-modified"); |
|
446 |
ap_update_mtime(r, finfo.mtime); |
|
447 |
ap_set_last_modified(r); |
|
448 |
}
|
|
449 |
if ( |
|
450 |
conf->ignoreETag == XSENDFILE_ENABLED |
|
451 |
|| ( |
|
452 |
!apr_table_get(r->headers_out, "etag") |
|
453 |
&& !apr_table_get(r->err_headers_out, "etag") |
|
454 |
)
|
|
455 |
) { |
|
456 |
apr_table_unset(r->err_headers_out, "etag"); |
|
457 |
ap_set_etag(r); |
|
458 |
}
|
|
459 |
||
460 |
ap_set_content_length(r, finfo.size); |
|
461 |
||
462 |
/* cache or something? */
|
|
463 |
if ((errcode = ap_meets_conditions(r)) != OK) { |
|
464 |
#ifdef _DEBUG
|
|
465 |
ap_log_error( |
|
466 |
APLOG_MARK, |
|
467 |
APLOG_DEBUG, |
|
468 |
0, |
|
469 |
r->server, |
|
470 |
"xsendfile: met condition %d for %s", |
|
471 |
errcode, |
|
472 |
file
|
|
473 |
);
|
|
474 |
#endif
|
|
475 |
apr_file_close(fd); |
|
476 |
r->status = errcode; |
|
477 |
}
|
|
478 |
else { |
|
479 |
/* For platforms where the size of the file may be larger than
|
|
480 |
* that which can be stored in a single bucket (where the
|
|
481 |
* length field is an apr_size_t), split it into several
|
|
482 |
* buckets: */
|
|
483 |
if (sizeof(apr_off_t) > sizeof(apr_size_t) |
|
484 |
&& finfo.size > AP_MAX_SENDFILE) { |
|
485 |
apr_off_t fsize = finfo.size; |
|
486 |
e = apr_bucket_file_create(fd, 0, AP_MAX_SENDFILE, r->pool, |
|
487 |
in->bucket_alloc); |
|
488 |
while (fsize > AP_MAX_SENDFILE) { |
|
489 |
apr_bucket *ce; |
|
490 |
apr_bucket_copy(e, &ce); |
|
491 |
APR_BRIGADE_INSERT_TAIL(in, ce); |
|
492 |
e->start += AP_MAX_SENDFILE; |
|
493 |
fsize -= AP_MAX_SENDFILE; |
|
494 |
}
|
|
495 |
e->length = (apr_size_t)fsize; /* Resize just the last bucket */ |
|
496 |
}
|
|
497 |
else { |
|
498 |
e = apr_bucket_file_create(fd, 0, (apr_size_t)finfo.size, |
|
499 |
r->pool, in->bucket_alloc); |
|
500 |
}
|
|
501 |
||
502 |
||
503 |
#if APR_HAS_MMAP
|
|
504 |
if (coreconf->enable_mmap == ENABLE_MMAP_ON) { |
|
505 |
apr_bucket_file_enable_mmap(e, 0); |
|
506 |
}
|
|
507 |
#if defined(_DEBUG)
|
|
508 |
else { |
|
509 |
ap_log_error( |
|
510 |
APLOG_MARK, |
|
511 |
APLOG_WARNING, |
|
512 |
0, |
|
513 |
r->server, |
|
514 |
"xsendfile: mmap configured, but not active %d", |
|
515 |
coreconf->enable_mmap |
|
516 |
);
|
|
517 |
}
|
|
518 |
#endif /* _DEBUG */ |
|
519 |
#endif /* APR_HAS_MMAP */ |
|
520 |
APR_BRIGADE_INSERT_TAIL(in, e); |
|
521 |
}
|
|
522 |
||
523 |
e = apr_bucket_eos_create(in->bucket_alloc); |
|
524 |
APR_BRIGADE_INSERT_TAIL(in, e); |
|
525 |
||
526 |
/* remove ourselves from the filter chain */
|
|
527 |
ap_remove_output_filter(f); |
|
528 |
||
529 |
#ifdef _DEBUG
|
|
530 |
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "xsendfile: sending %d bytes", (int)finfo.size); |
|
531 |
#endif
|
|
532 |
||
533 |
/* send the data up the stack */
|
|
534 |
return ap_pass_brigade(f->next, in); |
|
535 |
}
|
|
536 |
||
537 |
static void ap_xsendfile_insert_output_filter(request_rec *r) { |
|
538 |
xsendfile_conf_active_t enabled = ((xsendfile_conf_t *)ap_get_module_config(r->per_dir_config, &xsendfile_module))->enabled; |
|
539 |
if (XSENDFILE_UNSET == enabled) { |
|
540 |
enabled = ((xsendfile_conf_t*)ap_get_module_config(r->server->module_config, &xsendfile_module))->enabled; |
|
541 |
}
|
|
542 |
||
543 |
if (XSENDFILE_ENABLED != enabled) { |
|
544 |
return; |
|
545 |
}
|
|
546 |
||
547 |
ap_add_output_filter( |
|
548 |
"XSENDFILE", |
|
549 |
NULL, |
|
550 |
r, |
|
551 |
r->connection |
|
552 |
);
|
|
553 |
}
|
|
554 |
static const command_rec xsendfile_command_table[] = { |
|
555 |
AP_INIT_FLAG( |
|
556 |
"XSendFile", |
|
557 |
xsendfile_cmd_flag, |
|
558 |
NULL, |
|
559 |
OR_FILEINFO, |
|
560 |
"On|Off - Enable/disable(default) processing"
|
|
561 |
),
|
|
562 |
AP_INIT_FLAG( |
|
563 |
"XSendFileIgnoreEtag", |
|
564 |
xsendfile_cmd_flag, |
|
565 |
NULL, |
|
566 |
OR_FILEINFO, |
|
567 |
"On|Off - Ignore script provided Etag headers (default: Off)"
|
|
568 |
),
|
|
569 |
AP_INIT_FLAG( |
|
570 |
"XSendFileIgnoreLastModified", |
|
571 |
xsendfile_cmd_flag, |
|
572 |
NULL, |
|
573 |
OR_FILEINFO, |
|
574 |
"On|Off - Ignore script provided Last-Modified headers (default: Off)"
|
|
575 |
),
|
|
576 |
AP_INIT_TAKE1( |
|
577 |
"XSendFilePath", |
|
578 |
xsendfile_cmd_path, |
|
579 |
NULL, |
|
580 |
RSRC_CONF|ACCESS_CONF, |
|
581 |
"Allow to serve files from that Path. Must be absolute"
|
|
582 |
),
|
|
583 |
{ NULL } |
|
584 |
};
|
|
585 |
static void xsendfile_register_hooks(apr_pool_t *p) { |
|
586 |
ap_register_output_filter( |
|
587 |
"XSENDFILE", |
|
588 |
ap_xsendfile_output_filter, |
|
589 |
NULL, |
|
590 |
AP_FTYPE_CONTENT_SET
|
|
591 |
);
|
|
592 |
||
593 |
ap_hook_insert_filter( |
|
594 |
ap_xsendfile_insert_output_filter, |
|
595 |
NULL, |
|
596 |
NULL, |
|
597 |
APR_HOOK_LAST + 1 |
|
598 |
);
|
|
599 |
}
|
|
600 |
module AP_MODULE_DECLARE_DATA xsendfile_module = { |
|
601 |
STANDARD20_MODULE_STUFF, |
|
602 |
xsendfile_config_perdir_create, |
|
603 |
xsendfile_config_merge, |
|
604 |
xsendfile_config_server_create, |
|
605 |
xsendfile_config_merge, |
|
606 |
xsendfile_command_table, |
|
607 |
xsendfile_register_hooks
|
|
608 |
};
|