Return-Path: Delivered-To: apmail-httpd-dev-archive@httpd.apache.org Received: (qmail 46627 invoked by uid 500); 13 May 2003 18:23:37 -0000 Mailing-List: contact dev-help@httpd.apache.org; run by ezmlm Precedence: bulk Reply-To: dev@httpd.apache.org list-help: list-unsubscribe: list-post: Delivered-To: mailing list dev@httpd.apache.org Received: (qmail 46520 invoked from network); 13 May 2003 18:23:34 -0000 Message-ID: <3EC1383B.8000407@Golux.Com> Date: Tue, 13 May 2003 14:23:55 -0400 From: Rodent of Unusual Size Organization: The Apache Software Foundation User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.2.1) Gecko/20021130 X-Accept-Language: en-us, en MIME-Version: 1.0 To: Apache httpd developers Subject: fix for ExpiresByType, including dynamic content Content-Type: multipart/mixed; boundary="------------090803050509010403030303" X-Spam-Rating: daedalus.apache.org 1.6.2 0/1000/N This is a multi-part message in MIME format. --------------090803050509010403030303 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit the attached patch (against 2.0) defers handling of the ExpiresByType settings to an output filter, so it should correctly add the fields to a cgi script in the scope of the directive. -- #ken P-)} Ken Coar, Sanagendamgagwedweinini http://Golux.Com/coar/ Author, developer, opinionist http://Apache-Server.Com/ "Millennium hand and shrimp!" --------------090803050509010403030303 Content-Type: text/plain; name="expiresbytype.patch" Content-Transfer-Encoding: 7bit Content-Disposition: inline; filename="expiresbytype.patch" Index: modules/metadata/mod_expires.c =================================================================== RCS file: /home/cvs/httpd-2.0/modules/metadata/mod_expires.c,v retrieving revision 1.39.2.1 diff -u -r1.39.2.1 mod_expires.c --- modules/metadata/mod_expires.c 3 Feb 2003 17:31:46 -0000 1.39.2.1 +++ modules/metadata/mod_expires.c 13 May 2003 18:15:13 -0000 @@ -212,6 +212,11 @@ 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 @@ 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,148 @@ } 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; + apr_time_t base; + apr_time_t additional; + apr_time_t expires; + int additional_sec; + char *timestr; + + 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); } --------------090803050509010403030303--