|
Packit |
90a5c9 |
/* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
Packit |
90a5c9 |
* contributor license agreements. See the NOTICE file distributed with
|
|
Packit |
90a5c9 |
* this work for additional information regarding copyright ownership.
|
|
Packit |
90a5c9 |
* The ASF licenses this file to You under the Apache License, Version 2.0
|
|
Packit |
90a5c9 |
* (the "License"); you may not use this file except in compliance with
|
|
Packit |
90a5c9 |
* the License. You may obtain a copy of the License at
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* http://www.apache.org/licenses/LICENSE-2.0
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* Unless required by applicable law or agreed to in writing, software
|
|
Packit |
90a5c9 |
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
Packit |
90a5c9 |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
Packit |
90a5c9 |
* See the License for the specific language governing permissions and
|
|
Packit |
90a5c9 |
* limitations under the License.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "apr_strings.h"
|
|
Packit |
90a5c9 |
#include "apr_thread_proc.h" /* for RLIMIT stuff */
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#define APR_WANT_STRFUNC
|
|
Packit |
90a5c9 |
#include "apr_want.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#include "httpd.h"
|
|
Packit |
90a5c9 |
#include "http_config.h"
|
|
Packit |
90a5c9 |
#include "http_connection.h"
|
|
Packit |
90a5c9 |
#include "http_core.h"
|
|
Packit |
90a5c9 |
#include "http_protocol.h" /* For index_of_response(). Grump. */
|
|
Packit |
90a5c9 |
#include "http_request.h"
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* Generate the human-readable hex representation of an apr_uint64_t
|
|
Packit |
90a5c9 |
* (basically a faster version of 'sprintf("%llx")')
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
#define HEX_DIGITS "0123456789abcdef"
|
|
Packit |
90a5c9 |
static char *etag_uint64_to_hex(char *next, apr_uint64_t u)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
int printing = 0;
|
|
Packit |
90a5c9 |
int shift = sizeof(apr_uint64_t) * 8 - 4;
|
|
Packit |
90a5c9 |
do {
|
|
Packit |
90a5c9 |
unsigned short next_digit = (unsigned short)
|
|
Packit |
90a5c9 |
((u >> shift) & (apr_uint64_t)0xf);
|
|
Packit |
90a5c9 |
if (next_digit) {
|
|
Packit |
90a5c9 |
*next++ = HEX_DIGITS[next_digit];
|
|
Packit |
90a5c9 |
printing = 1;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else if (printing) {
|
|
Packit |
90a5c9 |
*next++ = HEX_DIGITS[next_digit];
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
shift -= 4;
|
|
Packit |
90a5c9 |
} while (shift);
|
|
Packit |
90a5c9 |
*next++ = HEX_DIGITS[u & (apr_uint64_t)0xf];
|
|
Packit |
90a5c9 |
return next;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
#define ETAG_WEAK "W/"
|
|
Packit |
90a5c9 |
#define CHARS_PER_UINT64 (sizeof(apr_uint64_t) * 2)
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Construct an entity tag (ETag) from resource information. If it's a real
|
|
Packit |
90a5c9 |
* file, build in some of the file characteristics. If the modification time
|
|
Packit |
90a5c9 |
* is newer than (request-time minus 1 second), mark the ETag as weak - it
|
|
Packit |
90a5c9 |
* could be modified again in as short an interval. We rationalize the
|
|
Packit |
90a5c9 |
* modification time we're given to keep it from being in the future.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
char *weak;
|
|
Packit |
90a5c9 |
apr_size_t weak_len;
|
|
Packit |
90a5c9 |
char *etag;
|
|
Packit |
90a5c9 |
char *next;
|
|
Packit |
90a5c9 |
core_dir_config *cfg;
|
|
Packit |
90a5c9 |
etag_components_t etag_bits;
|
|
Packit |
90a5c9 |
etag_components_t bits_added;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
cfg = (core_dir_config *)ap_get_core_module_config(r->per_dir_config);
|
|
Packit |
90a5c9 |
etag_bits = (cfg->etag_bits & (~ cfg->etag_remove)) | cfg->etag_add;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* If it's a file (or we wouldn't be here) and no ETags
|
|
Packit |
90a5c9 |
* should be set for files, return an empty string and
|
|
Packit |
90a5c9 |
* note it for the header-sender to ignore.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if (etag_bits & ETAG_NONE) {
|
|
Packit |
90a5c9 |
apr_table_setn(r->notes, "no-etag", "omit");
|
|
Packit |
90a5c9 |
return "";
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (etag_bits == ETAG_UNSET) {
|
|
Packit |
90a5c9 |
etag_bits = ETAG_BACKWARD;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Make an ETag header out of various pieces of information. We use
|
|
Packit |
90a5c9 |
* the last-modified date and, if we have a real file, the
|
|
Packit |
90a5c9 |
* length and inode number - note that this doesn't have to match
|
|
Packit |
90a5c9 |
* the content-length (i.e. includes), it just has to be unique
|
|
Packit |
90a5c9 |
* for the file.
|
|
Packit |
90a5c9 |
*
|
|
Packit |
90a5c9 |
* If the request was made within a second of the last-modified date,
|
|
Packit |
90a5c9 |
* we send a weak tag instead of a strong one, since it could
|
|
Packit |
90a5c9 |
* be modified again later in the second, and the validation
|
|
Packit |
90a5c9 |
* would be incorrect.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
if ((r->request_time - r->mtime > (1 * APR_USEC_PER_SEC)) &&
|
|
Packit |
90a5c9 |
!force_weak) {
|
|
Packit |
90a5c9 |
weak = NULL;
|
|
Packit |
90a5c9 |
weak_len = 0;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
weak = ETAG_WEAK;
|
|
Packit |
90a5c9 |
weak_len = sizeof(ETAG_WEAK);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (r->finfo.filetype != APR_NOFILE) {
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* ETag gets set to [W/]"inode-size-mtime", modulo any
|
|
Packit |
90a5c9 |
* FileETag keywords.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
etag = apr_palloc(r->pool, weak_len + sizeof("\"--\"") +
|
|
Packit |
90a5c9 |
3 * CHARS_PER_UINT64 + 1);
|
|
Packit |
90a5c9 |
next = etag;
|
|
Packit |
90a5c9 |
if (weak) {
|
|
Packit |
90a5c9 |
while (*weak) {
|
|
Packit |
90a5c9 |
*next++ = *weak++;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
*next++ = '"';
|
|
Packit |
90a5c9 |
bits_added = 0;
|
|
Packit |
90a5c9 |
if (etag_bits & ETAG_INODE) {
|
|
Packit |
90a5c9 |
next = etag_uint64_to_hex(next, r->finfo.inode);
|
|
Packit |
90a5c9 |
bits_added |= ETAG_INODE;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (etag_bits & ETAG_SIZE) {
|
|
Packit |
90a5c9 |
if (bits_added != 0) {
|
|
Packit |
90a5c9 |
*next++ = '-';
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
next = etag_uint64_to_hex(next, r->finfo.size);
|
|
Packit |
90a5c9 |
bits_added |= ETAG_SIZE;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
if (etag_bits & ETAG_MTIME) {
|
|
Packit |
90a5c9 |
if (bits_added != 0) {
|
|
Packit |
90a5c9 |
*next++ = '-';
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
next = etag_uint64_to_hex(next, r->mtime);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
*next++ = '"';
|
|
Packit |
90a5c9 |
*next = '\0';
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
/*
|
|
Packit |
90a5c9 |
* Not a file document, so just use the mtime: [W/]"mtime"
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
etag = apr_palloc(r->pool, weak_len + sizeof("\"\"") +
|
|
Packit |
90a5c9 |
CHARS_PER_UINT64 + 1);
|
|
Packit |
90a5c9 |
next = etag;
|
|
Packit |
90a5c9 |
if (weak) {
|
|
Packit |
90a5c9 |
while (*weak) {
|
|
Packit |
90a5c9 |
*next++ = *weak++;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
*next++ = '"';
|
|
Packit |
90a5c9 |
next = etag_uint64_to_hex(next, r->mtime);
|
|
Packit |
90a5c9 |
*next++ = '"';
|
|
Packit |
90a5c9 |
*next = '\0';
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
return etag;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
AP_DECLARE(void) ap_set_etag(request_rec *r)
|
|
Packit |
90a5c9 |
{
|
|
Packit |
90a5c9 |
char *etag;
|
|
Packit |
90a5c9 |
char *variant_etag, *vlv;
|
|
Packit |
90a5c9 |
int vlv_weak;
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
if (!r->vlist_validator) {
|
|
Packit |
90a5c9 |
etag = ap_make_etag(r, 0);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* If we get a blank etag back, don't set the header. */
|
|
Packit |
90a5c9 |
if (!etag[0]) {
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
/* If we have a variant list validator (vlv) due to the
|
|
Packit |
90a5c9 |
* response being negotiated, then we create a structured
|
|
Packit |
90a5c9 |
* entity tag which merges the variant etag with the variant
|
|
Packit |
90a5c9 |
* list validator (vlv). This merging makes revalidation
|
|
Packit |
90a5c9 |
* somewhat safer, ensures that caches which can deal with
|
|
Packit |
90a5c9 |
* Vary will (eventually) be updated if the set of variants is
|
|
Packit |
90a5c9 |
* changed, and is also a protocol requirement for transparent
|
|
Packit |
90a5c9 |
* content negotiation.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* if the variant list validator is weak, we make the whole
|
|
Packit |
90a5c9 |
* structured etag weak. If we would not, then clients could
|
|
Packit |
90a5c9 |
* have problems merging range responses if we have different
|
|
Packit |
90a5c9 |
* variants with the same non-globally-unique strong etag.
|
|
Packit |
90a5c9 |
*/
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
vlv = r->vlist_validator;
|
|
Packit |
90a5c9 |
vlv_weak = (vlv[0] == 'W');
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
variant_etag = ap_make_etag(r, vlv_weak);
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* If we get a blank etag back, don't append vlv and stop now. */
|
|
Packit |
90a5c9 |
if (!variant_etag[0]) {
|
|
Packit |
90a5c9 |
return;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
/* merge variant_etag and vlv into a structured etag */
|
|
Packit |
90a5c9 |
variant_etag[strlen(variant_etag) - 1] = '\0';
|
|
Packit |
90a5c9 |
if (vlv_weak) {
|
|
Packit |
90a5c9 |
vlv += 3;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
else {
|
|
Packit |
90a5c9 |
vlv++;
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
etag = apr_pstrcat(r->pool, variant_etag, ";", vlv, NULL);
|
|
Packit |
90a5c9 |
}
|
|
Packit |
90a5c9 |
|
|
Packit |
90a5c9 |
apr_table_setn(r->headers_out, "ETag", etag);
|
|
Packit |
90a5c9 |
}
|