├── README ├── README.md ├── config ├── module_patch.sh ├── ngx_http_req_status_module.c ├── write_filter-1.7.11.patch └── write_filter.patch /README: -------------------------------------------------------------------------------- 1 | * 模块使用需要打 patch, 根据 nginx 的版本选用合适的 patch, 对 2 | 应关系参见 README.md 3 | cd nginx-1.0.x; patch -p1 < path/write_filter-VERSION.patch 4 | 5 | * 配置说明 6 | * req_status_zone 7 | syntax: req_status_zone zone_name $variable memory_max_size 8 | context: http 9 | 10 | 定义统计类型, 内容及内存使用限制. 11 | 12 | 如: 13 | req_status_zone server_name $server_name 256k; 14 | req_status_zone server_addr $server_addr 256k; 15 | req_status server_name server_addr; 16 | 17 | variable 可以是组合, 比如 "$server_name,$server_addr" 18 | 19 | * req_status 20 | syntax: req_status zone_name1 [zone_name2] 21 | context: http, server, location 22 | 23 | 为 location 启用定义的 req_status_zone 统计, 外层的定义会被 24 | 内层继承, 除非内层在 req_status 中单独或者在某个 zone_name 25 | 前使用了 @ 26 | 27 | * req_status_show 28 | syntax: req_status_show 29 | context: location 30 | 31 | 统计信息页面. 支持以下参数: 32 | c: 统计信息重置 33 | l: 数字使用原始格式显示 34 | 35 | * 配置示例 36 | 37 | http { 38 | req_status_zone server_name $server_name 256k; 39 | req_status_zone server_addr $server_addr 256k; 40 | req_status server_name server_addr; 41 | } 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Name 2 | ==== 3 | 4 | ngx_req_status - Request status in nginx 5 | 6 | Synopsis 7 | ======== 8 | 9 | http { 10 | req_status_zone server_name $server_name 256k; 11 | req_status_zone server_addr $server_addr 256k; 12 | 13 | req_status server_name server_addr; 14 | 15 | server { 16 | location /req-status { 17 | req_status_show on; 18 | 19 | allow 10.0.0.0/8; 20 | allow 127.0.0.1; 21 | deny all; 22 | } 23 | } 24 | } 25 | 26 | Directives 27 | ========== 28 | 29 | req_status_zone 30 | --------------- 31 | **syntax:** *req_status_zone name string size* 32 | 33 | **default:** *None* 34 | 35 | **context:** *http* 36 | 37 | Define a request status zone. 38 | Requests are grouped by the value of string specified in the second paramter. 39 | 40 | req_status_zone server_addr "$server_addr:$server_port" 256k; 41 | 42 | req_status 43 | ---------- 44 | **syntax:** *req_status zone1[ zone2]* 45 | 46 | **default:** *None* 47 | 48 | **context:** *http, server, location* 49 | 50 | Enables request status in this location. 51 | You can specify as many zones as needed. 52 | 53 | req_status_show 54 | --------------- 55 | **syntax:** *req_status_show on* 56 | 57 | **default:** *None* 58 | 59 | **context:** *location* 60 | 61 | Enables the request status handler in this location. 62 | For example: 63 | 64 | location /req-status { 65 | req_status_show on; 66 | 67 | allow 10.0.0.0/8; 68 | allow 127.0.0.1; 69 | deny all; 70 | } 71 | 72 | Then you can see the page by 73 | 74 | curl http://127.0.0.1/req-status 75 | 76 | It is plain text information like: 77 | 78 | zone_name key max_active max_bw traffic requests active bandwidth 79 | imgstore_appid 43 27 6M 63G 374063 0 0 80 | imgstore_appid 53 329 87M 2058G 7870529 50 25M 81 | server_addr 10.128.1.17 2 8968 24M 1849 0 0 82 | server_addr 127.0.0.1 1 6M 5G 912 1 0 83 | server_addr 180.96.x.1 3358 934M 27550G 141277391 891 356M 84 | server_addr 180.96.x.2 78 45M 220G 400704 0 0 85 | server_addr 180.96.x.3 242 58M 646G 2990547 42 7M 86 | server_name d.123.sogou.com 478 115M 2850G 30218726 115 39M 87 | server_name dl.pinyin.sogou.com 913 312M 8930G 35345453 225 97M 88 | server_name download.ie.sogou.com 964 275M 7462G 7979817 297 135M 89 | 90 | Installation 91 | ============ 92 | 93 | wget "http://nginx.org/download/nginx-1.3.5.tar.gz" 94 | tar -xzvf nginx-1.3.5.tar.gz 95 | cd nginx-1.3.5/ 96 | 97 | patch -p1 < /path/to/ngx_req_status/write_filter-VERSION.patch 98 | 99 | ./configure --prefix=/usr/local/nginx \ 100 | --add-module=/path/to/ngx_req_status 101 | 102 | make -j2 103 | make install 104 | 105 | Patches 106 | ======= 107 | 108 | Choose patch file according to Nginx version: 109 | 110 | ### write_filter-1.7.11.patch 111 | 112 | * **1.9.0-1.9.1** 113 | * **1.8.0** 114 | * **1.7.11-1.7.12** 115 | 116 | ### write_filter.patch 117 | 118 | * **1.7.0-1.7.10** 119 | * **1.6.x** 120 | * **1.5.x** 121 | * **1.4.x** 122 | * **1.3.x** 123 | * **1.2.x** 124 | * **1.1.x** 125 | * **1.0.x** 126 | 127 | Changes 128 | ======= 129 | 130 | Authors 131 | ======= 132 | 133 | - Lanshun Zhou *<zls0424@gmail.com>* 134 | 135 | Copyright & License 136 | =================== 137 | 138 | This README template is from agentzh (http://github.com/agentzh). 139 | 140 | I borrowed a lot of codes from [limit_req module](http://nginx.org/en/docs/http/ngx_http_limit_req_module.html) of Nginx. This part of code is copyrighted by Igor Sysoev. 141 | 142 | This module is licensed under the terms of the BSD license. 143 | 144 | Redistribution and use in source and binary forms, with or without 145 | modification, are permitted provided that the following conditions 146 | are met: 147 | 148 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 149 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 150 | 151 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 152 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 153 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 154 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 155 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 156 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 157 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 158 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 159 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 160 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 161 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 162 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | ngx_addon_name=ngx_http_req_status_module 2 | HTTP_MODULES="$HTTP_MODULES ngx_http_req_status_module" 3 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_req_status_module.c" 4 | -------------------------------------------------------------------------------- /module_patch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | v=$(echo ${1:-1.7.0} | awk -F'.' '{print $1*1000000+$2*1000+$3}') 4 | p= 5 | 6 | if [ $v -lt 1007011 ]; then 7 | p="$p write_filter.patch" 8 | else 9 | p="$p write_filter-1.7.11.patch" 10 | fi 11 | 12 | echo $p 13 | -------------------------------------------------------------------------------- /ngx_http_req_status_module.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) Sogou, Inc 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | static ngx_int_t ngx_http_req_status_init_zone(ngx_shm_zone_t *shm_zone, 10 | void *data); 11 | static void ngx_http_req_status_rbtree_insert_value(ngx_rbtree_node_t *temp, 12 | ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); 13 | static ngx_int_t ngx_http_req_status_init(ngx_conf_t *cf); 14 | static ngx_int_t ngx_http_req_status_handler(ngx_http_request_t *r); 15 | static void *ngx_http_req_status_lookup(void *conf, ngx_uint_t hash, 16 | ngx_str_t *key); 17 | static void ngx_http_req_status_expire(void *conf); 18 | 19 | static void *ngx_http_req_status_create_main_conf(ngx_conf_t *cf); 20 | static char *ngx_http_req_status_init_main_conf(ngx_conf_t *cf, void *conf); 21 | static void *ngx_http_req_status_create_loc_conf(ngx_conf_t *cf); 22 | static char *ngx_http_req_status_merge_loc_conf(ngx_conf_t *cf, void *parent, 23 | void *child); 24 | 25 | static char *ngx_http_req_status_zone(ngx_conf_t *cf, ngx_command_t *cmd, 26 | void *conf); 27 | static char *ngx_http_req_status(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 28 | static char *ngx_http_req_status_show(ngx_conf_t *cf, ngx_command_t *cmd, 29 | void *conf); 30 | 31 | static ngx_int_t ngx_http_req_status_show_handler(ngx_http_request_t *r); 32 | static ngx_int_t ngx_http_req_status_write_filter(ngx_http_request_t *r, 33 | off_t bsent); 34 | 35 | typedef struct ngx_http_req_status_loc_conf_s ngx_http_req_status_loc_conf_t; 36 | 37 | typedef struct { 38 | ngx_uint_t requests; 39 | ngx_uint_t traffic; 40 | 41 | ngx_uint_t bandwidth; 42 | ngx_uint_t max_bandwidth; 43 | 44 | ngx_uint_t max_active; 45 | } ngx_http_req_status_data_t; 46 | 47 | typedef struct { 48 | ngx_rbtree_node_t node; 49 | ngx_queue_t queue; 50 | 51 | ngx_uint_t count; // ref count 52 | 53 | ngx_http_req_status_data_t data; 54 | 55 | ngx_uint_t active; 56 | ngx_uint_t last_traffic; 57 | ngx_msec_t last_traffic_start; 58 | ngx_msec_t last_traffic_update; 59 | 60 | ngx_uint_t len; 61 | u_char key[0]; 62 | } ngx_http_req_status_node_t; 63 | 64 | typedef struct { 65 | ngx_rbtree_t rbtree; 66 | ngx_rbtree_node_t sentinel; 67 | ngx_queue_t queue; 68 | time_t expire_lock; 69 | } ngx_http_req_status_sh_t; 70 | 71 | typedef struct { 72 | ngx_str_t *zone_name; 73 | ngx_http_req_status_node_t *node; 74 | ngx_http_req_status_data_t *pdata; 75 | ngx_http_req_status_data_t data[0]; 76 | } ngx_http_req_status_print_item_t; 77 | 78 | typedef struct { 79 | ngx_http_req_status_sh_t *sh; 80 | ngx_slab_pool_t *shpool; 81 | ngx_shm_zone_t *shm_zone; 82 | ngx_http_complex_value_t key; 83 | } ngx_http_req_status_zone_t; 84 | 85 | typedef struct { 86 | ngx_http_req_status_zone_t *zone; 87 | ngx_http_req_status_node_t *node; 88 | } ngx_http_req_status_zone_node_t; 89 | 90 | typedef struct { 91 | ngx_array_t req_zones; 92 | } ngx_http_req_status_ctx_t; 93 | 94 | typedef struct { 95 | ngx_array_t zones; 96 | 97 | ngx_msec_t interval; 98 | time_t lock_time; 99 | } ngx_http_req_status_main_conf_t; 100 | 101 | struct ngx_http_req_status_loc_conf_s { 102 | ngx_array_t req_zones; 103 | ngx_http_req_status_loc_conf_t *parent; 104 | }; 105 | 106 | 107 | static ngx_http_module_t ngx_http_req_status_module_ctx = { 108 | NULL, /* preconfiguration */ 109 | ngx_http_req_status_init, /* postconfiguration */ 110 | 111 | ngx_http_req_status_create_main_conf, /* create main configuration */ 112 | ngx_http_req_status_init_main_conf, /* init main configuration */ 113 | 114 | NULL, /* create server configuration */ 115 | NULL, /* merge server configuration */ 116 | 117 | ngx_http_req_status_create_loc_conf, /* create location configration */ 118 | ngx_http_req_status_merge_loc_conf /* merge location configration */ 119 | }; 120 | 121 | 122 | static ngx_command_t ngx_http_req_status_commands[] = { 123 | { ngx_string("req_status_interval"), 124 | NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, 125 | ngx_conf_set_msec_slot, 126 | 0, 127 | offsetof(ngx_http_req_status_main_conf_t, interval), 128 | NULL }, 129 | 130 | { ngx_string("req_status_lock_time"), 131 | NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1, 132 | ngx_conf_set_sec_slot, 133 | 0, 134 | offsetof(ngx_http_req_status_main_conf_t, lock_time), 135 | NULL }, 136 | 137 | { ngx_string("req_status_zone"), 138 | NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3, 139 | ngx_http_req_status_zone, 140 | 0, 141 | 0, 142 | NULL 143 | }, 144 | 145 | { ngx_string("req_status"), 146 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE, 147 | ngx_http_req_status, 148 | NGX_HTTP_LOC_CONF_OFFSET, 149 | 0, 150 | NULL 151 | }, 152 | 153 | { ngx_string("req_status_show"), 154 | NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 155 | ngx_http_req_status_show, 156 | 0, 157 | 0, 158 | NULL 159 | }, 160 | 161 | ngx_null_command 162 | }; 163 | 164 | 165 | ngx_module_t ngx_http_req_status_module = { 166 | NGX_MODULE_V1, 167 | &ngx_http_req_status_module_ctx, 168 | ngx_http_req_status_commands, 169 | NGX_HTTP_MODULE, /* module type */ 170 | NULL, /* init master */ 171 | NULL, /* init module */ 172 | NULL, /* init process */ 173 | NULL, /* init thread */ 174 | NULL, /* exit thread */ 175 | NULL, /* exit process */ 176 | NULL, /* exit master */ 177 | NGX_MODULE_V1_PADDING 178 | }; 179 | 180 | 181 | static ngx_int_t 182 | ngx_http_req_status_write_filter(ngx_http_request_t *r, off_t bsent) 183 | { 184 | off_t bytes; 185 | ngx_uint_t i; 186 | ngx_msec_t td; 187 | ngx_http_req_status_ctx_t *r_ctx; 188 | ngx_http_req_status_data_t *data; 189 | ngx_http_req_status_zone_node_t *pzn; 190 | ngx_http_req_status_main_conf_t *rmcf; 191 | 192 | r_ctx = ngx_http_get_module_ctx(r, ngx_http_req_status_module); 193 | if (r_ctx == NULL || r_ctx->req_zones.nelts == 0){ 194 | return NGX_DECLINED; 195 | } 196 | 197 | rmcf = ngx_http_get_module_main_conf(r, ngx_http_req_status_module); 198 | 199 | pzn = r_ctx->req_zones.elts; 200 | 201 | bytes = r->connection->sent - bsent; 202 | 203 | for (i = 0; i < r_ctx->req_zones.nelts; i++){ 204 | data = &pzn[i].node->data; 205 | 206 | ngx_shmtx_lock(&pzn[i].zone->shpool->mutex); 207 | 208 | data->traffic += bytes; 209 | 210 | if (ngx_current_msec > pzn[i].node->last_traffic_start){ 211 | 212 | td = ngx_current_msec - pzn[i].node->last_traffic_start; 213 | 214 | if (td >= rmcf->interval){ 215 | data->bandwidth = pzn[i].node->last_traffic * 1000 / td; 216 | if (data->bandwidth > data->max_bandwidth){ 217 | data->max_bandwidth = data->bandwidth; 218 | } 219 | 220 | pzn[i].node->last_traffic = 0; 221 | pzn[i].node->last_traffic_start = ngx_current_msec; 222 | } 223 | } 224 | 225 | pzn[i].node->last_traffic += bytes; 226 | 227 | if (ngx_current_msec > pzn[i].node->last_traffic_update){ 228 | pzn[i].node->last_traffic_update = ngx_current_msec; 229 | } 230 | 231 | ngx_shmtx_unlock(&pzn[i].zone->shpool->mutex); 232 | } 233 | 234 | return NGX_DECLINED; 235 | } 236 | 237 | static void 238 | ngx_http_req_status_rbtree_insert_value(ngx_rbtree_node_t *temp, 239 | ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel) 240 | { 241 | ngx_rbtree_node_t **p; 242 | ngx_http_req_status_node_t *cn, *cnt; 243 | 244 | for ( ;; ) { 245 | 246 | if (node->key < temp->key) { 247 | 248 | p = &temp->left; 249 | 250 | } else if (node->key > temp->key) { 251 | 252 | p = &temp->right; 253 | 254 | } else { /* node->key == temp->key */ 255 | 256 | cn = (ngx_http_req_status_node_t *) node; 257 | cnt = (ngx_http_req_status_node_t *) temp; 258 | 259 | p = (ngx_memn2cmp(cn->key, cnt->key, cn->len, cnt->len) < 0) 260 | ? &temp->left : &temp->right; 261 | } 262 | 263 | if (*p == sentinel) { 264 | break; 265 | } 266 | 267 | temp = *p; 268 | } 269 | 270 | *p = node; 271 | node->parent = temp; 272 | node->left = sentinel; 273 | node->right = sentinel; 274 | ngx_rbt_red(node); 275 | } 276 | 277 | static void 278 | ngx_http_req_status_cleanup(void *data) 279 | { 280 | ngx_uint_t i; 281 | ngx_http_req_status_ctx_t *r_ctx = data; 282 | ngx_http_req_status_zone_node_t *pzn; 283 | 284 | if (r_ctx->req_zones.nelts == 0) 285 | return; 286 | 287 | pzn = r_ctx->req_zones.elts; 288 | 289 | for (i = 0; i < r_ctx->req_zones.nelts; i++){ 290 | ngx_shmtx_lock(&pzn[i].zone->shpool->mutex); 291 | 292 | pzn[i].node->count --; 293 | pzn[i].node->active --; 294 | 295 | ngx_shmtx_unlock(&pzn[i].zone->shpool->mutex); 296 | } 297 | } 298 | 299 | static ngx_int_t 300 | ngx_http_req_status_handler(ngx_http_request_t *r) 301 | { 302 | size_t len; 303 | uint32_t hash; 304 | ngx_str_t key; 305 | ngx_uint_t i; 306 | ngx_shm_zone_t **pzone; 307 | ngx_pool_cleanup_t *cln; 308 | ngx_http_req_status_ctx_t *r_ctx; 309 | ngx_http_req_status_zone_t *ctx; 310 | ngx_http_req_status_node_t *ssn; 311 | ngx_http_req_status_loc_conf_t *rlcf; 312 | ngx_http_req_status_zone_node_t *pzn; 313 | 314 | r_ctx = ngx_http_get_module_ctx(r, ngx_http_req_status_module); 315 | 316 | rlcf = ngx_http_get_module_loc_conf(r, ngx_http_req_status_module); 317 | 318 | do { 319 | pzone = rlcf->req_zones.elts; 320 | 321 | for (i = 0; i < rlcf->req_zones.nelts; i++) { 322 | ctx = pzone[i]->data; 323 | 324 | if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) { 325 | continue; 326 | } 327 | 328 | if (key.len == 0) { 329 | continue; 330 | } 331 | 332 | if (key.len > 65535) { 333 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 334 | "req-status, the value of the \"%V\" variable " 335 | "is more than 65535 bytes: \"%v\"", 336 | &ctx->key.value, &key); 337 | continue; 338 | } 339 | 340 | if (r_ctx == NULL) { 341 | 342 | r_ctx = ngx_palloc(r->pool, sizeof(ngx_http_req_status_ctx_t)); 343 | if (r_ctx == NULL) { 344 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 345 | } 346 | 347 | if (ngx_array_init(&r_ctx->req_zones, r->pool, 2, 348 | sizeof(ngx_http_req_status_zone_node_t)) 349 | != NGX_OK) 350 | { 351 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 352 | } 353 | 354 | cln = ngx_pool_cleanup_add(r->pool, 0); 355 | if (cln == NULL) { 356 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 357 | } 358 | 359 | cln->handler = ngx_http_req_status_cleanup; 360 | cln->data = r_ctx; 361 | 362 | ngx_http_set_ctx(r, r_ctx, ngx_http_req_status_module); 363 | } 364 | 365 | hash = ngx_crc32_short(key.data, key.len); 366 | 367 | ngx_shmtx_lock(&ctx->shpool->mutex); 368 | 369 | ssn = ngx_http_req_status_lookup(ctx, hash, &key); 370 | 371 | if (ssn == NULL) { 372 | len = sizeof(ngx_http_req_status_node_t) + key.len + 1; 373 | 374 | ssn = ngx_slab_alloc_locked(ctx->shpool, len); 375 | if (ssn == NULL) { 376 | ngx_http_req_status_expire(ctx); 377 | 378 | ssn = ngx_slab_alloc_locked(ctx->shpool, len); 379 | if (ssn == NULL) { 380 | ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, 381 | "req-status, slab alloc fail, zone = \"%V\", " 382 | "key = \"%V\", size = %uz", 383 | &ctx->shm_zone->shm.name, &key, len); 384 | 385 | ngx_shmtx_unlock(&ctx->shpool->mutex); 386 | 387 | continue; 388 | } 389 | } 390 | 391 | ssn->node.key = hash; 392 | ssn->len = key.len; 393 | ssn->count = 1; 394 | 395 | ngx_memzero(&ssn->data, sizeof(ssn->data)); 396 | ngx_memcpy(ssn->key, key.data, key.len); 397 | ssn->key[key.len] = '\0'; 398 | ssn->last_traffic_update = 0; 399 | 400 | ngx_rbtree_insert(&ctx->sh->rbtree, &ssn->node); 401 | } 402 | 403 | ssn->data.requests ++; 404 | ssn->active ++; 405 | if (ssn->active > ssn->data.max_active) { 406 | ssn->data.max_active = ssn->active; 407 | } 408 | 409 | ngx_queue_insert_head(&ctx->sh->queue, &ssn->queue); 410 | 411 | ngx_shmtx_unlock(&ctx->shpool->mutex); 412 | 413 | pzn = ngx_array_push(&r_ctx->req_zones); 414 | if (pzn == NULL) { 415 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 416 | } 417 | 418 | pzn->node = ssn; 419 | pzn->zone = ctx; 420 | } 421 | 422 | rlcf = rlcf->parent; 423 | } while (rlcf); 424 | 425 | return NGX_DECLINED; 426 | } 427 | 428 | static void * 429 | ngx_http_req_status_lookup(void *conf, ngx_uint_t hash, ngx_str_t *key) 430 | { 431 | ngx_int_t rc; 432 | ngx_rbtree_node_t *node, *sentinel; 433 | ngx_http_req_status_node_t *ssn; 434 | ngx_http_req_status_zone_t *ctx = conf; 435 | 436 | node = ctx->sh->rbtree.root; 437 | sentinel = ctx->sh->rbtree.sentinel; 438 | 439 | while (node != sentinel) { 440 | if (hash < node->key) { 441 | node = node->left; 442 | continue; 443 | } 444 | 445 | if (hash > node->key) { 446 | node = node->right; 447 | continue; 448 | } 449 | 450 | /* hash == node->key */ 451 | ssn = (ngx_http_req_status_node_t *)node; 452 | 453 | rc = ngx_memn2cmp(key->data, ssn->key, key->len, ssn->len); 454 | if (rc == 0){ 455 | ngx_queue_remove(&ssn->queue); 456 | 457 | ssn->count ++; 458 | 459 | return ssn; 460 | } 461 | 462 | node = (rc < 0) ? node->left : node->right; 463 | } 464 | 465 | return NULL; 466 | } 467 | 468 | static void 469 | ngx_http_req_status_expire(void *conf) 470 | { 471 | ngx_queue_t *q; 472 | ngx_http_req_status_zone_t *ctx = conf; 473 | ngx_http_req_status_node_t *ssn; 474 | 475 | if (ngx_queue_empty(&ctx->sh->queue)) { 476 | return; 477 | } 478 | 479 | if (ctx->sh->expire_lock > ngx_time()){ 480 | return; 481 | } 482 | 483 | q = ngx_queue_last(&ctx->sh->queue); 484 | 485 | ssn = ngx_queue_data(q, ngx_http_req_status_node_t, queue); 486 | 487 | if (!ssn->data.requests || (ngx_current_msec > ssn->last_traffic_update && 488 | ngx_current_msec - ssn->last_traffic_update >= 10 * 1000)){ 489 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, 490 | "req-status, release node, zone = \"%V\", key = \"%s\"", 491 | &ctx->shm_zone->shm.name, ssn->key); 492 | 493 | ngx_queue_remove(q); 494 | 495 | ngx_rbtree_delete(&ctx->sh->rbtree, &ssn->node); 496 | 497 | ngx_slab_free_locked(ctx->shpool, ssn); 498 | } 499 | } 500 | 501 | 502 | static u_char * 503 | ngx_http_req_status_format_size(u_char *buf, ngx_uint_t v) 504 | { 505 | u_char scale; 506 | ngx_uint_t size; 507 | 508 | if (v > 1024 * 1024 * 1024 - 1) { 509 | size = v / (1024 * 1024 * 1024); 510 | if ((v % (1024 * 1024 * 1024)) > (1024 * 1024 * 1024 / 2 - 1)) { 511 | size++; 512 | } 513 | scale = 'G'; 514 | } else if (v > 1024 * 1024 - 1) { 515 | size = v / (1024 * 1024); 516 | if ((v % (1024 * 1024)) > (1024 * 1024 / 2 - 1)) { 517 | size++; 518 | } 519 | scale = 'M'; 520 | } else if (v > 9999) { 521 | size = v / 1024; 522 | if (v % 1024 > 511) { 523 | size++; 524 | } 525 | scale = 'K'; 526 | } else { 527 | size = v; 528 | scale = '\0'; 529 | } 530 | 531 | if (scale) { 532 | return ngx_sprintf(buf, "%ui%c", size, scale); 533 | } 534 | 535 | return ngx_sprintf(buf, " %ui", size); 536 | } 537 | 538 | static int ngx_libc_cdecl 539 | ngx_http_req_status_cmp_items(const void *one, const void *two) 540 | { 541 | ngx_http_req_status_print_item_t *first = (ngx_http_req_status_print_item_t*) one; 542 | ngx_http_req_status_print_item_t *second = (ngx_http_req_status_print_item_t*) two; 543 | 544 | return (int)(first->zone_name == second->zone_name) ? 545 | ngx_strcmp(first->node->key, second->node->key) : 546 | ngx_strncmp(first->zone_name->data, second->zone_name->data, first->zone_name->len); 547 | } 548 | 549 | static ngx_int_t 550 | ngx_http_req_status_show_handler(ngx_http_request_t *r) 551 | { 552 | size_t size, item_size; 553 | u_char long_num, full_info, clear_status; 554 | ngx_int_t rc; 555 | ngx_buf_t *b; 556 | ngx_uint_t i; 557 | ngx_array_t items; 558 | ngx_queue_t *q; 559 | ngx_chain_t out; 560 | ngx_http_req_status_zone_t **pzone; 561 | ngx_http_req_status_node_t *rsn; 562 | ngx_http_req_status_main_conf_t *rmcf; 563 | ngx_http_req_status_print_item_t *item; 564 | static u_char header[] = 565 | "zone_name\tkey\tmax_active\tmax_bw\ttraffic\trequests\t" 566 | "active\tbandwidth\n"; 567 | 568 | if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) { 569 | return NGX_HTTP_NOT_ALLOWED; 570 | } 571 | 572 | rc = ngx_http_discard_request_body(r); 573 | 574 | if (rc != NGX_OK) { 575 | return rc; 576 | } 577 | 578 | ngx_str_set(&r->headers_out.content_type, "text/plain"); 579 | 580 | if (r->method == NGX_HTTP_HEAD) { 581 | r->headers_out.status = NGX_HTTP_OK; 582 | 583 | rc = ngx_http_send_header(r); 584 | 585 | if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { 586 | return rc; 587 | } 588 | } 589 | 590 | #define NGX_STRCHR(s, u) \ 591 | (ngx_strlchr((s)->data, (s)->data + (s)->len, u) != NULL) 592 | 593 | full_info = NGX_STRCHR(&r->args, 'f'); 594 | long_num = NGX_STRCHR(&r->args, 'l'); 595 | clear_status = NGX_STRCHR(&r->args, 'c'); 596 | 597 | item_size = sizeof(ngx_http_req_status_print_item_t) + 598 | (clear_status ? sizeof(ngx_http_req_status_data_t) : 0); 599 | 600 | if (ngx_array_init(&items, r->pool, 40, item_size) != NGX_OK){ 601 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 602 | } 603 | 604 | size = sizeof(header) - 1; 605 | 606 | rmcf = ngx_http_get_module_main_conf(r, ngx_http_req_status_module); 607 | 608 | pzone = rmcf->zones.elts; 609 | 610 | for (i = 0; i < rmcf->zones.nelts; i++){ 611 | ngx_shmtx_lock(&pzone[i]->shpool->mutex); 612 | 613 | for (q = ngx_queue_last(&pzone[i]->sh->queue); 614 | q != ngx_queue_sentinel(&pzone[i]->sh->queue); 615 | q = ngx_queue_prev(q)) 616 | { 617 | rsn = ngx_queue_data(q, ngx_http_req_status_node_t, queue); 618 | if (!rsn->data.requests){ 619 | continue; 620 | } 621 | 622 | if (rsn->last_traffic){ 623 | if (ngx_current_msec > rsn->last_traffic_update && 624 | ngx_current_msec - rsn->last_traffic_update >= 625 | rmcf->interval){ 626 | rsn->last_traffic_start = 0; 627 | rsn->last_traffic = 0; 628 | rsn->data.bandwidth = 0; 629 | rsn->last_traffic_update = ngx_current_msec; 630 | } 631 | } 632 | 633 | size += pzone[i]->shm_zone->shm.name.len + 634 | rsn->len + (sizeof("\t") - 1) * 8 + 635 | (NGX_INT64_LEN) * 6; 636 | 637 | if (full_info){ 638 | size += (NGX_INT64_LEN) * 3 + (sizeof("\t") - 1) * 3; 639 | } 640 | 641 | item = ngx_array_push(&items); 642 | if (item == NULL){ 643 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 644 | } 645 | 646 | item->zone_name = &pzone[i]->shm_zone->shm.name; 647 | item->node = rsn; 648 | 649 | if (clear_status){ 650 | item->pdata = NULL; 651 | ngx_memcpy(&item->data[0], &rsn->data, sizeof(ngx_http_req_status_data_t)); 652 | ngx_memzero(&rsn->data, sizeof(ngx_http_req_status_data_t)); 653 | } else { 654 | item->pdata = &rsn->data; 655 | } 656 | } 657 | 658 | pzone[i]->sh->expire_lock = ngx_time() + rmcf->lock_time; 659 | 660 | ngx_shmtx_unlock(&pzone[i]->shpool->mutex); 661 | } 662 | 663 | if (items.nelts > 1) { 664 | ngx_qsort(items.elts, (size_t) items.nelts, item_size, 665 | ngx_http_req_status_cmp_items); 666 | } 667 | 668 | b = ngx_create_temp_buf(r->pool, size); 669 | if (b == NULL) { 670 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 671 | } 672 | 673 | b->last = ngx_cpymem(b->last, header, sizeof(header) - 1); 674 | 675 | item = items.elts; 676 | 677 | for (i = 0; i < items.nelts; i++){ 678 | if (i) { 679 | item = (ngx_http_req_status_print_item_t *) 680 | ((u_char *)item + item_size); 681 | } 682 | 683 | /* set pdata here because of qsort above */ 684 | if (item->pdata == NULL){ 685 | item->pdata = &item->data[0]; 686 | } 687 | 688 | b->last = ngx_cpymem(b->last, item->zone_name->data, item->zone_name->len); 689 | *b->last ++ = '\t'; 690 | 691 | b->last = ngx_cpymem(b->last, item->node->key, item->node->len); 692 | *b->last ++ = '\t'; 693 | 694 | if (long_num){ 695 | b->last = ngx_sprintf(b->last, "%ui\t%ui\t%ui\t%ui\t%ui\t%ui", 696 | item->pdata->max_active, 697 | item->pdata->max_bandwidth * 8, 698 | item->pdata->traffic * 8, 699 | item->pdata->requests, 700 | item->node->active, 701 | item->pdata->bandwidth * 8); 702 | } else { 703 | b->last = ngx_sprintf(b->last, "%ui\t", item->pdata->max_active); 704 | b->last = ngx_http_req_status_format_size(b->last, 705 | item->pdata->max_bandwidth * 8); 706 | *b->last ++ = '\t'; 707 | 708 | b->last = ngx_http_req_status_format_size(b->last, 709 | item->pdata->traffic * 8); 710 | *b->last ++ = '\t'; 711 | 712 | b->last = ngx_sprintf(b->last, "%ui\t", item->pdata->requests); 713 | b->last = ngx_sprintf(b->last, "%ui\t", item->node->active); 714 | 715 | b->last = ngx_http_req_status_format_size(b->last, 716 | item->pdata->bandwidth * 8); 717 | } 718 | 719 | if (full_info){ 720 | b->last = ngx_sprintf(b->last, "\t%ui\t%ui\t%ui", 721 | item->node->last_traffic * 8, 722 | item->node->last_traffic_start, 723 | item->node->last_traffic_update); 724 | } 725 | 726 | *b->last ++ = '\n'; 727 | } 728 | 729 | out.buf = b; 730 | out.next = NULL; 731 | 732 | r->headers_out.status = NGX_HTTP_OK; 733 | r->headers_out.content_length_n = b->last - b->pos; 734 | 735 | b->last_buf = 1; 736 | 737 | rc = ngx_http_send_header(r); 738 | 739 | if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { 740 | return rc; 741 | } 742 | 743 | return ngx_http_output_filter(r, &out); 744 | } 745 | 746 | static void * 747 | ngx_http_req_status_create_main_conf(ngx_conf_t *cf) 748 | { 749 | ngx_http_req_status_main_conf_t *rmcf; 750 | 751 | rmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_req_status_main_conf_t)); 752 | if (rmcf == NULL) { 753 | return NULL; 754 | } 755 | 756 | if (ngx_array_init(&rmcf->zones, cf->pool, 4, 757 | sizeof(ngx_http_req_status_zone_t *)) != NGX_OK) 758 | { 759 | return NULL; 760 | } 761 | 762 | rmcf->interval = NGX_CONF_UNSET_MSEC; 763 | rmcf->lock_time = NGX_CONF_UNSET; 764 | 765 | return rmcf; 766 | } 767 | 768 | static char * 769 | ngx_http_req_status_init_main_conf(ngx_conf_t *cf, void *conf) 770 | { 771 | ngx_http_req_status_main_conf_t *rmcf = conf; 772 | 773 | ngx_conf_init_msec_value(rmcf->interval, 3000); 774 | ngx_conf_init_value(rmcf->lock_time, 10); 775 | 776 | return NGX_CONF_OK; 777 | } 778 | 779 | static void * 780 | ngx_http_req_status_create_loc_conf(ngx_conf_t *cf) 781 | { 782 | ngx_http_req_status_loc_conf_t *rlcf; 783 | 784 | rlcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_req_status_loc_conf_t)); 785 | if (rlcf == NULL) { 786 | return NULL; 787 | } 788 | 789 | rlcf->parent = NGX_CONF_UNSET_PTR; 790 | 791 | return rlcf; 792 | } 793 | 794 | static char * 795 | ngx_http_req_status_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) 796 | { 797 | ngx_http_req_status_loc_conf_t *prev = parent; 798 | ngx_http_req_status_loc_conf_t *conf = child; 799 | ngx_http_req_status_loc_conf_t *rlcf; 800 | 801 | if (conf->parent == NGX_CONF_UNSET_PTR){ 802 | rlcf = prev; 803 | 804 | if (rlcf->parent == NGX_CONF_UNSET_PTR) { 805 | rlcf->parent = NULL; 806 | } else { 807 | while (rlcf->parent && rlcf->req_zones.nelts == 0) { 808 | rlcf = rlcf->parent; 809 | } 810 | } 811 | 812 | conf->parent = rlcf->req_zones.nelts ? rlcf : NULL; 813 | } 814 | 815 | return NGX_CONF_OK; 816 | } 817 | 818 | static ngx_int_t 819 | ngx_http_req_status_init_zone(ngx_shm_zone_t *shm_zone, void *data) 820 | { 821 | size_t len; 822 | ngx_http_req_status_zone_t *ctx = shm_zone->data; 823 | ngx_http_req_status_zone_t *octx = data; 824 | 825 | if (octx){ 826 | if (ngx_strcmp(&octx->key.value, &ctx->key.value) != 0) { 827 | ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0, 828 | "req_status \"%V\" uses the \"%V\" variable " 829 | "while previously it used the \"%V\" variable", 830 | &shm_zone->shm.name, &ctx->key.value, &octx->key.value); 831 | return NGX_ERROR; 832 | } 833 | 834 | ctx->sh = octx->sh; 835 | ctx->shpool = octx->shpool; 836 | 837 | return NGX_OK; 838 | } 839 | 840 | ctx->shpool = (ngx_slab_pool_t *)shm_zone->shm.addr; 841 | 842 | if (shm_zone->shm.exists){ 843 | ctx->sh = ctx->shpool->data; 844 | 845 | return NGX_OK; 846 | } 847 | 848 | ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_req_status_sh_t)); 849 | if (ctx->sh == NULL){ 850 | return NGX_ERROR; 851 | } 852 | 853 | ctx->shpool->data = ctx->sh; 854 | 855 | ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel, 856 | ngx_http_req_status_rbtree_insert_value); 857 | 858 | ngx_queue_init(&ctx->sh->queue); 859 | 860 | ctx->sh->expire_lock = 0; 861 | 862 | len = sizeof("in req_status zone \"\"") + shm_zone->shm.name.len; 863 | 864 | ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len); 865 | if (ctx->shpool->log_ctx == NULL) { 866 | return NGX_ERROR; 867 | } 868 | 869 | ngx_sprintf(ctx->shpool->log_ctx, " in req_status zone \"%V\"%Z", 870 | &shm_zone->shm.name); 871 | 872 | return NGX_OK; 873 | } 874 | 875 | static char * 876 | ngx_http_req_status_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 877 | { 878 | ssize_t size; 879 | ngx_str_t *value; 880 | ngx_http_req_status_zone_t *ctx, **pctx; 881 | ngx_http_req_status_main_conf_t *rmcf; 882 | ngx_http_compile_complex_value_t ccv; 883 | 884 | value = cf->args->elts; 885 | 886 | size = ngx_parse_size(&value[3]); 887 | 888 | if (size == NGX_ERROR) { 889 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 890 | "invalid size of %V \"%V\"", &cmd->name, &value[3]); 891 | return NGX_CONF_ERROR; 892 | } 893 | 894 | if (size < (ssize_t) (8 * ngx_pagesize)) { 895 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 896 | "%V \"%V\" is too small", &cmd->name, &value[1]); 897 | return NGX_CONF_ERROR; 898 | } 899 | 900 | ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_req_status_zone_t)); 901 | if (ctx == NULL){ 902 | return NGX_CONF_ERROR; 903 | } 904 | 905 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 906 | 907 | ccv.cf = cf; 908 | ccv.value = &value[2]; 909 | ccv.complex_value = &ctx->key; 910 | 911 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 912 | return NGX_CONF_ERROR; 913 | } 914 | 915 | ctx->shm_zone = ngx_shared_memory_add(cf, &value[1], size, 916 | &ngx_http_req_status_module); 917 | if (ctx->shm_zone == NULL) { 918 | return NGX_CONF_ERROR; 919 | } 920 | 921 | if (ctx->shm_zone->data) { 922 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 923 | "%V \"%V\" is already bound", 924 | &cmd->name, &value[1]); 925 | return NGX_CONF_ERROR; 926 | } 927 | 928 | ctx->shm_zone->init = ngx_http_req_status_init_zone; 929 | ctx->shm_zone->data = ctx; 930 | 931 | rmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_req_status_module); 932 | 933 | pctx = ngx_array_push(&rmcf->zones); 934 | 935 | if (pctx == NULL){ 936 | return NGX_CONF_ERROR; 937 | } 938 | 939 | *pctx = ctx; 940 | 941 | return NGX_CONF_OK; 942 | } 943 | 944 | static char * 945 | ngx_http_req_status(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 946 | { 947 | ngx_http_req_status_loc_conf_t *rlcf = conf; 948 | 949 | ngx_str_t *value; 950 | ngx_uint_t i, m; 951 | ngx_shm_zone_t *shm_zone, **zones, **pzone; 952 | 953 | value = cf->args->elts; 954 | 955 | zones = rlcf->req_zones.elts; 956 | 957 | for (i = 1; i < cf->args->nelts; i++){ 958 | if (value[i].data[0] == '@') { 959 | rlcf->parent = NULL; 960 | 961 | if (value[i].len == 1) { 962 | continue; 963 | } 964 | 965 | value[i].data ++; 966 | value[i].len --; 967 | } 968 | 969 | shm_zone = ngx_shared_memory_add(cf, &value[i], 0, 970 | &ngx_http_req_status_module); 971 | if (shm_zone == NULL) { 972 | return NGX_CONF_ERROR; 973 | } 974 | 975 | if (zones == NULL) { 976 | if (ngx_array_init(&rlcf->req_zones, cf->pool, 2, sizeof(ngx_shm_zone_t *)) 977 | != NGX_OK) 978 | { 979 | return NGX_CONF_ERROR; 980 | } 981 | 982 | zones = rlcf->req_zones.elts; 983 | } 984 | 985 | for (m = 0; m < rlcf->req_zones.nelts; m++) { 986 | if (shm_zone == zones[m]) { 987 | return "is duplicate"; 988 | } 989 | } 990 | 991 | pzone = ngx_array_push(&rlcf->req_zones); 992 | if (pzone == NULL){ 993 | return NGX_CONF_ERROR; 994 | } 995 | 996 | *pzone = shm_zone; 997 | } 998 | 999 | return NGX_CONF_OK; 1000 | } 1001 | 1002 | static char * 1003 | ngx_http_req_status_show(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 1004 | { 1005 | ngx_http_core_loc_conf_t *clcf; 1006 | 1007 | clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); 1008 | clcf->handler = ngx_http_req_status_show_handler; 1009 | 1010 | return NGX_CONF_OK; 1011 | } 1012 | 1013 | static ngx_int_t 1014 | ngx_http_req_status_init(ngx_conf_t *cf) 1015 | { 1016 | ngx_http_handler_pt *h; 1017 | ngx_http_core_main_conf_t *cmcf; 1018 | ngx_http_req_status_main_conf_t *rmcf; 1019 | 1020 | rmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_req_status_module); 1021 | 1022 | if (rmcf->zones.nelts == 0){ 1023 | return NGX_OK; 1024 | } 1025 | 1026 | cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); 1027 | 1028 | h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers); 1029 | if (h == NULL) { 1030 | return NGX_ERROR; 1031 | } 1032 | 1033 | *h = ngx_http_req_status_handler; 1034 | 1035 | ngx_http_top_write_filter = ngx_http_req_status_write_filter; 1036 | 1037 | return NGX_OK; 1038 | } 1039 | -------------------------------------------------------------------------------- /write_filter-1.7.11.patch: -------------------------------------------------------------------------------- 1 | diff -pruN nginx-1.7.11/src/http/ngx_http.c nginx-1.7.11.zls/src/http/ngx_http.c 2 | --- nginx-1.7.11/src/http/ngx_http.c 2015-04-21 22:12:00.000000000 +0800 3 | +++ nginx-1.7.11.zls/src/http/ngx_http.c 2015-06-04 12:10:58.989314197 +0800 4 | @@ -71,6 +71,7 @@ ngx_uint_t ngx_http_max_module; 5 | 6 | ngx_http_output_header_filter_pt ngx_http_top_header_filter; 7 | ngx_http_output_body_filter_pt ngx_http_top_body_filter; 8 | +ngx_http_output_write_filter_pt ngx_http_top_write_filter; 9 | ngx_http_request_body_filter_pt ngx_http_top_request_body_filter; 10 | 11 | 12 | diff -pruN nginx-1.7.11/src/http/ngx_http_core_module.h nginx-1.7.11.zls/src/http/ngx_http_core_module.h 13 | --- nginx-1.7.11/src/http/ngx_http_core_module.h 2015-04-21 22:12:00.000000000 +0800 14 | +++ nginx-1.7.11.zls/src/http/ngx_http_core_module.h 2015-06-04 12:12:03.985376197 +0800 15 | @@ -533,6 +533,8 @@ ngx_http_cleanup_t *ngx_http_cleanup_add 16 | typedef ngx_int_t (*ngx_http_output_header_filter_pt)(ngx_http_request_t *r); 17 | typedef ngx_int_t (*ngx_http_output_body_filter_pt) 18 | (ngx_http_request_t *r, ngx_chain_t *chain); 19 | +typedef ngx_int_t (*ngx_http_output_write_filter_pt) 20 | + (ngx_http_request_t *r, off_t bsent); 21 | typedef ngx_int_t (*ngx_http_request_body_filter_pt) 22 | (ngx_http_request_t *r, ngx_chain_t *chain); 23 | 24 | diff -pruN nginx-1.7.11/src/http/ngx_http.h nginx-1.7.11.zls/src/http/ngx_http.h 25 | --- nginx-1.7.11/src/http/ngx_http.h 2015-04-21 22:12:00.000000000 +0800 26 | +++ nginx-1.7.11.zls/src/http/ngx_http.h 2015-06-04 12:12:25.374712947 +0800 27 | @@ -178,6 +178,7 @@ extern ngx_str_t ngx_http_html_default_ 28 | 29 | extern ngx_http_output_header_filter_pt ngx_http_top_header_filter; 30 | extern ngx_http_output_body_filter_pt ngx_http_top_body_filter; 31 | +extern ngx_http_output_write_filter_pt ngx_http_top_write_filter; 32 | extern ngx_http_request_body_filter_pt ngx_http_top_request_body_filter; 33 | 34 | 35 | diff -pruN nginx-1.7.11/src/http/ngx_http_write_filter_module.c nginx-1.7.11.zls/src/http/ngx_http_write_filter_module.c 36 | --- nginx-1.7.11/src/http/ngx_http_write_filter_module.c 2015-04-21 22:12:01.000000000 +0800 37 | +++ nginx-1.7.11.zls/src/http/ngx_http_write_filter_module.c 2015-06-04 12:16:13.964998947 +0800 38 | @@ -261,6 +261,10 @@ ngx_http_write_filter(ngx_http_request_t 39 | return NGX_ERROR; 40 | } 41 | 42 | + if (ngx_http_top_write_filter){ 43 | + ngx_http_top_write_filter(r, sent); 44 | + } 45 | + 46 | if (r->limit_rate) { 47 | 48 | nsent = c->sent; 49 | -------------------------------------------------------------------------------- /write_filter.patch: -------------------------------------------------------------------------------- 1 | diff -pruN nginx-1.0.2/src/http/ngx_http.c nginx-1.0.2.zls/src/http/ngx_http.c 2 | --- nginx-1.0.2/src/http/ngx_http.c 2010-12-13 05:13:27.000000000 +0800 3 | +++ nginx-1.0.2.zls/src/http/ngx_http.c 2011-05-11 13:32:52.524157563 +0800 4 | @@ -70,6 +70,7 @@ ngx_uint_t ngx_http_max_module; 5 | 6 | ngx_int_t (*ngx_http_top_header_filter) (ngx_http_request_t *r); 7 | ngx_int_t (*ngx_http_top_body_filter) (ngx_http_request_t *r, ngx_chain_t *ch); 8 | +ngx_int_t (*ngx_http_top_write_filter) (ngx_http_request_t *r, off_t bsent); 9 | 10 | 11 | ngx_str_t ngx_http_html_default_types[] = { 12 | diff -pruN nginx-1.0.2/src/http/ngx_http_core_module.h nginx-1.0.2.zls/src/http/ngx_http_core_module.h 13 | --- nginx-1.0.2/src/http/ngx_http_core_module.h 2010-11-26 20:40:56.000000000 +0800 14 | +++ nginx-1.0.2.zls/src/http/ngx_http_core_module.h 2011-05-11 13:36:03.024063063 +0800 15 | @@ -485,6 +485,8 @@ ngx_http_cleanup_t *ngx_http_cleanup_add 16 | typedef ngx_int_t (*ngx_http_output_header_filter_pt)(ngx_http_request_t *r); 17 | typedef ngx_int_t (*ngx_http_output_body_filter_pt) 18 | (ngx_http_request_t *r, ngx_chain_t *chain); 19 | +typedef ngx_int_t (*ngx_http_output_write_filter_pt)(ngx_http_request_t *r, 20 | + off_t bsent); 21 | 22 | 23 | ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *chain); 24 | diff -pruN nginx-1.0.2/src/http/ngx_http.h nginx-1.0.2.zls/src/http/ngx_http.h 25 | --- nginx-1.0.2/src/http/ngx_http.h 2010-10-04 22:59:41.000000000 +0800 26 | +++ nginx-1.0.2.zls/src/http/ngx_http.h 2011-05-11 13:32:21.290205563 +0800 27 | @@ -154,6 +154,7 @@ extern ngx_str_t ngx_http_html_default_ 28 | 29 | extern ngx_http_output_header_filter_pt ngx_http_top_header_filter; 30 | extern ngx_http_output_body_filter_pt ngx_http_top_body_filter; 31 | +extern ngx_http_output_write_filter_pt ngx_http_top_write_filter; 32 | 33 | 34 | #endif /* _NGX_HTTP_H_INCLUDED_ */ 35 | diff -pruN nginx-1.0.2/src/http/ngx_http_write_filter_module.c nginx-1.0.2.zls/src/http/ngx_http_write_filter_module.c 36 | --- nginx-1.0.2/src/http/ngx_http_write_filter_module.c 2009-06-02 22:01:50.000000000 +0800 37 | +++ nginx-1.0.2.zls/src/http/ngx_http_write_filter_module.c 2011-05-11 13:33:35.306831313 +0800 38 | @@ -245,6 +245,10 @@ ngx_http_write_filter(ngx_http_request_t 39 | return NGX_ERROR; 40 | } 41 | 42 | + if (ngx_http_top_write_filter){ 43 | + ngx_http_top_write_filter(r, sent); 44 | + } 45 | + 46 | if (r->limit_rate) { 47 | 48 | nsent = c->sent; 49 | --------------------------------------------------------------------------------