菜鸟学习nginx之接收HTTP Header

上一篇介绍了《菜鸟学习nginx之接收HTTP请求行》,本篇介绍Nginx接收HTTP Header处理流程。

一、HTTP Header处理流程

处理Header与上一篇处理HTTP请求行很类似,比较容易理解

1.1、流程图

1.2、代码

/**
 * 处理HTTP Header
 * @param rev 读事件
 */
static void
ngx_http_process_request_headers(ngx_event_t *rev)
{
    u_char *p;
    size_t len;
    ssize_t n;
    ngx_int_t rc, rv;
    ngx_table_elt_t *h;
    ngx_connection_t *c;
    ngx_http_header_t *hh;
    ngx_http_request_t *r;
    ngx_http_core_srv_conf_t *cscf;
    ngx_http_core_main_conf_t *cmcf;

    c = rev->data;
    r = c->data;

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
                   "http process request header line");

    if (rev->timedout)
    {
        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
        c->timedout = 1;
        ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
        return;
    }

这部分代码是处理定时器超时事件,与解析HTTP请求行是相同的,不在多说。

 

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    rc = NGX_AGAIN;

    for (;;)
    {

        if (rc == NGX_AGAIN)
        {

            if (r->header_in->pos == r->header_in->end)
            {//表示分配的内存不足 需要申请更大内存

                rv = ngx_http_alloc_large_header_buffer(r, 0);

                if (rv == NGX_ERROR)
                {
                    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                    return;
                }

                if (rv == NGX_DECLINED)
                {
                    p = r->header_name_start;

                    r->lingering_close = 1;

                    if (p == NULL)
                    {
                        ngx_log_error(NGX_LOG_INFO, c->log, 0,
                                      "client sent too large request");
                        ngx_http_finalize_request(r,
                                                  NGX_HTTP_REQUEST_HEADER_TOO_LARGE);
                        return;
                    }

                    len = r->header_in->end - p;

                    if (len > NGX_MAX_ERROR_STR - 300)
                    {
                        len = NGX_MAX_ERROR_STR - 300;
                    }

                    ngx_log_error(NGX_LOG_INFO, c->log, 0,
                                  "client sent too long header line: \"%*s...\"",
                                  len, r->header_name_start);

                    ngx_http_finalize_request(r,
                                              NGX_HTTP_REQUEST_HEADER_TOO_LARGE);
                    return;
                }
            }
            //接收 HTTP Header
            n = ngx_http_read_request_header(r);

            if (n == NGX_AGAIN || n == NGX_ERROR)
            {
                return;
            }
        }

这部分代码说明:

1、 判断内存是否足够,若不足则申请最大内存(默认8k)。由于HTTP报文是不定长报文,所以无法一次性申请足够内存。

2、调用ngx_http_read_request_header方法接收报文

        /* the host header could change the server configuration context */
        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
        /**
         * http协议解析 
         * 返回值 : NGX_OK: 解析完header中的一行时返回
         *          NGX_HTTP_PARSE_HEADER_DONE: 解析完整个header时返回; 
         *          NGX_HTTP_PARSE_INVALID_HEADER: 解析出错时返回; 
         *          NGX_AGAIN: 表示需要读取新的请求头内容
         */
        rc = ngx_http_parse_header_line(r, r->header_in,
                                        cscf->underscores_in_headers);

 ngx_http_parse_header_line是纯粹解析HTTP报文的,将解析出的报文保存到header_in中。ngx_http_parse_header_line,返回值如下表:

返回值含义
NGX_OK当前header line解析成功(一个http请求中可以有多个header line)
NGX_HTTP_PARSE_HEADER_DONE表示所有HTTP Header都已经成功解析完毕
NGX_AGAIN表是当前header line没有接收完毕,需要再次接收
NGX_HTTP_PARSE_INVALID_HEADER解析失败,结束htt request

根据上表,我们来看一下具体处理内容:

        if (rc == NGX_OK)
        {//返回 NGX_OK 处理解析后数据

            r->request_length += r->header_in->pos - r->header_name_start;

            if (r->invalid_header && cscf->ignore_invalid_headers)
            {

                /* there was error while a header line parsing */

                ngx_log_error(NGX_LOG_INFO, c->log, 0,
                              "client sent invalid header line: \"%*s\"",
                              r->header_end - r->header_name_start,
                              r->header_name_start);
                continue;
            }

            /* a header line has been parsed successfully */

            h = ngx_list_push(&r->headers_in.headers);
            if (h == NULL)
            {
                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                return;
            }

            h->hash = r->header_hash;

            h->key.len = r->header_name_end - r->header_name_start;
            h->key.data = r->header_name_start;
            h->key.data[h->key.len] = '\0';

            h->value.len = r->header_end - r->header_start;
            h->value.data = r->header_start;
            h->value.data[h->value.len] = '\0';

            h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);
            if (h->lowcase_key == NULL)
            {
                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
                return;
            }

            if (h->key.len == r->lowcase_index)
            {
                ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);
            }
            else
            {
                ngx_strlow(h->lowcase_key, h->key.data, h->key.len);
            }

            hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,
                               h->lowcase_key, h->key.len);

            if (hh && hh->handler(r, h, hh->offset) != NGX_OK)
            {
                return;
            }

            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "http header: \"%V: %V\"",
                           &h->key, &h->value);

            continue;
        }
        if (rc == NGX_HTTP_PARSE_HEADER_DONE)
        {

            /**
             * a whole header has been parsed successfully 
             * 所有header行都解析成功 准备处理该http请求
             * 1、没有body则对该http请求进行后续处理
             * 2、如果有body则接收body
             */

            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "http header done");

            r->request_length += r->header_in->pos - r->header_name_start;

            r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;
            //http header解析完毕 进行处理request header
            rc = ngx_http_process_request_header(r);

            if (rc != NGX_OK)
            {
                return;
            }
            /* 处理http请求 */
            ngx_http_process_request(r);

            return;
        }

 这段代码比较重要,该代码段主要处理request header请求以及处理http request。

        if (rc == NGX_AGAIN)
        {

            /* a header line parsing is still not complete */

            continue;
        }

        /* rc == NGX_HTTP_PARSE_INVALID_HEADER */

        ngx_log_error(NGX_LOG_INFO, c->log, 0,
                      "client sent invalid header line");

        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
        return;
}

 这段代码针对NGX_AGAIN和NGX_HTTP_PARSE_INVALID_HEADER处理流程,比较简单。

二、ngx_http_process_request_header函数

该函数处理相对简单,主要针对header中参数合法性校验以及request对象相关成员进行再次赋值,例如:content_length_n、keep_alive等

ngx_int_t
ngx_http_process_request_header(ngx_http_request_t *r)
{
    if (r->headers_in.server.len == 0 && ngx_http_set_virtual_server(r, &r->headers_in.server) == NGX_ERROR)
    {
        return NGX_ERROR;
    }

    if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10)
    {
        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                      "client sent HTTP/1.1 request without \"Host\" header");
        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
        return NGX_ERROR;
    }

    if (r->headers_in.content_length)
    {//如果body不空 则需要设置body长度
        r->headers_in.content_length_n =
            ngx_atoof(r->headers_in.content_length->value.data,
                      r->headers_in.content_length->value.len);

        if (r->headers_in.content_length_n == NGX_ERROR)
        {
            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                          "client sent invalid \"Content-Length\" header");
            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
            return NGX_ERROR;
        }
    }

    if (r->method == NGX_HTTP_TRACE)
    {
        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                      "client sent TRACE method");
        ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);
        return NGX_ERROR;
    }

    if (r->headers_in.transfer_encoding)
    {
        if (r->headers_in.transfer_encoding->value.len == 7 && ngx_strncasecmp(r->headers_in.transfer_encoding->value.data,
                                                                               (u_char *)"chunked", 7) == 0)
        {
            r->headers_in.content_length = NULL;
            r->headers_in.content_length_n = -1;
            r->headers_in.chunked = 1;
        }
        else if (r->headers_in.transfer_encoding->value.len != 8 || ngx_strncasecmp(r->headers_in.transfer_encoding->value.data,
                                                                                    (u_char *)"identity", 8) != 0)
        {
            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
                          "client sent unknown \"Transfer-Encoding\": \"%V\"",
                          &r->headers_in.transfer_encoding->value);
            ngx_http_finalize_request(r, NGX_HTTP_NOT_IMPLEMENTED);
            return NGX_ERROR;
        }
    }

    if (r->headers_in.connection_type == NGX_HTTP_CONNECTION_KEEP_ALIVE)
    {//处理长连接 设置超时时间
        if (r->headers_in.keep_alive)
        {
            r->headers_in.keep_alive_n =
                ngx_atotm(r->headers_in.keep_alive->value.data,
                          r->headers_in.keep_alive->value.len);
        }
    }

    return NGX_OK;
}

三、ngx_http_read_request_header函数

本篇和上一篇都有该函数,该函数主要功能就是接收HTTP报文,功能也比较简单。

/**
 * 获取request header
 * @param r http请求
 */
static ssize_t
ngx_http_read_request_header(ngx_http_request_t *r)
{
    ssize_t n;
    ngx_event_t *rev;
    ngx_connection_t *c;
    ngx_http_core_srv_conf_t *cscf;

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

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

    if (n > 0)
    {/* 表示header_in中有数据 不需要从socket中获取数据 */
        return n;
    }

    if (rev->ready)
    {//获取报文
        n = c->recv(c, r->header_in->last,
                    r->header_in->end - r->header_in->last);
    }
    else
    {
        n = NGX_AGAIN;
    }

    if (n == NGX_AGAIN)
    {//加入事件驱动
        if (!rev->timer_set)
        {
            cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
            ngx_add_timer(rev, cscf->client_header_timeout);
        }

        if (ngx_handle_read_event(rev, 0) != NGX_OK)
        {
            ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
            return NGX_ERROR;
        }

        return NGX_AGAIN;
    }

    if (n == 0)
    {
        ngx_log_error(NGX_LOG_INFO, c->log, 0,
                      "client prematurely closed connection");
    }

    if (n == 0 || n == NGX_ERROR)
    {
        c->error = 1;
        c->log->action = "reading client request headers";

        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);
        return NGX_ERROR;
    }

    r->header_in->last += n; /* 获取到数据 */

    return n;
}

四、总结

本篇介绍了Nginx如何接收HTTP Header以及处理Header,下一篇介绍如何处理当前HTTP Request请求,即ngx_http_process_request。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值