]> granicus.if.org Git - apache/blob - modules/cache/cache_storage.c
Cleanup structures in mod_cache and friends to remove unused or unnecessary
[apache] / modules / cache / cache_storage.c
1 /* Copyright 2001-2005 The Apache Software Foundation or its licensors, as
2  * applicable.
3  *
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
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #define CORE_PRIVATE
18
19 #include "mod_cache.h"
20
21 extern APR_OPTIONAL_FN_TYPE(ap_cache_generate_key) *cache_generate_key;
22
23 extern module AP_MODULE_DECLARE_DATA cache_module;
24
25 /* -------------------------------------------------------------- */
26
27 /*
28  * delete all URL entities from the cache
29  *
30  */
31 int cache_remove_url(request_rec *r, char *url)
32 {
33     cache_provider_list *list;
34     apr_status_t rv;
35     char *key;
36     cache_request_rec *cache = (cache_request_rec *) 
37                          ap_get_module_config(r->request_config, &cache_module);
38
39     rv = cache_generate_key(r,r->pool,&key);
40     if (rv != APR_SUCCESS) {
41         return rv;
42     }
43
44     list = cache->providers;
45
46     /* for each specified cache type, delete the URL */
47     while(list) {
48         list->provider->remove_url(key);
49         list = list->next;
50     }
51     return OK;
52 }
53
54
55 /*
56  * create a new URL entity in the cache
57  *
58  * It is possible to store more than once entity per URL. This
59  * function will always create a new entity, regardless of whether
60  * other entities already exist for the same URL.
61  *
62  * The size of the entity is provided so that a cache module can
63  * decide whether or not it wants to cache this particular entity.
64  * If the size is unknown, a size of -1 should be set.
65  */
66 int cache_create_entity(request_rec *r, char *url, apr_off_t size)
67 {
68     cache_provider_list *list;
69     cache_handle_t *h = apr_pcalloc(r->pool, sizeof(cache_handle_t));
70     char *key;
71     apr_status_t rv;
72     cache_request_rec *cache = (cache_request_rec *) 
73                          ap_get_module_config(r->request_config, &cache_module);
74
75     rv = cache_generate_key(r, r->pool, &key);
76     if (rv != APR_SUCCESS) {
77         return rv;
78     }
79
80     list = cache->providers;
81     /* for each specified cache type, delete the URL */
82     while (list) {
83         switch (rv = list->provider->create_entity(h, r, key, size)) {
84         case OK: {
85             cache->handle = h;
86             cache->provider = list->provider;
87             cache->provider_name = list->provider_name;
88             return OK;
89         }
90         case DECLINED: {
91             list = list->next;
92             continue;
93         }
94         default: {
95             return rv;
96         }
97         }
98     }
99     return DECLINED;
100 }
101
102 static int set_cookie_doo_doo(void *v, const char *key, const char *val)
103 {
104     apr_table_addn(v, key, val);
105     return 1;
106 }
107
108 static void accept_headers(cache_handle_t *h, request_rec *r)
109 {
110     apr_table_t *cookie_table;
111     const char *v;
112
113     v = apr_table_get(h->resp_hdrs, "Content-Type");
114     if (v) {
115         ap_set_content_type(r, v);
116         apr_table_unset(h->resp_hdrs, "Content-Type");
117     }
118
119     /* If the cache gave us a Last-Modified header, we can't just
120      * pass it on blindly because of restrictions on future values.
121      */
122     v = apr_table_get(h->resp_hdrs, "Last-Modified");
123     if (v) {
124         ap_update_mtime(r, apr_date_parse_http(v));
125         ap_set_last_modified(r);
126         apr_table_unset(h->resp_hdrs, "Last-Modified");
127     }
128
129     /* The HTTP specification says that it is legal to merge duplicate
130      * headers into one.  Some browsers that support Cookies don't like
131      * merged headers and prefer that each Set-Cookie header is sent
132      * separately.  Lets humour those browsers by not merging.
133      * Oh what a pain it is.
134      */
135     cookie_table = apr_table_make(r->pool, 2);
136     apr_table_do(set_cookie_doo_doo, cookie_table, r->err_headers_out,
137                  "Set-Cookie", NULL);
138     apr_table_do(set_cookie_doo_doo, cookie_table, h->resp_hdrs,
139                  "Set-Cookie", NULL);
140     apr_table_unset(r->err_headers_out, "Set-Cookie");
141     apr_table_unset(h->resp_hdrs, "Set-Cookie");
142
143     apr_table_overlap(r->headers_out, h->resp_hdrs, APR_OVERLAP_TABLES_SET);
144     if (!apr_is_empty_table(cookie_table)) {
145         r->err_headers_out = apr_table_overlay(r->pool, r->err_headers_out,
146                                                cookie_table);
147     }
148 }
149
150 /*
151  * select a specific URL entity in the cache
152  *
153  * It is possible to store more than one entity per URL. Content
154  * negotiation is used to select an entity. Once an entity is
155  * selected, details of it are stored in the per request
156  * config to save time when serving the request later.
157  *
158  * This function returns OK if successful, DECLINED if no
159  * cached entity fits the bill.
160  */
161 int cache_select_url(request_rec *r, char *url)
162 {
163     cache_provider_list *list;
164     apr_status_t rv;
165     cache_handle_t *h;
166     char *key;
167     cache_request_rec *cache = (cache_request_rec *) 
168                          ap_get_module_config(r->request_config, &cache_module);
169
170     rv = cache_generate_key(r, r->pool, &key);
171     if (rv != APR_SUCCESS) {
172         return rv;
173     }
174     /* go through the cache types till we get a match */
175     h = apr_palloc(r->pool, sizeof(cache_handle_t));
176
177     list = cache->providers;
178
179     while (list) {
180         switch ((rv = list->provider->open_entity(h, r, key))) {
181         case OK: {
182             char *vary = NULL;
183             const char *varyhdr = NULL;
184             int fresh;
185
186             if (list->provider->recall_headers(h, r) != APR_SUCCESS) {
187                 /* TODO: Handle this error */
188                 return DECLINED;
189             }
190
191             /*
192              * Check Content-Negotiation - Vary
193              * 
194              * At this point we need to make sure that the object we found in
195              * the cache is the same object that would be delivered to the
196              * client, when the effects of content negotiation are taken into
197              * effect.
198              *
199              * In plain english, we want to make sure that a language-negotiated
200              * document in one language is not given to a client asking for a
201              * language negotiated document in a different language by mistake.
202              *
203              * This code makes the assumption that the storage manager will
204              * cache the req_hdrs if the response contains a Vary
205              * header.
206              *
207              * RFC2616 13.6 and 14.44 describe the Vary mechanism.
208              */
209             vary = apr_pstrdup(r->pool, apr_table_get(h->resp_hdrs, "Vary"));
210             while (vary && *vary) {
211                 char *name = vary;
212                 const char *h1, *h2;
213
214                 /* isolate header name */
215                 while (*vary && !apr_isspace(*vary) && (*vary != ','))
216                     ++vary;
217                 while (*vary && (apr_isspace(*vary) || (*vary == ','))) {
218                     *vary = '\0';
219                     ++vary;
220                 }
221
222                 /*
223                  * is this header in the request and the header in the cached
224                  * request identical? If not, we give up and do a straight get
225                  */
226                 h1 = apr_table_get(r->headers_in, name);
227                 h2 = apr_table_get(h->req_hdrs, name);
228                 if (h1 == h2) {
229                     /* both headers NULL, so a match - do nothing */
230                 }
231                 else if (h1 && h2 && !strcmp(h1, h2)) {
232                     /* both headers exist and are equal - do nothing */
233                 }
234                 else {
235                     /* headers do not match, so Vary failed */
236                     ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
237                                 r->server,
238                                 "cache_select_url(): Vary header mismatch.");
239                     return DECLINED;
240                 }
241             }
242
243             cache->provider = list->provider;
244             cache->provider_name = list->provider_name;
245
246             /* Is our cached response fresh enough? */
247             fresh = ap_cache_check_freshness(h, r);
248             if (!fresh) {
249                 const char *etag, *lastmod;
250
251                 /* Make response into a conditional */
252                 /* FIXME: What if the request is already conditional? */
253                 etag = apr_table_get(h->resp_hdrs, "ETag");
254                 if (!etag) {
255                     lastmod = apr_table_get(h->resp_hdrs, "Last-Modified");
256                 }
257                 if (etag || lastmod) {
258                     /* if we have a cached etag or Last-Modified */
259                     cache->stale_headers = apr_table_copy(r->pool,
260                                                           r->headers_in);
261                     if (etag) {
262                         apr_table_set(r->headers_in, "If-None-Match", etag);
263                     }
264                     else if (lastmod) {
265                         apr_table_set(r->headers_in, "If-Modified-Since",
266                                       lastmod);
267                     }
268                     cache->stale_handle = h;
269                 }
270
271                 return DECLINED;
272             }
273
274             /* Okay, this response looks okay.  Merge in our stuff and go. */
275             accept_headers(h, r);
276
277             cache->handle = h;
278             return OK;
279         }
280         case DECLINED: {
281             /* try again with next cache type */
282             list = list->next;
283             continue;
284         }
285         default: {
286             /* oo-er! an error */
287             return rv;
288         }
289         }
290     }
291     return DECLINED;
292 }
293
294 apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
295                                         char**key)
296 {
297     if (r->hostname) {
298         *key = apr_pstrcat(p, r->hostname, r->uri, "?", r->args, NULL);
299     }
300     else {
301         *key = apr_pstrcat(p, r->uri, "?", r->args, NULL);
302     }
303     return APR_SUCCESS;
304 }
305