📄 mod_negotiation.c
字号:
* * For each check, we have three possible outcomes: * This variant is worse than current best: return 0 * This variant is better than the current best: * assign this variant's q to *p_bestq, and return 1 * This variant is just as desirable as the current best: * drop through to the next test. * * This code is written in this long-winded way to allow future * customisation, either by the addition of additional * checks, or to allow the order of the checks to be determined * by configuration options (e.g. we might prefer to check * language quality _before_ content type). */ /* First though, eliminate this variant if it is not * acceptable by type, charset, encoding or language. */ if (variant->encoding_quality == 0 || variant->lang_quality == 0 || variant->type_quality == 0 || variant->charset_quality == 0 || variant->accept_type_quality == 0) { return 0; /* don't consider unacceptables */ } q = variant->accept_type_quality * variant->type_quality; if (q == 0.0 || q < bestq) { return 0; } if (q > bestq || !best) { *p_bestq = q; return 1; } /* language */ if (variant->lang_quality < best->lang_quality) { return 0; } if (variant->lang_quality > best->lang_quality) { *p_bestq = q; return 1; } /* if language qualities were equal, try the LanguagePriority * stuff */ if (best->lang_index != -1 && variant->lang_index > best->lang_index) { return 0; } if (variant->lang_index != -1 && (variant->lang_index < best->lang_index || best->lang_index == -1)) { *p_bestq = q; return 1; } /* content-type level (text/html only?) */ levcmp = level_cmp(variant, best); if (levcmp == -1) { return 0; } if (levcmp == 1) { *p_bestq = q; return 1; } /* encoding -- can only be 1 or 0, and if 0 we eliminated this * variant at the start of this function. However we * prefer variants with no encoding over those with encoding */ if (best->content_encoding == NULL && variant->content_encoding) { return 0; } if (best->content_encoding && variant->content_encoding == NULL) { *p_bestq = q; return 1; } /* charset */ if (variant->charset_quality < best->charset_quality) { return 0; } /* 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; } /* content length if all else equal */ if (find_content_length(neg, variant) >= find_content_length(neg, best)) { return 0; } /* ok, to get here means every thing turned out equal, except * we have a shorter content length, so use this variant */ *p_bestq = q; return 1;}static int best_match(negotiation_state *neg, var_rec **pbest){ int j; var_rec *best = NULL; float bestq = 0.0f; enum algorithm_results algorithm_result = na_not_applied; var_rec *avail_recs = (var_rec *) neg->avail_vars->elts; set_default_lang_quality(neg); /* * Find the 'best' variant */ for (j = 0; j < neg->avail_vars->nelts; ++j) { var_rec *variant = &avail_recs[j]; /* Find all the relevant 'quality' values from the * Accept... headers, and store in the variant */ set_accept_quality(neg, variant); set_language_quality(neg, variant); set_encoding_quality(neg, variant); set_charset_quality(neg, variant); /* Now find out if this variant is better than the current * best, either using the network algorithm, or Apache's * internal server-driven algorithm. Presumably other * server-driven algorithms are possible, and could be * implemented here. */ if (neg->use_transparent_neg) { if (is_variant_better_na(neg, variant, best, &bestq)) { best = variant; } } else { if (is_variant_better(neg, variant, best, &bestq)) { best = variant; } } } /* We now either have a best variant, or no best variant */ if (neg->use_transparent_neg) { if (neg->short_accept_headers) { algorithm_result = na_list; } else { /* From Holtman, result is: * If variant & URI are not neigbors, list_ua or list_os * Else * If UA can do trans neg * IF best is definite && best q > 0, choice_ua * ELSE list_ua * ELSE * IF best q > 0, choose_os * ELSE list_os (or forward_os on proxy) */ /* assume variant and URI are neigbors (since URI in * var map must be in same directory) */ if (neg->use_transparent_neg) { algorithm_result = (best && best->definite) && (bestq > 0) ? na_choice : na_list; } else { algorithm_result = bestq > 0 ? na_choice : na_list; } } } *pbest = best; return algorithm_result;}/* * Sets the Alternates and Vary headers, used if we are going to * return 406 Not Acceptable status, a 300 Multiple Choice status, * or a Choice response. * * 'type' is the result of the network algorithm, if applied. * We do different things if the network algorithm was not applied * (type == na_not_applied): no Alternates header, and Vary: * does not include 'negotiate'. * * We should also add a max-age lifetime for the Alternates header, * but how long we we give it? Presumably this should be * configurable in the map file. */static void set_neg_headers(request_rec *r, negotiation_state *neg, int na_result){ int j; var_rec *avail_recs = (var_rec *) neg->avail_vars->elts; char *sample_type = NULL; char *sample_language = NULL; const char *sample_encoding = NULL; char *sample_charset = NULL; int vary_by_type = 0; int vary_by_language = 0; int vary_by_charset = 0; int vary_by_encoding = 0; table *hdrs; /* Put headers into err_headers_out, new send_http_header() * outputs both headers_out and err_headers_out */ hdrs = r->err_headers_out; for (j = 0; j < neg->avail_vars->nelts; ++j) { var_rec *variant = &avail_recs[j]; char *rec; char qstr[6]; long len; char lenstr[22]; /* enough for 2^64 */ ap_snprintf(qstr, sizeof(qstr), "%1.3f", variant->type_quality); /* Strip trailing zeros (saves those valuable network bytes) */ if (qstr[4] == '0') { qstr[4] = '\0'; if (qstr[3] == '0') { qstr[3] = '\0'; if (qstr[2] == '0') { qstr[1] = '\0'; } } } rec = ap_pstrcat(r->pool, "{\"", variant->file_name, "\" ", qstr, NULL); if (variant->type_name) { if (*variant->type_name) { rec = ap_pstrcat(r->pool, rec, " {type ", variant->type_name, "}", NULL); } if (!sample_type) { sample_type = variant->type_name; } else if (strcmp(sample_type, variant->type_name)) { vary_by_type = 1; } } if (variant->content_languages && variant->content_languages->nelts) { char *langs = merge_string_array(r->pool, variant->content_languages, ","); rec = ap_pstrcat(r->pool, rec, " {language ", langs, "}", NULL); if (!sample_language) { sample_language = langs; } else if (strcmp(sample_language, langs)) { vary_by_language = 1; } } if (variant->content_encoding) { if (!sample_encoding) { sample_encoding = variant->content_encoding; } else if (strcmp(sample_encoding, variant->content_encoding)) { vary_by_encoding = 1; } } if (variant->content_charset) { if (*variant->content_charset) { rec = ap_pstrcat(r->pool, rec, " {charset ", variant->content_charset, "}", NULL); } if (!sample_charset) { sample_charset = variant->content_charset; } else if (strcmp(sample_charset, variant->content_charset)) { vary_by_charset = 1; } } if ((len = find_content_length(neg, variant)) != 0) { ap_snprintf(lenstr, sizeof(lenstr), "%ld", len); rec = ap_pstrcat(r->pool, rec, " {length ", lenstr, "}", NULL); } rec = ap_pstrcat(r->pool, rec, "}", NULL); if (na_result != na_not_applied) { ap_table_mergen(hdrs, "Alternates", rec); } } if (na_result != na_not_applied) { ap_table_mergen(hdrs, "Vary", "negotiate"); } if (vary_by_type) { ap_table_mergen(hdrs, "Vary", "accept"); } if (vary_by_language) { ap_table_mergen(hdrs, "Vary", "accept-language"); } if (vary_by_charset) { ap_table_mergen(hdrs, "Vary", "accept-charset"); } if (vary_by_encoding && na_result == na_not_applied) { ap_table_mergen(hdrs, "Vary", "accept-encoding"); }}/********************************************************************** * * Return an HTML list of variants. This is output as part of the * 300 or 406 status body. *//* XXX: this is disgusting, this has O(n^2) behaviour! -djg */static char *make_variant_list(request_rec *r, negotiation_state *neg){ int i; char *t; t = ap_pstrdup(r->pool, "Available variants:\n<ul>\n"); for (i = 0; i < neg->avail_vars->nelts; ++i) { var_rec *variant = &((var_rec *) neg->avail_vars->elts)[i]; char *filename = variant->file_name ? variant->file_name : ""; array_header *languages = variant->content_languages; char *description = variant->description ? variant->description : ""; /* The format isn't very neat, and it would be nice to make * the tags human readable (eg replace 'language en' with * 'English'). */ t = ap_pstrcat(r->pool, t, "<li><a href=\"", filename, "\">", filename, "</a> ", description, NULL); if (variant->type_name && *variant->type_name) { t = ap_pstrcat(r->pool, t, ", type ", variant->type_name, NULL); } if (languages && languages->nelts) { t = ap_pstrcat(r->pool, t, ", language ", merge_string_array(r->pool, languages, ", "), NULL); } if (variant->content_charset && *variant->content_charset) { t = ap_pstrcat(r->pool, t, ", charset ", variant->content_charset, NULL); } t = ap_pstrcat(r->pool, t, "\n", NULL); } t = ap_pstrcat(r->pool, t, "</ul>\n", NULL); return t;}static void store_variant_list(request_rec *r, negotiation_state *neg){ if (r->main == NULL) { ap_table_setn(r->notes, "variant-list", make_variant_list(r, neg)); } else { ap_table_setn(r->main->notes, "variant-list", make_variant_list(r->main, neg)); }}/* Called if we got a "Choice" response from the network algorithm. * It checks the result of the chosen variant to see if it * is itself negotiated (if so, return error VARIANT_ALSO_VARIES). * Otherwise, add the appropriate headers to the current response. */static int setup_choice_response(request_rec *r, negotiation_state *neg, var_rec *variant){ request_rec *sub_req; const char *sub_vary; if (!variant->sub_req) { int status; sub_req = ap_sub_req_lookup_file(variant->file_name, r); status = sub_req->status; if (status != HTTP_OK && status != HTTP_MULTIPLE_CHOICES) { ap_destroy_sub_req(sub_req); return status; } variant->sub_req = sub_req; } else { sub_req = variant->sub_req; } /* The network algorithm told us to return a "Choice" * response. This is the normal v
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -