http 请求体数据处理2--ngx
  WNX8ZrUuErM5 2023年11月02日 87 0
  • HTTP 处理数据包, 有的业务不需要,此时只需要将数据包文读取后丢弃, 但是ngx 为什么还要提供一个丢弃接口呢???解决了什么问题??
  • ------对于HTTP模块而言,放弃接收包体就是简单地不处理包体了,可是对于HTTP框架而言,并不是不接收包体就可以的。因为对于客户端而言,通常
    会调用一些阻塞的发送方法来发送包体,如果HTTP框架一直不接收包体,会导致实现上不够健壮的客户端认为服务器超时无响应,因而简单地关
    闭连接,可这时Nginx模块可能还在处理这个连接。因此,HTTP模块中的放弃接收包体,对HTTP框架而言就是接收包体,但是接收后不做保存,直接丢弃。

    HTTP模块调用的ngx_http_discard_request_body方法用于第一次启动丢弃包体动作,而ngx_http_discarded_request_body_handler是作为请
    求的read_event_handler方法的,在有新的可读事件时会调用它处理包体。ngx_http_read discarded_request_body方法则是根据上述两个方法
    通用部分提取出的公共方法,用来读取包体且不做任何处理。

 

ngx_int_t
ngx_http_discard_request_body(ngx_http_request_t *r)
{
ssize_t size;
ngx_int_t rc;
ngx_event_t *rev;

#if (NGX_HTTP_V2)
if (r->stream && r == r->main) {
r->stream->skip_data = NGX_HTTP_V2_DATA_DISCARD;
return NGX_OK;
}
#endif

/*
首先检查当前请求是一个子请求还是原始请求。为什么要检查这个呢?因为对于子请求而言,它不是来自客户端的请求,所以不存在处理HTTP
请求包体的概念。如果当前请求是原始请求,则继续执行;如果它是子请求,则直接返回NGX_OK表示丢弃包体成功。检查ngx_http_request_t结构
体的request_body成员,如果它已经被赋值过且不再为NULL空指针,则说明已经接收过包体了,这时也需要返回NGX_OK表示成功。
*/
if (r != r->main || r->discard_body || r->request_body) {
return NGX_OK;
}

if (ngx_http_test_expect(r) != NGX_OK) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}

rev = r->connection->read;

ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body");

/*
检查请求连接上的读事件是否在定时器中,这是因为丢弃包体不用考虑超时问题(linger_timer例外,本章不考虑此情况)。如果读事件
的timer set标志位为1,则从定时器中移除此事件。还要检查content-length头部,如果它的值小于或等于0,同样意味着可以直接返回
NGX一OK,表示成功丢弃了全部包体。
*/
if (rev->timer_set) {
ngx_del_timer(rev, NGX_FUNC_LINE);
}
if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) {
return NGX_OK;
}


size = r->header_in->last - r->header_in->pos;

if (size || r->headers_in.chunked) {
rc = ngx_http_discard_request_body_filter(r, r->header_in);

if (rc != NGX_OK) {
return rc;
}

if (r->headers_in.content_length_n == 0) {
return NGX_OK;
}
}

/*
在接收HTTP头部时,还是要检查是否凑巧已经接收到完整的包体(如果包体很小,那么这是非常可能发生的事),如果已经接收到完整的包
体,则直接返回NGX OK,表示丢弃包体成功,否则,说明需要多次的调度才能完成丢弃包体这一动作,此时把请求的read_event_handler
成员设置为ngx_http_discarded_request_body_handler方法。
*/
rc = ngx_http_read_discarded_request_body(r);

if (rc == NGX_OK) {
/* 返回NGX一OK表示已经接收到完整的包体了,这时将请求的lingering_close延时关闭标志位设为0,表示不需要为了包体的接收而
延时关闭了,同时返回NGX—OK表示丢弃包体成功。 */
r->lingering_close = 0;
return NGX_OK;
}

if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return rc;
}

//返回非NGX_OK表示Nginx的事件框架触发事件需要多次调度才能完成丢弃包体这一动作

/* rc == NGX_AGAIN */

r->read_event_handler = ngx_http_discarded_request_body_handler; //下次读事件到来时通过ngx_http_request_handler来调用
/* 有可能执行了ngx_http_block_reading->ngx_http_block_reading,所以如果需要继续读取客户端请求,需要add event */
if (ngx_handle_read_event(rev, 0, NGX_FUNC_LINE) != NGX_OK) { //调用ngx_handle_read_event方法把读事件添加到epoll中handle为ngx_http_request_handler
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}

/*
返回非NGX_OK表示Nginx的事件框架触发事件需要多次调度才能完成丢弃包体这一动作,于是先把引用计数加1,防止这边还在丢弃包体,
而其他事件却已让请求意外销毁,引发严重错误。同时把ngx_http_request_t结构体的discard_body标志位置为1,表示正在丢弃包体,并
返回NGX_OK,当然,达时的NGX_OK绝不表示已经成功地接收完包体,只是说明ngx_http_discard_request_body执行完毕而已。
*/
r->count++;
r->discard_body = 1;

return NGX_OK;
}

 

  HTTP模块调用的ngx_http_discard_request_body方法用于第一次启动丢弃包体动作,而ngx_http_discarded_request_body_handler是作为请
求的read_event_handler方法的,在有新的可读事件时会调用它处理包体。ngx_http_read_discarded_request_body方法则是根据上述两个方法
通用部分提取出的公共方法,用来读取包体且不做任何处理。

void
ngx_http_discarded_request_body_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_msec_t timer;
ngx_event_t *rev;
ngx_connection_t *c;
ngx_http_core_loc_conf_t *clcf;

c = r->connection;
rev = c->read;

//首先检查TCP连接上的读事件的timedout标志位,为1时表示已经超时,这时调用ngx_http_finalize_request方法结束请求,传递的参数是NGX_ERROR,流程结束
if (rev->timedout) {
c->timedout = 1;
c->error = 1;
ngx_http_finalize_request(r, NGX_ERROR);
return;
}

if (r->lingering_time) {
timer = (ngx_msec_t) r->lingering_time - (ngx_msec_t) ngx_time();

if ((ngx_msec_int_t) timer <= 0) {
r->discard_body = 0;
r->lingering_close = 0;
ngx_http_finalize_request(r, NGX_ERROR);
return;
}

} else {
timer = 0;
}

//调用ngx_http_read_discarded_request_body方法接收包体,检测其返回值。
rc = ngx_http_read_discarded_request_body(r);

if (rc == NGX_OK) {
r->discard_body = 0;
r->lingering_close = 0;
ngx_http_finalize_request(r, NGX_DONE);
return;
}

if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
c->error = 1;
ngx_http_finalize_request(r, NGX_ERROR);
return;
}

/* rc == NGX_AGAIN */

if (ngx_handle_read_event(rev, 0, NGX_FUNC_LINE) != NGX_OK) {
c->error = 1;
ngx_http_finalize_request(r, NGX_ERROR);
return;
}

if (timer) {

clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

timer *= 1000;

if (timer > clcf->lingering_timeout) {
timer = clcf->lingering_timeout;
}

ngx_add_timer(rev, timer, NGX_FUNC_LINE);
}
}

 

 

 

*/ //ngx_http_read_discarded_request_body方法与ngx_http_do_read_client_request_body方法很类似
static ngx_int_t
ngx_http_read_discarded_request_body(ngx_http_request_t *r)
{
size_t size;
ssize_t n;
ngx_int_t rc;
ngx_buf_t b;
u_char buffer[NGX_HTTP_DISCARD_BUFFER_SIZE];

ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http read discarded body");

ngx_memzero(&b, sizeof(ngx_buf_t));

b.temporary = 1;

for ( ;; ) {
/*
丢弃包体时请求的request_body成员实际上是NULL室指针,那么用什么变量来表示已经丢弃的包体有多大呢?实际上这时使用
了请求ngx_http_request_t结构体headers_in成员里的content_length_n,最初它等于content-length头部,而每丢弃一部分包体,就会在
content_length_n变量中减去相应的大小。因此,content_length_n表示还需要丢弃的包体长度,这里首先检查请求的content_length_n成员,
如果它已经等于0,则表示已经接收到完整的包体,这时要把read event_handler重置为ngx_http_block_reading方法,表示如果再有可读
事件被触发时,不做任何处理。同时返回NGX_OK,告诉上层的方法已经丢弃了所有包体。
*/
if (r->headers_in.content_length_n == 0) {
r->read_event_handler = ngx_http_block_reading;
return NGX_OK;
}

/* 如果连接套接字的缓冲区上没有可读内容,则直接返回NGX_AGAIN,告诉上层方法需要等待读事件的触发,等待Nginx框架的再次调度。 */
if (!r->connection->read->ready) {
return NGX_AGAIN;
}

size = (size_t) ngx_min(r->headers_in.content_length_n,
NGX_HTTP_DISCARD_BUFFER_SIZE);

n = r->connection->recv(r->connection, buffer, size);

if (n == NGX_ERROR) {
r->connection->error = 1;
return NGX_OK;
}

if (n == NGX_AGAIN) { //如果套接字缓冲区中没有读取到内容
return NGX_AGAIN;
}

if (n == 0) { //如果客户端主动关闭了连接
return NGX_OK;
}

b.pos = buffer;
b.last = buffer + n;

//接收到包体后,要更新请求的content_length_n成员,从而判断是否读取完毕,如果为0表示读取完毕,同时继续循环
rc = ngx_http_discard_request_body_filter(r, &b);

if (rc != NGX_OK) {
return rc;
}
}
}

 

http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!! 但行好事 莫问前程 --身高体重180的胖子

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  bifOjSxj34Bv   2023年12月07日   38   0   0 nginxDockerdockernginx
WNX8ZrUuErM5
作者其他文章 更多