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