📄 mod_negotiation.c
字号:
if (!strcmp(accs[i].type_name, "*")) { if (!star) { star = &accs[i]; } continue; } /* Find language. We match if either the variant language * tag exactly matches, or the prefix of the tag up to the * '-' character matches the whole of the language in the * Accept-Language header. We only use this accept-language * item as the best match for the current tag if it * is longer than the previous best match */ if ((!strcmp(lang, accs[i].type_name) || (prefixlen && !strncmp(lang, accs[i].type_name, prefixlen) && (accs[i].type_name[prefixlen] == '\0'))) && ((len = strlen(accs[i].type_name)) > longest_lang_range_len)) { longest_lang_range_len = len; bestthistag = &accs[i]; } if (!bestthistag) { /* The next bit is a fiddle. Some browsers might be * configured to send more specific language ranges * than desirable. For example, an Accept-Language of * en-US should never match variants with languages en * or en-GB. But US English speakers might pick en-US * as their language choice. So this fiddle checks if * the language range has a prefix, and if so, it * matches variants which match that prefix with a * priority of 0.001. So a request for en-US would * match variants of types en and en-GB, but at much * lower priority than matches of en-US directly, or * of any other language listed on the Accept-Language * header */ if ((p = strchr(accs[i].type_name, '-'))) { int plen = p - accs[i].type_name; if (!strncmp(lang, accs[i].type_name, plen)) { fiddle_q = 0.001f; } } } } /* Finished looking at Accept-Language headers, the best * (longest) match is in bestthistag, or NULL if no match */ if (!best || (bestthistag && bestthistag->quality > best->quality)) { best = bestthistag; } } variant->lang_quality = best ? best->quality : (star ? star->quality : fiddle_q); } /* Now set the old lang_index field. Since this is old * stuff anyway, don't both with handling multiple languages * per variant, just use the first one assigned to it */ idx = 0; if (variant->content_languages && variant->content_languages->nelts) { firstlang = ((char **) variant->content_languages->elts)[0]; } else { firstlang = ""; } if (naccept == 0) { /* Client doesn't care */ idx = find_default_index(conf, firstlang); } else { /* Client has Accept-Language */ idx = find_lang_index(neg->accept_langs, firstlang); } variant->lang_index = idx; return;}/* Determining the content length --- if the map didn't tell us, * we have to do a stat() and remember for next time. * * Grump. For Apache, even the first stat here may well be * redundant (for multiviews) with a stat() done by the sub_req * machinery. At some point, that ought to be fixed. */static float find_content_length(negotiation_state *neg, var_rec *variant){ struct stat statb; if (variant->bytes == 0) { char *fullname = ap_make_full_path(neg->pool, neg->dir_name, variant->file_name); if (stat(fullname, &statb) >= 0) { /* Note, precision may be lost */ variant->bytes = (float) statb.st_size; } } return variant->bytes;}/* For a given variant, find the best matching Accept: header * and assign the Accept: header's quality value to the * accept_type_quality field of the variant, for later use in * determining the best matching variant. */static void set_accept_quality(negotiation_state *neg, var_rec *variant){ int i; accept_rec *accept_recs = (accept_rec *) neg->accepts->elts; float q = 0.0f; int q_definite = 1; /* if no Accept: header, leave quality alone (will * remain at the default value of 1) */ if (!neg->accepts || neg->accepts->nelts == 0) { return; } /* * Go through each of the ranges on the Accept: header, * looking for the 'best' match with this variant's * content-type. We use the best match's quality * value (from the Accept: header) for this variant's * accept_type_quality field. * * The best match is determined like this: * type/type is better than type/ * is better than * / * * if match is type/type, use the level mime param if available */ for (i = 0; i < neg->accepts->nelts; ++i) { accept_rec *type = &accept_recs[i]; int prev_mime_stars; prev_mime_stars = variant->mime_stars; if (!mime_match(type, variant)) { continue; /* didn't match the content type at all */ } else { /* did match - see if there were less or more stars than * in previous match */ if (prev_mime_stars == variant->mime_stars) { continue; /* more stars => not as good a match */ } } /* Check maxbytes -- not in HTTP/1.1 or Holtman */ if (type->max_bytes > 0 && (find_content_length(neg, variant) > type->max_bytes)) { continue; } /* If we are allowed to mess with the q-values, * make wildcards very low, so we have a low chance * of ending up with them if there's something better. */ if (!neg->accept_q && variant->mime_stars == 1) { q = 0.01f; } else if (!neg->accept_q && variant->mime_stars == 2) { q = 0.02f; } else { q = type->quality; } q_definite = (variant->mime_stars == 3); } variant->accept_type_quality = q; variant->definite = variant->definite && q_definite; /* if the _best_ quality we got for this variant was 0.0, * eliminate it now */}/* For a given variant, find the 'q' value of the charset given * on the Accept-Charset line. If not charsets are listed, * assume value of '1'. */static void set_charset_quality(negotiation_state *neg, var_rec *variant){ int i; accept_rec *accept_recs = (accept_rec *) neg->accept_charsets->elts; char *charset = variant->content_charset; accept_rec *star = NULL; /* if no Accept-Charset: header, leave quality alone (will * remain at the default value of 1) */ if (!neg->accept_charsets || neg->accept_charsets->nelts == 0) { return; } if (charset == NULL || !*charset) { charset = "iso-8859-1"; } /* * Go through each of the items on the Accept-Charset: header, * looking for a match with this variant's charset. If none * match, charset is unacceptable, so set quality to 0. */ for (i = 0; i < neg->accept_charsets->nelts; ++i) { accept_rec *type = &accept_recs[i]; if (!strcmp(type->type_name, charset)) { variant->charset_quality = type->quality; return; } else if (strcmp(type->type_name, "*") == 0) { star = type; } } /* No explicit match */ if (star) { variant->charset_quality = star->quality; return; } /* If this variant is in charset iso-8859-1, the default is 1.0 */ if (strcmp(charset, "iso-8859-1") == 0) { variant->charset_quality = 1.0f; } else { variant->charset_quality = 0.0f; }}/* For a given variant, find the best matching Accept: header * and assign the Accept: header's quality value to the * accept_type_quality field of the variant, for later use in * determining the best matching variant. *//* is_identity_encoding is included for back-compat, but does anyone * use 7bit, 8bin or binary in their var files?? */static int is_identity_encoding(const char *enc){ return (!enc || !enc[0] || !strcmp(enc, "7bit") || !strcmp(enc, "8bit") || !strcmp(enc, "binary"));}static void set_encoding_quality(negotiation_state *neg, var_rec *variant){ int i; accept_rec *accept_recs = (accept_rec *) neg->accept_encodings->elts; const char *enc = variant->content_encoding; if (!enc || is_identity_encoding(enc)) { return; } /* if no Accept: header, leave quality alone (will * remain at the default value of 1) */ if (neg->accept_encodings->nelts == 0) { /* If we had an empty Accept-Encoding header, assume that * no encodings are acceptable, else all encodings are ok */ variant->encoding_quality = neg->have_accept_header ? 0 : 1; return; } /* Go through each of the encodings on the Accept-Encoding: header, * looking for a match with our encoding. x- prefixes are ignored. */ if (enc[0] == 'x' && enc[1] == '-') { enc += 2; } for (i = 0; i < neg->accept_encodings->nelts; ++i) { char *name = accept_recs[i].type_name; if (name[0] == 'x' && name[1] == '-') { name += 2; } if (!strcmp(name, enc)) { variant->encoding_quality = 1; return; } } /* Encoding not found on Accept-Encoding: header, so it is * _not_ acceptable */ variant->encoding_quality = 0;}/* Possible results of the network algorithm */enum algorithm_results { na_not_applied = -1, /* net algorithm not used */ na_choice = 1, /* choose variant */ na_list /* list variants */};/* * This is a heavily-rewritten 'best_match' function. For a start, it * now returns an int, which has one of the three values: na_not_applied, * na_choice or na_list, which give the result of the network algorithm * (if it was not applied, the return value is na_not_applied). * The best variable is returned in *pbest. It also has two possible * algorithms for determining the best match: the network algorithm, * and the standard Apache algorithm. These are split out into * separate functions (is_variant_better_na() and is_variant_better()). * * Previously, best_match iterated first through the content_types * in the Accept: header, then checked each variant, and eliminated * those that didn't match the variant's type. We cannot do this because * we need full information, including language, charset, etc * quality for _every_ variant, for the Alternates: header, * and (possibly) the human-readable choice responses or 406 errors. * * After the 'best' (if any) is determined, the overall result of * the negotiation is obtained. If the network algorithm was not * in use, the result is na_not_applied. Else the result is * na_list if 'short accept header' is in use, else na_list * if _no_ best match was found, or na_choice if a best match * was found. *//* Firstly, the negotiation 'network algorithm' from Holtman. */static int is_variant_better_na(negotiation_state *neg, var_rec *variant, var_rec *best, float *p_bestq){ float bestq = *p_bestq, q; /* Note: Encoding is not negotiated in the Holtman * transparent neg draft, so we ignored it here. But * it does mean we could return encodings the UA * or proxy cannot handle. Eek. */ q = variant->accept_type_quality * variant->type_quality * variant->charset_quality * variant->lang_quality;#ifdef NEG_DEBUG fprintf(stderr, "Variant: file=%s type=%s lang=%s acceptq=%1.3f " "langq=%1.3f typeq=%1.3f q=%1.3f definite=%d\n", (variant->file_name ? variant->file_name : ""), (variant->type_name ? variant->type_name : ""), (variant->content_languages ? merge_string_array(neg->pool, variant->content_languages, ",") : ""), variant->accept_type_quality, variant->lang_quality, variant->type_quality, q, variant->definite);#endif if (q > bestq) { *p_bestq = q; return 1; } if (q == bestq) { /* If the best variant's charset is ISO-8859-1 and this variant has * the same charset quality, then we prefer this variant */ if (variant->charset_quality == best->charset_quality && (variant->content_charset != NULL && *variant->content_charset != '\0' && strcmp(variant->content_charset, "iso-8859-1") != 0) && (best->content_charset == NULL || *best->content_charset == '\0' || strcmp(best->content_charset, "iso-8859-1") == 0)) { *p_bestq = q; return 1; } } return 0;}/* Negotiation algorithm as used by previous versions of Apache * (just about). */static int is_variant_better(negotiation_state *neg, var_rec *variant, var_rec *best, float *p_bestq){ float bestq = *p_bestq, q; int levcmp; /* * For non-transparent negotiation, server can choose how * to handle the negotiation. We'll use the following in * order: content-type, language, content-type level, charset, * content length.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -