~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to modules/http/http_etag.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

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