Skip to content

Commit 264cf54

Browse files
committed
if there are any ExpiresByType directives for the current scope, defer the setting of the expiration header fields to an output filter, which can then apply any ExpiresByType or ExpiresDefault settings -- including to dynamic documents. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@99827 13f79535-47bb-0310-9956-ffa450edef68
1 parent 1143343 commit 264cf54

1 file changed

Lines changed: 145 additions & 50 deletions

File tree

modules/metadata/mod_expires.c

Lines changed: 145 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,11 @@ typedef struct {
212212
apr_table_t *expiresbytype;
213213
} expires_dir_config;
214214

215+
typedef struct {
216+
int defaulted;
217+
apr_table_t *expfields;
218+
} expires_interphase_t;
219+
215220
/* from mod_dir, why is this alias used?
216221
*/
217222
#define DIR_CMD_PERMS OR_INDEXES
@@ -416,59 +421,23 @@ static void *merge_expires_dir_configs(apr_pool_t *p, void *basev, void *addv)
416421
return new;
417422
}
418423

419-
static int add_expires(request_rec *r)
424+
/*
425+
* Handle the setting of the expiration response header fields according
426+
* to our criteria.
427+
*/
428+
429+
static int set_expiration_fields(request_rec *r, const char *code,
430+
apr_table_t *t)
420431
{
421-
expires_dir_config *conf;
422-
char *code;
423432
apr_time_t base;
424433
apr_time_t additional;
425434
apr_time_t expires;
426435
int additional_sec;
427436
char *timestr;
437+
expires_interphase_t *notes;
428438

429-
if (ap_is_HTTP_ERROR(r->status)) /* Don't add Expires headers to errors */
430-
return DECLINED;
431-
432-
if (r->main != NULL) /* Say no to subrequests */
433-
return DECLINED;
434-
435-
conf = (expires_dir_config *) ap_get_module_config(r->per_dir_config, &expires_module);
436-
if (conf == NULL) {
437-
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
438-
"internal error: %s", r->filename);
439-
return HTTP_INTERNAL_SERVER_ERROR;
440-
}
441-
442-
if (conf->active != ACTIVE_ON)
443-
return DECLINED;
444-
445-
/* we perhaps could use the default_type(r) in its place but that
446-
* may be 2nd guesing the desired configuration... calling table_get
447-
* with a NULL key will SEGV us
448-
*
449-
* I still don't know *why* r->content_type would ever be NULL, this
450-
* is possibly a result of fixups being called in many different
451-
* places. Fixups is probably the wrong place to be doing all this
452-
* work... Bah.
453-
*
454-
* Changed as of 08.Jun.96 don't DECLINE, look for an ExpiresDefault.
455-
*/
456-
if (r->content_type == NULL)
457-
code = NULL;
458-
else
459-
code = (char *) apr_table_get(conf->expiresbytype,
460-
ap_field_noparam(r->pool, r->content_type));
461-
462-
if (code == NULL) {
463-
/* no expires defined for that type, is there a default? */
464-
code = conf->expiresdefault;
465-
466-
if (code[0] == '\0')
467-
return OK;
468-
}
469-
470-
/* we have our code */
471-
439+
notes = (expires_interphase_t *) ap_get_module_config(r->request_config,
440+
&expires_module);
472441
switch (code[0]) {
473442
case 'M':
474443
if (r->finfo.filetype == 0) {
@@ -499,17 +468,143 @@ static int add_expires(request_rec *r)
499468
}
500469

501470
expires = base + additional;
502-
apr_table_mergen(r->headers_out, "Cache-Control",
503-
apr_psprintf(r->pool, "max-age=%" APR_TIME_T_FMT,
504-
apr_time_sec(expires - r->request_time)));
471+
apr_table_mergen(t, "Cache-Control",
472+
apr_psprintf(r->pool, "max-age=%" APR_TIME_T_FMT,
473+
apr_time_sec(expires - r->request_time)));
505474
timestr = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
506475
apr_rfc822_date(timestr, expires);
507-
apr_table_setn(r->headers_out, "Expires", timestr);
476+
apr_table_setn(t, "Expires", timestr);
508477
return OK;
509478
}
510479

480+
/*
481+
* Output filter to set the Expires response header field
482+
* according to the content-type of the response -- if it hasn't
483+
* already been set.
484+
*/
485+
static apr_status_t expires_by_type_filter(ap_filter_t *f,
486+
apr_bucket_brigade *b)
487+
{
488+
request_rec *r;
489+
expires_dir_config *conf;
490+
expires_interphase_t *notes;
491+
const char *bytype_expiry;
492+
apr_table_t *t;
493+
494+
r = f->r;
495+
conf = (expires_dir_config *) ap_get_module_config(r->per_dir_config,
496+
&expires_module);
497+
notes = (expires_interphase_t *) ap_get_module_config(r->request_config,
498+
&expires_module);
499+
500+
/*
501+
* If this filter is getting called, it *should* mean that
502+
* the fixup-phase handler ran and set things up for us.
503+
* Check to see which output header table we should use;
504+
* mod_cgi loads script fields into r->err_headers_out,
505+
* for instance.
506+
*/
507+
bytype_expiry = apr_table_get(r->err_headers_out, "Expires");
508+
if (bytype_expiry != NULL) {
509+
t = r->err_headers_out;
510+
}
511+
else {
512+
bytype_expiry = apr_table_get(r->headers_out, "Expires");
513+
t = r->headers_out;
514+
}
515+
if (bytype_expiry == NULL) {
516+
/*
517+
* No expiration has been set, so we can apply any managed by
518+
* this module. Check for one for this content type.
519+
*/
520+
bytype_expiry = apr_table_get(conf->expiresbytype,
521+
ap_field_noparam(r->pool,
522+
r->content_type));
523+
if (bytype_expiry != NULL) {
524+
set_expiration_fields(r, bytype_expiry, t);
525+
}
526+
else if ((notes != NULL) && notes->defaulted) {
527+
/*
528+
* None for this type, but there was a default defined --
529+
* so use it.
530+
*/
531+
t = apr_table_overlay(r->pool, notes->expfields, t);
532+
}
533+
}
534+
ap_remove_output_filter(f);
535+
return ap_pass_brigade(f->next, b);
536+
}
537+
538+
static int add_expires(request_rec *r)
539+
{
540+
expires_dir_config *conf;
541+
expires_interphase_t *notes;
542+
apr_table_t *rfields;
543+
char *code;
544+
545+
if (ap_is_HTTP_ERROR(r->status)) {/* Don't add Expires headers to errors */
546+
return DECLINED;
547+
}
548+
549+
if (r->main != NULL) { /* Say no to subrequests */
550+
return DECLINED;
551+
}
552+
553+
conf = (expires_dir_config *) ap_get_module_config(r->per_dir_config,
554+
&expires_module);
555+
if (conf == NULL) {
556+
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
557+
"internal error: %s", r->filename);
558+
return HTTP_INTERNAL_SERVER_ERROR;
559+
}
560+
561+
if (conf->active != ACTIVE_ON) {
562+
return DECLINED;
563+
}
564+
565+
notes = apr_palloc(r->pool, sizeof(expires_interphase_t));
566+
notes->defaulted = 0;
567+
notes->expfields = apr_table_make(r->pool, 4);
568+
ap_set_module_config(r->request_config, &expires_module, notes);
569+
570+
/*
571+
* If there are any ExpiresByType directives for this scope,
572+
* add the output filter and defer all setting to it. We
573+
* do make a note of any ExpiresDefault value for its use.
574+
*/
575+
if (! apr_is_empty_table(conf->expiresbytype)) {
576+
ap_add_output_filter("EXPIRATION", NULL, r, r->connection);
577+
rfields = notes->expfields;
578+
}
579+
else {
580+
rfields = r->headers_out;
581+
}
582+
/*
583+
* Apply the default expiration if there is one; the filter will
584+
* narrow it down later if possible.
585+
*/
586+
code = conf->expiresdefault;
587+
588+
if (code[0] == '\0') {
589+
return OK;
590+
}
591+
else {
592+
/*
593+
* Note that we're setting it from the default, so that
594+
* the output filter (if it runs) knows it can override the
595+
* value. This allows the by-type filter to be able to
596+
* tell the difference between a value set by, say, a
597+
* CGI script and the one we set by default.
598+
*/
599+
notes->defaulted = 1;
600+
}
601+
return set_expiration_fields(r, code, rfields);
602+
}
603+
511604
static void register_hooks(apr_pool_t *p)
512605
{
606+
ap_register_output_filter("EXPIRATION", expires_by_type_filter, NULL,
607+
AP_FTYPE_CONTENT_SET);
513608
ap_hook_fixups(add_expires,NULL,NULL,APR_HOOK_MIDDLE);
514609
}
515610

0 commit comments

Comments
 (0)