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
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.
17 #include "apr_strings.h"
18 #include "apr_thread_proc.h" /* for RLIMIT stuff */
20 #define APR_WANT_STRFUNC
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"
30 /* Generate the human-readable hex representation of an apr_uint64_t
31 * (basically a faster version of 'sprintf("%llx")')
33 #define HEX_DIGITS "0123456789abcdef"
34 static char *etag_uint64_to_hex(char *next, apr_uint64_t u)
37 int shift = sizeof(apr_uint64_t) * 8 - 4;
39 unsigned short next_digit = (unsigned short)
40 ((u >> shift) & (apr_uint64_t)0xf);
42 *next++ = HEX_DIGITS[next_digit];
46 *next++ = HEX_DIGITS[next_digit];
50 *next++ = HEX_DIGITS[u & (apr_uint64_t)0xf];
54 #define ETAG_WEAK "W/"
55 #define CHARS_PER_UINT64 (sizeof(apr_uint64_t) * 2)
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.
63 AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak)
70 etag_components_t etag_bits;
71 etag_components_t bits_added;
73 cfg = (core_dir_config *)ap_get_core_module_config(r->per_dir_config);
74 etag_bits = (cfg->etag_bits & (~ cfg->etag_remove)) | cfg->etag_add;
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.
81 if (etag_bits & ETAG_NONE) {
82 apr_table_setn(r->notes, "no-etag", "omit");
86 if (etag_bits == ETAG_UNSET) {
87 etag_bits = ETAG_BACKWARD;
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
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
101 if ((r->request_time - r->mtime > (1 * APR_USEC_PER_SEC)) &&
108 weak_len = sizeof(ETAG_WEAK);
111 if (r->finfo.filetype != APR_NOFILE) {
113 * ETag gets set to [W/]"inode-size-mtime", modulo any
116 etag = apr_palloc(r->pool, weak_len + sizeof("\"--\"") +
117 3 * CHARS_PER_UINT64 + 1);
126 if (etag_bits & ETAG_INODE) {
127 next = etag_uint64_to_hex(next, r->finfo.inode);
128 bits_added |= ETAG_INODE;
130 if (etag_bits & ETAG_SIZE) {
131 if (bits_added != 0) {
134 next = etag_uint64_to_hex(next, r->finfo.size);
135 bits_added |= ETAG_SIZE;
137 if (etag_bits & ETAG_MTIME) {
138 if (bits_added != 0) {
141 next = etag_uint64_to_hex(next, r->mtime);
148 * Not a file document, so just use the mtime: [W/]"mtime"
150 etag = apr_palloc(r->pool, weak_len + sizeof("\"\"") +
151 CHARS_PER_UINT64 + 1);
159 next = etag_uint64_to_hex(next, r->mtime);
167 AP_DECLARE(void) ap_set_etag(request_rec *r)
170 char *variant_etag, *vlv;
173 if (!r->vlist_validator) {
174 etag = ap_make_etag(r, 0);
176 /* If we get a blank etag back, don't set the header. */
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.
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.
198 vlv = r->vlist_validator;
199 vlv_weak = (vlv[0] == 'W');
201 variant_etag = ap_make_etag(r, vlv_weak);
203 /* If we get a blank etag back, don't append vlv and stop now. */
204 if (!variant_etag[0]) {
208 /* merge variant_etag and vlv into a structured etag */
209 variant_etag[strlen(variant_etag) - 1] = '\0';
216 etag = apr_pstrcat(r->pool, variant_etag, ";", vlv, NULL);
219 apr_table_setn(r->headers_out, "ETag", etag);