📄 mod_negotiation.c
字号:
++filp; prefix_len = strlen(filp); dirp = ap_popendir(neg->pool, neg->dir_name); if (dirp == NULL) { ap_log_rerror(APLOG_MARK, APLOG_ERR, r, "cannot read directory for multi: %s", neg->dir_name); return HTTP_FORBIDDEN; } while ((dir_entry = readdir(dirp))) { request_rec *sub_req; /* Do we have a match? */ if (strncmp(dir_entry->d_name, filp, prefix_len)) { continue; } if (dir_entry->d_name[prefix_len] != '.') { continue; } /* Yep. See if it's something which we have access to, and * which has a known type and encoding (as opposed to something * which we'll be slapping default_type on later). */ sub_req = ap_sub_req_lookup_file(dir_entry->d_name, r); /* If it has a handler, we'll pretend it's a CGI script, * since that's a good indication of the sort of thing it * might be doing. */ if (sub_req->handler && !sub_req->content_type) { sub_req->content_type = CGI_MAGIC_TYPE; } if (sub_req->status != HTTP_OK || !sub_req->content_type) { ap_destroy_sub_req(sub_req); continue; } /* If it's a map file, we use that instead of the map * we're building... */ if (((sub_req->content_type) && !strcmp(sub_req->content_type, MAP_FILE_MAGIC_TYPE)) || ((sub_req->handler) && !strcmp(sub_req->handler, "type-map"))) { ap_pclosedir(neg->pool, dirp); neg->avail_vars->nelts = 0; if (sub_req->status != HTTP_OK) { return sub_req->status; } return read_type_map(neg, sub_req); } /* Have reasonable variant --- gather notes. */ mime_info.sub_req = sub_req; mime_info.file_name = ap_pstrdup(neg->pool, dir_entry->d_name); if (sub_req->content_encoding) { mime_info.content_encoding = sub_req->content_encoding; } if (sub_req->content_languages) { mime_info.content_languages = sub_req->content_languages; } get_entry(neg->pool, &accept_info, sub_req->content_type); set_mime_fields(&mime_info, &accept_info); new_var = ap_push_array(neg->avail_vars); memcpy(new_var, (void *) &mime_info, sizeof(var_rec)); neg->count_multiviews_variants++; clean_var_rec(&mime_info); } ap_pclosedir(neg->pool, dirp); return OK;}/***************************************************************** * And now for the code you've been waiting for... actually * finding a match to the client's requirements. *//* Matching MIME types ... the star/star and foo/star commenting conventions * are implemented here. (You know what I mean by star/star, but just * try mentioning those three characters in a C comment). Using strcmp() * is legit, because everything has already been smashed to lowercase. * * Note also that if we get an exact match on the media type, we update * level_matched for use in level_cmp below... * * We also give a value for mime_stars, which is used later. It should * be 1 for star/star, 2 for type/star and 3 for type/subtype. */static int mime_match(accept_rec *accept_r, var_rec *avail){ char *accept_type = accept_r->type_name; char *avail_type = avail->type_name; int len = strlen(accept_type); if (accept_type[0] == '*') { /* Anything matches star/star */ if (avail->mime_stars < 1) { avail->mime_stars = 1; } return 1; } else if ((accept_type[len - 1] == '*') && !strncmp(accept_type, avail_type, len - 2)) { if (avail->mime_stars < 2) { avail->mime_stars = 2; } return 1; } else if (!strcmp(accept_type, avail_type) || (!strcmp(accept_type, "text/html") && (!strcmp(avail_type, INCLUDES_MAGIC_TYPE) || !strcmp(avail_type, INCLUDES_MAGIC_TYPE3)))) { if (accept_r->level >= avail->level) { avail->level_matched = avail->level; avail->mime_stars = 3; return 1; } } return OK;}/* This code implements a piece of the tie-breaking algorithm between * variants of equal quality. This piece is the treatment of variants * of the same base media type, but different levels. What we want to * return is the variant at the highest level that the client explicitly * claimed to accept. * * If all the variants available are at a higher level than that, or if * the client didn't say anything specific about this media type at all * and these variants just got in on a wildcard, we prefer the lowest * level, on grounds that that's the one that the client is least likely * to choke on. * * (This is all motivated by treatment of levels in HTML --- we only * want to give level 3 to browsers that explicitly ask for it; browsers * that don't, including HTTP/0.9 browsers that only get the implicit * "Accept: * / *" [space added to avoid confusing cpp --- no, that * syntax doesn't really work] should get HTML2 if available). * * (Note that this code only comes into play when we are choosing among * variants of equal quality, where the draft standard gives us a fair * bit of leeway about what to do. It ain't specified by the standard; * rather, it is a choice made by this server about what to do in cases * where the standard does not specify a unique course of action). */static int level_cmp(var_rec *var1, var_rec *var2){ /* Levels are only comparable between matching media types */ if (var1->is_pseudo_html && !var2->is_pseudo_html) { return 0; } if (!var1->is_pseudo_html && strcmp(var1->type_name, var2->type_name)) { return 0; } /* Take highest level that matched, if either did match. */ if (var1->level_matched > var2->level_matched) { return 1; } if (var1->level_matched < var2->level_matched) { return -1; } /* Neither matched. Take lowest level, if there's a difference. */ if (var1->level < var2->level) { return 1; } if (var1->level > var2->level) { return -1; } /* Tied */ return 0;}/* Finding languages. The main entry point is set_language_quality() * which is called for each variant. It sets two elements in the * variant record: * language_quality - the 'q' value of the 'best' matching language * from Accept-Language: header (HTTP/1.1) * lang_index - Pre HTTP/1.1 language priority, using * position of language on the Accept-Language: * header, if present, else LanguagePriority * directive order. * * When we do the variant checking for best variant, we use language * quality first, and if a tie, language_index next (this only * applies when _not_ using the network algorithm). If using * network algorithm, lang_index is never used. * * set_language_quality() calls find_lang_index() and find_default_index() * to set lang_index. */static int find_lang_index(array_header *accept_langs, char *lang){ accept_rec *accs; int i; if (!lang) { return -1; } accs = (accept_rec *) accept_langs->elts; for (i = 0; i < accept_langs->nelts; ++i) { if (!strncmp(lang, accs[i].type_name, strlen(accs[i].type_name))) { return i; } } return -1;}/* This function returns the priority of a given language * according to LanguagePriority. It is used in case of a tie * between several languages. */static int find_default_index(neg_dir_config *conf, char *lang){ array_header *arr; int nelts; char **elts; int i; if (!lang) { return -1; } arr = conf->language_priority; nelts = arr->nelts; elts = (char **) arr->elts; for (i = 0; i < nelts; ++i) { if (!strcasecmp(elts[i], lang)) { return i; } } return -1;}/* set_default_lang_quality() sets the quality we apply to variants * which have no language assigned to them. If none of the variants * have a language, we are not negotiating on language, so all are * acceptable, and we set the default q value to 1.0. However if * some of the variants have languages, we set this default to 0.001. * The value of this default will be applied to all variants with * no explicit language -- which will have the effect of making them * acceptable, but only if no variants with an explicit language * are acceptable. The default q value set here is assigned to variants * with no language type in set_language_quality(). * * Note that if using the transparent negotiation network algorythm, * we don't use this fiddle. */static void set_default_lang_quality(negotiation_state *neg){ var_rec *avail_recs = (var_rec *) neg->avail_vars->elts; int j; if (!neg->use_transparent_neg) { for (j = 0; j < neg->avail_vars->nelts; ++j) { var_rec *variant = &avail_recs[j]; if (variant->content_languages && variant->content_languages->nelts) { neg->default_lang_quality = 0.001f; return; } } } neg->default_lang_quality = 1.0f;}/* Set the language_quality value in the variant record. Also * assigns lang_index for back-compat. * * To find the language_quality value, we look for the 'q' value * of the 'best' matching language on the Accept-Language: * header. The'best' match is the language on Accept-Language: * header which matches the language of this variant either fully, * or as far as the prefix marker (-). If two or more languages * match, use the longest string from the Accept-Language: header * (see HTTP/1.1 [14.4]) * * When a variant has multiple languages, we find the 'best' * match for each variant language tag as above, then select the * one with the highest q value. Because both the accept-header * and variant can have multiple languages, we now have a hairy * loop-within-a-loop here. * * If the variant has no language and we have no Accept-Language * items, leave the quality at 1.0 and return. * * If the variant has no language, we use the default as set by * set_default_lang_quality() (1.0 if we are not negotiating on * language, 0.001 if we are). * * Following the setting of the language quality, we drop through to * set the old 'lang_index'. This is set based on either the order * of the languages on the Accept-Language header, or the * order on the LanguagePriority directive. This is only used * in the negotiation if the language qualities tie. */static void set_language_quality(negotiation_state *neg, var_rec *variant){ int i; int naccept = neg->accept_langs->nelts; int idx; neg_dir_config *conf = NULL; char *firstlang; if (naccept == 0) { conf = (neg_dir_config *) ap_get_module_config(neg->r->per_dir_config, &negotiation_module); } if (naccept == 0 && (!variant->content_languages || !variant->content_languages->nelts)) { return; /* no accept-language and no variant lang */ } if (!variant->content_languages || !variant->content_languages->nelts) { /* This variant has no content-language, so use the default * quality factor for variants with no content-language * (previously set by set_default_lang_quality()). */ variant->lang_quality = neg->default_lang_quality; if (naccept == 0) { return; /* no accept-language items */ } } else if (naccept) { /* Variant has one (or more) languages, and we have one (or more) * language ranges on the Accept-Language header. Look for * the best match. We do this by going through each language * on the variant description looking for a match on the * Accept-Language header. The best match is the longest matching * language on the header. The final result is the best q value * from all the languages on the variant description. */ int j; float fiddle_q = 0.0f; accept_rec *accs = (accept_rec *) neg->accept_langs->elts; accept_rec *best = NULL, *star = NULL; char *p; for (j = 0; j < variant->content_languages->nelts; ++j) { char *lang; /* language from variant description */ accept_rec *bestthistag = NULL; int prefixlen = 0; int longest_lang_range_len = 0; int len; /* lang is the variant's language-tag, which is the one * we are allowed to use the prefix of in HTTP/1.1 */ lang = ((char **) (variant->content_languages->elts))[j]; p = strchr(lang, '-'); /* find prefix part (if any) */ if (p) { prefixlen = p - lang; } /* now find the best (i.e. longest) matching Accept-Language * header language. We put the best match for this tag in * bestthistag. We cannot update the overall best (based on * q value) because the best match for this tag is the longest * language item on the accept header, not necessarily the * highest q. */ for (i = 0; i < neg->accept_langs->nelts; ++i) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -