]> granicus.if.org Git - apache/commitdiff
if there are any ExpiresByType directives for the current
authorKen Coar <coar@apache.org>
Wed, 14 May 2003 17:39:14 +0000 (17:39 +0000)
committerKen Coar <coar@apache.org>
Wed, 14 May 2003 17:39:14 +0000 (17:39 +0000)
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

modules/metadata/mod_expires.c

index e35e5544e4e93970621cb09e5740b94e2b89d9d4..d8423bbd06c65c27ae24a485458850ad4df74f16 100644 (file)
@@ -212,6 +212,11 @@ typedef struct {
     apr_table_t *expiresbytype;
 } expires_dir_config;
 
+typedef struct {
+    int defaulted;
+    apr_table_t *expfields;
+} expires_interphase_t;
+
 /* from mod_dir, why is this alias used?
  */
 #define DIR_CMD_PERMS OR_INDEXES
@@ -416,59 +421,23 @@ static void *merge_expires_dir_configs(apr_pool_t *p, void *basev, void *addv)
     return new;
 }
 
-static int add_expires(request_rec *r)
+/*
+ * Handle the setting of the expiration response header fields according
+ * to our criteria.
+ */
+
+static int set_expiration_fields(request_rec *r, const char *code,
+                                 apr_table_t *t)
 {
-    expires_dir_config *conf;
-    char *code;
     apr_time_t base;
     apr_time_t additional;
     apr_time_t expires;
     int additional_sec;
     char *timestr;
+    expires_interphase_t *notes;
 
-    if (ap_is_HTTP_ERROR(r->status))       /* Don't add Expires headers to errors */
-        return DECLINED;
-
-    if (r->main != NULL)        /* Say no to subrequests */
-        return DECLINED;
-
-    conf = (expires_dir_config *) ap_get_module_config(r->per_dir_config, &expires_module);
-    if (conf == NULL) {
-        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
-                    "internal error: %s", r->filename);
-        return HTTP_INTERNAL_SERVER_ERROR;
-    }
-
-    if (conf->active != ACTIVE_ON)
-        return DECLINED;
-
-    /* we perhaps could use the default_type(r) in its place but that
-     * may be 2nd guesing the desired configuration...  calling table_get
-     * with a NULL key will SEGV us
-     *
-     * I still don't know *why* r->content_type would ever be NULL, this
-     * is possibly a result of fixups being called in many different
-     * places.  Fixups is probably the wrong place to be doing all this
-     * work...  Bah.
-     *
-     * Changed as of 08.Jun.96 don't DECLINE, look for an ExpiresDefault.
-     */
-    if (r->content_type == NULL)
-        code = NULL;
-    else
-        code = (char *) apr_table_get(conf->expiresbytype, 
-               ap_field_noparam(r->pool, r->content_type));
-
-    if (code == NULL) {
-        /* no expires defined for that type, is there a default? */
-        code = conf->expiresdefault;
-
-        if (code[0] == '\0')
-            return OK;
-    }
-
-    /* we have our code */
-
+    notes = (expires_interphase_t *) ap_get_module_config(r->request_config,
+                                                          &expires_module);
     switch (code[0]) {
     case 'M':
        if (r->finfo.filetype == 0) { 
@@ -499,17 +468,143 @@ static int add_expires(request_rec *r)
     }
 
     expires = base + additional;
-    apr_table_mergen(r->headers_out, "Cache-Control",
-                   apr_psprintf(r->pool, "max-age=%" APR_TIME_T_FMT,
-                                 apr_time_sec(expires - r->request_time)));
+    apr_table_mergen(t, "Cache-Control",
+                     apr_psprintf(r->pool, "max-age=%" APR_TIME_T_FMT,
+                                  apr_time_sec(expires - r->request_time)));
     timestr = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
     apr_rfc822_date(timestr, expires);
-    apr_table_setn(r->headers_out, "Expires", timestr);
+    apr_table_setn(t, "Expires", timestr);
     return OK;
 }
 
+/*
+ * Output filter to set the Expires response header field
+ * according to the content-type of the response -- if it hasn't
+ * already been set.
+ */
+static apr_status_t expires_by_type_filter(ap_filter_t *f,
+                                           apr_bucket_brigade *b)
+{
+    request_rec *r;
+    expires_dir_config *conf;
+    expires_interphase_t *notes;
+    const char *bytype_expiry;
+    apr_table_t *t;
+
+    r = f->r;
+    conf = (expires_dir_config *) ap_get_module_config(r->per_dir_config,
+                                                       &expires_module);
+    notes = (expires_interphase_t *) ap_get_module_config(r->request_config,
+                                                          &expires_module);
+
+    /*
+     * If this filter is getting called, it *should* mean that
+     * the fixup-phase handler ran and set things up for us.
+     * Check to see which output header table we should use;
+     * mod_cgi loads script fields into r->err_headers_out,
+     * for instance.
+     */
+    bytype_expiry = apr_table_get(r->err_headers_out, "Expires");
+    if (bytype_expiry != NULL) {
+        t = r->err_headers_out;
+    }
+    else {
+        bytype_expiry = apr_table_get(r->headers_out, "Expires");
+        t = r->headers_out;
+    }
+    if (bytype_expiry == NULL) {
+        /*
+         * No expiration has been set, so we can apply any managed by
+         * this module.  Check for one for this content type.
+         */
+        bytype_expiry = apr_table_get(conf->expiresbytype,
+                                      ap_field_noparam(r->pool,
+                                                       r->content_type));
+        if (bytype_expiry != NULL) {
+            set_expiration_fields(r, bytype_expiry, t);
+        }
+        else if ((notes != NULL) && notes->defaulted) {
+            /*
+             * None for this type, but there was a default defined --
+             * so use it.
+             */
+            t = apr_table_overlay(r->pool, notes->expfields, t);
+        }
+    }
+    ap_remove_output_filter(f);
+    return ap_pass_brigade(f->next, b);
+}
+
+static int add_expires(request_rec *r)
+{
+    expires_dir_config *conf;
+    expires_interphase_t *notes;
+    apr_table_t *rfields;
+    char *code;
+
+    if (ap_is_HTTP_ERROR(r->status)) {/* Don't add Expires headers to errors */
+        return DECLINED;
+    }
+
+    if (r->main != NULL) {      /* Say no to subrequests */
+        return DECLINED;
+    }
+
+    conf = (expires_dir_config *) ap_get_module_config(r->per_dir_config,
+                                                       &expires_module);
+    if (conf == NULL) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                    "internal error: %s", r->filename);
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    if (conf->active != ACTIVE_ON) {
+        return DECLINED;
+    }
+
+    notes = apr_palloc(r->pool, sizeof(expires_interphase_t));
+    notes->defaulted = 0;
+    notes->expfields = apr_table_make(r->pool, 4);
+    ap_set_module_config(r->request_config, &expires_module, notes);
+
+    /*
+     * If there are any ExpiresByType directives for this scope,
+     * add the output filter and defer all setting to it.  We
+     * do make a note of any ExpiresDefault value for its use.
+     */
+    if (! apr_is_empty_table(conf->expiresbytype)) {
+        ap_add_output_filter("EXPIRATION", NULL, r, r->connection);
+        rfields = notes->expfields;
+    }
+    else {
+        rfields = r->headers_out;
+    }
+    /*
+     * Apply the default expiration if there is one; the filter will
+     * narrow it down later if possible.
+     */
+    code = conf->expiresdefault;
+
+    if (code[0] == '\0') {
+        return OK;
+    }
+    else {
+        /*
+         * Note that we're setting it from the default, so that
+         * the output filter (if it runs) knows it can override the
+         * value.  This allows the by-type filter to be able to
+         * tell the difference between a value set by, say, a
+         * CGI script and the one we set by default.
+         */
+        notes->defaulted = 1;
+    }
+    return set_expiration_fields(r, code, rfields);
+}
+
 static void register_hooks(apr_pool_t *p)
 {
+    ap_register_output_filter("EXPIRATION", expires_by_type_filter, NULL,
+                              AP_FTYPE_CONTENT_SET);
     ap_hook_fixups(add_expires,NULL,NULL,APR_HOOK_MIDDLE);
 }