Bug in supporting Quality zero (negate) parameter in Accept-Encoding
Igor Sysoev
igor at sysoev.ru
Sun Jul 31 17:40:19 UTC 2011
On Fri, Jul 29, 2011 at 03:11:28PM -0400, rburkat wrote:
> > What really surprises me WHY they send this header at all ?
>
> You would assume that a CDN would clean/normalize the requests to the
> backend, and not use the Q qualifiers at all. But maybe in some cases
> they just pass the accept-encoding header which they receive from the
> client.
>
> In this case I tracked it down as a large corporation behind a Microsoft
> ISA firewall, which could not access our css/js. They were accessing
> the cdn with the origin being our nginx servers. I can't comment on the
> choices that microsoft firewalls make in their request headers ( or the
> companies that use them, or how they configure them ), but it appears
> that they do follow the RFC in this case.
>
> In order to guarantee correct delivery via nginx, is the only workaround
> right now to disable compression on all our css/js content? Or can you
> think of something else?
>
> Thanks Igor.
>
> Let me know if there is anything I can do to help.
The attached patch accounts quantity in Accept-Encondig.
--
Igor Sysoev
-------------- next part --------------
Index: src/http/ngx_http_core_module.c
===================================================================
--- src/http/ngx_http_core_module.c (revision 3978)
+++ src/http/ngx_http_core_module.c (working copy)
@@ -70,6 +70,7 @@
static char *ngx_http_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
#if (NGX_HTTP_GZIP)
+static ngx_int_t ngx_http_gzip_accept_encoding(ngx_str_t *ae);
static char *ngx_http_gzip_disable(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
#endif
@@ -2016,24 +2017,35 @@
time_t date, expires;
ngx_uint_t p;
ngx_array_t *cc;
- ngx_table_elt_t *e, *d;
+ ngx_table_elt_t *e, *d, *ae;
ngx_http_core_loc_conf_t *clcf;
r->gzip_tested = 1;
- if (r != r->main
- || r->headers_in.accept_encoding == NULL
- || ngx_strcasestrn(r->headers_in.accept_encoding->value.data,
- "gzip", 4 - 1)
- == NULL
+ if (r != r->main) {
+ return NGX_DECLINED;
+ }
- /*
- * if the URL (without the "http://" prefix) is longer than 253 bytes,
- * then MSIE 4.x can not handle the compressed stream - it waits
- * too long, hangs up or crashes
- */
+ ae = r->headers_in.accept_encoding;
+ if (ae == NULL) {
+ return NGX_DECLINED;
+ }
- || (r->headers_in.msie4 && r->unparsed_uri.len > 200))
+ if (ae->value.len < 5) {
+ return NGX_DECLINED;
+ }
+
+ /*
+ * test first for the most common case "gzip,...":
+ * MSIE: "gzip, deflate"
+ * Firefox: "gzip,deflate"
+ * Chrome: "gzip,deflate,sdch"
+ * Safari: "gzip, deflate"
+ * Opera: "gzip, deflate"
+ */
+
+ if (ngx_memcmp(ae->value.data, "gzip,", 5) != 0
+ && ngx_http_gzip_accept_encoding(&ae->value) != NGX_OK)
{
return NGX_DECLINED;
}
@@ -2159,6 +2171,124 @@
return NGX_OK;
}
+
+static ngx_int_t
+ngx_http_gzip_accept_encoding(ngx_str_t *ae)
+{
+ u_char c, *p, *start, *last;
+ ngx_uint_t n, q;
+
+ start = ae->data;
+ last = start + ae->len;
+
+ for ( ;; ) {
+ p = ngx_strcasestrn(start, "gzip", 4 - 1);
+ if (p == NULL) {
+ return NGX_DECLINED;
+ }
+
+ if (p > start && (*(p - 1) == ',' || *(p - 1) == ' ')) {
+ break;
+ }
+
+ start = p + 4;
+ }
+
+ p += 4;
+
+ while (p < last) {
+ switch(*p++) {
+ case ',':
+ return NGX_OK;
+ case ';':
+ goto quantity;
+ case ' ':
+ continue;
+ default:
+ return NGX_DECLINED;
+ }
+ }
+
+ return NGX_OK;
+
+quantity:
+
+ while (p < last) {
+ switch(*p++) {
+ case 'q':
+ case 'Q':
+ goto equal;
+ case ' ':
+ continue;
+ default:
+ return NGX_DECLINED;
+ }
+ }
+
+ return NGX_OK;
+
+equal:
+
+ if (p + 2 > last || *p++ != '=') {
+ return NGX_DECLINED;
+ }
+
+ c = *p++;
+
+ if (c == '1') {
+ return NGX_OK;
+ }
+
+ if (c != '0') {
+ return NGX_DECLINED;
+ }
+
+ while (p < last) {
+ switch(*p++) {
+ case ',':
+ return NGX_DECLINED;
+ case '.':
+ goto zero;
+ default:
+ return NGX_DECLINED;
+ }
+ }
+
+ return NGX_DECLINED;
+
+zero:
+
+ n = 0;
+ q = 0;
+
+ while (p < last) {
+ c = *p++;
+
+ if (c == ',') {
+ break;
+ }
+
+ if (c >= '1' && c <= '9') {
+ n++;
+ q++;
+ continue;
+ }
+
+ if (c == '0') {
+ n++;
+ continue;
+ }
+
+ return NGX_DECLINED;
+ }
+
+ if (n < 4 && q != 0) {
+ return NGX_OK;
+ }
+
+ return NGX_DECLINED;
+}
+
#endif
Index: src/http/ngx_http_request.c
===================================================================
--- src/http/ngx_http_request.c (revision 3978)
+++ src/http/ngx_http_request.c (working copy)
@@ -1439,8 +1439,6 @@
switch (msie[5]) {
case '4':
- r->headers_in.msie4 = 1;
- /* fall through */
case '5':
r->headers_in.msie6 = 1;
break;
@@ -1463,7 +1461,6 @@
if (ngx_strstrn(user_agent, "Opera", 5 - 1)) {
r->headers_in.opera = 1;
r->headers_in.msie = 0;
- r->headers_in.msie4 = 0;
r->headers_in.msie6 = 0;
}
Index: src/http/ngx_http_request.h
===================================================================
--- src/http/ngx_http_request.h (revision 3978)
+++ src/http/ngx_http_request.h (working copy)
@@ -221,7 +221,6 @@
unsigned connection_type:2;
unsigned msie:1;
- unsigned msie4:1;
unsigned msie6:1;
unsigned opera:1;
unsigned gecko:1;
More information about the nginx
mailing list