1 /* Copyright 2001-2005 The Apache Software Foundation or its licensors, as
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include "mod_cache.h"
21 extern APR_OPTIONAL_FN_TYPE(ap_cache_generate_key) *cache_generate_key;
23 extern module AP_MODULE_DECLARE_DATA cache_module;
25 /* -------------------------------------------------------------- */
28 * delete all URL entities from the cache
31 int cache_remove_url(cache_request_rec *cache, apr_pool_t *p)
33 cache_provider_list *list;
36 list = cache->providers;
38 /* Remove the stale cache entry if present. If not, we're
39 * being called from outside of a request; remove the
42 h = cache->stale_handle ? cache->stale_handle : cache->handle;
46 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
47 "cache: Removing url %s from the cache", h->cache_obj->key);
49 /* for each specified cache type, delete the URL */
51 list->provider->remove_url(h, p);
59 * create a new URL entity in the cache
61 * It is possible to store more than once entity per URL. This
62 * function will always create a new entity, regardless of whether
63 * other entities already exist for the same URL.
65 * The size of the entity is provided so that a cache module can
66 * decide whether or not it wants to cache this particular entity.
67 * If the size is unknown, a size of -1 should be set.
69 int cache_create_entity(request_rec *r, char *url, apr_off_t size)
71 cache_provider_list *list;
72 cache_handle_t *h = apr_pcalloc(r->pool, sizeof(cache_handle_t));
75 cache_request_rec *cache = (cache_request_rec *)
76 ap_get_module_config(r->request_config, &cache_module);
78 rv = cache_generate_key(r, r->pool, &key);
79 if (rv != APR_SUCCESS) {
83 list = cache->providers;
84 /* for each specified cache type, delete the URL */
86 switch (rv = list->provider->create_entity(h, r, key, size)) {
89 cache->provider = list->provider;
90 cache->provider_name = list->provider_name;
105 static int set_cookie_doo_doo(void *v, const char *key, const char *val)
107 apr_table_addn(v, key, val);
111 CACHE_DECLARE(void) ap_cache_accept_headers(cache_handle_t *h, request_rec *r,
114 apr_table_t *cookie_table, *hdr_copy;
117 v = apr_table_get(h->resp_hdrs, "Content-Type");
119 ap_set_content_type(r, v);
120 apr_table_unset(h->resp_hdrs, "Content-Type");
123 /* If the cache gave us a Last-Modified header, we can't just
124 * pass it on blindly because of restrictions on future values.
126 v = apr_table_get(h->resp_hdrs, "Last-Modified");
128 ap_update_mtime(r, apr_date_parse_http(v));
129 ap_set_last_modified(r);
130 apr_table_unset(h->resp_hdrs, "Last-Modified");
133 /* The HTTP specification says that it is legal to merge duplicate
134 * headers into one. Some browsers that support Cookies don't like
135 * merged headers and prefer that each Set-Cookie header is sent
136 * separately. Lets humour those browsers by not merging.
137 * Oh what a pain it is.
139 cookie_table = apr_table_make(r->pool, 2);
140 apr_table_do(set_cookie_doo_doo, cookie_table, r->err_headers_out,
142 apr_table_do(set_cookie_doo_doo, cookie_table, h->resp_hdrs,
144 apr_table_unset(r->err_headers_out, "Set-Cookie");
145 apr_table_unset(h->resp_hdrs, "Set-Cookie");
148 hdr_copy = apr_table_copy(r->pool, h->resp_hdrs);
149 apr_table_overlap(hdr_copy, r->headers_out, APR_OVERLAP_TABLES_SET);
150 r->headers_out = hdr_copy;
153 apr_table_overlap(r->headers_out, h->resp_hdrs, APR_OVERLAP_TABLES_SET);
155 if (!apr_is_empty_table(cookie_table)) {
156 r->err_headers_out = apr_table_overlay(r->pool, r->err_headers_out,
162 * select a specific URL entity in the cache
164 * It is possible to store more than one entity per URL. Content
165 * negotiation is used to select an entity. Once an entity is
166 * selected, details of it are stored in the per request
167 * config to save time when serving the request later.
169 * This function returns OK if successful, DECLINED if no
170 * cached entity fits the bill.
172 int cache_select(request_rec *r)
174 cache_provider_list *list;
178 cache_request_rec *cache = (cache_request_rec *)
179 ap_get_module_config(r->request_config, &cache_module);
181 rv = cache_generate_key(r, r->pool, &key);
182 if (rv != APR_SUCCESS) {
185 /* go through the cache types till we get a match */
186 h = apr_palloc(r->pool, sizeof(cache_handle_t));
188 list = cache->providers;
191 switch ((rv = list->provider->open_entity(h, r, key))) {
196 if (list->provider->recall_headers(h, r) != APR_SUCCESS) {
197 /* TODO: Handle this error */
202 * Check Content-Negotiation - Vary
204 * At this point we need to make sure that the object we found in
205 * the cache is the same object that would be delivered to the
206 * client, when the effects of content negotiation are taken into
209 * In plain english, we want to make sure that a language-negotiated
210 * document in one language is not given to a client asking for a
211 * language negotiated document in a different language by mistake.
213 * This code makes the assumption that the storage manager will
214 * cache the req_hdrs if the response contains a Vary
217 * RFC2616 13.6 and 14.44 describe the Vary mechanism.
219 vary = apr_pstrdup(r->pool, apr_table_get(h->resp_hdrs, "Vary"));
220 while (vary && *vary) {
224 /* isolate header name */
225 while (*vary && !apr_isspace(*vary) && (*vary != ','))
227 while (*vary && (apr_isspace(*vary) || (*vary == ','))) {
233 * is this header in the request and the header in the cached
234 * request identical? If not, we give up and do a straight get
236 h1 = apr_table_get(r->headers_in, name);
237 h2 = apr_table_get(h->req_hdrs, name);
239 /* both headers NULL, so a match - do nothing */
241 else if (h1 && h2 && !strcmp(h1, h2)) {
242 /* both headers exist and are equal - do nothing */
245 /* headers do not match, so Vary failed */
246 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
248 "cache_select_url(): Vary header mismatch.");
253 cache->provider = list->provider;
254 cache->provider_name = list->provider_name;
256 /* Is our cached response fresh enough? */
257 fresh = ap_cache_check_freshness(h, r);
259 const char *etag, *lastmod;
261 ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
262 "Cached response for %s isn't fresh. Adding/replacing "
263 "conditional request headers.", r->uri);
265 /* Make response into a conditional */
266 cache->stale_headers = apr_table_copy(r->pool,
269 /* We can only revalidate with our own conditionals: remove the
270 * conditions from the original request.
272 apr_table_unset(r->headers_in, "If-Match");
273 apr_table_unset(r->headers_in, "If-Modified-Since");
274 apr_table_unset(r->headers_in, "If-None-Match");
275 apr_table_unset(r->headers_in, "If-Range");
276 apr_table_unset(r->headers_in, "If-Unmodified-Since");
278 etag = apr_table_get(h->resp_hdrs, "ETag");
279 lastmod = apr_table_get(h->resp_hdrs, "Last-Modified");
281 if (etag || lastmod) {
282 /* If we have a cached etag and/or Last-Modified add in
283 * our own conditionals.
287 apr_table_set(r->headers_in, "If-None-Match", etag);
291 apr_table_set(r->headers_in, "If-Modified-Since",
294 cache->stale_handle = h;
300 /* Okay, this response looks okay. Merge in our stuff and go. */
301 ap_cache_accept_headers(h, r, 0);
307 /* try again with next cache type */
312 /* oo-er! an error */
320 apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
323 char *port_str, *scheme, *hn;
324 const char * hostname;
327 /* Use the canonical name to improve cache hit rate, but only if this is
328 * not a proxy request.
331 /* Use _default_ as the hostname if none present, as in mod_vhost */
332 hostname = ap_get_server_name(r);
334 hostname = "_default_";
337 else if(r->parsed_uri.hostname) {
338 /* Copy the parsed uri hostname */
339 hn = apr_pcalloc(p, strlen(r->parsed_uri.hostname) + 1);
340 for (i = 0; r->parsed_uri.hostname[i]; i++) {
341 hn[i] = apr_tolower(r->parsed_uri.hostname[i]);
343 /* const work-around */
347 /* We are a proxied request, with no hostname. Unlikely
348 * to get very far - but just in case */
349 hostname = "_default_";
352 /* Copy the scheme, ensuring that it is lower case. If the parsed uri
353 * contains no string or if this is not a proxy request.
355 if (r->proxyreq && r->parsed_uri.scheme) {
356 /* Copy the scheme */
357 scheme = apr_pcalloc(p, strlen(r->parsed_uri.scheme) + 1);
358 for (i = 0; r->parsed_uri.scheme[i]; i++) {
359 scheme[i] = apr_tolower(r->parsed_uri.scheme[i]);
366 /* If the content is locally generated, use the port-number of the
367 * current server. Otherwise. copy the URI's port-string (which may be a
368 * service name). If the URI contains no port-string, use apr-util's
369 * notion of the default port for that scheme - if available.
372 if (r->parsed_uri.port_str) {
373 port_str = apr_pcalloc(p, strlen(r->parsed_uri.port_str) + 2);
375 for (i = 0; r->parsed_uri.port_str[i]; i++) {
376 port_str[i + 1] = apr_tolower(r->parsed_uri.port_str[i]);
379 else if (apr_uri_port_of_scheme(scheme)) {
380 port_str = apr_psprintf(p, ":%u", apr_uri_port_of_scheme(scheme));
383 /* No port string given in the AbsoluteUri, and we have no
384 * idea what the default port for the scheme is. Leave it
385 * blank and live with the inefficiency of some extra cached
392 /* Use the server port */
393 port_str = apr_psprintf(p, ":%u", ap_get_server_port(r));
396 /* Key format is a URI */
397 *key = apr_pstrcat(p, scheme, "://", hostname, port_str,
398 r->parsed_uri.path, "?", r->args, NULL);