bottleneck testcase based on rubbos
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / modules / experimental / cache_storage.c
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements.  See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License.  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,
144                       APR_OVERLAP_TABLES_SET);
145     apr_table_overlap(r->err_headers_out, h->resp_err_hdrs,
146                       APR_OVERLAP_TABLES_SET);
147     if (!apr_is_empty_table(cookie_table)) {
148         r->err_headers_out = apr_table_overlay(r->pool, r->err_headers_out,
149                                                cookie_table);
150     }
151 }
152
153 /*
154  * select a specific URL entity in the cache
155  *
156  * It is possible to store more than one entity per URL. Content
157  * negotiation is used to select an entity. Once an entity is
158  * selected, details of it are stored in the per request
159  * config to save time when serving the request later.
160  *
161  * This function returns OK if successful, DECLINED if no
162  * cached entity fits the bill.
163  */
164 int cache_select_url(request_rec *r, char *url)
165 {
166     cache_provider_list *list;
167     apr_status_t rv;
168     cache_handle_t *h;
169     char *key;
170     cache_request_rec *cache = (cache_request_rec *) 
171                          ap_get_module_config(r->request_config, &cache_module);
172
173     rv = cache_generate_key(r, r->pool, &key);
174     if (rv != APR_SUCCESS) {
175         return rv;
176     }
177     /* go through the cache types till we get a match */
178     h = apr_palloc(r->pool, sizeof(cache_handle_t));
179
180     list = cache->providers;
181
182     while (list) {
183         switch ((rv = list->provider->open_entity(h, r, key))) {
184         case OK: {
185             char *vary = NULL;
186             const char *varyhdr = NULL;
187             int fresh;
188
189             if (list->provider->recall_headers(h, r) != APR_SUCCESS) {
190                 /* TODO: Handle this error */
191                 return DECLINED;
192             }
193
194             /*
195              * Check Content-Negotiation - Vary
196              * 
197              * At this point we need to make sure that the object we found in
198              * the cache is the same object that would be delivered to the
199              * client, when the effects of content negotiation are taken into
200              * effect.
201              *
202              * In plain english, we want to make sure that a language-negotiated
203              * document in one language is not given to a client asking for a
204              * language negotiated document in a different language by mistake.
205              *
206              * This code makes the assumption that the storage manager will
207              * cache the req_hdrs if the response contains a Vary
208              * header.
209              *
210              * RFC2616 13.6 and 14.44 describe the Vary mechanism.
211              */
212             if ((varyhdr = apr_table_get(h->resp_err_hdrs, "Vary")) == NULL) {
213                 varyhdr = apr_table_get(h->resp_hdrs, "Vary");
214             }
215             vary = apr_pstrdup(r->pool, varyhdr);
216             while (vary && *vary) {
217                 char *name = vary;
218                 const char *h1, *h2;
219
220                 /* isolate header name */
221                 while (*vary && !apr_isspace(*vary) && (*vary != ','))
222                     ++vary;
223                 while (*vary && (apr_isspace(*vary) || (*vary == ','))) {
224                     *vary = '\0';
225                     ++vary;
226                 }
227
228                 /*
229                  * is this header in the request and the header in the cached
230                  * request identical? If not, we give up and do a straight get
231                  */
232                 h1 = apr_table_get(r->headers_in, name);
233                 h2 = apr_table_get(h->req_hdrs, name);
234                 if (h1 == h2) {
235                     /* both headers NULL, so a match - do nothing */
236                 }
237                 else if (h1 && h2 && !strcmp(h1, h2)) {
238                     /* both headers exist and are equal - do nothing */
239                 }
240                 else {
241                     /* headers do not match, so Vary failed */
242                     ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
243                                 r->server,
244                                 "cache_select_url(): Vary header mismatch.");
245                     return DECLINED;
246                 }
247             }
248
249             cache->provider = list->provider;
250             cache->provider_name = list->provider_name;
251
252             /* Is our cached response fresh enough? */
253             fresh = ap_cache_check_freshness(h, r);
254             if (!fresh) {
255                 cache_info *info = &(h->cache_obj->info);
256
257                 /* Make response into a conditional */
258                 /* FIXME: What if the request is already conditional? */
259                 if (info && info->etag) {
260                     /* if we have a cached etag */
261                     cache->stale_headers = apr_table_copy(r->pool,
262                                                           r->headers_in);
263                     apr_table_set(r->headers_in, "If-None-Match", info->etag);
264                     cache->stale_handle = h;
265                 }
266                 else if (info && info->lastmods) {
267                     /* if we have a cached Last-Modified header */
268                     cache->stale_headers = apr_table_copy(r->pool,
269                                                           r->headers_in);
270                     apr_table_set(r->headers_in, "If-Modified-Since",
271                                   info->lastmods);
272                     cache->stale_handle = h;
273                 }
274
275                 return DECLINED;
276             }
277
278             /* Okay, this response looks okay.  Merge in our stuff and go. */
279             apr_table_setn(r->headers_out, "Content-Type",
280                            ap_make_content_type(r, h->content_type));
281             r->filename = apr_pstrdup(r->pool, h->cache_obj->info.filename);
282             accept_headers(h, r);
283
284             cache->handle = h;
285             return OK;
286         }
287         case DECLINED: {
288             /* try again with next cache type */
289             list = list->next;
290             continue;
291         }
292         default: {
293             /* oo-er! an error */
294             return rv;
295         }
296         }
297     }
298     return DECLINED;
299 }
300
301 apr_status_t cache_generate_key_default( request_rec *r, apr_pool_t*p, char**key ) 
302 {
303     if (r->hostname) {
304         *key = apr_pstrcat(p, r->hostname, r->uri, "?", r->args, NULL);
305     }
306     else {
307         *key = apr_pstrcat(p, r->uri, "?", r->args, NULL);
308     }
309     return APR_SUCCESS;
310 }
311