cache_storage.c revision 61ee78d6fec267112988f5f38202ae5e9b0cb035
2N/A/* Copyright 2001-2005 The Apache Software Foundation or its licensors, as
2N/A * applicable.
2N/A *
2N/A * Licensed under the Apache License, Version 2.0 (the "License");
2N/A * you may not use this file except in compliance with the License.
2N/A * You may obtain a copy of the License at
2N/A *
2N/A * http://www.apache.org/licenses/LICENSE-2.0
2N/A *
2N/A * Unless required by applicable law or agreed to in writing, software
2N/A * distributed under the License is distributed on an "AS IS" BASIS,
2N/A * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2N/A * See the License for the specific language governing permissions and
2N/A * limitations under the License.
2N/A */
2N/A
2N/A#define CORE_PRIVATE
2N/A
2N/A#include "mod_cache.h"
2N/A
2790N/Aextern APR_OPTIONAL_FN_TYPE(ap_cache_generate_key) *cache_generate_key;
2790N/A
3739N/Aextern module AP_MODULE_DECLARE_DATA cache_module;
2N/A
2N/A/* -------------------------------------------------------------- */
2N/A
2N/A/*
2N/A * delete all URL entities from the cache
2N/A *
3817N/A */
2N/Aint cache_remove_url(cache_request_rec *cache, apr_pool_t *p)
2N/A{
2N/A cache_provider_list *list;
59N/A cache_handle_t *h;
59N/A
2N/A list = cache->providers;
2N/A
2N/A /* Remove the stale cache entry if present. If not, we're
2N/A * being called from outside of a request; remove the
26N/A * non-stalle handle.
26N/A */
2N/A h = cache->stale_handle ? cache->stale_handle : cache->handle;
26N/A if (!h) {
1470N/A return OK;
38N/A }
1470N/A ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
1470N/A "cache: Removing url %s from the cache", h->cache_obj->key);
1470N/A
181N/A /* for each specified cache type, delete the URL */
26N/A while(list) {
4811N/A list->provider->remove_url(h, p);
4811N/A list = list->next;
4811N/A }
3739N/A return OK;
3739N/A}
3739N/A
3739N/A
3739N/A/*
3739N/A * create a new URL entity in the cache
3739N/A *
3739N/A * It is possible to store more than once entity per URL. This
3817N/A * function will always create a new entity, regardless of whether
3817N/A * other entities already exist for the same URL.
26N/A *
26N/A * The size of the entity is provided so that a cache module can
26N/A * decide whether or not it wants to cache this particular entity.
26N/A * If the size is unknown, a size of -1 should be set.
26N/A */
26N/Aint cache_create_entity(request_rec *r, char *url, apr_off_t size)
26N/A{
700N/A cache_provider_list *list;
700N/A cache_handle_t *h = apr_pcalloc(r->pool, sizeof(cache_handle_t));
26N/A char *key;
26N/A apr_status_t rv;
26N/A cache_request_rec *cache = (cache_request_rec *)
1498N/A ap_get_module_config(r->request_config, &cache_module);
1498N/A
26N/A rv = cache_generate_key(r, r->pool, &key);
1498N/A if (rv != APR_SUCCESS) {
151N/A return rv;
206N/A }
26N/A
26N/A list = cache->providers;
26N/A /* for each specified cache type, delete the URL */
26N/A while (list) {
26N/A switch (rv = list->provider->create_entity(h, r, key, size)) {
3998N/A case OK: {
2818N/A cache->handle = h;
2830N/A cache->provider = list->provider;
3127N/A cache->provider_name = list->provider_name;
3998N/A return OK;
26N/A }
3294N/A case DECLINED: {
26N/A list = list->next;
2N/A continue;
4747N/A }
4437N/A default: {
4437N/A return rv;
4488N/A }
4488N/A }
4437N/A }
4437N/A return DECLINED;
26N/A}
26N/A
26N/Astatic int set_cookie_doo_doo(void *v, const char *key, const char *val)
26N/A{
883N/A apr_table_addn(v, key, val);
26N/A return 1;
26N/A}
26N/A
26N/ACACHE_DECLARE(void) ap_cache_accept_headers(cache_handle_t *h, request_rec *r,
1043N/A int preserve_orig)
4953N/A{
4953N/A apr_table_t *cookie_table, *hdr_copy;
586N/A const char *v;
26N/A
5329N/A v = apr_table_get(h->resp_hdrs, "Content-Type");
5329N/A if (v) {
93N/A ap_set_content_type(r, v);
5329N/A apr_table_unset(h->resp_hdrs, "Content-Type");
5329N/A }
166N/A
26N/A /* If the cache gave us a Last-Modified header, we can't just
379N/A * pass it on blindly because of restrictions on future values.
379N/A */
1498N/A v = apr_table_get(h->resp_hdrs, "Last-Modified");
5123N/A if (v) {
1498N/A ap_update_mtime(r, apr_date_parse_http(v));
4518N/A ap_set_last_modified(r);
4518N/A apr_table_unset(h->resp_hdrs, "Last-Modified");
2899N/A }
2236N/A
2818N/A /* The HTTP specification says that it is legal to merge duplicate
4910N/A * headers into one. Some browsers that support Cookies don't like
2N/A * merged headers and prefer that each Set-Cookie header is sent
26N/A * separately. Lets humour those browsers by not merging.
26N/A * Oh what a pain it is.
181N/A */
181N/A cookie_table = apr_table_make(r->pool, 2);
4353N/A apr_table_do(set_cookie_doo_doo, cookie_table, r->err_headers_out,
4353N/A "Set-Cookie", NULL);
4353N/A apr_table_do(set_cookie_doo_doo, cookie_table, h->resp_hdrs,
4488N/A "Set-Cookie", NULL);
99N/A apr_table_unset(r->err_headers_out, "Set-Cookie");
59N/A apr_table_unset(h->resp_hdrs, "Set-Cookie");
12N/A
30N/A if (preserve_orig) {
4811N/A hdr_copy = apr_table_copy(r->pool, h->resp_hdrs);
4811N/A apr_table_overlap(hdr_copy, r->headers_out, APR_OVERLAP_TABLES_SET);
4811N/A r->headers_out = hdr_copy;
1256N/A }
1256N/A else {
1256N/A apr_table_overlap(r->headers_out, h->resp_hdrs, APR_OVERLAP_TABLES_SET);
2818N/A }
1256N/A if (!apr_is_empty_table(cookie_table)) {
1256N/A r->err_headers_out = apr_table_overlay(r->pool, r->err_headers_out,
1256N/A cookie_table);
1256N/A }
1256N/A}
1256N/A
1256N/A/*
1256N/A * select a specific URL entity in the cache
1256N/A *
2818N/A * It is possible to store more than one entity per URL. Content
1256N/A * negotiation is used to select an entity. Once an entity is
1256N/A * selected, details of it are stored in the per request
1256N/A * config to save time when serving the request later.
1256N/A *
1256N/A * This function returns OK if successful, DECLINED if no
1256N/A * cached entity fits the bill.
1256N/A */
3109N/Aint cache_select_url(request_rec *r, char *url)
3109N/A{
3109N/A cache_provider_list *list;
3109N/A apr_status_t rv;
3109N/A cache_handle_t *h;
3109N/A char *key;
3109N/A cache_request_rec *cache = (cache_request_rec *)
3109N/A ap_get_module_config(r->request_config, &cache_module);
3109N/A
3109N/A rv = cache_generate_key(r, r->pool, &key);
3109N/A if (rv != APR_SUCCESS) {
3109N/A return rv;
3109N/A }
3109N/A /* go through the cache types till we get a match */
3109N/A h = apr_palloc(r->pool, sizeof(cache_handle_t));
3109N/A
3109N/A list = cache->providers;
1256N/A
1256N/A while (list) {
1256N/A switch ((rv = list->provider->open_entity(h, r, key))) {
3109N/A case OK: {
4811N/A char *vary = NULL;
1256N/A int fresh;
26N/A
26N/A if (list->provider->recall_headers(h, r) != APR_SUCCESS) {
1256N/A /* TODO: Handle this error */
2N/A return DECLINED;
26N/A }
1256N/A
1256N/A /*
185N/A * Check Content-Negotiation - Vary
2N/A *
255N/A * At this point we need to make sure that the object we found in
145N/A * the cache is the same object that would be delivered to the
7N/A * client, when the effects of content negotiation are taken into
26N/A * effect.
26N/A *
38N/A * In plain english, we want to make sure that a language-negotiated
26N/A * document in one language is not given to a client asking for a
197N/A * language negotiated document in a different language by mistake.
197N/A *
197N/A * This code makes the assumption that the storage manager will
197N/A * cache the req_hdrs if the response contains a Vary
197N/A * header.
26N/A *
30N/A * RFC2616 13.6 and 14.44 describe the Vary mechanism.
26N/A */
46N/A vary = apr_pstrdup(r->pool, apr_table_get(h->resp_hdrs, "Vary"));
46N/A while (vary && *vary) {
46N/A char *name = vary;
64N/A const char *h1, *h2;
64N/A
26N/A /* isolate header name */
46N/A while (*vary && !apr_isspace(*vary) && (*vary != ','))
46N/A ++vary;
46N/A while (*vary && (apr_isspace(*vary) || (*vary == ','))) {
2N/A *vary = '\0';
1256N/A ++vary;
1256N/A }
1256N/A
1256N/A /*
1256N/A * is this header in the request and the header in the cached
2818N/A * request identical? If not, we give up and do a straight get
1256N/A */
1256N/A h1 = apr_table_get(r->headers_in, name);
1256N/A h2 = apr_table_get(h->req_hdrs, name);
1256N/A if (h1 == h2) {
1256N/A /* both headers NULL, so a match - do nothing */
2818N/A }
2818N/A else if (h1 && h2 && !strcmp(h1, h2)) {
1256N/A /* both headers exist and are equal - do nothing */
2236N/A }
2236N/A else {
1256N/A /* headers do not match, so Vary failed */
2818N/A ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
3739N/A r->server,
1256N/A "cache_select_url(): Vary header mismatch.");
1256N/A return DECLINED;
1256N/A }
1256N/A }
1256N/A
1256N/A cache->provider = list->provider;
1256N/A cache->provider_name = list->provider_name;
1256N/A
3817N/A /* Is our cached response fresh enough? */
1256N/A fresh = ap_cache_check_freshness(h, r);
1256N/A if (!fresh) {
1256N/A const char *etag, *lastmod;
1256N/A
1256N/A ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
2818N/A "Cached response for %s isn't fresh. Adding/replacing "
3449N/A "conditional request headers.", r->uri);
3449N/A
3449N/A /* Make response into a conditional */
3449N/A cache->stale_headers = apr_table_copy(r->pool,
3449N/A r->headers_in);
3449N/A
3449N/A /* We can only revalidate with our own conditionals: remove the
1934N/A * conditions from the original request.
3739N/A */
2818N/A apr_table_unset(r->headers_in, "If-Match");
1256N/A apr_table_unset(r->headers_in, "If-Modified-Since");
1256N/A apr_table_unset(r->headers_in, "If-None-Match");
1256N/A apr_table_unset(r->headers_in, "If-Range");
1413N/A apr_table_unset(r->headers_in, "If-Unmodified-Since");
1413N/A
1413N/A etag = apr_table_get(h->resp_hdrs, "ETag");
1413N/A lastmod = apr_table_get(h->resp_hdrs, "Last-Modified");
3739N/A
1413N/A if (etag || lastmod) {
1413N/A /* If we have a cached etag and/or Last-Modified add in
1413N/A * our own conditionals.
1413N/A */
1413N/A
1256N/A if (etag) {
1256N/A apr_table_set(r->headers_in, "If-None-Match", etag);
1256N/A }
1256N/A
3817N/A if (lastmod) {
1256N/A apr_table_set(r->headers_in, "If-Modified-Since",
1256N/A lastmod);
1256N/A }
1256N/A cache->stale_handle = h;
1256N/A }
3449N/A
3449N/A return DECLINED;
1934N/A }
3739N/A
3449N/A /* Okay, this response looks okay. Merge in our stuff and go. */
1256N/A ap_cache_accept_headers(h, r, 0);
1256N/A
1256N/A cache->handle = h;
3109N/A return OK;
3109N/A }
3109N/A case DECLINED: {
3109N/A /* try again with next cache type */
3109N/A list = list->next;
3109N/A continue;
3109N/A }
3109N/A default: {
3109N/A /* oo-er! an error */
3109N/A return rv;
3109N/A }
3109N/A }
3109N/A }
3739N/A return DECLINED;
3109N/A}
3109N/A
3109N/Aapr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
3109N/A char**key)
3109N/A{
3109N/A char *port_str, *scheme, *hn;
3109N/A const char * hostname;
3109N/A int i;
3109N/A
3109N/A /* Use the canonical name to improve cache hit rate, but only if this is
3109N/A * not a proxy request.
3817N/A */
3109N/A if (!r->proxyreq) {
3109N/A /* Use _default_ as the hostname if none present, as in mod_vhost */
3109N/A hostname = ap_get_server_name(r);
3109N/A if (!hostname) {
3109N/A hostname = "_default_";
3109N/A }
3109N/A }
3109N/A else if(r->parsed_uri.hostname) {
3449N/A /* Copy the parsed uri hostname */
3449N/A hn = apr_pcalloc(p, strlen(r->parsed_uri.hostname) + 1);
3109N/A for (i = 0; r->parsed_uri.hostname[i]; i++) {
3739N/A hn[i] = apr_tolower(r->parsed_uri.hostname[i]);
3109N/A }
3109N/A /* const work-around */
3109N/A hostname = hn;
3109N/A }
4811N/A else {
4811N/A /* We are a proxied request, with no hostname. Unlikely
4811N/A * to get very far - but just in case */
4811N/A hostname = "_default_";
4811N/A }
4811N/A
4811N/A /* Copy the scheme, ensuring that it is lower case. If the parsed uri
4811N/A * contains no string or if this is not a proxy request.
1256N/A */
1256N/A if (r->proxyreq && r->parsed_uri.scheme) {
3739N/A /* Copy the scheme */
1256N/A scheme = apr_pcalloc(p, strlen(r->parsed_uri.scheme) + 1);
1256N/A for (i = 0; r->parsed_uri.scheme[i]; i++) {
1256N/A scheme[i] = apr_tolower(r->parsed_uri.scheme[i]);
1256N/A }
1256N/A }
1256N/A else {
53N/A scheme = "http";
46N/A }
46N/A
26N/A /* If the content is locally generated, use the port-number of the
181N/A * current server. Otherwise. copy the URI's port-string (which may be a
369N/A * service name). If the URI contains no port-string, use apr-util's
181N/A * notion of the default port for that scheme - if available.
181N/A */
181N/A if(r->proxyreq) {
181N/A if (r->parsed_uri.port_str) {
181N/A port_str = apr_pcalloc(p, strlen(r->parsed_uri.port_str) + 2);
181N/A port_str[0] = ':';
76N/A for (i = 0; r->parsed_uri.port_str[i]; i++) {
99N/A port_str[i + 1] = apr_tolower(r->parsed_uri.port_str[i]);
181N/A }
2818N/A }
2818N/A else if (apr_uri_port_of_scheme(scheme)) {
26N/A port_str = apr_psprintf(p, ":%u", apr_uri_port_of_scheme(scheme));
3817N/A }
3817N/A else {
3817N/A /* No port string given in the AbsoluteUri, and we have no
3817N/A * idea what the default port for the scheme is. Leave it
3817N/A * blank and live with the inefficiency of some extra cached
3817N/A * entities.
3817N/A */
3817N/A port_str = "";
3817N/A }
1549N/A }
3817N/A else {
3817N/A /* Use the server port */
3817N/A port_str = apr_psprintf(p, ":%u", ap_get_server_port(r));
3817N/A }
1549N/A
185N/A /* Key format is a URI */
3817N/A *key = apr_pstrcat(p, scheme, "://", hostname, port_str,
3817N/A r->parsed_uri.path, "?", r->args, NULL);
145N/A
145N/A return APR_SUCCESS;
3817N/A}
3817N/A