diff --git a/src/proto_http.c b/src/proto_http.c index 89e75b717..0bca45cfe 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -2144,6 +2144,38 @@ static inline int http_skip_chunk_crlf(struct http_msg *msg) return 1; } +/* Parses a qvalue and returns it multipled by 1000, from 0 to 1000. If the + * value is larger than 1000, it is bound to 1000. The parser consumes up to + * 1 digit, one dot and 3 digits and stops on the first invalid character. + * Unparsable qvalues return 1000 as "q=1.000". + */ +int parse_qvalue(const char *qvalue) +{ + int q = 1000; + + if (!isdigit(*qvalue)) + goto out; + q = (*qvalue++ - '0') * 1000; + + if (*qvalue++ != '.') + goto out; + + if (!isdigit(*qvalue)) + goto out; + q += (*qvalue++ - '0') * 100; + + if (!isdigit(*qvalue)) + goto out; + q += (*qvalue++ - '0') * 10; + + if (!isdigit(*qvalue)) + goto out; + q += (*qvalue++ - '0') * 1; + out: + if (q > 1000) + q = 1000; + return q; +} /* * Selects a compression algorithm depending on the client request. @@ -2175,28 +2207,73 @@ int select_compression_request_header(struct session *s, struct buffer *req) /* search for the algo in the backend in priority or the frontend */ if ((s->be->comp && (comp_algo_back = s->be->comp->algos)) || (s->fe->comp && (comp_algo_back = s->fe->comp->algos))) { + int best_q = 0; + ctx.idx = 0; while (http_find_header2("Accept-Encoding", 15, req->p, &txn->hdr_idx, &ctx)) { + const char *qval; + int q; + int toklen; + + /* try to isolate the token from the optional q-value */ + toklen = 0; + while (toklen < ctx.vlen && http_is_token[(unsigned char)*(ctx.line + ctx.val + toklen)]) + toklen++; + + qval = ctx.line + ctx.val + toklen; + while (1) { + while (qval < ctx.line + ctx.val + ctx.vlen && http_is_lws[(unsigned char)*qval]) + qval++; + + if (qval >= ctx.line + ctx.val + ctx.vlen || *qval != ';') { + qval = NULL; + break; + } + qval++; + + while (qval < ctx.line + ctx.val + ctx.vlen && http_is_lws[(unsigned char)*qval]) + qval++; + + if (qval >= ctx.line + ctx.val + ctx.vlen) { + qval = NULL; + break; + } + if (strncmp(qval, "q=", MIN(ctx.line + ctx.val + ctx.vlen - qval, 2)) == 0) + break; + + while (qval < ctx.line + ctx.val + ctx.vlen && *qval != ';') + qval++; + } + + /* here we have qval pointing to the first "q=" attribute or NULL if not found */ + q = qval ? parse_qvalue(qval + 2) : 1000; + + if (q <= best_q) + continue; + for (comp_algo = comp_algo_back; comp_algo; comp_algo = comp_algo->next) { - if (word_match(ctx.line + ctx.val, ctx.vlen, comp_algo->name, comp_algo->name_len)) { + if (*(ctx.line + ctx.val) == '*' || + word_match(ctx.line + ctx.val, toklen, comp_algo->name, comp_algo->name_len)) { s->comp_algo = comp_algo; - - /* remove all occurrences of the header when "compression offload" is set */ - - if ((s->be->comp && s->be->comp->offload) || - (s->fe->comp && s->fe->comp->offload)) { - http_remove_header2(msg, &txn->hdr_idx, &ctx); - ctx.idx = 0; - while (http_find_header2("Accept-Encoding", 15, req->p, &txn->hdr_idx, &ctx)) { - http_remove_header2(msg, &txn->hdr_idx, &ctx); - } - } - return 1; + best_q = q; + break; } } } } + /* remove all occurrences of the header when "compression offload" is set */ + if (s->comp_algo) { + if ((s->be->comp && s->be->comp->offload) || (s->fe->comp && s->fe->comp->offload)) { + http_remove_header2(msg, &txn->hdr_idx, &ctx); + ctx.idx = 0; + while (http_find_header2("Accept-Encoding", 15, req->p, &txn->hdr_idx, &ctx)) { + http_remove_header2(msg, &txn->hdr_idx, &ctx); + } + } + return 1; + } + /* identity is implicit does not require headers */ if ((s->be->comp && (comp_algo_back = s->be->comp->algos)) || (s->fe->comp && (comp_algo_back = s->fe->comp->algos))) { for (comp_algo = comp_algo_back; comp_algo; comp_algo = comp_algo->next) {