PageRenderTime 29ms CodeModel.GetById 11ms app.highlight 14ms 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
  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#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 apr_uint64_t
 31 * (basically a faster version of 'sprintf("%llx")')
 32 */
 33#define HEX_DIGITS "0123456789abcdef"
 34static char *etag_uint64_to_hex(char *next, apr_uint64_t u)
 35{
 36    int printing = 0;
 37    int shift = sizeof(apr_uint64_t) * 8 - 4;
 38    do {
 39        unsigned short next_digit = (unsigned short)
 40                                    ((u >> shift) & (apr_uint64_t)0xf);
 41        if (next_digit) {
 42            *next++ = HEX_DIGITS[next_digit];
 43            printing = 1;
 44        }
 45        else if (printing) {
 46            *next++ = HEX_DIGITS[next_digit];
 47        }
 48        shift -= 4;
 49    } while (shift);
 50    *next++ = HEX_DIGITS[u & (apr_uint64_t)0xf];
 51    return next;
 52}
 53
 54#define ETAG_WEAK "W/"
 55#define CHARS_PER_UINT64 (sizeof(apr_uint64_t) * 2)
 56/*
 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.
 62 */
 63AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak)
 64{
 65    char *weak;
 66    apr_size_t weak_len;
 67    char *etag;
 68    char *next;
 69    core_dir_config *cfg;
 70    etag_components_t etag_bits;
 71    etag_components_t bits_added;
 72
 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;
 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 != APR_NOFILE) {
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_UINT64 + 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_uint64_to_hex(next, 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_uint64_to_hex(next, 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_uint64_to_hex(next, 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_UINT64 + 1);
152        next = etag;
153        if (weak) {
154            while (*weak) {
155                *next++ = *weak++;
156            }
157        }
158        *next++ = '"';
159        next = etag_uint64_to_hex(next, r->mtime);
160        *next++ = '"';
161        *next = '\0';
162    }
163
164    return etag;
165}
166
167AP_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}