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