]> granicus.if.org Git - apache/blob - modules/http/http_etag.c
As long as we have the AP_BUCKET_IS_ERROR macro, let's use it
[apache] / modules / http / http_etag.c
1 /* Copyright 1999-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 #include "apr_strings.h"
18 #include "apr_thread_proc.h"    /* for RLIMIT stuff */
19
20 #define APR_WANT_STRFUNC
21 #include "apr_want.h"
22
23 #define CORE_PRIVATE
24 #include "httpd.h"
25 #include "http_config.h"
26 #include "http_connection.h"
27 #include "http_core.h"
28 #include "http_protocol.h"   /* For index_of_response().  Grump. */
29 #include "http_request.h"
30
31 /* Generate the human-readable hex representation of an unsigned long
32  * (basically a faster version of 'sprintf("%lx")')
33  */
34 #define HEX_DIGITS "0123456789abcdef"
35 static char *etag_ulong_to_hex(char *next, unsigned long u)
36 {
37     int printing = 0;
38     int shift = sizeof(unsigned long) * 8 - 4;
39     do {
40         unsigned long next_digit = ((u >> shift) & (unsigned long)0xf);
41         if (next_digit) {
42             *next++ = HEX_DIGITS[next_digit];
43             printing = 1;
44         }
45         else if (printing) {
46             *next++ = HEX_DIGITS[next_digit];
47         }
48         shift -= 4;
49     } while (shift);
50     *next++ = HEX_DIGITS[u & (unsigned long)0xf];
51     return next;
52 }
53
54 #define ETAG_WEAK "W/"
55 #define CHARS_PER_UNSIGNED_LONG (sizeof(unsigned long) * 2)
56 /*
57  * Construct an entity tag (ETag) from resource information.  If it's a real
58  * file, build in some of the file characteristics.  If the modification time
59  * is newer than (request-time minus 1 second), mark the ETag as weak - it
60  * could be modified again in as short an interval.  We rationalize the
61  * modification time we're given to keep it from being in the future.
62  */
63 AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak)
64 {
65     char *weak;
66     apr_size_t weak_len;
67     char *etag;
68     char *next;
69     core_dir_config *cfg;
70     etag_components_t etag_bits;
71     etag_components_t bits_added;
72
73     cfg = (core_dir_config *)ap_get_module_config(r->per_dir_config,
74                                                   &core_module);
75     etag_bits = (cfg->etag_bits & (~ cfg->etag_remove)) | cfg->etag_add;
76
77     /*
78      * If it's a file (or we wouldn't be here) and no ETags
79      * should be set for files, return an empty string and
80      * note it for the header-sender to ignore.
81      */
82     if (etag_bits & ETAG_NONE) {
83         apr_table_setn(r->notes, "no-etag", "omit");
84         return "";
85     }
86
87     if (etag_bits == ETAG_UNSET) {
88         etag_bits = ETAG_BACKWARD;
89     }
90     /*
91      * Make an ETag header out of various pieces of information. We use
92      * the last-modified date and, if we have a real file, the
93      * length and inode number - note that this doesn't have to match
94      * the content-length (i.e. includes), it just has to be unique
95      * for the file.
96      *
97      * If the request was made within a second of the last-modified date,
98      * we send a weak tag instead of a strong one, since it could
99      * be modified again later in the second, and the validation
100      * would be incorrect.
101      */
102     if ((r->request_time - r->mtime > (1 * APR_USEC_PER_SEC)) &&
103         !force_weak) {
104         weak = NULL;
105         weak_len = 0;
106     }
107     else {
108         weak = ETAG_WEAK;
109         weak_len = sizeof(ETAG_WEAK);
110     }
111
112     if (r->finfo.filetype != 0) {
113         /*
114          * ETag gets set to [W/]"inode-size-mtime", modulo any
115          * FileETag keywords.
116          */
117         etag = apr_palloc(r->pool, weak_len + sizeof("\"--\"") +
118                           3 * CHARS_PER_UNSIGNED_LONG + 1);
119         next = etag;
120         if (weak) {
121             while (*weak) {
122                 *next++ = *weak++;
123             }
124         }
125         *next++ = '"';
126         bits_added = 0;
127         if (etag_bits & ETAG_INODE) {
128             next = etag_ulong_to_hex(next, (unsigned long)r->finfo.inode);
129             bits_added |= ETAG_INODE;
130         }
131         if (etag_bits & ETAG_SIZE) {
132             if (bits_added != 0) {
133                 *next++ = '-';
134             }
135             next = etag_ulong_to_hex(next, (unsigned long)r->finfo.size);
136             bits_added |= ETAG_SIZE;
137         }
138         if (etag_bits & ETAG_MTIME) {
139             if (bits_added != 0) {
140                 *next++ = '-';
141             }
142             next = etag_ulong_to_hex(next, (unsigned long)r->mtime);
143         }
144         *next++ = '"';
145         *next = '\0';
146     }
147     else {
148         /*
149          * Not a file document, so just use the mtime: [W/]"mtime"
150          */
151         etag = apr_palloc(r->pool, weak_len + sizeof("\"\"") +
152                           CHARS_PER_UNSIGNED_LONG + 1);
153         next = etag;
154         if (weak) {
155             while (*weak) {
156                 *next++ = *weak++;
157             }
158         }
159         *next++ = '"';
160         next = etag_ulong_to_hex(next, (unsigned long)r->mtime);
161         *next++ = '"';
162         *next = '\0';
163     }
164
165     return etag;
166 }
167
168 AP_DECLARE(void) ap_set_etag(request_rec *r)
169 {
170     char *etag;
171     char *variant_etag, *vlv;
172     int vlv_weak;
173
174     if (!r->vlist_validator) {
175         etag = ap_make_etag(r, 0);
176
177         /* If we get a blank etag back, don't set the header. */
178         if (!etag[0]) {
179             return;
180         }
181     }
182     else {
183         /* If we have a variant list validator (vlv) due to the
184          * response being negotiated, then we create a structured
185          * entity tag which merges the variant etag with the variant
186          * list validator (vlv).  This merging makes revalidation
187          * somewhat safer, ensures that caches which can deal with
188          * Vary will (eventually) be updated if the set of variants is
189          * changed, and is also a protocol requirement for transparent
190          * content negotiation.
191          */
192
193         /* if the variant list validator is weak, we make the whole
194          * structured etag weak.  If we would not, then clients could
195          * have problems merging range responses if we have different
196          * variants with the same non-globally-unique strong etag.
197          */
198
199         vlv = r->vlist_validator;
200         vlv_weak = (vlv[0] == 'W');
201
202         variant_etag = ap_make_etag(r, vlv_weak);
203
204         /* If we get a blank etag back, don't append vlv and stop now. */
205         if (!variant_etag[0]) {
206             return;
207         }
208
209         /* merge variant_etag and vlv into a structured etag */
210         variant_etag[strlen(variant_etag) - 1] = '\0';
211         if (vlv_weak) {
212             vlv += 3;
213         }
214         else {
215             vlv++;
216         }
217         etag = apr_pstrcat(r->pool, variant_etag, ";", vlv, NULL);
218     }
219
220     apr_table_setn(r->headers_out, "ETag", etag);
221 }