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
8
* http://www.apache.org/licenses/LICENSE-2.0
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.
18
* mod_negotiation.c: keeps track of MIME types the client is willing to
19
* accept, and contains code to handle type arbitration.
25
#include "apr_strings.h"
26
#include "apr_file_io.h"
29
#define APR_WANT_STRFUNC
32
#include "ap_config.h"
34
#include "http_config.h"
35
#include "http_request.h"
36
#include "http_protocol.h"
37
#include "http_core.h"
39
#include "util_script.h"
42
#define MAP_FILE_MAGIC_TYPE "application/x-type-map"
44
/* Commands --- configuring document caching on a per (virtual?)
49
int forcelangpriority;
50
apr_array_header_t *language_priority;
53
/* forcelangpriority flags
55
#define FLP_UNDEF 0 /* Same as FLP_DEFAULT, but base overrides */
56
#define FLP_NONE 1 /* Return 406, HTTP_NOT_ACCEPTABLE */
57
#define FLP_PREFER 2 /* Use language_priority rather than MC */
58
#define FLP_FALLBACK 4 /* Use language_priority rather than NA */
60
#define FLP_DEFAULT FLP_PREFER
64
#define DISCARD_ALL_ENCODINGS 1 /* no-gzip */
65
#define DISCARD_ALL_BUT_HTML 2 /* gzip-only-text/html */
67
module AP_MODULE_DECLARE_DATA negotiation_module;
69
static void *create_neg_dir_config(apr_pool_t *p, char *dummy)
71
neg_dir_config *new = (neg_dir_config *) apr_palloc(p,
72
sizeof(neg_dir_config));
74
new->forcelangpriority = FLP_UNDEF;
75
new->language_priority = NULL;
79
static void *merge_neg_dir_configs(apr_pool_t *p, void *basev, void *addv)
81
neg_dir_config *base = (neg_dir_config *) basev;
82
neg_dir_config *add = (neg_dir_config *) addv;
83
neg_dir_config *new = (neg_dir_config *) apr_palloc(p,
84
sizeof(neg_dir_config));
86
/* give priority to the config in the subdirectory */
87
new->forcelangpriority = (add->forcelangpriority != FLP_UNDEF)
88
? add->forcelangpriority
89
: base->forcelangpriority;
90
new->language_priority = add->language_priority
91
? add->language_priority
92
: base->language_priority;
96
static const char *set_language_priority(cmd_parms *cmd, void *n_,
99
neg_dir_config *n = n_;
102
if (!n->language_priority)
103
n->language_priority = apr_array_make(cmd->pool, 4, sizeof(char *));
105
langp = (const char **) apr_array_push(n->language_priority);
110
static const char *set_force_priority(cmd_parms *cmd, void *n_, const char *w)
112
neg_dir_config *n = n_;
114
if (!strcasecmp(w, "None")) {
115
if (n->forcelangpriority & ~FLP_NONE) {
116
return "Cannot combine ForceLanguagePriority options with None";
118
n->forcelangpriority = FLP_NONE;
120
else if (!strcasecmp(w, "Prefer")) {
121
if (n->forcelangpriority & FLP_NONE) {
122
return "Cannot combine ForceLanguagePriority options None and "
125
n->forcelangpriority |= FLP_PREFER;
127
else if (!strcasecmp(w, "Fallback")) {
128
if (n->forcelangpriority & FLP_NONE) {
129
return "Cannot combine ForceLanguagePriority options None and "
132
n->forcelangpriority |= FLP_FALLBACK;
135
return apr_pstrcat(cmd->pool, "Invalid ForceLanguagePriority option ",
142
static const char *cache_negotiated_docs(cmd_parms *cmd, void *dummy,
145
ap_set_module_config(cmd->server->module_config, &negotiation_module,
146
(arg ? "Cache" : NULL));
150
static int do_cache_negotiated_docs(server_rec *s)
152
return (ap_get_module_config(s->module_config,
153
&negotiation_module) != NULL);
156
static const command_rec negotiation_cmds[] =
158
AP_INIT_FLAG("CacheNegotiatedDocs", cache_negotiated_docs, NULL, RSRC_CONF,
159
"Either 'on' or 'off' (default)"),
160
AP_INIT_ITERATE("LanguagePriority", set_language_priority, NULL,
162
"space-delimited list of MIME language abbreviations"),
163
AP_INIT_ITERATE("ForceLanguagePriority", set_force_priority, NULL,
165
"Force LanguagePriority elections, either None, or "
166
"Fallback and/or Prefer"),
171
* Record of available info on a media type specified by the client
172
* (we also use 'em for encodings and languages)
175
typedef struct accept_rec {
176
char *name; /* MUST be lowercase */
179
char *charset; /* for content-type only */
183
* Record of available info on a particular variant
185
* Note that a few of these fields are updated by the actual negotiation
188
* level_matched --- initialized to zero. Set to the value of level
189
* if the client actually accepts this media type at that
190
* level (and *not* if it got in on a wildcard). See level_cmp
192
* mime_stars -- initialized to zero. Set to the number of stars
193
* present in the best matching Accept header element.
194
* 1 for star/star, 2 for type/star and 3 for
197
* definite -- initialized to 1. Set to 0 if there is a match which
198
* makes the variant non-definite according to the rules
202
typedef struct var_rec {
203
request_rec *sub_req; /* May be NULL (is, for map files) */
204
const char *mime_type; /* MUST be lowercase */
205
const char *file_name; /* Set to 'this' (for map file body content) */
206
apr_off_t body; /* Only for map file body content */
207
const char *content_encoding;
208
apr_array_header_t *content_languages; /* list of lang. for this variant */
209
const char *content_charset;
210
const char *description;
212
/* The next five items give the quality values for the dimensions
213
* of negotiation for this variant. They are obtained from the
214
* appropriate header lines, except for source_quality, which
215
* is obtained from the variant itself (the 'qs' parameter value
216
* from the variant's mime-type). Apart from source_quality,
217
* these values are set when we find the quality for each variant
218
* (see best_match()). source_quality is set from the 'qs' parameter
219
* of the variant description or mime type: see set_mime_fields().
221
float lang_quality; /* quality of this variant's language */
222
float encoding_quality; /* ditto encoding */
223
float charset_quality; /* ditto charset */
224
float mime_type_quality; /* ditto media type */
225
float source_quality; /* source quality for this variant */
227
/* Now some special values */
228
float level; /* Auxiliary to content-type... */
229
apr_off_t bytes; /* content length, if known */
230
int lang_index; /* Index into LanguagePriority list */
231
int is_pseudo_html; /* text/html, *or* the INCLUDES_MAGIC_TYPEs */
233
/* Above are all written-once properties of the variant. The
234
* three fields below are changed during negotiation:
242
/* Something to carry around the state of negotiation (and to keep
243
* all of this thread-safe)...
249
neg_dir_config *conf;
251
int accept_q; /* 1 if an Accept item has a q= param */
252
float default_lang_quality; /* fiddle lang q for variants with no lang */
254
/* the array pointers below are NULL if the corresponding accept
255
* headers are not present
257
apr_array_header_t *accepts; /* accept_recs */
258
apr_array_header_t *accept_encodings; /* accept_recs */
259
apr_array_header_t *accept_charsets; /* accept_recs */
260
apr_array_header_t *accept_langs; /* accept_recs */
262
apr_array_header_t *avail_vars; /* available variants */
264
int count_multiviews_variants; /* number of variants found on disk */
266
int is_transparent; /* 1 if this resource is trans. negotiable */
268
int dont_fiddle_headers; /* 1 if we may not fiddle with accept hdrs */
269
int ua_supports_trans; /* 1 if ua supports trans negotiation */
270
int send_alternates; /* 1 if we want to send an Alternates header */
271
int may_choose; /* 1 if we may choose a variant for the client */
272
int use_rvsa; /* 1 if we must use RVSA/1.0 negotiation algo */
275
/* A few functions to manipulate var_recs.
276
* Cleaning out the fields...
279
static void clean_var_rec(var_rec *mime_info)
281
mime_info->sub_req = NULL;
282
mime_info->mime_type = "";
283
mime_info->file_name = "";
285
mime_info->content_encoding = NULL;
286
mime_info->content_languages = NULL;
287
mime_info->content_charset = "";
288
mime_info->description = "";
290
mime_info->is_pseudo_html = 0;
291
mime_info->level = 0.0f;
292
mime_info->level_matched = 0.0f;
293
mime_info->bytes = -1;
294
mime_info->lang_index = -1;
295
mime_info->mime_stars = 0;
296
mime_info->definite = 1;
298
mime_info->charset_quality = 1.0f;
299
mime_info->encoding_quality = 1.0f;
300
mime_info->lang_quality = 1.0f;
301
mime_info->mime_type_quality = 1.0f;
302
mime_info->source_quality = 0.0f;
305
/* Initializing the relevant fields of a variant record from the
306
* accept_info read out of its content-type, one way or another.
309
static void set_mime_fields(var_rec *var, accept_rec *mime_info)
311
var->mime_type = mime_info->name;
312
var->source_quality = mime_info->quality;
313
var->level = mime_info->level;
314
var->content_charset = mime_info->charset;
316
var->is_pseudo_html = (!strcmp(var->mime_type, "text/html")
317
|| !strcmp(var->mime_type, INCLUDES_MAGIC_TYPE)
318
|| !strcmp(var->mime_type, INCLUDES_MAGIC_TYPE3));
321
/* Create a variant list validator in r using info from vlistr. */
323
static void set_vlist_validator(request_rec *r, request_rec *vlistr)
325
/* Calculating the variant list validator is similar to
326
* calculating an etag for the source of the variant list
327
* information, so we use ap_make_etag(). Note that this
328
* validator can be 'weak' in extreme case.
330
ap_update_mtime(vlistr, vlistr->finfo.mtime);
331
r->vlist_validator = ap_make_etag(vlistr, 0);
333
/* ap_set_etag will later take r->vlist_validator into account
334
* when creating the etag header
339
/*****************************************************************
341
* Parsing (lists of) media types and their parameters, as seen in
342
* HTTPD header lines and elsewhere.
346
* parse quality value. atof(3) is not well-usable here, because it
347
* depends on the locale (argh).
349
* However, RFC 2616 states:
352
* [...] HTTP/1.1 applications MUST NOT generate more than three digits
353
* after the decimal point. User configuration of these values SHOULD also
354
* be limited in this fashion.
356
* qvalue = ( "0" [ "." 0*3DIGIT ] )
357
* | ( "1" [ "." 0*3("0") ] )
359
* This is quite easy. If the supplied string doesn't match the above
360
* definition (loosely), we simply return 1 (same as if there's no qvalue)
363
static float atoq(const char *string)
365
if (!string || !*string) {
369
while (*string && apr_isspace(*string)) {
373
/* be tolerant and accept qvalues without leading zero
374
* (also for backwards compat, where atof() was in use)
376
if (*string != '.' && *string++ != '0') {
380
if (*string == '.') {
381
/* better only one division later, than dealing with fscking
382
* IEEE format 0.1 factors ...
386
if (*++string >= '0' && *string <= '9') {
387
i += (*string - '0') * 100;
389
if (*++string >= '0' && *string <= '9') {
390
i += (*string - '0') * 10;
392
if (*++string > '0' && *string <= '9') {
393
i += (*string - '0');
398
return (float)i / 1000.0f;
405
* Get a single mime type entry --- one media type and parameters;
406
* enter the values we recognize into the argument accept_rec
409
static const char *get_entry(apr_pool_t *p, accept_rec *result,
410
const char *accept_line)
412
result->quality = 1.0f;
413
result->level = 0.0f;
414
result->charset = "";
417
* Note that this handles what I gather is the "old format",
419
* Accept: text/html text/plain moo/zot
421
* without any compatibility kludges --- if the token after the
422
* MIME type begins with a semicolon, we know we're looking at parms,
423
* otherwise, we know we aren't. (So why all the pissing and moaning
424
* in the CERN server code? I must be missing something).
427
result->name = ap_get_token(p, &accept_line, 0);
428
ap_str_tolower(result->name); /* You want case insensitive,
429
* you'll *get* case insensitive.
432
/* KLUDGE!!! Default HTML to level 2.0 unless the browser
433
* *explicitly* says something else.
436
if (!strcmp(result->name, "text/html") && (result->level == 0.0)) {
437
result->level = 2.0f;
439
else if (!strcmp(result->name, INCLUDES_MAGIC_TYPE)) {
440
result->level = 2.0f;
442
else if (!strcmp(result->name, INCLUDES_MAGIC_TYPE3)) {
443
result->level = 3.0f;
446
while (*accept_line == ';') {
454
parm = ap_get_token(p, &accept_line, 1);
456
/* Look for 'var = value' --- and make sure the var is in lcase. */
458
for (cp = parm; (*cp && !apr_isspace(*cp) && *cp != '='); ++cp) {
459
*cp = apr_tolower(*cp);
463
continue; /* No '='; just ignore it. */
466
*cp++ = '\0'; /* Delimit var */
467
while (*cp && (apr_isspace(*cp) || *cp == '=')) {
474
(*end && *end != '\n' && *end != '\r' && *end != '\"');
478
for (end = cp; (*end && !apr_isspace(*end)); end++);
481
*end = '\0'; /* strip ending quote or return */
486
&& (parm[1] == '\0' || (parm[1] == 's' && parm[2] == '\0'))) {
487
result->quality = atoq(cp);
489
else if (parm[0] == 'l' && !strcmp(&parm[1], "evel")) {
490
result->level = (float)atoi(cp);
492
else if (!strcmp(parm, "charset")) {
493
result->charset = cp;
497
if (*accept_line == ',') {
504
/*****************************************************************
506
* Dealing with header lines ...
508
* Accept, Accept-Charset, Accept-Language and Accept-Encoding
509
* are handled by do_header_line() - they all have the same
510
* basic structure of a list of items of the format
511
* name; q=N; charset=TEXT
513
* where charset is only valid in Accept.
516
static apr_array_header_t *do_header_line(apr_pool_t *p,
517
const char *accept_line)
519
apr_array_header_t *accept_recs;
525
accept_recs = apr_array_make(p, 40, sizeof(accept_rec));
527
while (*accept_line) {
528
accept_rec *new = (accept_rec *) apr_array_push(accept_recs);
529
accept_line = get_entry(p, new, accept_line);
535
/* Given the text of the Content-Languages: line from the var map file,
536
* return an array containing the languages of this variant
539
static apr_array_header_t *do_languages_line(apr_pool_t *p,
540
const char **lang_line)
542
apr_array_header_t *lang_recs = apr_array_make(p, 2, sizeof(char *));
548
while (**lang_line) {
549
char **new = (char **) apr_array_push(lang_recs);
550
*new = ap_get_token(p, lang_line, 0);
551
ap_str_tolower(*new);
552
if (**lang_line == ',' || **lang_line == ';') {
560
/*****************************************************************
562
* Handling header lines from clients...
565
static negotiation_state *parse_accept_headers(request_rec *r)
567
negotiation_state *new =
568
(negotiation_state *) apr_pcalloc(r->pool, sizeof(negotiation_state));
570
apr_table_t *hdrs = r->headers_in;
575
new->conf = (neg_dir_config *)ap_get_module_config(r->per_dir_config,
576
&negotiation_module);
578
new->dir_name = ap_make_dirstr_parent(r->pool, r->filename);
580
new->accepts = do_header_line(r->pool, apr_table_get(hdrs, "Accept"));
582
/* calculate new->accept_q value */
584
elts = (accept_rec *) new->accepts->elts;
586
for (i = 0; i < new->accepts->nelts; ++i) {
587
if (elts[i].quality < 1.0) {
593
new->accept_encodings =
594
do_header_line(r->pool, apr_table_get(hdrs, "Accept-Encoding"));
596
do_header_line(r->pool, apr_table_get(hdrs, "Accept-Language"));
597
new->accept_charsets =
598
do_header_line(r->pool, apr_table_get(hdrs, "Accept-Charset"));
600
/* This is possibly overkill for some servers, heck, we have
601
* only 33 index.html variants in docs/docroot (today).
602
* Make this configurable?
604
new->avail_vars = apr_array_make(r->pool, 40, sizeof(var_rec));
610
static void parse_negotiate_header(request_rec *r, negotiation_state *neg)
612
const char *negotiate = apr_table_get(r->headers_in, "Negotiate");
615
/* First, default to no TCN, no Alternates, and the original Apache
616
* negotiation algorithm with fiddles for broken browser configs.
618
* To save network bandwidth, we do not configure to send an
619
* Alternates header to the user agent by default. User
620
* agents that want an Alternates header for agent-driven
621
* negotiation will have to request it by sending an
622
* appropriate Negotiate header.
624
neg->ua_supports_trans = 0;
625
neg->send_alternates = 0;
628
neg->dont_fiddle_headers = 0;
633
if (strcmp(negotiate, "trans") == 0) {
634
/* Lynx 2.7 and 2.8 send 'negotiate: trans' even though they
635
* do not support transparent content negotiation, so for Lynx we
636
* ignore the negotiate header when its contents are exactly "trans".
637
* If future versions of Lynx ever need to say 'negotiate: trans',
638
* they can send the equivalent 'negotiate: trans, trans' instead
639
* to avoid triggering the workaround below.
641
const char *ua = apr_table_get(r->headers_in, "User-Agent");
643
if (ua && (strncmp(ua, "Lynx", 4) == 0))
647
neg->may_choose = 0; /* An empty Negotiate would require 300 response */
649
while ((tok = ap_get_list_item(neg->pool, &negotiate)) != NULL) {
651
if (strcmp(tok, "trans") == 0 ||
652
strcmp(tok, "vlist") == 0 ||
653
strcmp(tok, "guess-small") == 0 ||
654
apr_isdigit(tok[0]) ||
655
strcmp(tok, "*") == 0) {
657
/* The user agent supports transparent negotiation */
658
neg->ua_supports_trans = 1;
660
/* Send-alternates could be configurable, but note
661
* that it must be 1 if we have 'vlist' in the
664
neg->send_alternates = 1;
666
if (strcmp(tok, "1.0") == 0) {
667
/* we may use the RVSA/1.0 algorithm, configure for it */
670
neg->dont_fiddle_headers = 1;
672
else if (tok[0] == '*') {
673
/* we may use any variant selection algorithm, configure
674
* to use the Apache algorithm
678
/* We disable header fiddles on the assumption that a
679
* client sending Negotiate knows how to send correct
680
* headers which don't need fiddling.
682
neg->dont_fiddle_headers = 1;
688
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
689
"dont_fiddle_headers=%d use_rvsa=%d ua_supports_trans=%d "
690
"send_alternates=%d, may_choose=%d",
691
neg->dont_fiddle_headers, neg->use_rvsa,
692
neg->ua_supports_trans, neg->send_alternates, neg->may_choose);
697
/* Sometimes clients will give us no Accept info at all; this routine sets
698
* up the standard default for that case, and also arranges for us to be
699
* willing to run a CGI script if we find one. (In fact, we set up to
700
* dramatically prefer CGI scripts in cases where that's appropriate,
701
* e.g., POST or when URI includes query args or extra path info).
703
static void maybe_add_default_accepts(negotiation_state *neg,
706
accept_rec *new_accept;
709
neg->accepts = apr_array_make(neg->pool, 4, sizeof(accept_rec));
711
new_accept = (accept_rec *) apr_array_push(neg->accepts);
713
new_accept->name = "*/*";
714
new_accept->quality = 1.0f;
715
new_accept->level = 0.0f;
718
new_accept = (accept_rec *) apr_array_push(neg->accepts);
720
new_accept->name = CGI_MAGIC_TYPE;
722
new_accept->quality = 0;
725
new_accept->quality = prefer_scripts ? 2.0f : 0.001f;
727
new_accept->level = 0.0f;
730
/*****************************************************************
732
* Parsing type-map files, in Roy's meta/http format augmented with
736
/* Reading RFC822-style header lines, ignoring #-comments and
737
* handling continuations.
741
header_eof, header_seen, header_sep
744
static enum header_state get_header_line(char *buffer, int len, apr_file_t *map)
746
char *buf_end = buffer + len;
750
/* Get a noncommented line */
753
if (apr_file_gets(buffer, MAX_STRING_LEN, map) != APR_SUCCESS) {
756
} while (buffer[0] == '#');
758
/* If blank, just return it --- this ends information on this variant */
760
for (cp = buffer; (*cp && apr_isspace(*cp)); ++cp) {
768
/* If non-blank, go looking for header lines, but note that we still
769
* have to treat comments specially...
774
/* We need to shortcut the rest of this block following the Body:
775
* tag - we will not look for continutation after this line.
777
if (!strncasecmp(buffer, "Body:", 5))
780
while (apr_file_getc(&c, map) != APR_EOF) {
783
while (apr_file_getc(&c, map) != APR_EOF && c != '\n') {
787
else if (apr_isspace(c)) {
788
/* Leading whitespace. POSSIBLE continuation line
789
* Also, possibly blank --- if so, we ungetc() the final newline
790
* so that we will pick up the blank line the next time 'round.
793
while (c != '\n' && apr_isspace(c)) {
794
if(apr_file_getc(&c, map) != APR_SUCCESS)
798
apr_file_ungetc(c, map);
801
return header_seen; /* Blank line */
806
while ( cp < buf_end - 2
807
&& (apr_file_getc(&c, map)) != APR_EOF
817
/* Line beginning with something other than whitespace */
819
apr_file_ungetc(c, map);
827
static apr_off_t get_body(char *buffer, apr_size_t *len, const char *tag,
835
taglen = strlen(tag);
838
/* We are at the first character following a body:tag\n entry
839
* Suck in the body, then backspace to the first char after the
840
* closing tag entry. If we fail to read, find the tag or back
841
* up then we have a hosed file, so give up already
843
if (apr_file_read(map, buffer, len) != APR_SUCCESS) {
847
/* put a copy of the tag *after* the data read from the file
848
* so that strstr() will find something with no reliance on
851
memcpy(buffer + *len, tag, taglen);
852
endbody = strstr(buffer, tag);
853
if (endbody == buffer + *len) {
856
bodylen = endbody - buffer;
858
/* Skip all the trailing cruft after the end tag to the next line */
860
if (*endbody == '\n') {
867
pos = -(apr_off_t)(*len - (endbody - buffer));
868
if (apr_file_seek(map, APR_CUR, &pos) != APR_SUCCESS) {
872
/* Give the caller back the actual body's file offset and length */
874
return pos - (endbody - buffer);
878
/* Stripping out RFC822 comments */
880
static void strip_paren_comments(char *hdr)
882
/* Hmmm... is this correct? In Roy's latest draft, (comments) can nest! */
883
/* Nope, it isn't correct. Fails to handle backslash escape as well. */
887
hdr = strchr(hdr, '"');
893
else if (*hdr == '(') {
894
while (*hdr && *hdr != ')') {
908
/* Getting to a header body from the header */
910
static char *lcase_header_name_return_body(char *header, request_rec *r)
914
for ( ; *cp && *cp != ':' ; ++cp) {
915
*cp = apr_tolower(*cp);
919
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
920
"Syntax error in type map, no ':' in %s for header %s",
921
r->filename, header);
927
} while (*cp && apr_isspace(*cp));
930
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
931
"Syntax error in type map --- no header body: %s for %s",
932
r->filename, header);
939
static int read_type_map(apr_file_t **map, negotiation_state *neg,
942
request_rec *r = neg->r;
943
apr_file_t *map_ = NULL;
945
char buffer[MAX_STRING_LEN];
946
enum header_state hstate;
947
struct var_rec mime_info;
953
/* We are not using multiviews */
954
neg->count_multiviews_variants = 0;
956
if ((status = apr_file_open(map, rr->filename, APR_READ | APR_BUFFERED,
957
APR_OS_DEFAULT, neg->pool)) != APR_SUCCESS) {
958
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r,
959
"cannot access type map file: %s", rr->filename);
960
if (APR_STATUS_IS_ENOTDIR(status) || APR_STATUS_IS_ENOENT(status)) {
961
return HTTP_NOT_FOUND;
964
return HTTP_FORBIDDEN;
968
clean_var_rec(&mime_info);
972
hstate = get_header_line(buffer, MAX_STRING_LEN, *map);
974
if (hstate == header_seen) {
975
char *body1 = lcase_header_name_return_body(buffer, neg->r);
979
return HTTP_INTERNAL_SERVER_ERROR;
982
strip_paren_comments(body1);
985
if (!strncmp(buffer, "uri:", 4)) {
986
mime_info.file_name = ap_get_token(neg->pool, &body, 0);
988
else if (!strncmp(buffer, "content-type:", 13)) {
989
struct accept_rec accept_info;
991
get_entry(neg->pool, &accept_info, body);
992
set_mime_fields(&mime_info, &accept_info);
995
else if (!strncmp(buffer, "content-length:", 15)) {
999
if (apr_strtoff(&number, body, &errp, 10)
1000
|| *errp || number < 0) {
1001
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1002
"Parse error in type map, Content-Length: "
1003
"'%s' in %s is invalid.",
1007
mime_info.bytes = number;
1010
else if (!strncmp(buffer, "content-language:", 17)) {
1011
mime_info.content_languages = do_languages_line(neg->pool,
1015
else if (!strncmp(buffer, "content-encoding:", 17)) {
1016
mime_info.content_encoding = ap_get_token(neg->pool, &body, 0);
1019
else if (!strncmp(buffer, "description:", 12)) {
1020
char *desc = apr_pstrdup(neg->pool, body);
1023
for (cp = desc; *cp; ++cp) {
1024
if (*cp=='\n') *cp=' ';
1026
if (cp>desc) *(cp-1)=0;
1027
mime_info.description = desc;
1029
else if (!strncmp(buffer, "body:", 5)) {
1030
char *tag = apr_pstrdup(neg->pool, body);
1031
char *eol = strchr(tag, '\0');
1032
apr_size_t len = MAX_STRING_LEN;
1033
while (--eol >= tag && apr_isspace(*eol))
1035
if ((mime_info.body = get_body(buffer, &len, tag, *map)) < 0) {
1036
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1037
"Syntax error in type map, no end tag '%s'"
1038
"found in %s for Body: content.",
1042
mime_info.bytes = len;
1043
mime_info.file_name = apr_filepath_name_get(rr->filename);
1047
if (*mime_info.file_name && has_content) {
1048
void *new_var = apr_array_push(neg->avail_vars);
1050
memcpy(new_var, (void *) &mime_info, sizeof(var_rec));
1053
clean_var_rec(&mime_info);
1056
} while (hstate != header_eof);
1059
apr_file_close(map_);
1061
set_vlist_validator(r, rr);
1067
/* Sort function used by read_types_multi. */
1068
static int variantsortf(var_rec *a, var_rec *b) {
1070
/* First key is the source quality, sort in descending order. */
1072
/* XXX: note that we currently implement no method of setting the
1073
* source quality for multiviews variants, so we are always comparing
1074
* 1.0 to 1.0 for now
1076
if (a->source_quality < b->source_quality)
1078
if (a->source_quality > b->source_quality)
1081
/* Second key is the variant name */
1082
return strcmp(a->file_name, b->file_name);
1085
/*****************************************************************
1087
* Same as read_type_map, except we use a filtered directory listing
1091
static int read_types_multi(negotiation_state *neg)
1093
request_rec *r = neg->r;
1099
apr_status_t status;
1100
struct var_rec mime_info;
1101
struct accept_rec accept_info;
1105
clean_var_rec(&mime_info);
1107
if (r->proxyreq || !r->filename
1108
|| !ap_os_is_path_absolute(neg->pool, r->filename)) {
1112
/* Only absolute paths here */
1113
if (!(filp = strrchr(r->filename, '/'))) {
1117
prefix_len = strlen(filp);
1119
if ((status = apr_dir_open(&dirp, neg->dir_name,
1120
neg->pool)) != APR_SUCCESS) {
1121
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r,
1122
"cannot read directory for multi: %s", neg->dir_name);
1123
return HTTP_FORBIDDEN;
1126
while (apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp) == APR_SUCCESS) {
1127
apr_array_header_t *exception_list;
1128
request_rec *sub_req;
1130
/* Do we have a match? */
1131
#ifdef CASE_BLIND_FILESYSTEM
1132
if (strncasecmp(dirent.name, filp, prefix_len)) {
1134
if (strncmp(dirent.name, filp, prefix_len)) {
1138
if (dirent.name[prefix_len] != '.') {
1142
/* Don't negotiate directories and other unusual files
1143
* Really shouldn't see anything but DIR/LNK/REG here,
1144
* and we aught to discover if the LNK was interesting.
1146
* Of course, this only helps platforms that capture the
1147
* the filetype in apr_dir_read(), which most can once
1148
* they are optimized with some magic [it's known to the
1149
* dirent, not associated to the inode, on most FS's.]
1151
if ((dirent.valid & APR_FINFO_TYPE) && (dirent.filetype == APR_DIR))
1154
/* Ok, something's here. Maybe nothing useful. Remember that
1155
* we tried, if we completely fail, so we can reject the request!
1159
/* See if it's something which we have access to, and which
1160
* has a known type and encoding (as opposed to something
1161
* which we'll be slapping default_type on later).
1163
sub_req = ap_sub_req_lookup_dirent(&dirent, r, AP_SUBREQ_MERGE_ARGS,
1166
/* Double check, we still don't multi-resolve non-ordinary files
1168
if (sub_req->finfo.filetype != APR_REG)
1171
/* If it has a handler, we'll pretend it's a CGI script,
1172
* since that's a good indication of the sort of thing it
1175
if (sub_req->handler && !sub_req->content_type) {
1176
ap_set_content_type(sub_req, CGI_MAGIC_TYPE);
1180
* mod_mime will _always_ provide us the base name in the
1181
* ap-mime-exception-list, if it processed anything. If
1182
* this list is empty, give up immediately, there was
1183
* nothing interesting. For example, looking at the files
1184
* readme.txt and readme.foo, we will throw away .foo if
1185
* it's an insignificant file (e.g. did not identify a
1186
* language, charset, encoding, content type or handler,)
1189
(apr_array_header_t *)apr_table_get(sub_req->notes,
1190
"ap-mime-exceptions-list");
1192
if (!exception_list) {
1193
ap_destroy_sub_req(sub_req);
1197
/* Each unregonized bit better match our base name, in sequence.
1198
* A test of index.html.foo will match index.foo or index.html.foo,
1199
* but it will never transpose the segments and allow index.foo.html
1200
* because that would introduce too much CPU consumption. Better that
1201
* we don't attempt a many-to-many match here.
1204
int nexcept = exception_list->nelts;
1205
char **cur_except = (char**)exception_list->elts;
1206
char *segstart = filp, *segend, saveend;
1208
while (*segstart && nexcept) {
1209
if (!(segend = strchr(segstart, '.')))
1210
segend = strchr(segstart, '\0');
1214
#ifdef CASE_BLIND_FILESYSTEM
1215
if (strcasecmp(segstart, *cur_except) == 0) {
1217
if (strcmp(segstart, *cur_except) == 0) {
1227
segstart = segend + 1;
1231
/* Something you don't know is, something you don't know...
1233
ap_destroy_sub_req(sub_req);
1239
* ###: be warned, the _default_ content type is already
1240
* picked up here! If we failed the subrequest, or don't
1241
* know what we are serving, then continue.
1243
if (sub_req->status != HTTP_OK || (!sub_req->content_type)) {
1244
ap_destroy_sub_req(sub_req);
1248
/* If it's a map file, we use that instead of the map
1251
if (((sub_req->content_type) &&
1252
!strcmp(sub_req->content_type, MAP_FILE_MAGIC_TYPE)) ||
1253
((sub_req->handler) &&
1254
!strcmp(sub_req->handler, "type-map"))) {
1256
apr_dir_close(dirp);
1257
neg->avail_vars->nelts = 0;
1258
if (sub_req->status != HTTP_OK) {
1259
return sub_req->status;
1261
return read_type_map(NULL, neg, sub_req);
1264
/* Have reasonable variant --- gather notes. */
1266
mime_info.sub_req = sub_req;
1267
mime_info.file_name = apr_pstrdup(neg->pool, dirent.name);
1268
if (sub_req->content_encoding) {
1269
mime_info.content_encoding = sub_req->content_encoding;
1271
if (sub_req->content_languages) {
1272
mime_info.content_languages = sub_req->content_languages;
1275
get_entry(neg->pool, &accept_info, sub_req->content_type);
1276
set_mime_fields(&mime_info, &accept_info);
1278
new_var = apr_array_push(neg->avail_vars);
1279
memcpy(new_var, (void *) &mime_info, sizeof(var_rec));
1281
neg->count_multiviews_variants++;
1283
clean_var_rec(&mime_info);
1286
apr_dir_close(dirp);
1288
/* We found some file names that matched. None could be served.
1289
* Rather than fall out to autoindex or some other mapper, this
1292
if (anymatch && !neg->avail_vars->nelts) {
1293
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
1294
"Negotiation: discovered file(s) matching request: %s"
1295
" (None could be negotiated).",
1297
return HTTP_NOT_FOUND;
1300
set_vlist_validator(r, r);
1302
/* Sort the variants into a canonical order. The negotiation
1303
* result sometimes depends on the order of the variants. By
1304
* sorting the variants into a canonical order, rather than using
1305
* the order in which readdir() happens to return them, we ensure
1306
* that the negotiation result will be consistent over filesystem
1307
* backup/restores and over all mirror sites.
1310
qsort((void *) neg->avail_vars->elts, neg->avail_vars->nelts,
1311
sizeof(var_rec), (int (*)(const void *, const void *)) variantsortf);
1317
/*****************************************************************
1318
* And now for the code you've been waiting for... actually
1319
* finding a match to the client's requirements.
1322
/* Matching MIME types ... the star/star and foo/star commenting conventions
1323
* are implemented here. (You know what I mean by star/star, but just
1324
* try mentioning those three characters in a C comment). Using strcmp()
1325
* is legit, because everything has already been smashed to lowercase.
1327
* Note also that if we get an exact match on the media type, we update
1328
* level_matched for use in level_cmp below...
1330
* We also give a value for mime_stars, which is used later. It should
1331
* be 1 for star/star, 2 for type/star and 3 for type/subtype.
1334
static int mime_match(accept_rec *accept_r, var_rec *avail)
1336
const char *accept_type = accept_r->name;
1337
const char *avail_type = avail->mime_type;
1338
int len = strlen(accept_type);
1340
if (accept_type[0] == '*') { /* Anything matches star/star */
1341
if (avail->mime_stars < 1) {
1342
avail->mime_stars = 1;
1346
else if ((accept_type[len - 1] == '*') &&
1347
!strncmp(accept_type, avail_type, len - 2)) {
1348
if (avail->mime_stars < 2) {
1349
avail->mime_stars = 2;
1353
else if (!strcmp(accept_type, avail_type)
1354
|| (!strcmp(accept_type, "text/html")
1355
&& (!strcmp(avail_type, INCLUDES_MAGIC_TYPE)
1356
|| !strcmp(avail_type, INCLUDES_MAGIC_TYPE3)))) {
1357
if (accept_r->level >= avail->level) {
1358
avail->level_matched = avail->level;
1359
avail->mime_stars = 3;
1367
/* This code implements a piece of the tie-breaking algorithm between
1368
* variants of equal quality. This piece is the treatment of variants
1369
* of the same base media type, but different levels. What we want to
1370
* return is the variant at the highest level that the client explicitly
1371
* claimed to accept.
1373
* If all the variants available are at a higher level than that, or if
1374
* the client didn't say anything specific about this media type at all
1375
* and these variants just got in on a wildcard, we prefer the lowest
1376
* level, on grounds that that's the one that the client is least likely
1379
* (This is all motivated by treatment of levels in HTML --- we only
1380
* want to give level 3 to browsers that explicitly ask for it; browsers
1381
* that don't, including HTTP/0.9 browsers that only get the implicit
1382
* "Accept: * / *" [space added to avoid confusing cpp --- no, that
1383
* syntax doesn't really work] should get HTML2 if available).
1385
* (Note that this code only comes into play when we are choosing among
1386
* variants of equal quality, where the draft standard gives us a fair
1387
* bit of leeway about what to do. It ain't specified by the standard;
1388
* rather, it is a choice made by this server about what to do in cases
1389
* where the standard does not specify a unique course of action).
1392
static int level_cmp(var_rec *var1, var_rec *var2)
1394
/* Levels are only comparable between matching media types */
1396
if (var1->is_pseudo_html && !var2->is_pseudo_html) {
1400
if (!var1->is_pseudo_html && strcmp(var1->mime_type, var2->mime_type)) {
1403
/* The result of the above if statements is that, if we get to
1404
* here, both variants have the same mime_type or both are
1408
/* Take highest level that matched, if either did match. */
1410
if (var1->level_matched > var2->level_matched) {
1413
if (var1->level_matched < var2->level_matched) {
1417
/* Neither matched. Take lowest level, if there's a difference. */
1419
if (var1->level < var2->level) {
1422
if (var1->level > var2->level) {
1431
/* Finding languages. The main entry point is set_language_quality()
1432
* which is called for each variant. It sets two elements in the
1434
* language_quality - the 'q' value of the 'best' matching language
1435
* from Accept-Language: header (HTTP/1.1)
1436
* lang_index - Non-negotiated language priority, using
1437
* position of language on the Accept-Language:
1438
* header, if present, else LanguagePriority
1441
* When we do the variant checking for best variant, we use language
1442
* quality first, and if a tie, language_index next (this only applies
1443
* when _not_ using the RVSA/1.0 algorithm). If using the RVSA/1.0
1444
* algorithm, lang_index is never used.
1446
* set_language_quality() calls find_lang_index() and find_default_index()
1447
* to set lang_index.
1450
static int find_lang_index(apr_array_header_t *accept_langs, char *lang)
1455
if (!lang || !accept_langs) {
1459
alang = (const char **) accept_langs->elts;
1461
for (i = 0; i < accept_langs->nelts; ++i) {
1462
if (!strncmp(lang, *alang, strlen(*alang))) {
1465
alang += (accept_langs->elt_size / sizeof(char*));
1471
/* set_default_lang_quality() sets the quality we apply to variants
1472
* which have no language assigned to them. If none of the variants
1473
* have a language, we are not negotiating on language, so all are
1474
* acceptable, and we set the default q value to 1.0. However if
1475
* some of the variants have languages, we set this default to 0.0001.
1476
* The value of this default will be applied to all variants with
1477
* no explicit language -- which will have the effect of making them
1478
* acceptable, but only if no variants with an explicit language
1479
* are acceptable. The default q value set here is assigned to variants
1480
* with no language type in set_language_quality().
1482
* Note that if using the RVSA/1.0 algorithm, we don't use this
1486
static void set_default_lang_quality(negotiation_state *neg)
1488
var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
1491
if (!neg->dont_fiddle_headers) {
1492
for (j = 0; j < neg->avail_vars->nelts; ++j) {
1493
var_rec *variant = &avail_recs[j];
1494
if (variant->content_languages &&
1495
variant->content_languages->nelts) {
1496
neg->default_lang_quality = 0.0001f;
1502
neg->default_lang_quality = 1.0f;
1505
/* Set the language_quality value in the variant record. Also
1506
* assigns lang_index for ForceLanguagePriority.
1508
* To find the language_quality value, we look for the 'q' value
1509
* of the 'best' matching language on the Accept-Language
1510
* header. The 'best' match is the language on Accept-Language
1511
* header which matches the language of this variant either fully,
1512
* or as far as the prefix marker (-). If two or more languages
1513
* match, use the longest string from the Accept-Language header
1514
* (see HTTP/1.1 [14.4])
1516
* When a variant has multiple languages, we find the 'best'
1517
* match for each variant language tag as above, then select the
1518
* one with the highest q value. Because both the accept-header
1519
* and variant can have multiple languages, we now have a hairy
1520
* loop-within-a-loop here.
1522
* If the variant has no language and we have no Accept-Language
1523
* items, leave the quality at 1.0 and return.
1525
* If the variant has no language, we use the default as set by
1526
* set_default_lang_quality() (1.0 if we are not negotiating on
1527
* language, 0.001 if we are).
1529
* Following the setting of the language quality, we drop through to
1530
* set the old 'lang_index'. This is set based on either the order
1531
* of the languages on the Accept-Language header, or the
1532
* order on the LanguagePriority directive. This is only used
1533
* in the negotiation if the language qualities tie.
1536
static void set_language_quality(negotiation_state *neg, var_rec *variant)
1538
int forcepriority = neg->conf->forcelangpriority;
1539
if (forcepriority == FLP_UNDEF) {
1540
forcepriority = FLP_DEFAULT;
1543
if (!variant->content_languages || !variant->content_languages->nelts) {
1544
/* This variant has no content-language, so use the default
1545
* quality factor for variants with no content-language
1546
* (previously set by set_default_lang_quality()).
1547
* Leave the factor alone (it remains at 1.0) when we may not fiddle
1550
if (!neg->dont_fiddle_headers) {
1551
variant->lang_quality = neg->default_lang_quality;
1553
if (!neg->accept_langs) {
1554
return; /* no accept-language header */
1559
/* Variant has one (or more) languages. Look for the best
1560
* match. We do this by going through each language on the
1561
* variant description looking for a match on the
1562
* Accept-Language header. The best match is the longest
1563
* matching language on the header. The final result is the
1564
* best q value from all the languages on the variant
1568
if (!neg->accept_langs) {
1569
/* no accept-language header makes the variant indefinite */
1570
variant->definite = 0;
1572
else { /* There is an accept-language with 0 or more items */
1573
accept_rec *accs = (accept_rec *) neg->accept_langs->elts;
1574
accept_rec *best = NULL, *star = NULL;
1575
accept_rec *bestthistag;
1577
float fiddle_q = 0.0f;
1578
int any_match_on_star = 0;
1580
apr_size_t alen, longest_lang_range_len;
1582
for (j = 0; j < variant->content_languages->nelts; ++j) {
1585
longest_lang_range_len = 0;
1588
/* lang is the variant's language-tag, which is the one
1589
* we are allowed to use the prefix of in HTTP/1.1
1591
lang = ((char **) (variant->content_languages->elts))[j];
1593
/* now find the best (i.e. longest) matching
1594
* Accept-Language header language. We put the best match
1595
* for this tag in bestthistag. We cannot update the
1596
* overall best (based on q value) because the best match
1597
* for this tag is the longest language item on the accept
1598
* header, not necessarily the highest q.
1600
for (i = 0; i < neg->accept_langs->nelts; ++i) {
1601
if (!strcmp(accs[i].name, "*")) {
1607
/* Find language. We match if either the variant
1608
* language tag exactly matches the language range
1609
* from the accept header, or a prefix of the variant
1610
* language tag up to a '-' character matches the
1611
* whole of the language range in the Accept-Language
1612
* header. Note that HTTP/1.x allows any number of
1613
* '-' characters in a tag or range, currently only
1614
* tags with zero or one '-' characters are defined
1615
* for general use (see rfc1766).
1617
* We only use language range in the Accept-Language
1618
* header the best match for the variant language tag
1619
* if it is longer than the previous best match.
1622
alen = strlen(accs[i].name);
1624
if ((strlen(lang) >= alen) &&
1625
!strncmp(lang, accs[i].name, alen) &&
1626
((lang[alen] == 0) || (lang[alen] == '-')) ) {
1628
if (alen > longest_lang_range_len) {
1629
longest_lang_range_len = alen;
1630
bestthistag = &accs[i];
1634
if (!bestthistag && !neg->dont_fiddle_headers) {
1635
/* The next bit is a fiddle. Some browsers might
1636
* be configured to send more specific language
1637
* ranges than desirable. For example, an
1638
* Accept-Language of en-US should never match
1639
* variants with languages en or en-GB. But US
1640
* English speakers might pick en-US as their
1641
* language choice. So this fiddle checks if the
1642
* language range has a prefix, and if so, it
1643
* matches variants which match that prefix with a
1644
* priority of 0.001. So a request for en-US would
1645
* match variants of types en and en-GB, but at
1646
* much lower priority than matches of en-US
1647
* directly, or of any other language listed on
1648
* the Accept-Language header. Note that this
1649
* fiddle does not handle multi-level prefixes.
1651
if ((p = strchr(accs[i].name, '-'))) {
1652
int plen = p - accs[i].name;
1654
if (!strncmp(lang, accs[i].name, plen)) {
1660
/* Finished looking at Accept-Language headers, the best
1661
* (longest) match is in bestthistag, or NULL if no match
1664
(bestthistag && bestthistag->quality > best->quality)) {
1668
/* See if the tag matches on a * in the Accept-Language
1669
* header. If so, record this fact for later use
1671
if (!bestthistag && star) {
1672
any_match_on_star = 1;
1676
/* If one of the language tags of the variant matched on *, we
1677
* need to see if its q is better than that of any non-* match
1678
* on any other tag of the variant. If so the * match takes
1679
* precedence and the overall match is not definite.
1681
if ( any_match_on_star &&
1682
((best && star->quality > best->quality) ||
1685
variant->definite = 0;
1688
variant->lang_quality = best ? best->quality : fiddle_q;
1692
/* Handle the ForceDefaultLanguage overrides, based on the best match
1693
* to LanguagePriority order. The best match is the lowest index of
1694
* any LanguagePriority match.
1696
if (((forcepriority & FLP_PREFER)
1697
&& (variant->lang_index < 0))
1698
|| ((forcepriority & FLP_FALLBACK)
1699
&& !variant->lang_quality))
1704
for (j = 0; j < variant->content_languages->nelts; ++j)
1706
/* lang is the variant's language-tag, which is the one
1707
* we are allowed to use the prefix of in HTTP/1.1
1709
char *lang = ((char **) (variant->content_languages->elts))[j];
1712
/* If we wish to fallback or
1713
* we use our own LanguagePriority index.
1715
idx = find_lang_index(neg->conf->language_priority, lang);
1716
if ((idx >= 0) && ((bestidx == -1) || (idx < bestidx))) {
1722
if (variant->lang_quality) {
1723
if (forcepriority & FLP_PREFER) {
1724
variant->lang_index = bestidx;
1728
if (forcepriority & FLP_FALLBACK) {
1729
variant->lang_index = bestidx;
1730
variant->lang_quality = .0001f;
1731
variant->definite = 0;
1739
/* Determining the content length --- if the map didn't tell us,
1740
* we have to do a stat() and remember for next time.
1743
static apr_off_t find_content_length(negotiation_state *neg, var_rec *variant)
1747
if (variant->bytes < 0) {
1748
if ( variant->sub_req
1749
&& (variant->sub_req->finfo.valid & APR_FINFO_SIZE)) {
1750
variant->bytes = variant->sub_req->finfo.size;
1753
char *fullname = ap_make_full_path(neg->pool, neg->dir_name,
1754
variant->file_name);
1756
if (apr_stat(&statb, fullname,
1757
APR_FINFO_SIZE, neg->pool) == APR_SUCCESS) {
1758
variant->bytes = statb.size;
1763
return variant->bytes;
1766
/* For a given variant, find the best matching Accept: header
1767
* and assign the Accept: header's quality value to the
1768
* mime_type_quality field of the variant, for later use in
1769
* determining the best matching variant.
1772
static void set_accept_quality(negotiation_state *neg, var_rec *variant)
1775
accept_rec *accept_recs;
1779
/* if no Accept: header, leave quality alone (will
1780
* remain at the default value of 1)
1782
* XXX: This if is currently never true because of the effect of
1783
* maybe_add_default_accepts().
1785
if (!neg->accepts) {
1786
if (variant->mime_type && *variant->mime_type)
1787
variant->definite = 0;
1791
accept_recs = (accept_rec *) neg->accepts->elts;
1794
* Go through each of the ranges on the Accept: header,
1795
* looking for the 'best' match with this variant's
1796
* content-type. We use the best match's quality
1797
* value (from the Accept: header) for this variant's
1798
* mime_type_quality field.
1800
* The best match is determined like this:
1801
* type/type is better than type/ * is better than * / *
1802
* if match is type/type, use the level mime param if available
1804
for (i = 0; i < neg->accepts->nelts; ++i) {
1806
accept_rec *type = &accept_recs[i];
1807
int prev_mime_stars;
1809
prev_mime_stars = variant->mime_stars;
1811
if (!mime_match(type, variant)) {
1812
continue; /* didn't match the content type at all */
1815
/* did match - see if there were less or more stars than
1818
if (prev_mime_stars == variant->mime_stars) {
1819
continue; /* more stars => not as good a match */
1823
/* If we are allowed to mess with the q-values
1824
* and have no explicit q= parameters in the accept header,
1825
* make wildcards very low, so we have a low chance
1826
* of ending up with them if there's something better.
1829
if (!neg->dont_fiddle_headers && !neg->accept_q &&
1830
variant->mime_stars == 1) {
1833
else if (!neg->dont_fiddle_headers && !neg->accept_q &&
1834
variant->mime_stars == 2) {
1841
q_definite = (variant->mime_stars == 3);
1843
variant->mime_type_quality = q;
1844
variant->definite = variant->definite && q_definite;
1848
/* For a given variant, find the 'q' value of the charset given
1849
* on the Accept-Charset line. If no charsets are listed,
1850
* assume value of '1'.
1852
static void set_charset_quality(negotiation_state *neg, var_rec *variant)
1855
accept_rec *accept_recs;
1856
const char *charset = variant->content_charset;
1857
accept_rec *star = NULL;
1859
/* if no Accept-Charset: header, leave quality alone (will
1860
* remain at the default value of 1)
1862
if (!neg->accept_charsets) {
1863
if (charset && *charset)
1864
variant->definite = 0;
1868
accept_recs = (accept_rec *) neg->accept_charsets->elts;
1870
if (charset == NULL || !*charset) {
1871
/* Charset of variant not known */
1873
/* if not a text / * type, leave quality alone */
1874
if (!(!strncmp(variant->mime_type, "text/", 5)
1875
|| !strcmp(variant->mime_type, INCLUDES_MAGIC_TYPE)
1876
|| !strcmp(variant->mime_type, INCLUDES_MAGIC_TYPE3)
1880
/* Don't go guessing if we are in strict header mode,
1881
* e.g. when running the rvsa, as any guess won't be reflected
1882
* in the variant list or content-location headers.
1884
if (neg->dont_fiddle_headers)
1887
charset = "iso-8859-1"; /* The default charset for HTTP text types */
1891
* Go through each of the items on the Accept-Charset header,
1892
* looking for a match with this variant's charset. If none
1893
* match, charset is unacceptable, so set quality to 0.
1895
for (i = 0; i < neg->accept_charsets->nelts; ++i) {
1897
accept_rec *type = &accept_recs[i];
1899
if (!strcmp(type->name, charset)) {
1900
variant->charset_quality = type->quality;
1903
else if (strcmp(type->name, "*") == 0) {
1907
/* No explicit match */
1909
variant->charset_quality = star->quality;
1910
variant->definite = 0;
1913
/* If this variant is in charset iso-8859-1, the default is 1.0 */
1914
if (strcmp(charset, "iso-8859-1") == 0) {
1915
variant->charset_quality = 1.0f;
1918
variant->charset_quality = 0.0f;
1923
/* is_identity_encoding is included for back-compat, but does anyone
1924
* use 7bit, 8bin or binary in their var files??
1927
static int is_identity_encoding(const char *enc)
1929
return (!enc || !enc[0] || !strcmp(enc, "7bit") || !strcmp(enc, "8bit")
1930
|| !strcmp(enc, "binary"));
1934
* set_encoding_quality determines whether the encoding for a particular
1935
* variant is acceptable for the user-agent.
1937
* The rules for encoding are that if the user-agent does not supply
1938
* any Accept-Encoding header, then all encodings are allowed but a
1939
* variant with no encoding should be preferred.
1940
* If there is an empty Accept-Encoding header, then no encodings are
1941
* acceptable. If there is a non-empty Accept-Encoding header, then
1942
* any of the listed encodings are acceptable, as well as no encoding
1943
* unless the "identity" encoding is specifically excluded.
1945
static void set_encoding_quality(negotiation_state *neg, var_rec *variant)
1947
accept_rec *accept_recs;
1948
const char *enc = variant->content_encoding;
1949
accept_rec *star = NULL;
1950
float value_if_not_found = 0.0f;
1953
if (!neg->accept_encodings) {
1954
/* We had no Accept-Encoding header, assume that all
1955
* encodings are acceptable with a low quality,
1956
* but we prefer no encoding if available.
1958
if (!enc || is_identity_encoding(enc))
1959
variant->encoding_quality = 1.0f;
1961
variant->encoding_quality = 0.5f;
1966
if (!enc || is_identity_encoding(enc)) {
1968
value_if_not_found = 0.0001f;
1971
accept_recs = (accept_rec *) neg->accept_encodings->elts;
1973
/* Go through each of the encodings on the Accept-Encoding: header,
1974
* looking for a match with our encoding. x- prefixes are ignored.
1976
if (enc[0] == 'x' && enc[1] == '-') {
1979
for (i = 0; i < neg->accept_encodings->nelts; ++i) {
1981
char *name = accept_recs[i].name;
1983
if (name[0] == 'x' && name[1] == '-') {
1987
if (!strcmp(name, enc)) {
1988
variant->encoding_quality = accept_recs[i].quality;
1992
if (strcmp(name, "*") == 0) {
1993
star = &accept_recs[i];
1997
/* No explicit match */
1999
variant->encoding_quality = star->quality;
2003
/* Encoding not found on Accept-Encoding: header, so it is
2004
* _not_ acceptable unless it is the identity (no encoding)
2006
variant->encoding_quality = value_if_not_found;
2009
/*************************************************************
2010
* Possible results of the variant selection algorithm
2012
enum algorithm_results {
2013
alg_choice = 1, /* choose variant */
2014
alg_list /* list variants */
2017
/* Below is the 'best_match' function. It returns an int, which has
2018
* one of the two values alg_choice or alg_list, which give the result
2019
* of the variant selection algorithm. alg_list means that no best
2020
* variant was found by the algorithm, alg_choice means that a best
2021
* variant was found and should be returned. The list/choice
2022
* terminology comes from TCN (rfc2295), but is used in a more generic
2023
* way here. The best variant is returned in *pbest. best_match has
2024
* two possible algorithms for determining the best variant: the
2025
* RVSA/1.0 algorithm (from RFC2296), and the standard Apache
2026
* algorithm. These are split out into separate functions
2027
* (is_variant_better_rvsa() and is_variant_better()). Selection of
2028
* one is through the neg->use_rvsa flag.
2030
* The call to best_match also creates full information, including
2031
* language, charset, etc quality for _every_ variant. This is needed
2032
* for generating a correct Vary header, and can be used for the
2033
* Alternates header, the human-readable list responses and 406 errors.
2036
/* Firstly, the RVSA/1.0 (HTTP Remote Variant Selection Algorithm
2037
* v1.0) from rfc2296. This is the algorithm that goes together with
2038
* transparent content negotiation (TCN).
2040
static int is_variant_better_rvsa(negotiation_state *neg, var_rec *variant,
2041
var_rec *best, float *p_bestq)
2043
float bestq = *p_bestq, q;
2045
/* TCN does not cover negotiation on content-encoding. For now,
2046
* we ignore the encoding unless it was explicitly excluded.
2048
if (variant->encoding_quality == 0.0f)
2051
q = variant->mime_type_quality *
2052
variant->source_quality *
2053
variant->charset_quality *
2054
variant->lang_quality;
2056
/* RFC 2296 calls for the result to be rounded to 5 decimal places,
2057
* but we don't do that because it serves no useful purpose other
2058
* than to ensure that a remote algorithm operates on the same
2059
* precision as ours. That is silly, since what we obviously want
2060
* is for the algorithm to operate on the best available precision
2061
* regardless of who runs it. Since the above calculation may
2062
* result in significant variance at 1e-12, rounding would be bogus.
2066
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
2067
"Variant: file=%s type=%s lang=%s sourceq=%1.3f "
2068
"mimeq=%1.3f langq=%1.3f charq=%1.3f encq=%1.3f "
2069
"q=%1.5f definite=%d",
2070
(variant->file_name ? variant->file_name : ""),
2071
(variant->mime_type ? variant->mime_type : ""),
2072
(variant->content_languages
2073
? apr_array_pstrcat(neg->pool, variant->content_languages, ',')
2075
variant->source_quality,
2076
variant->mime_type_quality,
2077
variant->lang_quality,
2078
variant->charset_quality,
2079
variant->encoding_quality,
2092
/* If the best variant's encoding is of lesser quality than
2093
* this variant, then we prefer this variant
2095
if (variant->encoding_quality > best->encoding_quality) {
2103
/* Negotiation algorithm as used by previous versions of Apache
2107
static int is_variant_better(negotiation_state *neg, var_rec *variant,
2108
var_rec *best, float *p_bestq)
2110
float bestq = *p_bestq, q;
2113
/* For non-transparent negotiation, server can choose how
2114
* to handle the negotiation. We'll use the following in
2115
* order: content-type, language, content-type level, charset,
2116
* content encoding, content length.
2118
* For each check, we have three possible outcomes:
2119
* This variant is worse than current best: return 0
2120
* This variant is better than the current best:
2121
* assign this variant's q to *p_bestq, and return 1
2122
* This variant is just as desirable as the current best:
2123
* drop through to the next test.
2125
* This code is written in this long-winded way to allow future
2126
* customisation, either by the addition of additional
2127
* checks, or to allow the order of the checks to be determined
2128
* by configuration options (e.g. we might prefer to check
2129
* language quality _before_ content type).
2132
/* First though, eliminate this variant if it is not
2133
* acceptable by type, charset, encoding or language.
2137
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
2138
"Variant: file=%s type=%s lang=%s sourceq=%1.3f "
2139
"mimeq=%1.3f langq=%1.3f langidx=%d charq=%1.3f encq=%1.3f ",
2140
(variant->file_name ? variant->file_name : ""),
2141
(variant->mime_type ? variant->mime_type : ""),
2142
(variant->content_languages
2143
? apr_array_pstrcat(neg->pool, variant->content_languages, ',')
2145
variant->source_quality,
2146
variant->mime_type_quality,
2147
variant->lang_quality,
2148
variant->lang_index,
2149
variant->charset_quality,
2150
variant->encoding_quality);
2153
if (variant->encoding_quality == 0.0f ||
2154
variant->lang_quality == 0.0f ||
2155
variant->source_quality == 0.0f ||
2156
variant->charset_quality == 0.0f ||
2157
variant->mime_type_quality == 0.0f) {
2158
return 0; /* don't consider unacceptables */
2161
q = variant->mime_type_quality * variant->source_quality;
2162
if (q == 0.0 || q < bestq) {
2165
if (q > bestq || !best) {
2171
if (variant->lang_quality < best->lang_quality) {
2174
if (variant->lang_quality > best->lang_quality) {
2179
/* if language qualities were equal, try the LanguagePriority stuff */
2180
if (best->lang_index != -1 &&
2181
(variant->lang_index == -1 || variant->lang_index > best->lang_index)) {
2184
if (variant->lang_index != -1 &&
2185
(best->lang_index == -1 || variant->lang_index < best->lang_index)) {
2190
/* content-type level (sometimes used with text/html, though we
2191
* support it on other types too)
2193
levcmp = level_cmp(variant, best);
2203
if (variant->charset_quality < best->charset_quality) {
2206
/* If the best variant's charset is ISO-8859-1 and this variant has
2207
* the same charset quality, then we prefer this variant
2210
if (variant->charset_quality > best->charset_quality ||
2211
((variant->content_charset != NULL &&
2212
*variant->content_charset != '\0' &&
2213
strcmp(variant->content_charset, "iso-8859-1") != 0) &&
2214
(best->content_charset == NULL ||
2215
*best->content_charset == '\0' ||
2216
strcmp(best->content_charset, "iso-8859-1") == 0))) {
2221
/* Prefer the highest value for encoding_quality.
2223
if (variant->encoding_quality < best->encoding_quality) {
2226
if (variant->encoding_quality > best->encoding_quality) {
2231
/* content length if all else equal */
2232
if (find_content_length(neg, variant) >= find_content_length(neg, best)) {
2236
/* ok, to get here means every thing turned out equal, except
2237
* we have a shorter content length, so use this variant
2243
/* figure out, whether a variant is in a specific language
2244
* it returns also false, if the variant has no language.
2246
static int variant_has_language(var_rec *variant, const char *lang)
2252
|| !variant->content_languages
2253
|| !(max = variant->content_languages->nelts)) {
2257
for (j = 0; j < max; ++j) {
2259
((char **) (variant->content_languages->elts))[j])) {
2267
/* check for environment variables 'no-gzip' and
2268
* 'gzip-only-text/html' to get a behaviour similiar
2271
static int discard_variant_by_env(var_rec *variant, int discard)
2273
if ( is_identity_encoding(variant->content_encoding)
2274
|| !strcmp(variant->content_encoding, "identity")) {
2278
return ( (discard == DISCARD_ALL_ENCODINGS)
2279
|| (discard == DISCARD_ALL_BUT_HTML
2280
&& (!variant->mime_type
2281
|| strncmp(variant->mime_type, "text/html", 9))));
2284
static int best_match(negotiation_state *neg, var_rec **pbest)
2289
enum algorithm_results algorithm_result;
2290
int may_discard = 0;
2292
var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
2294
/* fetch request dependent variables
2295
* prefer-language: prefer a certain language.
2297
const char *preferred_language = apr_table_get(neg->r->subprocess_env,
2300
/* no-gzip: do not send encoded documents */
2301
if (apr_table_get(neg->r->subprocess_env, "no-gzip")) {
2302
may_discard = DISCARD_ALL_ENCODINGS;
2305
/* gzip-only-text/html: send encoded documents only
2306
* if they are text/html. (no-gzip has a higher priority).
2309
const char *env_value = apr_table_get(neg->r->subprocess_env,
2310
"gzip-only-text/html");
2312
if (env_value && !strcmp(env_value, "1")) {
2313
may_discard = DISCARD_ALL_BUT_HTML;
2317
set_default_lang_quality(neg);
2320
* Find the 'best' variant
2321
* We run the loop possibly twice: if "prefer-language"
2322
* environment variable is set but we did not find an appropriate
2323
* best variant. In that case forget the preferred language and
2324
* negotiate over all variants.
2330
for (j = 0; j < neg->avail_vars->nelts; ++j) {
2331
var_rec *variant = &avail_recs[j];
2333
/* if this variant is encoded somehow and there are special
2334
* variables set, we do not negotiate it. see above.
2337
&& discard_variant_by_env(variant, may_discard)) {
2341
/* if a language is preferred, but the current variant
2342
* is not in that language, then drop it for now
2344
if ( preferred_language
2345
&& !variant_has_language(variant, preferred_language)) {
2349
/* Find all the relevant 'quality' values from the
2350
* Accept... headers, and store in the variant. This also
2351
* prepares for sending an Alternates header etc so we need to
2352
* do it even if we do not actually plan to find a best
2355
set_accept_quality(neg, variant);
2356
/* accept the preferred language, even when it's not listed within
2357
* the Accept-Language header
2359
if (preferred_language) {
2360
variant->lang_quality = 1.0f;
2361
variant->definite = 1;
2364
set_language_quality(neg, variant);
2366
set_encoding_quality(neg, variant);
2367
set_charset_quality(neg, variant);
2369
/* Only do variant selection if we may actually choose a
2370
* variant for the client
2372
if (neg->may_choose) {
2374
/* Now find out if this variant is better than the current
2375
* best, either using the RVSA/1.0 algorithm, or Apache's
2376
* internal server-driven algorithm. Presumably other
2377
* server-driven algorithms are possible, and could be
2381
if (neg->use_rvsa) {
2382
if (is_variant_better_rvsa(neg, variant, best, &bestq)) {
2387
if (is_variant_better(neg, variant, best, &bestq)) {
2394
/* We now either have a best variant, or no best variant */
2396
if (neg->use_rvsa) {
2397
/* calculate result for RVSA/1.0 algorithm:
2398
* only a choice response if the best variant has q>0
2401
algorithm_result = (best && best->definite) && (bestq > 0) ?
2402
alg_choice : alg_list;
2405
/* calculate result for Apache negotiation algorithm */
2406
algorithm_result = bestq > 0 ? alg_choice : alg_list;
2409
/* run the loop again, if the "prefer-language" got no clear result */
2410
if (preferred_language && (!best || algorithm_result != alg_choice)) {
2411
preferred_language = NULL;
2418
/* Returning a choice response with a non-neighboring variant is a
2419
* protocol security error in TCN (see rfc2295). We do *not*
2420
* verify here that the variant and URI are neighbors, even though
2421
* we may return alg_choice. We depend on the environment (the
2422
* caller) to only declare the resource transparently negotiable if
2423
* all variants are neighbors.
2426
return algorithm_result;
2429
/* Sets response headers for a negotiated response.
2430
* neg->is_transparent determines whether a transparently negotiated
2431
* response or a plain `server driven negotiation' response is
2432
* created. Applicable headers are Alternates, Vary, and TCN.
2434
* The Vary header we create is sometimes longer than is required for
2435
* the correct caching of negotiated results by HTTP/1.1 caches. For
2436
* example if we have 3 variants x.html, x.ps.en and x.ps.nl, and if
2437
* the Accept: header assigns a 0 quality to .ps, then the results of
2438
* the two server-side negotiation algorithms we currently implement
2439
* will never depend on Accept-Language so we could return `Vary:
2440
* negotiate, accept' instead of the longer 'Vary: negotiate, accept,
2441
* accept-language' which the code below will return. A routine for
2442
* computing the exact minimal Vary header would be a huge pain to code
2443
* and maintain though, especially because we need to take all possible
2444
* twiddles in the server-side negotiation algorithms into account.
2446
static void set_neg_headers(request_rec *r, negotiation_state *neg,
2450
var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
2451
const char *sample_type = NULL;
2452
const char *sample_language = NULL;
2453
const char *sample_encoding = NULL;
2454
const char *sample_charset = NULL;
2458
apr_array_header_t *arr;
2459
int max_vlist_array = (neg->avail_vars->nelts * 21);
2460
int first_variant = 1;
2461
int vary_by_type = 0;
2462
int vary_by_language = 0;
2463
int vary_by_charset = 0;
2464
int vary_by_encoding = 0;
2467
/* In order to avoid O(n^2) memory copies in building Alternates,
2468
* we preallocate a apr_table_t with the maximum substrings possible,
2469
* fill it with the variant list, and then concatenate the entire array.
2470
* Note that if you change the number of substrings pushed, you also
2471
* need to change the calculation of max_vlist_array above.
2473
if (neg->send_alternates && neg->avail_vars->nelts)
2474
arr = apr_array_make(r->pool, max_vlist_array, sizeof(char *));
2478
/* Put headers into err_headers_out, since send_http_header()
2479
* outputs both headers_out and err_headers_out.
2481
hdrs = r->err_headers_out;
2483
for (j = 0; j < neg->avail_vars->nelts; ++j) {
2484
var_rec *variant = &avail_recs[j];
2486
if (variant->content_languages && variant->content_languages->nelts) {
2487
lang = apr_array_pstrcat(r->pool, variant->content_languages, ',');
2493
/* Calculate Vary by looking for any difference between variants */
2495
if (first_variant) {
2496
sample_type = variant->mime_type;
2497
sample_charset = variant->content_charset;
2498
sample_language = lang;
2499
sample_encoding = variant->content_encoding;
2502
if (!vary_by_type &&
2503
strcmp(sample_type ? sample_type : "",
2504
variant->mime_type ? variant->mime_type : "")) {
2507
if (!vary_by_charset &&
2508
strcmp(sample_charset ? sample_charset : "",
2509
variant->content_charset ?
2510
variant->content_charset : "")) {
2511
vary_by_charset = 1;
2513
if (!vary_by_language &&
2514
strcmp(sample_language ? sample_language : "",
2515
lang ? lang : "")) {
2516
vary_by_language = 1;
2518
if (!vary_by_encoding &&
2519
strcmp(sample_encoding ? sample_encoding : "",
2520
variant->content_encoding ?
2521
variant->content_encoding : "")) {
2522
vary_by_encoding = 1;
2527
if (!neg->send_alternates)
2530
/* Generate the string components for this Alternates entry */
2532
*((const char **) apr_array_push(arr)) = "{\"";
2533
*((const char **) apr_array_push(arr)) = variant->file_name;
2534
*((const char **) apr_array_push(arr)) = "\" ";
2536
qstr = (char *) apr_palloc(r->pool, 6);
2537
apr_snprintf(qstr, 6, "%1.3f", variant->source_quality);
2539
/* Strip trailing zeros (saves those valuable network bytes) */
2540
if (qstr[4] == '0') {
2542
if (qstr[3] == '0') {
2544
if (qstr[2] == '0') {
2549
*((const char **) apr_array_push(arr)) = qstr;
2551
if (variant->mime_type && *variant->mime_type) {
2552
*((const char **) apr_array_push(arr)) = " {type ";
2553
*((const char **) apr_array_push(arr)) = variant->mime_type;
2554
*((const char **) apr_array_push(arr)) = "}";
2556
if (variant->content_charset && *variant->content_charset) {
2557
*((const char **) apr_array_push(arr)) = " {charset ";
2558
*((const char **) apr_array_push(arr)) = variant->content_charset;
2559
*((const char **) apr_array_push(arr)) = "}";
2562
*((const char **) apr_array_push(arr)) = " {language ";
2563
*((const char **) apr_array_push(arr)) = lang;
2564
*((const char **) apr_array_push(arr)) = "}";
2566
if (variant->content_encoding && *variant->content_encoding) {
2567
/* Strictly speaking, this is non-standard, but so is TCN */
2569
*((const char **) apr_array_push(arr)) = " {encoding ";
2570
*((const char **) apr_array_push(arr)) = variant->content_encoding;
2571
*((const char **) apr_array_push(arr)) = "}";
2574
/* Note that the Alternates specification (in rfc2295) does
2575
* not require that we include {length x}, so we could omit it
2576
* if determining the length is too expensive. We currently
2577
* always include it though.
2579
* If the variant is a CGI script, find_content_length would
2580
* return the length of the script, not the output it
2581
* produces, so we check for the presence of a handler and if
2582
* there is one we don't add a length.
2584
* XXX: TODO: This check does not detect a CGI script if we
2585
* get the variant from a type map. This needs to be fixed
2586
* (without breaking things if the type map specifies a
2587
* content-length, which currently leads to the correct result).
2589
if (!(variant->sub_req && variant->sub_req->handler)
2590
&& (len = find_content_length(neg, variant)) >= 0) {
2592
*((const char **) apr_array_push(arr)) = " {length ";
2593
*((const char **) apr_array_push(arr)) = apr_off_t_toa(r->pool,
2595
*((const char **) apr_array_push(arr)) = "}";
2598
*((const char **) apr_array_push(arr)) = "}";
2599
*((const char **) apr_array_push(arr)) = ", "; /* trimmed below */
2602
if (neg->send_alternates && neg->avail_vars->nelts) {
2603
arr->nelts--; /* remove last comma */
2604
apr_table_mergen(hdrs, "Alternates",
2605
apr_array_pstrcat(r->pool, arr, '\0'));
2608
if (neg->is_transparent || vary_by_type || vary_by_language ||
2609
vary_by_language || vary_by_charset || vary_by_encoding) {
2611
apr_table_mergen(hdrs, "Vary", 2 + apr_pstrcat(r->pool,
2612
neg->is_transparent ? ", negotiate" : "",
2613
vary_by_type ? ", accept" : "",
2614
vary_by_language ? ", accept-language" : "",
2615
vary_by_charset ? ", accept-charset" : "",
2616
vary_by_encoding ? ", accept-encoding" : "", NULL));
2619
if (neg->is_transparent) { /* Create TCN response header */
2620
apr_table_setn(hdrs, "TCN",
2621
alg_result == alg_list ? "list" : "choice");
2625
/**********************************************************************
2627
* Return an HTML list of variants. This is output as part of the
2628
* choice response or 406 status body.
2631
static char *make_variant_list(request_rec *r, negotiation_state *neg)
2633
apr_array_header_t *arr;
2635
int max_vlist_array = (neg->avail_vars->nelts * 15) + 2;
2637
/* In order to avoid O(n^2) memory copies in building the list,
2638
* we preallocate a apr_table_t with the maximum substrings possible,
2639
* fill it with the variant list, and then concatenate the entire array.
2641
arr = apr_array_make(r->pool, max_vlist_array, sizeof(char *));
2643
*((const char **) apr_array_push(arr)) = "Available variants:\n<ul>\n";
2645
for (i = 0; i < neg->avail_vars->nelts; ++i) {
2646
var_rec *variant = &((var_rec *) neg->avail_vars->elts)[i];
2647
const char *filename = variant->file_name ? variant->file_name : "";
2648
apr_array_header_t *languages = variant->content_languages;
2649
const char *description = variant->description
2650
? variant->description
2653
/* The format isn't very neat, and it would be nice to make
2654
* the tags human readable (eg replace 'language en' with 'English').
2655
* Note that if you change the number of substrings pushed, you also
2656
* need to change the calculation of max_vlist_array above.
2658
*((const char **) apr_array_push(arr)) = "<li><a href=\"";
2659
*((const char **) apr_array_push(arr)) = filename;
2660
*((const char **) apr_array_push(arr)) = "\">";
2661
*((const char **) apr_array_push(arr)) = filename;
2662
*((const char **) apr_array_push(arr)) = "</a> ";
2663
*((const char **) apr_array_push(arr)) = description;
2665
if (variant->mime_type && *variant->mime_type) {
2666
*((const char **) apr_array_push(arr)) = ", type ";
2667
*((const char **) apr_array_push(arr)) = variant->mime_type;
2669
if (languages && languages->nelts) {
2670
*((const char **) apr_array_push(arr)) = ", language ";
2671
*((const char **) apr_array_push(arr)) = apr_array_pstrcat(r->pool,
2674
if (variant->content_charset && *variant->content_charset) {
2675
*((const char **) apr_array_push(arr)) = ", charset ";
2676
*((const char **) apr_array_push(arr)) = variant->content_charset;
2678
if (variant->content_encoding) {
2679
*((const char **) apr_array_push(arr)) = ", encoding ";
2680
*((const char **) apr_array_push(arr)) = variant->content_encoding;
2682
*((const char **) apr_array_push(arr)) = "</li>\n";
2684
*((const char **) apr_array_push(arr)) = "</ul>\n";
2686
return apr_array_pstrcat(r->pool, arr, '\0');
2689
static void store_variant_list(request_rec *r, negotiation_state *neg)
2691
if (r->main == NULL) {
2692
apr_table_setn(r->notes, "variant-list", make_variant_list(r, neg));
2695
apr_table_setn(r->main->notes, "variant-list",
2696
make_variant_list(r->main, neg));
2700
/* Called if we got a "Choice" response from the variant selection algorithm.
2701
* It checks the result of the chosen variant to see if it
2702
* is itself negotiated (if so, return error HTTP_VARIANT_ALSO_VARIES).
2703
* Otherwise, add the appropriate headers to the current response.
2706
static int setup_choice_response(request_rec *r, negotiation_state *neg,
2709
request_rec *sub_req;
2710
const char *sub_vary;
2712
if (!variant->sub_req) {
2715
sub_req = ap_sub_req_lookup_file(variant->file_name, r, NULL);
2716
status = sub_req->status;
2718
if (status != HTTP_OK &&
2719
!apr_table_get(sub_req->err_headers_out, "TCN")) {
2720
ap_destroy_sub_req(sub_req);
2723
variant->sub_req = sub_req;
2726
sub_req = variant->sub_req;
2729
/* The variant selection algorithm told us to return a "Choice"
2730
* response. This is the normal variant response, with
2731
* some extra headers. First, ensure that the chosen
2732
* variant did or will not itself engage in transparent negotiation.
2733
* If not, set the appropriate headers, and fall through to
2734
* the normal variant handling
2737
/* This catches the error that a transparent type map selects a
2738
* transparent multiviews resource as the best variant.
2740
* XXX: We do not signal an error if a transparent type map
2741
* selects a _non_transparent multiviews resource as the best
2742
* variant, because we can generate a legal negotiation response
2743
* in this case. In this case, the vlist_validator of the
2744
* nontransparent subrequest will be lost however. This could
2745
* lead to cases in which a change in the set of variants or the
2746
* negotiation algorithm of the nontransparent resource is never
2747
* propagated up to a HTTP/1.1 cache which interprets Vary. To be
2748
* completely on the safe side we should return HTTP_VARIANT_ALSO_VARIES
2749
* for this type of recursive negotiation too.
2751
if (neg->is_transparent &&
2752
apr_table_get(sub_req->err_headers_out, "TCN")) {
2753
return HTTP_VARIANT_ALSO_VARIES;
2756
/* This catches the error that a transparent type map recursively
2757
* selects, as the best variant, another type map which itself
2758
* causes transparent negotiation to be done.
2760
* XXX: Actually, we catch this error by catching all cases of
2761
* type map recursion. There are some borderline recursive type
2762
* map arrangements which would not produce transparent
2763
* negotiation protocol errors or lack of cache propagation
2764
* problems, but such arrangements are very hard to detect at this
2765
* point in the control flow, so we do not bother to single them
2768
* Recursive type maps imply a recursive arrangement of negotiated
2769
* resources which is visible to outside clients, and this is not
2770
* supported by the transparent negotiation caching protocols, so
2771
* if we are to have generic support for recursive type maps, we
2772
* have to create some configuration setting which makes all type
2773
* maps non-transparent when recursion is enabled. Also, if we
2774
* want recursive type map support which ensures propagation of
2775
* type map changes into HTTP/1.1 caches that handle Vary, we
2776
* would have to extend the current mechanism for generating
2777
* variant list validators.
2779
if (sub_req->handler && strcmp(sub_req->handler, "type-map") == 0) {
2780
return HTTP_VARIANT_ALSO_VARIES;
2783
/* This adds an appropriate Variant-Vary header if the subrequest
2784
* is a multiviews resource.
2786
* XXX: TODO: Note that this does _not_ handle any Vary header
2787
* returned by a CGI if sub_req is a CGI script, because we don't
2788
* see that Vary header yet at this point in the control flow.
2789
* This won't cause any cache consistency problems _unless_ the
2790
* CGI script also returns a Cache-Control header marking the
2791
* response as cachable. This needs to be fixed, also there are
2792
* problems if a CGI returns an Etag header which also need to be
2795
if ((sub_vary = apr_table_get(sub_req->err_headers_out, "Vary")) != NULL) {
2796
apr_table_setn(r->err_headers_out, "Variant-Vary", sub_vary);
2798
/* Move the subreq Vary header into the main request to
2799
* prevent having two Vary headers in the response, which
2800
* would be legal but strange.
2802
apr_table_setn(r->err_headers_out, "Vary", sub_vary);
2803
apr_table_unset(sub_req->err_headers_out, "Vary");
2806
apr_table_setn(r->err_headers_out, "Content-Location",
2807
apr_pstrdup(r->pool, variant->file_name));
2809
set_neg_headers(r, neg, alg_choice); /* add Alternates and Vary */
2811
/* Still to do by caller: add Expires */
2816
/****************************************************************
2821
static int do_negotiation(request_rec *r, negotiation_state *neg,
2822
var_rec **bestp, int prefer_scripts)
2824
var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
2825
int alg_result; /* result of variant selection algorithm */
2829
/* Decide if resource is transparently negotiable */
2831
/* GET or HEAD? (HEAD has same method number as GET) */
2832
if (r->method_number == M_GET) {
2834
/* maybe this should be configurable, see also the comment
2835
* about recursive type maps in setup_choice_response()
2837
neg->is_transparent = 1;
2839
/* We can't be transparent if we are a map file in the middle
2840
* of the request URI.
2842
if (r->path_info && *r->path_info)
2843
neg->is_transparent = 0;
2845
for (j = 0; j < neg->avail_vars->nelts; ++j) {
2846
var_rec *variant = &avail_recs[j];
2848
/* We can't be transparent, because of internal
2849
* assumptions in best_match(), if there is a
2850
* non-neighboring variant. We can have a non-neighboring
2851
* variant when processing a type map.
2853
if (ap_strchr_c(variant->file_name, '/'))
2854
neg->is_transparent = 0;
2856
/* We can't be transparent, because of the behavior
2857
* of variant typemap bodies.
2859
if (variant->body) {
2860
neg->is_transparent = 0;
2865
if (neg->is_transparent) {
2866
parse_negotiate_header(r, neg);
2868
else { /* configure negotiation on non-transparent resource */
2869
neg->may_choose = 1;
2872
maybe_add_default_accepts(neg, prefer_scripts);
2874
alg_result = best_match(neg, bestp);
2876
/* alg_result is one of
2877
* alg_choice: a best variant is chosen
2878
* alg_list: no best variant is chosen
2881
if (alg_result == alg_list) {
2882
/* send a list response or HTTP_NOT_ACCEPTABLE error response */
2884
neg->send_alternates = 1; /* always include Alternates header */
2885
set_neg_headers(r, neg, alg_result);
2886
store_variant_list(r, neg);
2888
if (neg->is_transparent && neg->ua_supports_trans) {
2889
/* XXX todo: expires? cachability? */
2891
/* Some HTTP/1.0 clients are known to choke when they get
2892
* a 300 (multiple choices) response without a Location
2893
* header. However the 300 code response we are are about
2894
* to generate will only reach 1.0 clients which support
2895
* transparent negotiation, and they should be OK. The
2896
* response should never reach older 1.0 clients, even if
2897
* we have CacheNegotiatedDocs enabled, because no 1.0
2898
* proxy cache (we know of) will cache and return 300
2899
* responses (they certainly won't if they conform to the
2900
* HTTP/1.0 specification).
2902
return HTTP_MULTIPLE_CHOICES;
2906
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
2907
"no acceptable variant: %s", r->filename);
2908
return HTTP_NOT_ACCEPTABLE;
2912
/* Variant selection chose a variant */
2914
/* XXX todo: merge the two cases in the if statement below */
2915
if (neg->is_transparent) {
2917
if ((res = setup_choice_response(r, neg, *bestp)) != 0) {
2918
return res; /* return if error */
2922
set_neg_headers(r, neg, alg_result);
2925
/* Make sure caching works - Vary should handle HTTP/1.1, but for
2926
* HTTP/1.0, we can't allow caching at all.
2929
/* XXX: Note that we only set r->no_cache to 1, which causes
2930
* Expires: <now> to be added, when responding to a HTTP/1.0
2931
* client. If we return the response to a 1.1 client, we do not
2932
* add Expires <now>, because doing so would degrade 1.1 cache
2933
* performance by preventing re-use of the response without prior
2934
* revalidation. On the other hand, if the 1.1 client is a proxy
2935
* which was itself contacted by a 1.0 client, or a proxy cache
2936
* which can be contacted later by 1.0 clients, then we currently
2937
* rely on this 1.1 proxy to add the Expires: <now> when it
2938
* forwards the response.
2940
* XXX: TODO: Find out if the 1.1 spec requires proxies and
2941
* tunnels to add Expires: <now> when forwarding the response to
2942
* 1.0 clients. I (kh) recall it is rather vague on this point.
2943
* Testing actual 1.1 proxy implementations would also be nice. If
2944
* Expires: <now> is not added by proxies then we need to always
2945
* include Expires: <now> ourselves to ensure correct caching, but
2946
* this would degrade HTTP/1.1 cache efficiency unless we also add
2947
* Cache-Control: max-age=N, which we currently don't.
2949
* Roy: No, we are not going to screw over HTTP future just to
2950
* ensure that people who can't be bothered to upgrade their
2951
* clients will always receive perfect server-side negotiation.
2952
* Hell, those clients are sending bogus accept headers anyway.
2954
* Manual setting of cache-control/expires always overrides this
2955
* automated kluge, on purpose.
2958
if ((!do_cache_negotiated_docs(r->server)
2959
&& (r->proto_num < HTTP_VERSION(1,1)))
2960
&& neg->count_multiviews_variants != 1) {
2967
static int handle_map_file(request_rec *r)
2969
negotiation_state *neg;
2975
if(strcmp(r->handler,MAP_FILE_MAGIC_TYPE) && strcmp(r->handler,"type-map"))
2978
neg = parse_accept_headers(r);
2979
if ((res = read_type_map(&map, neg, r))) {
2983
res = do_negotiation(r, neg, &best, 0);
2984
if (res != 0) return res;
2988
conn_rec *c = r->connection;
2989
apr_bucket_brigade *bb;
2992
ap_allow_standard_methods(r, REPLACE_ALLOW, M_GET, M_OPTIONS,
2995
* if (r->method_number == M_OPTIONS) {
2996
* return ap_send_http_options(r);
2999
if (r->method_number != M_GET && r->method_number != M_POST) {
3000
return HTTP_METHOD_NOT_ALLOWED;
3003
/* ### These may be implemented by adding some 'extra' info
3004
* of the file offset onto the etag
3005
* ap_update_mtime(r, r->finfo.mtime);
3006
* ap_set_last_modified(r);
3009
apr_table_setn(r->headers_out, "Accept-Ranges", "bytes");
3010
ap_set_content_length(r, best->bytes);
3012
/* set MIME type and charset as negotiated */
3013
if (best->mime_type && *best->mime_type) {
3014
if (best->content_charset && *best->content_charset) {
3015
ap_set_content_type(r, apr_pstrcat(r->pool,
3018
best->content_charset,
3022
ap_set_content_type(r, apr_pstrdup(r->pool, best->mime_type));
3026
/* set Content-language(s) as negotiated */
3027
if (best->content_languages && best->content_languages->nelts) {
3028
r->content_languages = apr_array_copy(r->pool,
3029
best->content_languages);
3032
/* set Content-Encoding as negotiated */
3033
if (best->content_encoding && *best->content_encoding) {
3034
r->content_encoding = apr_pstrdup(r->pool,
3035
best->content_encoding);
3038
if ((res = ap_meets_conditions(r)) != OK) {
3042
if ((res = ap_discard_request_body(r)) != OK) {
3045
bb = apr_brigade_create(r->pool, c->bucket_alloc);
3046
e = apr_bucket_file_create(map, best->body,
3047
(apr_size_t)best->bytes, r->pool,
3049
APR_BRIGADE_INSERT_TAIL(bb, e);
3050
e = apr_bucket_eos_create(c->bucket_alloc);
3051
APR_BRIGADE_INSERT_TAIL(bb, e);
3053
return ap_pass_brigade(r->output_filters, bb);
3056
if (r->path_info && *r->path_info) {
3057
/* remove any path_info from the end of the uri before trying
3058
* to change the filename. r->path_info from the original
3059
* request is passed along on the redirect.
3061
r->uri[ap_find_path_info(r->uri, r->path_info)] = '\0';
3063
udir = ap_make_dirstr_parent(r->pool, r->uri);
3064
udir = ap_escape_uri(r->pool, udir);
3065
ap_internal_redirect(apr_pstrcat(r->pool, udir, best->file_name,
3066
r->path_info, NULL), r);
3070
static int handle_multi(request_rec *r)
3072
negotiation_state *neg;
3073
var_rec *best, *avail_recs;
3074
request_rec *sub_req;
3078
if (r->finfo.filetype != APR_NOFILE
3079
|| !(ap_allow_options(r) & OPT_MULTI)) {
3083
neg = parse_accept_headers(r);
3085
if ((res = read_types_multi(neg))) {
3087
/* free all allocated memory from subrequests */
3088
avail_recs = (var_rec *) neg->avail_vars->elts;
3089
for (j = 0; j < neg->avail_vars->nelts; ++j) {
3090
var_rec *variant = &avail_recs[j];
3091
if (variant->sub_req) {
3092
ap_destroy_sub_req(variant->sub_req);
3097
if (neg->avail_vars->nelts == 0) {
3101
res = do_negotiation(r, neg, &best,
3102
(r->method_number != M_GET) || r->args ||
3103
(r->path_info && *r->path_info));
3105
goto return_from_multi;
3107
if (!(sub_req = best->sub_req)) {
3108
/* We got this out of a map file, so we don't actually have
3109
* a sub_req structure yet. Get one now.
3112
sub_req = ap_sub_req_lookup_file(best->file_name, r, NULL);
3113
if (sub_req->status != HTTP_OK) {
3114
res = sub_req->status;
3115
ap_destroy_sub_req(sub_req);
3116
goto return_from_multi;
3120
/* now do a "fast redirect" ... promotes the sub_req into the main req */
3121
ap_internal_fast_redirect(sub_req, r);
3123
/* give no advise for time on this subrequest. Perhaps we
3124
* should tally the last mtime amoung all variants, and date
3125
* the most recent, but that could confuse the proxies.
3129
/* clean up all but our favorite variant, since that sub_req
3130
* is now merged into the main request!
3132
avail_recs = (var_rec *) neg->avail_vars->elts;
3133
for (j = 0; j < neg->avail_vars->nelts; ++j) {
3134
var_rec *variant = &avail_recs[j];
3135
if (variant != best && variant->sub_req) {
3136
ap_destroy_sub_req(variant->sub_req);
3142
/**********************************************************************
3143
* There is a problem with content-encoding, as some clients send and
3144
* expect an x- token (e.g. x-gzip) while others expect the plain token
3145
* (i.e. gzip). To try and deal with this as best as possible we do
3146
* the following: if the client sent an Accept-Encoding header and it
3147
* contains a plain token corresponding to the content encoding of the
3148
* response, then set content encoding using the plain token. Else if
3149
* the A-E header contains the x- token use the x- token in the C-E
3150
* header. Else don't do anything.
3152
* Note that if no A-E header was sent, or it does not contain a token
3153
* compatible with the final content encoding, then the token in the
3154
* C-E header will be whatever was specified in the AddEncoding
3157
static int fix_encoding(request_rec *r)
3159
const char *enc = r->content_encoding;
3161
apr_array_header_t *accept_encodings;
3162
accept_rec *accept_recs;
3165
if (!enc || !*enc) {
3169
if (enc[0] == 'x' && enc[1] == '-') {
3173
if ((accept_encodings = do_header_line(r->pool,
3174
apr_table_get(r->headers_in, "Accept-Encoding"))) == NULL) {
3178
accept_recs = (accept_rec *) accept_encodings->elts;
3180
for (i = 0; i < accept_encodings->nelts; ++i) {
3181
char *name = accept_recs[i].name;
3183
if (!strcmp(name, enc)) {
3184
r->content_encoding = name;
3188
if (name[0] == 'x' && name[1] == '-' && !strcmp(name+2, enc)) {
3194
r->content_encoding = x_enc;
3201
static void register_hooks(apr_pool_t *p)
3203
ap_hook_fixups(fix_encoding,NULL,NULL,APR_HOOK_MIDDLE);
3204
ap_hook_type_checker(handle_multi,NULL,NULL,APR_HOOK_FIRST);
3205
ap_hook_handler(handle_map_file,NULL,NULL,APR_HOOK_MIDDLE);
3208
module AP_MODULE_DECLARE_DATA negotiation_module =
3210
STANDARD20_MODULE_STUFF,
3211
create_neg_dir_config, /* dir config creator */
3212
merge_neg_dir_configs, /* dir merger --- default is to override */
3213
NULL, /* server config */
3214
NULL, /* merge server config */
3215
negotiation_cmds, /* command apr_table_t */
3216
register_hooks /* register hooks */