翼度科技»论坛 云主机 服务器技术 查看内容

nginx中gzip_types匹配content-type的方式

7

主题

7

帖子

21

积分

新手上路

Rank: 1

积分
21
1.背景

我们系统中有一个功能,可以配置content-type类型来决定是否打开gzip压缩。
这个配置与nginx官方的gzip_type不同的地方在于,nginx官方的是写死在配置文件中的,所有请求都生效;我们自研的是,不同用户的gzip_type可以不同。

最近发现一个问题

content-type配置为:image/jpeg,但是后端响应的Content-Type为"
image/jped;charset:UTF-8"时,由于代码中是将配置的content-type与响应头中字符串作精确比较,因此,上述场景,并不能正确打开gzip功能。

nginx对此是如何处理的呢?

后端响应的Content-Type保持为image/jped;charset:UTF-8。
1、配置gzip_type 如下,gzip生效:
  1. gzip_type image/jpeg;
复制代码
2、配置gzip_type如下,gzip不生效:(nginx官方文档中也没有提及下面的配置方法)
  1. gzip_type "image/jpeg;charset:UTF-8";
复制代码
2.nginx处理流程

在进行header_filter时,对content-Type做了校验:
  1. static ngx_int_t
  2. ngx_http_gzip_header_filter(ngx_http_request_t *r)
  3. {
  4.     ngx_table_elt_t       *h;
  5.     ngx_http_gzip_ctx_t   *ctx;
  6.     ngx_http_gzip_conf_t  *conf;

  7.     conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);

  8.     if (!conf->enable
  9.         || (r->headers_out.status != NGX_HTTP_OK
  10.             && r->headers_out.status != NGX_HTTP_FORBIDDEN
  11.             && r->headers_out.status != NGX_HTTP_NOT_FOUND)
  12.         || (r->headers_out.content_encoding
  13.             && r->headers_out.content_encoding->value.len)
  14.         || (r->headers_out.content_length_n != -1
  15.             && r->headers_out.content_length_n < conf->min_length)
  16.             // ngx_http_test_content_type中对content_type做了校验
  17.         || ngx_http_test_content_type(r, &conf->types) == NULL
  18.         || r->header_only)
  19.     {
  20.         return ngx_http_next_header_filter(r);
  21.     }
  22.     ...
  23.     }
复制代码
ngx_http_test_content_type定义如下:
  1. void *
  2. ngx_http_test_content_type(ngx_http_request_t *r, ngx_hash_t *types_hash)
  3. {
  4.     u_char      c, *lowcase;
  5.     size_t      len;
  6.     ngx_uint_t  i, hash;

  7.     if (types_hash->size == 0) {
  8.         return (void *) 4;
  9.     }

  10.     if (r->headers_out.content_type.len == 0) {
  11.         return NULL;
  12.     }

  13.     len = r->headers_out.content_type_len;

  14.     if (r->headers_out.content_type_lowcase == NULL) {

  15.         lowcase = ngx_pnalloc(r->pool, len);
  16.         if (lowcase == NULL) {
  17.             return NULL;
  18.         }

  19.         r->headers_out.content_type_lowcase = lowcase;

  20.         hash = 0;

  21.         for (i = 0; i < len; i++) {
  22.             c = ngx_tolower(r->headers_out.content_type.data[i]);
  23.             hash = ngx_hash(hash, c);
  24.             lowcase[i] = c;
  25.         }

  26.         r->headers_out.content_type_hash = hash;
  27.     }

  28.     return ngx_hash_find(types_hash, r->headers_out.content_type_hash,
  29.                          r->headers_out.content_type_lowcase, len);
  30. }
复制代码
可以看出,将content-type头域内容转换为了小写,并使用了r->headers_out.content_type_len长度的内容,与配置的types进行比较。
但是针对第1种情况,配置和实际响应头明明是不相等的啊,是这么匹配成功的?
使用gdb打断点,发现

  • r->headers_out.content_type为:
  • {len = 24, data = “image/jpeg;charset=UTF-8”}
  • 但是,
  • r->headers_out.content_type_len却是10!这个与上面为什么不一致呢?
找到设置content_type的地方:
  1. static ngx_int_t
  2. ngx_http_set_content_type_header(ngx_http_request_t *r,
  3.     ngx_http_lua_header_val_t *hv, ngx_str_t *value)
  4. {
  5.     ngx_uint_t          i;

  6.         // 此时,r->headers_out.content_type_len与 value->len 还是相等的
  7.     r->headers_out.content_type_len = value->len;


  8. #if 1

  9.     for (i = 0; i < value->len; i++) {
  10.         if (value->data[i] == ';') {
  11.                 // 找到第一个分号,然后修改了r->headers_out.content_type_len
  12.             r->headers_out.content_type_len = i;
  13.             break;
  14.         }
  15.     }
  16. #endif

  17.     r->headers_out.content_type = *value;
  18.     r->headers_out.content_type_hash = hv->hash;
  19.     r->headers_out.content_type_lowcase = NULL;

  20.     value->len = 0;

  21.     return ngx_http_set_header_helper(r, hv, value, NULL, 1);
  22. }
复制代码
可以看出,nginx只使用了Content-Type响应头中第一个分号前的内容进行匹配。

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

来源:https://www.jb51.net/server/32110087o.htm
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具