From 615105e7e88843c43f052a1e2866eb31ccc67cb3 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Sat, 28 Mar 2015 16:40:46 +0100 Subject: [PATCH] MEDIUM: compression: add a distinction between UA- and config- algorithms Thanks to MSIE/IIS, the "deflate" name is ambigous. According to the RFC it's a zlib-wrapped deflate stream, but IIS used to send only a raw deflate stream, which is the only format MSIE understands for "deflate". The other widely used browsers do support both formats. For this reason some people prefer to emit a raw deflate stream on "deflate" to serve more users even it that means violating the standards. Haproxy only follows the standard, so they cannot do this. This patch makes it possible to have one algorithm name in the configuration and another one in the protocol. This will make it possible to have a new configuration token to add a different algorithm so that users can decide if they want a raw deflate or the standard one. --- include/types/compression.h | 14 ++++++++++++-- src/compression.c | 16 ++++++++-------- src/haproxy.c | 4 ++-- src/proto_http.c | 10 +++++----- 4 files changed, 27 insertions(+), 17 deletions(-) diff --git a/include/types/compression.h b/include/types/compression.h index e4b1f273d..ae1e87df8 100644 --- a/include/types/compression.h +++ b/include/types/compression.h @@ -47,9 +47,19 @@ struct comp_ctx { int cur_lvl; }; +/* Thanks to MSIE/IIS, the "deflate" name is ambigous, as according to the RFC + * it's a zlib-wrapped deflate stream, but MSIE only understands a raw deflate + * stream. For this reason some people prefer to emit a raw deflate stream on + * "deflate" and we'll need two algos for the same name, they are distinguished + * with the config name. + */ struct comp_algo { - char *name; - int name_len; + char *cfg_name; /* config name */ + int cfg_name_len; + + char *ua_name; /* name for the user-agent */ + int ua_name_len; + int (*init)(struct comp_ctx **comp_ctx, int level); int (*add_data)(struct comp_ctx *comp_ctx, const char *in_data, int in_len, struct buffer *out); int (*flush)(struct comp_ctx *comp_ctx, struct buffer *out, int flag); diff --git a/src/compression.c b/src/compression.c index eadeb9d5f..8ddee57bd 100644 --- a/src/compression.c +++ b/src/compression.c @@ -73,12 +73,12 @@ static int deflate_end(struct comp_ctx **comp_ctx); const struct comp_algo comp_algos[] = { - { "identity", 8, identity_init, identity_add_data, identity_flush, identity_reset, identity_end }, + { "identity", 8, "identity", 8, identity_init, identity_add_data, identity_flush, identity_reset, identity_end }, #ifdef USE_ZLIB - { "deflate", 7, deflate_init, deflate_add_data, deflate_flush, deflate_reset, deflate_end }, - { "gzip", 4, gzip_init, deflate_add_data, deflate_flush, deflate_reset, deflate_end }, + { "deflate", 7, "deflate", 7, deflate_init, deflate_add_data, deflate_flush, deflate_reset, deflate_end }, + { "gzip", 4, "gzip", 4, gzip_init, deflate_add_data, deflate_flush, deflate_reset, deflate_end }, #endif /* USE_ZLIB */ - { NULL, 0, NULL , NULL, NULL, NULL, NULL } + { NULL, 0, NULL, 0, NULL , NULL, NULL, NULL, NULL } }; /* @@ -104,8 +104,8 @@ int comp_append_algo(struct comp *comp, const char *algo) struct comp_algo *comp_algo; int i; - for (i = 0; comp_algos[i].name; i++) { - if (!strcmp(algo, comp_algos[i].name)) { + for (i = 0; comp_algos[i].cfg_name; i++) { + if (!strcmp(algo, comp_algos[i].cfg_name)) { comp_algo = calloc(1, sizeof(struct comp_algo)); memmove(comp_algo, &comp_algos[i], sizeof(struct comp_algo)); comp_algo->next = comp->algos; @@ -669,8 +669,8 @@ smp_fetch_res_comp_algo(struct proxy *px, struct session *l4, void *l7, unsigned smp->type = SMP_T_STR; smp->flags = SMP_F_CONST; - smp->data.str.str = l4->comp_algo->name; - smp->data.str.len = l4->comp_algo->name_len; + smp->data.str.str = l4->comp_algo->cfg_name; + smp->data.str.len = l4->comp_algo->cfg_name_len; return 1; } diff --git a/src/haproxy.c b/src/haproxy.c index 07d67bebf..8063f37b9 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -262,8 +262,8 @@ void display_build_opts() { int i; - for (i = 0; comp_algos[i].name; i++) { - printf("%s %s", (i == 0 ? "" : ","), comp_algos[i].name); + for (i = 0; comp_algos[i].cfg_name; i++) { + printf("%s %s(\"%s\")", (i == 0 ? "" : ","), comp_algos[i].cfg_name, comp_algos[i].ua_name); } if (i == 0) { printf("none"); diff --git a/src/proto_http.c b/src/proto_http.c index 56e4d3f66..24de4abf3 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -2309,7 +2309,7 @@ int select_compression_request_header(struct session *s, struct buffer *req) for (comp_algo = comp_algo_back; comp_algo; comp_algo = comp_algo->next) { if (*(ctx.line + ctx.val) == '*' || - word_match(ctx.line + ctx.val, toklen, comp_algo->name, comp_algo->name_len)) { + word_match(ctx.line + ctx.val, toklen, comp_algo->ua_name, comp_algo->ua_name_len)) { s->comp_algo = comp_algo; best_q = q; break; @@ -2333,7 +2333,7 @@ int select_compression_request_header(struct session *s, struct buffer *req) /* 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) { - if (comp_algo->name_len == 8 && memcmp(comp_algo->name, "identity", 8) == 0) { + if (comp_algo->cfg_name_len == 8 && memcmp(comp_algo->cfg_name, "identity", 8) == 0) { s->comp_algo = comp_algo; return 1; } @@ -2445,11 +2445,11 @@ int select_compression_response_header(struct session *s, struct buffer *res) * Accept-Encoding header, and SHOULD NOT be used in the Content-Encoding * header. */ - if (s->comp_algo->name_len != 8 || memcmp(s->comp_algo->name, "identity", 8) != 0) { + if (s->comp_algo->cfg_name_len != 8 || memcmp(s->comp_algo->cfg_name, "identity", 8) != 0) { trash.len = 18; memcpy(trash.str, "Content-Encoding: ", trash.len); - memcpy(trash.str + trash.len, s->comp_algo->name, s->comp_algo->name_len); - trash.len += s->comp_algo->name_len; + memcpy(trash.str + trash.len, s->comp_algo->ua_name, s->comp_algo->ua_name_len); + trash.len += s->comp_algo->ua_name_len; trash.str[trash.len] = '\0'; http_header_add_tail2(&txn->rsp, &txn->hdr_idx, trash.str, trash.len); }