Browse Source

update ngx_http_reqstat_module

pull/613/head
cfsego 10 years ago
parent
commit
ed7ecffe66
  1. 98
      src/http/modules/ngx_http_reqstat.h
  2. 518
      src/http/modules/ngx_http_reqstat_module.c
  3. 6
      src/http/ngx_http_write_filter_module.c
  4. 226
      tests/nginx-tests/cases/reqstat.t
  5. 497
      tests/nginx-tests/dso-nginx-tests/reqstat.t

98
src/http/modules/ngx_http_reqstat.h

@ -4,6 +4,11 @@
#include <ngx_http.h>
#define NGX_HTTP_REQSTAT_USI 29
#define NGX_HTTP_REQSTAT_UMAX 50
#define NGX_HTTP_REQSTAT_SLOT NGX_HTTP_REQSTAT_UMAX - NGX_HTTP_REQSTAT_USI
typedef struct ngx_http_reqstat_rbnode_s ngx_http_reqstat_rbnode_t;
@ -12,6 +17,8 @@ struct ngx_http_reqstat_rbnode_s {
u_char padding[3];
uint32_t len;
ngx_queue_t queue;
ngx_queue_t visit;
ngx_atomic_t bytes_in;
ngx_atomic_t bytes_out;
ngx_atomic_t conn_total;
@ -21,10 +28,32 @@ struct ngx_http_reqstat_rbnode_s {
ngx_atomic_t http_4xx;
ngx_atomic_t http_5xx;
ngx_atomic_t other_status;
ngx_atomic_t http_200;
ngx_atomic_t http_206;
ngx_atomic_t http_302;
ngx_atomic_t http_304;
ngx_atomic_t http_403;
ngx_atomic_t http_404;
ngx_atomic_t http_416;
ngx_atomic_t http_499;
ngx_atomic_t http_500;
ngx_atomic_t http_502;
ngx_atomic_t http_503;
ngx_atomic_t http_504;
ngx_atomic_t http_508;
ngx_atomic_t other_detail_status;
ngx_atomic_t http_ups_4xx;
ngx_atomic_t http_ups_5xx;
ngx_atomic_t rt;
ngx_atomic_t ureq;
ngx_atomic_t urt;
ngx_atomic_t utries;
ngx_atomic_t extra[NGX_HTTP_REQSTAT_SLOT];
ngx_atomic_int_t excess;
ngx_msec_t last_visit;
u_char data[1];
};
@ -40,6 +69,7 @@ typedef struct {
ngx_rbtree_t rbtree;
ngx_rbtree_node_t sentinel;
ngx_queue_t queue;
ngx_queue_t visit;
} ngx_http_reqstat_shctx_t;
@ -48,9 +78,20 @@ typedef struct {
ngx_slab_pool_t *shpool;
ngx_http_reqstat_shctx_t *sh;
ngx_http_complex_value_t value;
ngx_array_t *user_defined;
ngx_int_t key_len;
ngx_uint_t recycle_rate;
} ngx_http_reqstat_ctx_t;
typedef struct {
ngx_uint_t recv;
ngx_uint_t sent;
ngx_array_t monitor_index;
ngx_flag_t bypass;
} ngx_http_reqstat_store_t;
#define NGX_HTTP_REQSTAT_BYTES_IN \
offsetof(ngx_http_reqstat_rbnode_t, bytes_in)
@ -78,6 +119,48 @@ typedef struct {
#define NGX_HTTP_REQSTAT_OTHER_STATUS \
offsetof(ngx_http_reqstat_rbnode_t, other_status)
#define NGX_HTTP_REQSTAT_200 \
offsetof(ngx_http_reqstat_rbnode_t, http_200)
#define NGX_HTTP_REQSTAT_206 \
offsetof(ngx_http_reqstat_rbnode_t, http_206)
#define NGX_HTTP_REQSTAT_302 \
offsetof(ngx_http_reqstat_rbnode_t, http_302)
#define NGX_HTTP_REQSTAT_304 \
offsetof(ngx_http_reqstat_rbnode_t, http_304)
#define NGX_HTTP_REQSTAT_403 \
offsetof(ngx_http_reqstat_rbnode_t, http_403)
#define NGX_HTTP_REQSTAT_404 \
offsetof(ngx_http_reqstat_rbnode_t, http_404)
#define NGX_HTTP_REQSTAT_416 \
offsetof(ngx_http_reqstat_rbnode_t, http_416)
#define NGX_HTTP_REQSTAT_499 \
offsetof(ngx_http_reqstat_rbnode_t, http_499)
#define NGX_HTTP_REQSTAT_500 \
offsetof(ngx_http_reqstat_rbnode_t, http_500)
#define NGX_HTTP_REQSTAT_502 \
offsetof(ngx_http_reqstat_rbnode_t, http_502)
#define NGX_HTTP_REQSTAT_503 \
offsetof(ngx_http_reqstat_rbnode_t, http_503)
#define NGX_HTTP_REQSTAT_504 \
offsetof(ngx_http_reqstat_rbnode_t, http_504)
#define NGX_HTTP_REQSTAT_508 \
offsetof(ngx_http_reqstat_rbnode_t, http_508)
#define NGX_HTTP_REQSTAT_OTHER_DETAIL_STATUS \
offsetof(ngx_http_reqstat_rbnode_t, other_detail_status)
#define NGX_HTTP_REQSTAT_RT \
offsetof(ngx_http_reqstat_rbnode_t, rt)
@ -90,5 +173,18 @@ typedef struct {
#define NGX_HTTP_REQSTAT_UPS_TRIES \
offsetof(ngx_http_reqstat_rbnode_t, utries)
#define REQ_FIELD(node, offset) \
#define NGX_HTTP_REQSTAT_UPS_4XX \
offsetof(ngx_http_reqstat_rbnode_t, http_ups_4xx)
#define NGX_HTTP_REQSTAT_UPS_5XX \
offsetof(ngx_http_reqstat_rbnode_t, http_ups_5xx)
#define NGX_HTTP_REQSTAT_EXTRA(slot) \
(offsetof(ngx_http_reqstat_rbnode_t, extra) \
+ sizeof(ngx_atomic_t) * slot)
#define NGX_HTTP_REQSTAT_REQ_FIELD(node, offset) \
((ngx_atomic_t *) ((char *) node + offset))
ngx_http_reqstat_rbnode_t *ngx_http_reqstat_rbtree_lookup(
ngx_shm_zone_t *shm_zone, ngx_str_t *val);

518
src/http/modules/ngx_http_reqstat_module.c

@ -1,18 +1,11 @@
#include <ngx_http_reqstat.h>
typedef struct {
ngx_uint_t recv;
ngx_uint_t sent;
ngx_array_t monitor_index;
ngx_flag_t bypass;
} ngx_http_reqstat_store_t;
static ngx_http_input_body_filter_pt ngx_http_next_input_body_filter;
static ngx_http_output_body_filter_pt ngx_http_next_output_body_filter;
extern ngx_int_t (*ngx_http_log_flow)(ngx_http_request_t *r);
off_t ngx_http_reqstat_fields[13] = {
off_t ngx_http_reqstat_fields[29] = {
NGX_HTTP_REQSTAT_BYTES_IN,
NGX_HTTP_REQSTAT_BYTES_OUT,
NGX_HTTP_REQSTAT_CONN_TOTAL,
@ -25,7 +18,23 @@ off_t ngx_http_reqstat_fields[13] = {
NGX_HTTP_REQSTAT_RT,
NGX_HTTP_REQSTAT_UPS_REQ,
NGX_HTTP_REQSTAT_UPS_RT,
NGX_HTTP_REQSTAT_UPS_TRIES
NGX_HTTP_REQSTAT_UPS_TRIES,
NGX_HTTP_REQSTAT_200,
NGX_HTTP_REQSTAT_206,
NGX_HTTP_REQSTAT_302,
NGX_HTTP_REQSTAT_304,
NGX_HTTP_REQSTAT_403,
NGX_HTTP_REQSTAT_404,
NGX_HTTP_REQSTAT_416,
NGX_HTTP_REQSTAT_499,
NGX_HTTP_REQSTAT_500,
NGX_HTTP_REQSTAT_502,
NGX_HTTP_REQSTAT_503,
NGX_HTTP_REQSTAT_504,
NGX_HTTP_REQSTAT_508,
NGX_HTTP_REQSTAT_OTHER_DETAIL_STATUS,
NGX_HTTP_REQSTAT_UPS_4XX,
NGX_HTTP_REQSTAT_UPS_5XX
};
@ -38,6 +47,12 @@ static char *ngx_http_reqstat_show(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_reqstat_zone(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_reqstat_zone_add_indicator(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_http_reqstat_zone_key_length(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_http_reqstat_zone_recycle(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char *ngx_http_reqstat(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static void ngx_http_reqstat_count(void *data, off_t offset,
@ -48,9 +63,6 @@ static ngx_int_t ngx_http_reqstat_init_zone(ngx_shm_zone_t *shm_zone,
static ngx_int_t ngx_http_reqstat_log_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_reqstat_show_handler(ngx_http_request_t *r);
static ngx_http_reqstat_rbnode_t *
ngx_http_reqstat_rbtree_lookup(ngx_shm_zone_t *shm_zone,
ngx_str_t *val);
static void ngx_http_reqstat_rbtree_insert_value(ngx_rbtree_node_t *temp,
ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);
static ngx_http_reqstat_store_t *
@ -59,8 +71,9 @@ static ngx_http_reqstat_store_t *
static ngx_int_t ngx_http_reqstat_input_body_filter(ngx_http_request_t *r,
ngx_buf_t *buf);
static ngx_int_t ngx_http_reqstat_output_body_filter(ngx_http_request_t *r,
ngx_chain_t *in);
ngx_int_t ngx_http_reqstat_log_flow(ngx_http_request_t *r);
static ngx_command_t ngx_http_reqstat_commands[] = {
@ -92,6 +105,27 @@ static ngx_command_t ngx_http_reqstat_commands[] = {
0,
NULL },
{ ngx_string("req_status_zone_add_indicator"),
NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,
ngx_http_reqstat_zone_add_indicator,
0,
0,
NULL },
{ ngx_string("req_status_zone_key_length"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2,
ngx_http_reqstat_zone_key_length,
0,
0,
NULL },
{ ngx_string("req_status_zone_recycle"),
NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3,
ngx_http_reqstat_zone_recycle,
0,
0,
NULL },
ngx_null_command
};
@ -185,9 +219,6 @@ ngx_http_reqstat_init(ngx_conf_t *cf)
ngx_http_next_input_body_filter = ngx_http_top_input_body_filter;
ngx_http_top_input_body_filter = ngx_http_reqstat_input_body_filter;
ngx_http_next_output_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = ngx_http_reqstat_output_body_filter;
return NGX_OK;
}
@ -239,6 +270,152 @@ reg_handler:
}
static char *
ngx_http_reqstat_zone_add_indicator(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
ngx_int_t *i;
ngx_str_t *value;
ngx_uint_t j;
ngx_shm_zone_t *shm_zone;
ngx_http_reqstat_ctx_t *ctx;
value = cf->args->elts;
shm_zone = ngx_shared_memory_add(cf, &value[1], 0,
&ngx_http_reqstat_module);
if (shm_zone == NULL) {
return NGX_CONF_ERROR;
}
if (shm_zone->data == NULL) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"zone \"%V\" should be defined first",
&value[1]);
return NGX_CONF_ERROR;
}
ctx = shm_zone->data;
if (ctx->user_defined != NULL) {
return "is duplicate";
}
if (cf->args->nelts > NGX_HTTP_REQSTAT_SLOT + 2) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"too many user defined variables");
return NGX_CONF_ERROR;
}
ctx->user_defined = ngx_array_create(cf->pool, cf->args->nelts - 2,
sizeof(ngx_int_t));
if (ctx->user_defined == NULL) {
return NGX_CONF_ERROR;
}
for (j = 2; j < cf->args->nelts; j++) {
if (value[j].data[0] == '$') {
value[j].data++;
value[j].len--;
}
i = ngx_array_push(ctx->user_defined);
*i = ngx_http_get_variable_index(cf, &value[j]);
if (*i == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"failed to obtain variable \"%V\"",
&value[j]);
return NGX_CONF_ERROR;
}
}
return NGX_CONF_OK;
}
static char *
ngx_http_reqstat_zone_key_length(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
ngx_str_t *value;
ngx_shm_zone_t *shm_zone;
ngx_http_reqstat_ctx_t *ctx;
value = cf->args->elts;
shm_zone = ngx_shared_memory_add(cf, &value[1], 0,
&ngx_http_reqstat_module);
if (shm_zone == NULL) {
return NGX_CONF_ERROR;
}
if (shm_zone->data == NULL) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"zone \"%V\" should be defined first",
&value[1]);
return NGX_CONF_ERROR;
}
ctx = shm_zone->data;
ctx->key_len = ngx_atoi(value[2].data, value[2].len);
if (ctx->key_len == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid key length");
return NGX_CONF_ERROR;
}
return NGX_CONF_OK;
}
static char *
ngx_http_reqstat_zone_recycle(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
ngx_int_t rate, scale;
ngx_str_t *value;
ngx_shm_zone_t *shm_zone;
ngx_http_reqstat_ctx_t *ctx;
value = cf->args->elts;
shm_zone = ngx_shared_memory_add(cf, &value[1], 0,
&ngx_http_reqstat_module);
if (shm_zone == NULL) {
return NGX_CONF_ERROR;
}
if (shm_zone->data == NULL) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"zone \"%V\" should be defined first",
&value[1]);
return NGX_CONF_ERROR;
}
ctx = shm_zone->data;
rate = ngx_atoi(value[2].data, value[2].len);
if (rate == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid threshold");
return NGX_CONF_ERROR;
}
scale = ngx_atoi(value[3].data, value[3].len);
if (scale == NGX_ERROR) {
ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
"invalid scale");
return NGX_CONF_ERROR;
}
ctx->recycle_rate = rate * 1000 / scale;
return NGX_CONF_OK;
}
static char *
ngx_http_reqstat_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
@ -291,6 +468,9 @@ ngx_http_reqstat_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
}
*ctx->val = value[2];
ctx->key_len = 104; /* now an item is 640B at length. */
ctx->recycle_rate = 167; /* rate threshold is 10r/min */
shm_zone = ngx_shared_memory_add(cf, &value[1], size,
&ngx_http_reqstat_module);
if (shm_zone == NULL) {
@ -370,6 +550,8 @@ ngx_http_reqstat(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
}
}
ngx_http_log_flow = ngx_http_reqstat_log_flow;
return NGX_CONF_OK;
}
@ -377,12 +559,17 @@ ngx_http_reqstat(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
static ngx_int_t
ngx_http_reqstat_log_handler(ngx_http_request_t *r)
{
ngx_uint_t i, j, status, utries;
u_char *p;
ngx_int_t *indicator, iv;
ngx_uint_t i, j, k, status, utries;
ngx_time_t *tp;
ngx_msec_int_t ms, total_ms;
ngx_shm_zone_t **shm_zone, *z;
ngx_http_reqstat_ctx_t *ctx;
ngx_http_reqstat_conf_t *slcf;
ngx_http_reqstat_rbnode_t *fnode, **fnode_store;
ngx_http_upstream_state_t *state;
ngx_http_variable_value_t *v;
ngx_http_reqstat_store_t *store;
slcf = ngx_http_get_module_loc_conf(r, ngx_http_reqstat_module);
@ -404,6 +591,7 @@ ngx_http_reqstat_log_handler(ngx_http_request_t *r)
return NGX_OK;
}
shm_zone = slcf->monitor->elts;
fnode_store = store->monitor_index.elts;
for (i = 0; i < store->monitor_index.nelts; i++) {
fnode = fnode_store[i];
@ -415,9 +603,6 @@ ngx_http_reqstat_log_handler(ngx_http_request_t *r)
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_BYTES_IN,
r->connection->received
- (store ? store->recv : 0));
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_BYTES_OUT,
r->connection->sent
- (store ? store->sent : 0));
if (r->err_status) {
status = r->err_status;
@ -435,17 +620,112 @@ ngx_http_reqstat_log_handler(ngx_http_request_t *r)
if (status >= 200 && status < 300) {
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_2XX, 1);
switch (status) {
case 200:
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_200, 1);
break;
case 206:
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_206, 1);
break;
default:
ngx_http_reqstat_count(fnode,
NGX_HTTP_REQSTAT_OTHER_DETAIL_STATUS, 1);
break;
}
} else if (status >= 300 && status < 400) {
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_3XX, 1);
switch (status) {
case 302:
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_302, 1);
break;
case 304:
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_304, 1);
break;
default:
ngx_http_reqstat_count(fnode,
NGX_HTTP_REQSTAT_OTHER_DETAIL_STATUS, 1);
break;
}
} else if (status >= 400 && status < 500) {
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_4XX, 1);
switch (status) {
case 403:
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_403, 1);
break;
case 404:
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_404, 1);
break;
case 416:
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_416, 1);
break;
case 499:
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_499, 1);
break;
default:
ngx_http_reqstat_count(fnode,
NGX_HTTP_REQSTAT_OTHER_DETAIL_STATUS, 1);
break;
}
} else if (status >= 500 && status < 600) {
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_5XX, 1);
switch (status) {
case 500:
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_500, 1);
break;
case 502:
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_502, 1);
break;
case 503:
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_503, 1);
break;
case 504:
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_504, 1);
break;
case 508:
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_508, 1);
break;
default:
ngx_http_reqstat_count(fnode,
NGX_HTTP_REQSTAT_OTHER_DETAIL_STATUS, 1);
break;
}
} else {
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_OTHER_STATUS, 1);
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_OTHER_DETAIL_STATUS,
1);
}
/* response status of last upstream peer */
if (r->upstream_states != NULL && r->upstream_states->nelts > 0) {
ngx_http_upstream_state_t *state = r->upstream_states->elts;
status = state[r->upstream_states->nelts - 1].status;
if (status >= 400 && status < 500) {
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_UPS_4XX, 1);
} else if (status >= 500 && status < 600) {
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_UPS_5XX, 1);
}
}
tp = ngx_timeofday();
@ -488,6 +768,47 @@ ngx_http_reqstat_log_handler(ngx_http_request_t *r)
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_UPS_TRIES,
utries);
}
z = shm_zone[i];
ctx = z->data;
if (ctx->user_defined) {
indicator = ctx->user_defined->elts;
for (j = 0; j < ctx->user_defined->nelts; j++) {
v = ngx_http_get_indexed_variable(r, indicator[j]);
if (v == NULL || v->not_found || !v->valid) {
ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
"variable is uninitialized");
continue;
}
for (k = 0, p = v->data + v->len - 1; p >= v->data; p--) {
if (*p == '.') {
k = v->data + v->len - 1 - p;
continue;
}
if (*p < '0' || *p > '9') {
break;
}
}
p++;
if (k) {
iv = ngx_atofp(p, v->data + v->len - p, k);
} else {
iv = ngx_atoi(p, v->data + v->len - p);
}
if (iv == NGX_ERROR) {
continue;
}
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_EXTRA(j), iv);
}
}
}
return NGX_OK;
@ -501,7 +822,7 @@ ngx_http_reqstat_show_handler(ngx_http_request_t *r)
ngx_buf_t *b;
ngx_uint_t i, j;
ngx_array_t *display;
ngx_chain_t *tl, *free, *busy;
ngx_chain_t *tl, out, **cl;
ngx_queue_t *q;
ngx_shm_zone_t **shm_zone;
ngx_http_reqstat_ctx_t *ctx;
@ -528,7 +849,9 @@ ngx_http_reqstat_show_handler(ngx_http_request_t *r)
shm_zone = display->elts;
for (free = busy = NULL, i = 0; i < display->nelts; i++) {
cl = &out.next;
for (i = 0; i < display->nelts; i++) {
ctx = shm_zone[i]->data;
@ -538,23 +861,24 @@ ngx_http_reqstat_show_handler(ngx_http_request_t *r)
{
node = ngx_queue_data(q, ngx_http_reqstat_rbnode_t, queue);
tl = ngx_chain_get_free_buf(r->pool, &free);
tl = ngx_alloc_chain_link(r->pool);
if (tl == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
tl->buf = ngx_calloc_buf(r->pool);
if (tl->buf == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b = tl->buf;
b->start = ngx_pcalloc(r->pool, 512);
if (b->start == NULL) {
b->start = ngx_pcalloc(r->pool, 512);
if (b->start == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b->end = b->start + 512;
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b->end = b->start + 512;
b->last = b->pos = b->start;
b->memory = 1;
b->temporary = 1;
b->last = ngx_slprintf(b->last, b->end, "%*s,",
@ -565,35 +889,41 @@ ngx_http_reqstat_show_handler(ngx_http_request_t *r)
j++)
{
b->last = ngx_slprintf(b->last, b->end, "%uA,",
*REQ_FIELD(node,
*NGX_HTTP_REQSTAT_REQ_FIELD(node,
ngx_http_reqstat_fields[j]));
}
*(b->last - 1) = '\n';
if (ngx_http_output_filter(r, tl) == NGX_ERROR) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
if (ctx->user_defined) {
for (j = 0; j < ctx->user_defined->nelts; j++) {
b->last = ngx_slprintf(b->last, b->end, "%uA,",
*NGX_HTTP_REQSTAT_REQ_FIELD(node,
NGX_HTTP_REQSTAT_EXTRA(j)));
}
}
#if nginx_version >= 1002000
ngx_chain_update_chains(r->pool, &free, &busy, &tl,
(ngx_buf_tag_t) &ngx_http_reqstat_module);
#else
ngx_chain_update_chains(&free, &busy, &tl,
(ngx_buf_tag_t) &ngx_http_reqstat_module);
#endif
*(b->last - 1) = '\n';
tl->next = NULL;
*cl = tl;
cl = &tl->next;
}
}
tl = ngx_chain_get_free_buf(r->pool, &free);
tl = ngx_alloc_chain_link(r->pool);
if (tl == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b = tl->buf;
b->last_buf = 1;
tl->buf = ngx_calloc_buf(r->pool);
if (tl->buf == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
return ngx_http_output_filter(r, tl);
tl->buf->last_buf = 1;
tl->next = NULL;
*cl = tl;
return ngx_http_output_filter(r, out.next);
}
@ -602,26 +932,34 @@ ngx_http_reqstat_count(void *data, off_t offset, ngx_int_t incr)
{
ngx_http_reqstat_rbnode_t *node = data;
(void) ngx_atomic_fetch_add(REQ_FIELD(node, offset), incr);
(void) ngx_atomic_fetch_add(NGX_HTTP_REQSTAT_REQ_FIELD(node, offset), incr);
}
static ngx_http_reqstat_rbnode_t *
ngx_http_reqstat_rbnode_t *
ngx_http_reqstat_rbtree_lookup(ngx_shm_zone_t *shm_zone, ngx_str_t *val)
{
size_t size;
uint32_t hash;
ngx_int_t rc;
ngx_int_t rc, excess;
ngx_time_t *tp;
ngx_msec_t now;
ngx_queue_t *q;
ngx_msec_int_t ms;
ngx_rbtree_node_t *node, *sentinel;
ngx_http_reqstat_ctx_t *ctx;
ngx_http_reqstat_rbnode_t *rs;
ctx = shm_zone->data;
hash = ngx_crc32_short(val->data, val->len);
hash = ngx_murmur_hash2(val->data, val->len);
node = ctx->sh->rbtree.root;
sentinel = ctx->sh->rbtree.sentinel;
tp = ngx_timeofday();
now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
ngx_shmtx_lock(&ctx->shpool->mutex);
while (node != sentinel) {
@ -640,10 +978,31 @@ ngx_http_reqstat_rbtree_lookup(ngx_shm_zone_t *shm_zone, ngx_str_t *val)
rs = (ngx_http_reqstat_rbnode_t *) &node->color;
rc = ngx_memn2cmp(val->data, rs->data, val->len, (size_t) rs->len);
/* len < node->len */
if (val->len < (size_t) rs->len) {
node = node->left;
continue;
}
rc = ngx_strncmp(val->data, rs->data, (size_t) rs->len);
if (rc == 0) {
ms = (ngx_msec_int_t) (now - rs->last_visit);
rs->excess = rs->excess - ngx_abs(ms) * ctx->recycle_rate / 1000
+ 1000;
if (rs->excess > 0) {
ngx_queue_remove(&rs->visit);
ngx_queue_insert_head(&ctx->sh->visit, &rs->visit);
}
ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, "reqstat lookup exist: %*s", rs->len, rs->data);
ngx_shmtx_unlock(&ctx->shpool->mutex);
return rs;
}
@ -652,25 +1011,53 @@ ngx_http_reqstat_rbtree_lookup(ngx_shm_zone_t *shm_zone, ngx_str_t *val)
size = offsetof(ngx_rbtree_node_t, color)
+ offsetof(ngx_http_reqstat_rbnode_t, data)
+ val->len;
+ ctx->key_len;
node = ngx_slab_alloc_locked(ctx->shpool, size);
if (node == NULL) {
ngx_shmtx_unlock(&ctx->shpool->mutex);
return NULL;
/* try to free a vacant node */
q = ngx_queue_last(&ctx->sh->visit);
rs = ngx_queue_data(q, ngx_http_reqstat_rbnode_t, visit);
ms = (ngx_msec_int_t) (now - rs->last_visit);
excess = rs->excess - ngx_abs(ms) * ctx->recycle_rate / 1000;
ngx_log_debug3(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, "reqstat lookup try recycle: %*s, %d", rs->len, rs->data, excess);
if (excess < 0) {
node = (ngx_rbtree_node_t *)
((char *) rs - offsetof(ngx_rbtree_node_t, color));
ngx_rbtree_delete(&ctx->sh->rbtree, node);
ngx_queue_remove(&rs->visit);
ngx_queue_remove(&rs->queue);
ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, "reqstat lookup recycle: %*s", rs->len, rs->data);
ngx_memzero(node, size);
} else {
ngx_shmtx_unlock(&ctx->shpool->mutex);
return NULL;
}
}
node->key = hash;
rs = (ngx_http_reqstat_rbnode_t *) &node->color;
rs->len = val->len;
ngx_memcpy(rs->data, val->data, val->len);
rs->len = ngx_min(ctx->key_len, (ssize_t) val->len);
ngx_memcpy(rs->data, val->data, rs->len);
ngx_rbtree_insert(&ctx->sh->rbtree, node);
ngx_queue_insert_head(&ctx->sh->queue, &rs->queue);
ngx_queue_insert_head(&ctx->sh->visit, &rs->visit);
rs->last_visit = now;
rs->excess = 1000;
ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, "reqstat lookup build: %*s", rs->len, rs->data);
ngx_shmtx_unlock(&ctx->shpool->mutex);
@ -714,6 +1101,7 @@ ngx_http_reqstat_init_zone(ngx_shm_zone_t *shm_zone, void *data)
ngx_http_reqstat_rbtree_insert_value);
ngx_queue_init(&ctx->sh->queue);
ngx_queue_init(&ctx->sh->visit);
return NGX_OK;
}
@ -801,8 +1189,8 @@ ngx_http_reqstat_input_body_filter(ngx_http_request_t *r, ngx_buf_t *buf)
}
static ngx_int_t
ngx_http_reqstat_output_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
ngx_int_t
ngx_http_reqstat_log_flow(ngx_http_request_t *r)
{
ngx_uint_t i, diff;
ngx_http_reqstat_conf_t *slcf;
@ -812,7 +1200,7 @@ ngx_http_reqstat_output_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
slcf = ngx_http_get_module_loc_conf(r, ngx_http_reqstat_module);
if (slcf->monitor == NULL) {
return ngx_http_next_output_body_filter(r, in);
return NGX_OK;
}
store = ngx_http_get_module_ctx(r, ngx_http_reqstat_module);
@ -826,7 +1214,7 @@ ngx_http_reqstat_output_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
}
if (store->bypass) {
return ngx_http_next_output_body_filter(r, in);
return NGX_OK;
}
diff = r->connection->sent - store->sent;
@ -838,7 +1226,7 @@ ngx_http_reqstat_output_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_BYTES_OUT, diff);
}
return ngx_http_next_output_body_filter(r, in);
return NGX_OK;
}

6
src/http/ngx_http_write_filter_module.c

@ -12,6 +12,8 @@
static ngx_int_t ngx_http_write_filter_init(ngx_conf_t *cf);
ngx_int_t (*ngx_http_log_flow)(ngx_http_request_t *r) = NULL;
static ngx_http_module_t ngx_http_write_filter_module_ctx = {
NULL, /* preconfiguration */
@ -244,6 +246,10 @@ ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in)
chain = c->send_chain(c, r->out, limit);
if (ngx_http_log_flow && ngx_http_log_flow(r) == NGX_ERROR) {
return NGX_ERROR;
}
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http write filter %p", chain);

226
tests/nginx-tests/cases/reqstat.t

@ -23,7 +23,7 @@ use Test::Nginx;
select STDERR; $| = 1;
select STDOUT; $| = 1;
my $t = Test::Nginx->new()->has(qw/reqstat/);
my $t = Test::Nginx->new()->has('reqstat');
my $cf_1 = <<'EOF';
@ -36,15 +36,25 @@ http {
error_page default;
req_status_zone server "$host,$server_addr:$server_port" 40M;
req_status_zone test "$uri" 40M;
req_status_zone test1 "$uri" 40M;
req_status_zone_add_indicator test $test1 $upstream_response_time;
server {
listen 3128;
server_name www.test_cp.com;
location /usr {
req_status_show;
req_status_show server;
}
location /usr1 {
req_status_show test;
}
location /usr2 {
req_status_show test1;
}
}
server {
@ -53,6 +63,14 @@ http {
location / {
rewrite_by_lua 'ngx.sleep(2);ngx.exit(500)';
}
location = /404 {
return 404;
}
location = /504 {
return 504;
}
}
upstream test {
@ -66,8 +84,16 @@ http {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_next_upstream error timeout http_500;
req_status server;
req_status_bypass $bypass;
set $bypass 0;
if ($uri = '/BYPASS') {
set $bypass 1;
}
location /proxy/ {
req_status server test test1;
set $test1 2;
proxy_pass http://test/;
}
@ -130,9 +156,10 @@ EOF
#################################################################################
$t->plan(17);
$t->plan(46);
$t->write_file_expand('nginx.conf', $cf_1);
$t->write_file('B4', '1234567890');
$t->write_file('BYPASS', '1234567890');
$t->run();
my $w=my_http_get('/B4', 'www.test_app_a.com', 3129);
warn length $w;
@ -146,6 +173,11 @@ is (field($r, 7), 0, '3xx count is 0');
is (field($r, 8), 0, '4xx count is 0');
is (field($r, 9), 0, '5xx count is 0');
is (field($r, 15), 1, '200 count is 1');
is (field($r, 17), 0, '302 count is 0');
is (field($r, 20), 0, '404 count is 0');
is (field($r, 23), 0, '500 count is 0');
#2
$t->write_file_expand('nginx.conf', $cf_2);
#this time reload is failed
@ -168,6 +200,11 @@ is (field($r, 7), 0, '3xx count is 0');
is (field($r, 8), 0, '4xx count is 0');
is (field($r, 9), 0, '5xx count is 0');
is (field($r, 15), 2, '200 count is 2');
is (field($r, 17), 0, '302 count is 0');
is (field($r, 20), 0, '404 count is 0');
is (field($r, 23), 0, '500 count is 0');
#4
my %c = ();
my $i;
@ -184,7 +221,11 @@ my_http_get('/proxy/B4', 'www.test_app_a.com', 3129);
#5
$r = my_http_get('/usr', 'www.test_cp.com', 3128);
like($r, qr/1,400\d,2\n/, 'upstream');
like($r, qr/1,400\d,2,/, 'upstream');
my $r1 = content(my_http_get('/usr1', 'www.test_cp.com', 3128));
my $r2 = content(my_http_get('/usr2', 'www.test_cp.com', 3128));
$r = substr($r1, length($r2), length($r1)-length($r2));
like($r, qr/^,2,200\d$/, 'user defined variable');
#14
my_http_get('/B3', 'www.test_app_a.com', 3129);
@ -194,14 +235,159 @@ my_http_get('/302/B3', 'www.test_app_a.com', 3129);
my_http_get('/302/B3', 'www.test_app_a.com', 3129);
my_http_get('/302/B3', 'www.test_app_a.com', 3129);
my_http_get('/302/B3', 'www.test_app_a.com', 3129);
$r = my_http_get('/usr', 'www.test_cp.com', 3128);
is (field($r, 6), 2, '2xx count is 2');
is (field($r, 7), 4, '3xx count is 4');
is (field($r, 8), 3, '4xx count is 3');
is (field($r, 9), 1, '5xx count is 1');
is (field($r, 15), 2, '200 count is 2');
is (field($r, 17), 4, '302 count is 4');
is (field($r, 20), 3, '404 count is 3');
is (field($r, 23), 1, '500 count is 1');
#18
my_http_get('/BYPASS', 'www.test_app_a.com', 3129);
my_http_get('/BYPASS', 'www.test_app_a.com', 3129);
my_http_get('/BYPASS', 'www.test_app_a.com', 3129);
$r = my_http_get('/usr', 'www.test_cp.com', 3128);
is (field($r, 6), 2, '2xx count is 2');
is (field($r, 15), 2, '200 count is 2');
$t->stop();
#--- test ups 4xx/5xx ---
sleep(2);
$t->run();
sleep(2);
like(my_http_get('/proxy/404', 'www.test_app_a.com', 3129), qr/HTTP\/1\.. 404/, "request /proxy/404");
$r = my_http_get('/usr', 'www.test_cp.com', 3128);
is (field($r, 20), 1, '404 count is 1');
is (field($r, 26), 0, '504 count is 0');
is (field($r, 29), 1, 'ups 4xx count is 1');
is (field($r, 30), 0, 'ups 5xx count is 0');
like(my_http_get('/proxy/504', 'www.test_app_a.com', 3129), qr/HTTP\/1\.. 504/, "request /proxy/404");
$r = my_http_get('/usr', 'www.test_cp.com', 3128);
is (field($r, 20), 1, '404 count is 1');
is (field($r, 26), 1, '504 count is 1');
is (field($r, 29), 1, 'ups 4xx count is 1');
is (field($r, 30), 1, 'ups 5xx count is 1');
$t->stop();
my $cf_3 = <<'EOF';
%%TEST_GLOBALS%%
http {
root %%TESTDIR%%;
error_page default;
req_status_zone test3 "$uri" 1M;
req_status_zone_recycle test3 1 1;
req_status test3;
server {
listen 3128;
server_name www.test_cp.com;
location /usr {
req_status_show test3;
}
}
}
events {
use epoll;
}
EOF
#---test recycle----
$t->write_file_expand('nginx.conf', $cf_3);
$t->stop;
sleep(2);
$t->run();
for $i (0..1011) {
my_http_get('/test' . $i, 'www.test_cp.com', 3128);
}
sleep 1;
my_http_get('/test1012', 'www.test_cp.com', 3128);
my_http_get('/test1011', 'www.test_cp.com', 3128);
$r = my_http_get('/usr', 'www.test_cp.com', 3128);
like($r, qr/test1012/, 'test0 is recycled for test1012');
(my $l) = $r =~ /(.*test1011.*)/;
is (field_line($l, 4), 2, 'request count is 2 after recycle');
($l) = $r =~ /(.*test1012.*)/;
is (field_line($l, 4), 1, 'request count is 1');
$t->stop();
my $cf_4 = <<'EOF';
%%TEST_GLOBALS%%
http {
root %%TESTDIR%%;
error_page default;
req_status_zone test3 "$uri" 1M;
req_status_zone_recycle test3 1 1;
req_status_zone_key_length test3 4;
req_status test3;
server {
listen 3128;
server_name www.test_cp.com;
location /usr {
req_status_show test3;
}
}
}
events {
use epoll;
}
EOF
#---test key length----
$t->write_file_expand('nginx.conf', $cf_4);
$t->stop;
sleep(2);
$t->run();
for $i (0..1) {
my_http_get('/test' . $i, 'www.test_cp.com', 3128);
}
sleep 1;
$r = my_http_get('/usr', 'www.test_cp.com', 3128);
warn $r;
my @lines = split(m/\s/s, $r);
my @content = @lines[-5,-4];
is ($content[0], $content[1], 'key cut');
$t->stop();
#################################################################################
@ -215,18 +401,18 @@ sub my_http_ip {
my $s = IO::Socket::INET->new(
Proto => 'tcp',
PeerAddr => "${ip}:${port}"
);
);
log_out($request);
$s->print($request);
local $/;
local $/;
$reply = $s->getline();
alarm(0);
};
};
alarm(0);
if ($@) {
log_in("died: $@");
return undef;
}
}
log_in($reply);
return $reply;
}
@ -241,20 +427,20 @@ sub my_http($;%) {
my $s = IO::Socket::INET->new(
Proto => 'tcp',
PeerAddr => "127.0.0.1:$extra{port}"
);
);
log_out($request);
$s->print($request);
local $/;
local $/;
select undef, undef, undef, $extra{sleep} if $extra{sleep};
return '' if $extra{aborted};
$reply = $s->getline();
alarm(0);
};
};
alarm(0);
if ($@) {
log_in("died: $@");
return undef;
}
}
log_in($reply);
return $reply;
}
@ -278,10 +464,22 @@ Connection: close
EOF
}
sub field_line {
my ($line, $index) = @_;
my @fields = split(/,/, $line);
return $fields[$index];
}
sub field {
my ($result, $index) = @_;
my @lines = split(m/\s/s, $result);
my $content = $lines[-4];
my $content = content($result);
my @fields = split(/,/, $content);
return $fields[$index];
}
sub content {
my ($result) = @_;
my @lines = split(m/\s/s, $result);
my $content = $lines[-4];
return $content;
}

497
tests/nginx-tests/dso-nginx-tests/reqstat.t

@ -0,0 +1,497 @@
#!/usr/bin/perl
# (C) cfsego
# Tests for request statistics features.
###############################################################################
use warnings;
use strict;
use Test::More;
use POSIX;
use Cwd qw/ realpath /;
BEGIN { use FindBin; chdir($FindBin::Bin); }
use lib 'lib';
use Test::Nginx;
###############################################################################
select STDERR; $| = 1;
select STDOUT; $| = 1;
my $t = Test::Nginx->new()->has('reqstat');
$t->set_dso("ngx_http_reqstat_module", "ngx_http_reqstat_module.so");
$t->set_dso("ngx_http_lua_module", "ngx_http_lua_module.so");
$t->set_dso("ngx_http_rewrite_module", "ngx_http_rewrite_module.so");
my $cf_1 = <<'EOF';
%%TEST_GLOBALS%%
%%TEST_GLOBALS_DSO%%
http {
root %%TESTDIR%%;
error_page default;
req_status_zone server "$host,$server_addr:$server_port" 40M;
req_status_zone test "$uri" 40M;
req_status_zone test1 "$uri" 40M;
req_status_zone_add_indicator test $test1 $upstream_response_time;
server {
listen 3128;
server_name www.test_cp.com;
location /usr {
req_status_show server;
}
location /usr1 {
req_status_show test;
}
location /usr2 {
req_status_show test1;
}
}
server {
listen 3130;
listen 3131;
location / {
rewrite_by_lua 'ngx.sleep(2);ngx.exit(500)';
}
location = /404 {
return 404;
}
location = /504 {
return 504;
}
}
upstream test {
server 127.0.0.1:3131 max_fails=0;
server 127.0.0.1:3130 max_fails=0;
}
server {
listen 127.0.0.1:3129;
server_name www.test_app_a.com;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_next_upstream error timeout http_500;
req_status server;
req_status_bypass $bypass;
set $bypass 0;
if ($uri = '/BYPASS') {
set $bypass 1;
}
location /proxy/ {
req_status server test test1;
set $test1 2;
proxy_pass http://test/;
}
location /302/ {
return 302;
}
}
server {
listen 127.0.0.1:3129;
server_name www.test_app_a1.com;
req_status server;
}
}
events {
use epoll;
}
EOF
my $cf_2 = <<'EOF';
%%TEST_GLOBALS%%
%%TEST_GLOBALS_DSO%%
http {
root %%TESTDIR%%;
req_status_zone server "$host,$server_addr:$server_port error" 40M;
server {
listen 3128;
server_name www.test_cp.com;
location /usr {
req_status_show;
}
}
server {
listen 3130;
req_status server;
}
server {
listen 127.0.0.1:3129;
server_name www.test_app_a.com;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
req_status server;
}
}
events {
use epoll;
}
EOF
#################################################################################
$t->plan(46);
$t->write_file_expand('nginx.conf', $cf_1);
$t->write_file('B4', '1234567890');
$t->write_file('BYPASS', '1234567890');
$t->run();
my $w=my_http_get('/B4', 'www.test_app_a.com', 3129);
warn length $w;
my $r = my_http_get('/usr', 'www.test_cp.com', 3128);
#1
like($r, qr/242/, 'length check');
#6
is (field($r, 6), 1, '2xx count is 1');
is (field($r, 7), 0, '3xx count is 0');
is (field($r, 8), 0, '4xx count is 0');
is (field($r, 9), 0, '5xx count is 0');
is (field($r, 15), 1, '200 count is 1');
is (field($r, 17), 0, '302 count is 0');
is (field($r, 20), 0, '404 count is 0');
is (field($r, 23), 0, '500 count is 0');
#2
$t->write_file_expand('nginx.conf', $cf_2);
#this time reload is failed
$t->reload();
sleep 2;
$t->write_file_expand('nginx.conf', $cf_1);
#succeed to reload
$t->reload();
sleep 2;
$r = my_http_get('/usr', 'www.test_cp.com', 3128);
like($r, qr/242/, 'reload length check');
my_http_get('/B4', 'www.test_app_a.com', 3129);
#3
$r = my_http_get('/usr', 'www.test_cp.com', 3128);
like($r, qr/484/, 'length check again');
#7
is (field($r, 6), 2, '2xx count is 2');
is (field($r, 7), 0, '3xx count is 0');
is (field($r, 8), 0, '4xx count is 0');
is (field($r, 9), 0, '5xx count is 0');
is (field($r, 15), 2, '200 count is 2');
is (field($r, 17), 0, '302 count is 0');
is (field($r, 20), 0, '404 count is 0');
is (field($r, 23), 0, '500 count is 0');
#4
my %c = ();
my $i;
my $s = 1;
foreach $i (split(/\r\n/, $r)) {
if ($c{$i}) { fail('duplicate output'); $s = 0; last; }
else { $c{$i} = 1; }
}
if ($s == 1) {
pass('duplicate output');
}
my_http_get('/proxy/B4', 'www.test_app_a.com', 3129);
#5
$r = my_http_get('/usr', 'www.test_cp.com', 3128);
like($r, qr/1,400\d,2,/, 'upstream');
my $r1 = content(my_http_get('/usr1', 'www.test_cp.com', 3128));
my $r2 = content(my_http_get('/usr2', 'www.test_cp.com', 3128));
$r = substr($r1, length($r2), length($r1)-length($r2));
like($r, qr/^,2,200\d$/, 'user defined variable');
#14
my_http_get('/B3', 'www.test_app_a.com', 3129);
my_http_get('/B3', 'www.test_app_a.com', 3129);
my_http_get('/B3', 'www.test_app_a.com', 3129);
my_http_get('/302/B3', 'www.test_app_a.com', 3129);
my_http_get('/302/B3', 'www.test_app_a.com', 3129);
my_http_get('/302/B3', 'www.test_app_a.com', 3129);
my_http_get('/302/B3', 'www.test_app_a.com', 3129);
$r = my_http_get('/usr', 'www.test_cp.com', 3128);
is (field($r, 6), 2, '2xx count is 2');
is (field($r, 7), 4, '3xx count is 4');
is (field($r, 8), 3, '4xx count is 3');
is (field($r, 9), 1, '5xx count is 1');
is (field($r, 15), 2, '200 count is 2');
is (field($r, 17), 4, '302 count is 4');
is (field($r, 20), 3, '404 count is 3');
is (field($r, 23), 1, '500 count is 1');
#18
my_http_get('/BYPASS', 'www.test_app_a.com', 3129);
my_http_get('/BYPASS', 'www.test_app_a.com', 3129);
my_http_get('/BYPASS', 'www.test_app_a.com', 3129);
$r = my_http_get('/usr', 'www.test_cp.com', 3128);
is (field($r, 6), 2, '2xx count is 2');
is (field($r, 15), 2, '200 count is 2');
$t->stop();
#--- test ups 4xx/5xx ---
sleep(2);
$t->run();
sleep(2);
like(my_http_get('/proxy/404', 'www.test_app_a.com', 3129), qr/HTTP\/1\.. 404/, "request /proxy/404");
$r = my_http_get('/usr', 'www.test_cp.com', 3128);
is (field($r, 20), 1, '404 count is 1');
is (field($r, 26), 0, '504 count is 0');
is (field($r, 29), 1, 'ups 4xx count is 1');
is (field($r, 30), 0, 'ups 5xx count is 0');
like(my_http_get('/proxy/504', 'www.test_app_a.com', 3129), qr/HTTP\/1\.. 504/, "request /proxy/404");
$r = my_http_get('/usr', 'www.test_cp.com', 3128);
is (field($r, 20), 1, '404 count is 1');
is (field($r, 26), 1, '504 count is 1');
is (field($r, 29), 1, 'ups 4xx count is 1');
is (field($r, 30), 1, 'ups 5xx count is 1');
$t->stop();
my $cf_3 = <<'EOF';
%%TEST_GLOBALS%%
%%TEST_GLOBALS_DSO%%
http {
root %%TESTDIR%%;
error_page default;
req_status_zone test3 "$uri" 1M;
req_status_zone_recycle test3 1 1;
req_status test3;
server {
listen 3128;
server_name www.test_cp.com;
location /usr {
req_status_show test3;
}
}
}
events {
use epoll;
}
EOF
#---test recycle----
$t->write_file_expand('nginx.conf', $cf_3);
$t->stop;
sleep(2);
$t->run();
for $i (0..1011) {
my_http_get('/test' . $i, 'www.test_cp.com', 3128);
}
sleep 1;
my_http_get('/test1012', 'www.test_cp.com', 3128);
my_http_get('/test1011', 'www.test_cp.com', 3128);
$r = my_http_get('/usr', 'www.test_cp.com', 3128);
like($r, qr/test1012/, 'test0 is recycled for test1012');
(my $l) = $r =~ /(.*test1011.*)/;
is (field_line($l, 4), 2, 'request count is 2 after recycle');
($l) = $r =~ /(.*test1012.*)/;
is (field_line($l, 4), 1, 'request count is 1');
$t->stop();
my $cf_4 = <<'EOF';
%%TEST_GLOBALS%%
%%TEST_GLOBALS_DSO%%
http {
root %%TESTDIR%%;
error_page default;
req_status_zone test3 "$uri" 1M;
req_status_zone_recycle test3 1 1;
req_status_zone_key_length test3 4;
req_status test3;
server {
listen 3128;
server_name www.test_cp.com;
location /usr {
req_status_show test3;
}
}
}
events {
use epoll;
}
EOF
#---test key length----
$t->write_file_expand('nginx.conf', $cf_4);
$t->stop;
sleep(2);
$t->run();
for $i (0..1) {
my_http_get('/test' . $i, 'www.test_cp.com', 3128);
}
sleep 1;
$r = my_http_get('/usr', 'www.test_cp.com', 3128);
warn $r;
my @lines = split(m/\s/s, $r);
my @content = @lines[-5,-4];
is ($content[0], $content[1], 'key cut');
$t->stop();
#################################################################################
sub my_http_ip {
my ($request, $ip, $port) = @_;
my $reply;
eval {
local $SIG{ALRM} = sub { die "timeout\n" };
local $SIG{PIPE} = sub { die "sigpipe\n" };
alarm(5);
my $s = IO::Socket::INET->new(
Proto => 'tcp',
PeerAddr => "${ip}:${port}"
);
log_out($request);
$s->print($request);
local $/;
$reply = $s->getline();
alarm(0);
};
alarm(0);
if ($@) {
log_in("died: $@");
return undef;
}
log_in($reply);
return $reply;
}
sub my_http($;%) {
my ($request, %extra) = @_;
my $reply;
eval {
local $SIG{ALRM} = sub { die "timeout\n" };
local $SIG{PIPE} = sub { die "sigpipe\n" };
alarm(5);
my $s = IO::Socket::INET->new(
Proto => 'tcp',
PeerAddr => "127.0.0.1:$extra{port}"
);
log_out($request);
$s->print($request);
local $/;
select undef, undef, undef, $extra{sleep} if $extra{sleep};
return '' if $extra{aborted};
$reply = $s->getline();
alarm(0);
};
alarm(0);
if ($@) {
log_in("died: $@");
return undef;
}
log_in($reply);
return $reply;
}
sub my_http_get_ip {
my ($url, $ip, $port) = @_;
my $r = my_http_ip(<<EOF, $ip, $port);
GET $url HTTP/1.0
Connection: close
EOF
}
sub my_http_get {
my ($url, $host, $port) = @_;
my $r = my_http(<<EOF, 'port', $port);
GET $url HTTP/1.1
Host: $host
Connection: close
EOF
}
sub field_line {
my ($line, $index) = @_;
my @fields = split(/,/, $line);
return $fields[$index];
}
sub field {
my ($result, $index) = @_;
my $content = content($result);
my @fields = split(/,/, $content);
return $fields[$index];
}
sub content {
my ($result) = @_;
my @lines = split(m/\s/s, $result);
my $content = $lines[-4];
return $content;
}
Loading…
Cancel
Save