Apache HTTPD
http_etag.c
Go to the documentation of this file.
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#include "apr_strings.h"
18#include "apr_thread_proc.h" /* for RLIMIT stuff */
19#include "apr_sha1.h"
20#include "apr_base64.h"
21#include "apr_buckets.h"
22
23#define APR_WANT_STRFUNC
24#include "apr_want.h"
25
26#include "httpd.h"
27#include "http_config.h"
28#include "http_connection.h"
29#include "http_core.h"
30#include "http_log.h"
31#include "http_protocol.h" /* For index_of_response(). Grump. */
32#include "http_request.h"
33
34#if APR_HAS_MMAP
35#include "apr_mmap.h"
36#endif /* APR_HAS_MMAP */
37
38#define SHA1_DIGEST_BASE64_LEN 4*(APR_SHA1_DIGESTSIZE/3)
39
40/* Generate the human-readable hex representation of an apr_uint64_t
41 * (basically a faster version of 'sprintf("%llx")')
42 */
43#define HEX_DIGITS "0123456789abcdef"
44static char *etag_uint64_to_hex(char *next, apr_uint64_t u)
45{
46 int printing = 0;
47 int shift = sizeof(apr_uint64_t) * 8 - 4;
48 do {
49 unsigned short next_digit = (unsigned short)
50 ((u >> shift) & (apr_uint64_t)0xf);
51 if (next_digit) {
52 *next++ = HEX_DIGITS[next_digit];
53 printing = 1;
54 }
55 else if (printing) {
56 *next++ = HEX_DIGITS[next_digit];
57 }
58 shift -= 4;
59 } while (shift);
60 *next++ = HEX_DIGITS[u & (apr_uint64_t)0xf];
61 return next;
62}
63
64#define ETAG_WEAK "W/"
65#define CHARS_PER_UINT64 (sizeof(apr_uint64_t) * 2)
66
67static void etag_start(char *etag, const char *weak, char **next)
68{
69 if (weak) {
70 while (*weak) {
71 *etag++ = *weak++;
72 }
73 }
74 *etag++ = '"';
75
76 *next = etag;
77}
78
79static void etag_end(char *next, const char *vlv, apr_size_t vlv_len)
80{
81 if (vlv) {
82 *next++ = ';';
83 apr_cpystrn(next, vlv, vlv_len);
84 }
85 else {
86 *next++ = '"';
87 *next = '\0';
88 }
89}
90
91/*
92 * Construct a strong ETag by creating a SHA1 hash across the file content.
93 */
96{
98 unsigned char digest[APR_SHA1_DIGESTSIZE];
100 core_dir_config *cfg;
101 char *etag, *next;
103 apr_bucket *e;
104
106 apr_off_t offset = 0, zero = 0, len = 0;
108
110
111 if (er->fd) {
112 fd = er->fd;
113 }
114 else if (er->pathname) {
115 if ((status = apr_file_open(&fd, er->pathname, APR_READ | APR_BINARY,
116 0, r->pool)) != APR_SUCCESS) {
118 "Make etag: could not open %s", er->pathname);
119 return "";
120 }
121 }
122 if (!fd) {
123 return "";
124 }
125
128 "Make etag: could not seek");
129 if (er->pathname) {
131 }
132 return "";
133 }
134
135 if ((status = apr_file_seek(fd, APR_END, &len)) != APR_SUCCESS) {
137 "Make etag: could not seek");
138 if (er->pathname) {
140 }
141 return "";
142 }
143
146 "Make etag: could not seek");
147 if (er->pathname) {
149 }
150 return "";
151 }
152
154
155 e = apr_brigade_insert_file(bb, fd, 0, len, r->pool);
156
157#if APR_HAS_MMAP
158 if (cfg->enable_mmap == ENABLE_MMAP_OFF) {
160 }
161#endif
162
164 while (!APR_BRIGADE_EMPTY(bb))
165 {
166 const char *str;
167
168 e = APR_BRIGADE_FIRST(bb);
169
173 "Make etag: could not read");
174 if (er->pathname) {
176 }
177 return "";
178 }
179
182 }
183
186 "Make etag: could not seek");
187 if (er->pathname) {
189 }
190 return "";
191 }
193
194 etag = apr_palloc(r->pool, weak_len + sizeof("\"\"") +
196
197 etag_start(etag, weak, &next);
199 etag_end(next, vlv, vlv_len);
200
201 if (er->pathname) {
203 }
204
205 return etag;
206}
207
208/*
209 * Construct an entity tag (ETag) from resource information. If it's a real
210 * file, build in some of the file characteristics. If the modification time
211 * is newer than (request-time minus 1 second), mark the ETag as weak - it
212 * could be modified again in as short an interval.
213 */
215{
216 char *weak = NULL;
217 apr_size_t weak_len = 0, vlv_len = 0;
218 char *etag, *next, *vlv;
219 core_dir_config *cfg;
220 etag_components_t etag_bits;
222
224 etag_bits = (cfg->etag_bits & (~ cfg->etag_remove)) | cfg->etag_add;
225
226 if (er->force_weak) {
227 weak = ETAG_WEAK;
228 weak_len = sizeof(ETAG_WEAK);
229 }
230
231 if (r->vlist_validator) {
232
233 /* If we have a variant list validator (vlv) due to the
234 * response being negotiated, then we create a structured
235 * entity tag which merges the variant etag with the variant
236 * list validator (vlv). This merging makes revalidation
237 * somewhat safer, ensures that caches which can deal with
238 * Vary will (eventually) be updated if the set of variants is
239 * changed, and is also a protocol requirement for transparent
240 * content negotiation.
241 */
242
243 /* if the variant list validator is weak, we make the whole
244 * structured etag weak. If we would not, then clients could
245 * have problems merging range responses if we have different
246 * variants with the same non-globally-unique strong etag.
247 */
248
250 if (vlv[0] == 'W') {
251 vlv += 3;
252 weak = ETAG_WEAK;
253 weak_len = sizeof(ETAG_WEAK);
254 }
255 else {
256 vlv++;
257 }
258 vlv_len = strlen(vlv);
259
260 }
261 else {
262 vlv = NULL;
263 vlv_len = 0;
264 }
265
266 /*
267 * Did a module flag the need for a strong etag, or did the
268 * configuration tell us to generate a digest?
269 */
270 if (er->finfo->filetype == APR_REG &&
271 (AP_REQUEST_IS_STRONG_ETAG(r) || (etag_bits & ETAG_DIGEST))) {
272
274 }
275
276 /*
277 * If it's a file (or we wouldn't be here) and no ETags
278 * should be set for files, return an empty string and
279 * note it for the header-sender to ignore.
280 */
281 if (etag_bits & ETAG_NONE) {
282 return "";
283 }
284
285 if (etag_bits == ETAG_UNSET) {
286 etag_bits = ETAG_BACKWARD;
287 }
288 /*
289 * Make an ETag header out of various pieces of information. We use
290 * the last-modified date and, if we have a real file, the
291 * length and inode number - note that this doesn't have to match
292 * the content-length (i.e. includes), it just has to be unique
293 * for the file.
294 *
295 * If the request was made within a second of the last-modified date,
296 * we send a weak tag instead of a strong one, since it could
297 * be modified again later in the second, and the validation
298 * would be incorrect.
299 */
300 if ((er->request_time - er->finfo->mtime < (1 * APR_USEC_PER_SEC))) {
301 weak = ETAG_WEAK;
302 weak_len = sizeof(ETAG_WEAK);
303 }
304
305 if (er->finfo->filetype != APR_NOFILE) {
306 /*
307 * ETag gets set to [W/]"inode-size-mtime", modulo any
308 * FileETag keywords.
309 */
310 etag = apr_palloc(r->pool, weak_len + sizeof("\"--\"") +
311 3 * CHARS_PER_UINT64 + vlv_len + 2);
312
313 etag_start(etag, weak, &next);
314
315 bits_added = 0;
316 if (etag_bits & ETAG_INODE) {
317 next = etag_uint64_to_hex(next, er->finfo->inode);
319 }
320 if (etag_bits & ETAG_SIZE) {
321 if (bits_added != 0) {
322 *next++ = '-';
323 }
324 next = etag_uint64_to_hex(next, er->finfo->size);
326 }
327 if (etag_bits & ETAG_MTIME) {
328 if (bits_added != 0) {
329 *next++ = '-';
330 }
331 next = etag_uint64_to_hex(next, er->finfo->mtime);
332 }
333
334 etag_end(next, vlv, vlv_len);
335
336 }
337 else {
338 /*
339 * Not a file document, so just use the mtime: [W/]"mtime"
340 */
341 etag = apr_palloc(r->pool, weak_len + sizeof("\"\"") +
343
344 etag_start(etag, weak, &next);
345 next = etag_uint64_to_hex(next, er->finfo->mtime);
346 etag_end(next, vlv, vlv_len);
347
348 }
349
350 return etag;
351}
352
353AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak)
354{
355 etag_rec er;
356
358 er.request_time = r->request_time;
359 er.finfo = &r->finfo;
360 er.pathname = r->filename;
361 er.fd = NULL;
362 er.force_weak = force_weak;
363
364 return ap_make_etag_ex(r, &er);
365}
366
368{
369 char *etag;
370
371 etag_rec er;
372
374 er.request_time = r->request_time;
375 er.finfo = &r->finfo;
376 er.pathname = r->filename;
377 er.fd = NULL;
378 er.force_weak = 0;
379
380 etag = ap_make_etag_ex(r, &er);
381
382 if (etag && etag[0]) {
383 apr_table_setn(r->headers_out, "ETag", etag);
384 }
385 else {
386 apr_table_setn(r->notes, "no-etag", "omit");
387 }
388
389}
390
392{
393 char *etag;
394
395 etag_rec er;
396
398 er.request_time = r->request_time;
399 er.finfo = &r->finfo;
400 er.pathname = NULL;
401 er.fd = fd;
402 er.force_weak = 0;
403
404 etag = ap_make_etag_ex(r, &er);
405
406 if (etag && etag[0]) {
407 apr_table_setn(r->headers_out, "ETag", etag);
408 }
409 else {
410 apr_table_setn(r->notes, "no-etag", "omit");
411 }
412
413}
#define AP_DECLARE(type)
Definition ap_config.h:67
const char apr_size_t len
Definition ap_regex.h:187
APR-UTIL Base64 Encoding.
APR-UTIL Buckets/Bucket Brigades.
APR MMAP routines.
APR-UTIL SHA1 library.
#define APR_SHA1_DIGESTSIZE
Definition apr_sha1.h:39
APR Strings library.
APR Thread and Process Library.
APR Standard Headers Support.
request_rec * r
#define ETAG_BACKWARD
Definition http_core.h:501
#define ETAG_INODE
Definition http_core.h:496
#define ETAG_MTIME
Definition http_core.h:495
#define ETAG_NONE
Definition http_core.h:494
#define ap_get_core_module_config(v)
Definition http_core.h:383
#define ETAG_DIGEST
Definition http_core.h:498
#define ETAG_SIZE
Definition http_core.h:497
#define ETAG_UNSET
Definition http_core.h:493
unsigned long etag_components_t
Definition http_core.h:491
#define APLOGNO(n)
Definition http_log.h:117
#define ap_log_rerror
Definition http_log.h:454
#define APLOG_ERR
Definition http_log.h:67
#define APLOG_MARK
Definition http_log.h:283
apr_md5_ctx_t * context
Definition util_md5.h:58
void ap_set_etag(request_rec *r)
Definition http_etag.c:367
void ap_set_etag_fd(request_rec *r, apr_file_t *fd)
Definition http_etag.c:391
char * ap_make_etag_ex(request_rec *r, etag_rec *er)
Definition http_etag.c:214
char * ap_make_etag(request_rec *r, int force_weak)
Definition http_etag.c:353
apr_bucket * e
#define APR_BRIGADE_EMPTY(b)
#define apr_bucket_delete(e)
#define APR_BRIGADE_FIRST(b)
apr_file_t * fd
#define apr_bucket_read(e, str, len, block)
@ APR_BLOCK_READ
Definition apr_buckets.h:58
apr_size_t size
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
@ APR_REG
@ APR_NOFILE
void apr_size_t * nbytes
apr_seek_where_t apr_off_t * offset
#define APR_READ
Definition apr_file_io.h:93
#define APR_BINARY
Definition apr_file_io.h:98
#define APR_SET
#define APR_END
#define APR_CUR
int int status
#define APR_USEC_PER_SEC
Definition apr_time.h:60
#define AP_REQUEST_IS_STRONG_ETAG(r)
Definition httpd.h:689
Apache Configuration.
Apache connection library.
CORE HTTP Daemon.
#define ENABLE_MMAP_OFF
Definition http_core.h:618
static void etag_start(char *etag, const char *weak, char **next)
Definition http_etag.c:67
static void etag_end(char *next, const char *vlv, apr_size_t vlv_len)
Definition http_etag.c:79
#define HEX_DIGITS
Definition http_etag.c:43
#define ETAG_WEAK
Definition http_etag.c:64
#define SHA1_DIGEST_BASE64_LEN
Definition http_etag.c:38
static char * etag_uint64_to_hex(char *next, apr_uint64_t u)
Definition http_etag.c:44
#define CHARS_PER_UINT64
Definition http_etag.c:65
static char * make_digest_etag(request_rec *r, etag_rec *er, char *vlv, apr_size_t vlv_len, char *weak, apr_size_t weak_len)
Definition http_etag.c:94
Apache Logging library.
HTTP protocol handling.
Apache Request library.
HTTP Daemon routines.
return NULL
Definition mod_so.c:359
struct apr_bucket_alloc_t * bucket_alloc
Definition httpd.h:1201
Per-directory configuration.
Definition http_core.h:527
etag_components_t etag_add
Definition http_core.h:612
unsigned int enable_mmap
Definition http_core.h:621
etag_components_t etag_bits
Definition http_core.h:611
A structure with the ingredients for a file based etag.
const char * vlist_validator
A structure that represents the current request.
Definition httpd.h:845
apr_table_t * notes
Definition httpd.h:985
apr_pool_t * pool
Definition httpd.h:847
char * filename
Definition httpd.h:1018
apr_time_t request_time
Definition httpd.h:886
conn_rec * connection
Definition httpd.h:849
apr_finfo_t finfo
Definition httpd.h:1094
struct ap_conf_vector_t * per_dir_config
Definition httpd.h:1047
char * vlist_validator
Definition httpd.h:1002
apr_table_t * headers_out
Definition httpd.h:978
const char * digest
Definition testmd5.c:30
#define str