PageRenderTime 53ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/modules/http/http_etag.c

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