freeBuf
主站

分类

漏洞 工具 极客 Web安全 系统安全 网络安全 无线安全 设备/客户端安全 数据安全 安全管理 企业安全 工控安全

特色

头条 人物志 活动 视频 观点 招聘 报告 资讯 区块链安全 标准与合规 容器安全 公开课

官方公众号企业安全新浪微博

FreeBuf.COM网络安全行业门户,每日发布专业的安全资讯、技术剖析。

FreeBuf+小程序

FreeBuf+小程序

nginx记录状态码逻辑源码分析
2020-11-29 18:18:28


nginx记录状态码的函数是ngx_http_reqstat_log_handler。

这个函数在ngx_http_reqstat_init函数放在log phase阶段中。

static ngx_int_t ngx_http_reqstat_init(ngx_conf_t *cf)

{

...

h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers);

*h = ngx_http_reqstat_log_handler;

...

}

接下来根据保存好的状态码来进行记录

static ngx_int_t ngx_http_reqstat_log_handler(ngx_http_request_t *r)

{

...

// 判断status

if (r->err_status) {

status = r->err_status;


} else if (r->headers_out.status) {

status = r->headers_out.status;


} else if (r->http_version == NGX_HTTP_VERSION_9) {

status = 9;


} else {

status = 0;

}

// 接下来根据status来进行判断,对全部的req状态码进行处理

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;

// 处理5xx和4xx,注意这里upstream_states是个数组,他这块选取的是最后一个元素的状态码,原因在于下方代码块

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);

} else if (status >= 200 && status < 300) {

ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_UPS_2XX, 1);

}

}

...

}



ngx_http_upstream_connect过程中,每次连接都会使用一个新的state,这是个array

void ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u)

{

...

// 从states中取出一个state,来记录后端的状态

u->state = ngx_array_push(r->upstream_states);

ngx_memzero(u->state, sizeof(ngx_http_upstream_state_t));

rc = ngx_event_connect_peer(&u->peer);

// busy代表了后端断连了

if (rc == NGX_BUSY) {

ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no live upstreams");

ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_NOLIVE);

return;

}

// 如果connect阶段没问题,发送request

ngx_http_upstream_send_request(r, u, 1);

...

}



static void ngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t *u,

ngx_uint_t do_write)

{

...

// 获取状态码

rc = ngx_http_upstream_send_request_body(r, u, do_write);


if (rc == NGX_ERROR) {

ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);

return;

}

// 状态码异常,使用该状态码结束请求

if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {

ngx_http_upstream_finalize_request(r, u, rc);

return;

}

...

}


这块是判断健康检查失败如何判断出来的,是在get round robin peer的时候判断健康检查

ngx_int_t ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)

{

...

#if (NGX_HTTP_UPSTREAM_CHECK)

if (ngx_http_upstream_check_peer_down(peer->check_index)) {

goto failed;

}

#endif

failed:

return NGX_BUSY;

...

}


说会上文,ngx_http_upstream_next

static void ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u,

ngx_uint_t ft_type)

{

...

switch (ft_type) {


case NGX_HTTP_UPSTREAM_FT_TIMEOUT:

status = NGX_HTTP_GATEWAY_TIME_OUT;

break;


case NGX_HTTP_UPSTREAM_FT_HTTP_500:

status = NGX_HTTP_INTERNAL_SERVER_ERROR;

break;


case NGX_HTTP_UPSTREAM_FT_HTTP_403:

status = NGX_HTTP_FORBIDDEN;

break;


case NGX_HTTP_UPSTREAM_FT_HTTP_404:

status = NGX_HTTP_NOT_FOUND;

break;


/*

* NGX_HTTP_UPSTREAM_FT_BUSY_LOCK and NGX_HTTP_UPSTREAM_FT_MAX_WAITING

* never reach here

*/


default:

status = NGX_HTTP_BAD_GATEWAY;

if (status) {

u->state->status = status;

timeout = u->conf->next_upstream_timeout;

// 以这个status终结

ngx_http_upstream_finalize_request(r, u, status);

...

}



ngx_http_upstream_finalize_request中,会调用finalize request

void ngx_http_upstream_finalize_request(ngx_http_request_t *r, ngx_http_upstream_t *u, ngx_int_t rc)

{

...

// 以这个状态码结束request

ngx_http_finalize_request(r, rc);

...

}


void ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc)

{

...

// 状态码异常

if (rc >= NGX_HTTP_SPECIAL_RESPONSE

|| rc == NGX_HTTP_CREATED

|| rc == NGX_HTTP_NO_CONTENT)

{

if (rc == NGX_HTTP_CLOSE) {

ngx_http_terminate_request(r, rc);

return;

}


if (r == r->main) {

if (c->read->timer_set) {

ngx_del_timer(c->read);

}


if (c->write->timer_set) {

ngx_del_timer(c->write);

}

}


c->read->handler = ngx_http_request_handler;

c->write->handler = ngx_http_request_handler;

// 特殊状态码回调handler

ngx_http_finalize_request(r, ngx_http_special_response_handler(r, rc));

return;

}

...

}



ngx_int_t ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error)

{

...

// err_status赋值

r->err_status = error;

// 接下来是错误页的返回

...

}


真正展示状态码计数的是这个函数

location /check_req_status {

req_status_show req_server_status;

}


static ngx_int_t ngx_http_reqstat_show_handler(ngx_http_request_t *r)

{

...

for (j = 0; j < rlcf->user_select->nelts; j++) {

if (user[j] < NGX_HTTP_REQSTAT_RSRV) {

index = user[j];

b->last = ngx_slprintf(b->last, b->end, "%uA,",

*NGX_HTTP_REQSTAT_REQ_FIELD(node,

ngx_http_reqstat_fields[index]));


} else {

index = user[j] - NGX_HTTP_REQSTAT_RSRV;

b->last = ngx_slprintf(b->last, b->end, "%uA,",

*NGX_HTTP_REQSTAT_REQ_FIELD(node,

NGX_HTTP_REQSTAT_EXTRA(index)));

}

}

...

}


off_t ngx_http_reqstat_fields[NGX_HTTP_REQSTAT_RSRV] = {

NGX_HTTP_REQSTAT_BYTES_IN,

NGX_HTTP_REQSTAT_BYTES_OUT,

NGX_HTTP_REQSTAT_CONN_TOTAL,

NGX_HTTP_REQSTAT_REQ_TOTAL,

NGX_HTTP_REQSTAT_2XX,

NGX_HTTP_REQSTAT_3XX,

NGX_HTTP_REQSTAT_4XX,

NGX_HTTP_REQSTAT_5XX,

NGX_HTTP_REQSTAT_OTHER_STATUS,

NGX_HTTP_REQSTAT_RT,

NGX_HTTP_REQSTAT_UPS_REQ,

NGX_HTTP_REQSTAT_UPS_RT,

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,

NGX_HTTP_REQSTAT_UPS_2XX,

NGX_HTTP_REQSTAT_UPS_LATENCY

};


新版的nginx去掉了NGX_HTTP_REQSTAT_UPS_2XX(因为和NGX_HTTP_REQSTAT_2XX一致)

off_t ngx_http_reqstat_fields[30] = {

NGX_HTTP_REQSTAT_BYTES_IN,

NGX_HTTP_REQSTAT_BYTES_OUT,

NGX_HTTP_REQSTAT_CONN_TOTAL,

NGX_HTTP_REQSTAT_REQ_TOTAL,

NGX_HTTP_REQSTAT_2XX,

NGX_HTTP_REQSTAT_3XX,

NGX_HTTP_REQSTAT_4XX,

NGX_HTTP_REQSTAT_5XX,

NGX_HTTP_REQSTAT_OTHER_STATUS,

NGX_HTTP_REQSTAT_RT,

NGX_HTTP_REQSTAT_UPS_REQ,

NGX_HTTP_REQSTAT_UPS_RT,

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,

NGX_HTTP_REQSTAT_UPS_LATENCY

};

lantency状态码修改

#if nginx_version >= 1009002,所以使用最新的时间结构。

typedef struct {

ngx_uint_t                       status;

ngx_msec_t                       response_time;

ngx_msec_t                       connect_time;

ngx_msec_t                       header_time;

ngx_msec_t                       queue_time;

off_t                            response_length;

off_t                            bytes_received;

off_t                            bytes_sent;


ngx_str_t                       *peer;

} ngx_http_upstream_state_t;



u->start_time = ngx_current_msec;

u->state->response_time = (ngx_msec_t) -1;

u->state->connect_time = (ngx_msec_t) -1;

u->state->header_time = (ngx_msec_t) -1;


而header time的时间为

static void ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u)

{

...

rc = u->process_header(r);

// 获取header time时间

u->state->header_time = ngx_current_msec - u->start_time;

...

}

# web安全 # nginx
本文为 独立观点,未经允许不得转载,授权请联系FreeBuf客服小蜜蜂,微信:freebee2022
被以下专辑收录,发现更多精彩内容
+ 收入我的专辑
+ 加入我的收藏
相关推荐
  • 0 文章数
  • 0 关注者